import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:app_boilerplate/core/exceptions/firebase_exception.dart' show FirebaseServiceException; import 'package:app_boilerplate/data/firebase/firebase_service.dart'; import 'package:app_boilerplate/data/firebase/models/firebase_config.dart'; import 'package:app_boilerplate/data/local/local_storage_service.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:path/path.dart' as path; import 'firebase_service_test.mocks.dart'; @GenerateMocks([LocalStorageService]) void main() { // Initialize Flutter bindings and sqflite for testing TestWidgetsFlutterBinding.ensureInitialized(); sqfliteFfiInit(); databaseFactory = databaseFactoryFfi; late MockLocalStorageService mockLocalStorage; late Directory tempDir; setUp(() async { tempDir = await Directory.systemTemp.createTemp('firebase_test_'); mockLocalStorage = MockLocalStorageService(); }); tearDown(() async { if (await tempDir.exists()) { await tempDir.delete(recursive: true); } }); group('FirebaseService - Configuration', () { test('service is disabled when config.enabled is false', () { final config = FirebaseConfig.disabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); expect(service.isEnabled, isFalse); expect(service.isLoggedIn, isFalse); }); test('service can be enabled with config', () { final config = FirebaseConfig.enabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); // Service is not initialized yet, so isEnabled is false expect(service.isEnabled, isFalse); }); }); group('FirebaseService - Initialization', () { test('initialize does nothing when Firebase is disabled', () async { final config = FirebaseConfig.disabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); // Should not throw even though Firebase is not set up // (In real app, Firebase.initializeApp() would fail, but we're testing disabled case) expect(() => service.initialize(), returnsNormally); }); test('initialize fails gracefully when Firebase not configured', () async { // This test verifies that when Firebase is enabled but not configured, // the service handles the error gracefully // Note: In real scenarios, Firebase.initializeApp() requires actual config files final config = FirebaseConfig.enabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); // In a test environment without Firebase config, this will fail // but that's expected - Firebase requires actual project setup expect( () => service.initialize(), throwsA(isA()), ); }); }); group('FirebaseService - Authentication', () { test('loginWithEmailPassword throws when auth disabled', () async { final config = FirebaseConfig( enabled: true, authEnabled: false, ); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); expect( () => service.loginWithEmailPassword( email: 'test@example.com', password: 'password123', ), throwsA(isA()), ); }); test('loginWithEmailPassword throws when not initialized', () async { final config = FirebaseConfig.enabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); expect( () => service.loginWithEmailPassword( email: 'test@example.com', password: 'password123', ), throwsA(isA()), ); }); test('logout throws when auth disabled', () async { final config = FirebaseConfig( enabled: true, authEnabled: false, ); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); expect( () => service.logout(), throwsA(isA()), ); }); }); group('FirebaseService - Firestore Sync', () { test('syncItemsToFirestore throws when Firestore disabled', () async { final config = FirebaseConfig( enabled: true, firestoreEnabled: false, ); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); expect( () => service.syncItemsToFirestore('user1'), throwsA(isA()), ); }); test('syncItemsToFirestore throws when not initialized', () async { final config = FirebaseConfig.enabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); expect( () => service.syncItemsToFirestore('user1'), throwsA(isA()), ); }); test('syncItemsFromFirestore throws when Firestore disabled', () async { final config = FirebaseConfig( enabled: true, firestoreEnabled: false, ); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); expect( () => service.syncItemsFromFirestore('user1'), throwsA(isA()), ); }); test('syncItemsFromFirestore throws when not initialized', () async { final config = FirebaseConfig.enabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); expect( () => service.syncItemsFromFirestore('user1'), throwsA(isA()), ); }); }); group('FirebaseService - Storage', () { test('uploadFile throws when Storage disabled', () async { final config = FirebaseConfig( enabled: true, storageEnabled: false, ); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); final testFile = File(path.join(tempDir.path, 'test.txt')); await testFile.writeAsString('test content'); expect( () => service.uploadFile(testFile, 'test/path.txt'), throwsA(isA()), ); }); test('uploadFile throws when not initialized', () async { final config = FirebaseConfig.enabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); final testFile = File(path.join(tempDir.path, 'test.txt')); await testFile.writeAsString('test content'); expect( () => service.uploadFile(testFile, 'test/path.txt'), throwsA(isA()), ); }); }); group('FirebaseService - Messaging', () { test('getFcmToken returns null when messaging disabled', () async { final config = FirebaseConfig( enabled: true, messagingEnabled: false, ); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); final token = await service.getFcmToken(); expect(token, isNull); }); }); group('FirebaseService - Analytics', () { test('logEvent does nothing when Analytics disabled', () async { final config = FirebaseConfig( enabled: true, analyticsEnabled: false, ); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); // Should not throw even though Analytics is disabled await service.logEvent('test_event'); }); test('logEvent does nothing when Firebase disabled', () async { final config = FirebaseConfig.disabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); // Should not throw even though Firebase is disabled await service.logEvent('test_event'); }); }); group('FirebaseService - Offline Scenarios', () { test('service maintains offline-first behavior when disabled', () async { final config = FirebaseConfig.disabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); // Service should not interfere with local storage operations expect(service.isEnabled, isFalse); expect(service.isLoggedIn, isFalse); }); test('service can be disposed safely', () async { final config = FirebaseConfig.disabled(); final service = FirebaseService( config: config, localStorage: mockLocalStorage, ); // Should not throw await service.dispose(); }); }); }