import 'package:bloc_test/bloc_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:unionflow_mobile_apps/features/settings/presentation/bloc/system_settings_bloc.dart'; import 'package:unionflow_mobile_apps/features/settings/presentation/bloc/system_settings_event.dart'; import 'package:unionflow_mobile_apps/features/settings/presentation/bloc/system_settings_state.dart'; import 'package:unionflow_mobile_apps/features/settings/domain/usecases/get_settings.dart'; import 'package:unionflow_mobile_apps/features/settings/domain/usecases/update_settings.dart'; import 'package:unionflow_mobile_apps/features/settings/domain/usecases/get_cache_stats.dart'; import 'package:unionflow_mobile_apps/features/settings/domain/usecases/clear_cache.dart' as uc; import 'package:unionflow_mobile_apps/features/settings/domain/usecases/reset_settings.dart'; import 'package:unionflow_mobile_apps/features/settings/domain/repositories/system_config_repository.dart'; import 'package:unionflow_mobile_apps/features/settings/data/models/system_config_model.dart'; import 'package:unionflow_mobile_apps/features/settings/data/models/cache_stats_model.dart'; import 'package:unionflow_mobile_apps/features/settings/data/models/system_metrics_model.dart'; @GenerateMocks([ GetSettings, UpdateSettings, GetCacheStats, uc.ClearCache, ResetSettings, ISystemConfigRepository, ]) import 'system_settings_bloc_test.mocks.dart'; void main() { late SystemSettingsBloc bloc; late MockGetSettings mockGetSettings; late MockUpdateSettings mockUpdateSettings; late MockGetCacheStats mockGetCacheStats; late MockClearCache mockClearCache; late MockResetSettings mockResetSettings; late MockISystemConfigRepository mockRepository; // ── Fixtures ────────────────────────────────────────────────────────────── const fakeConfig = SystemConfigModel( applicationName: 'UnionFlow', version: '3.0.0', maintenanceMode: false, systemStatus: 'HEALTHY', ); const fakeCacheStats = CacheStatsModel( totalSizeBytes: 1048576, totalSizeFormatted: '1 MB', totalEntries: 250, hitRate: 0.85, ); const fakeMetrics = SystemMetricsModel( cpuUsagePercent: 30.0, memoryUsagePercent: 50.0, activeConnections: 20, ); setUp(() { mockGetSettings = MockGetSettings(); mockUpdateSettings = MockUpdateSettings(); mockGetCacheStats = MockGetCacheStats(); mockClearCache = MockClearCache(); mockResetSettings = MockResetSettings(); mockRepository = MockISystemConfigRepository(); bloc = SystemSettingsBloc( mockGetSettings, mockUpdateSettings, mockGetCacheStats, mockClearCache, mockResetSettings, mockRepository, ); }); tearDown(() => bloc.close()); // ── Initial state ───────────────────────────────────────────────────────── test('initial state is SystemSettingsInitial', () { expect(bloc.state, isA()); }); // ── LoadSystemConfig ────────────────────────────────────────────────────── group('LoadSystemConfig', () { blocTest( 'emits [Loading, SystemConfigLoaded] on success', build: () { when(mockGetSettings()).thenAnswer((_) async => fakeConfig); return bloc; }, act: (b) => b.add(LoadSystemConfig()), expect: () => [ isA(), isA().having( (s) => s.config.applicationName, 'applicationName', 'UnionFlow', ), ], verify: (_) => verify(mockGetSettings()).called(1), ); blocTest( 'emits [Loading, SystemSettingsError] on failure', build: () { when(mockGetSettings()).thenThrow(Exception('config not found')); return bloc; }, act: (b) => b.add(LoadSystemConfig()), expect: () => [ isA(), isA().having( (s) => s.error, 'error', contains('Erreur de chargement'), ), ], ); }); // ── UpdateSystemConfig ──────────────────────────────────────────────────── group('UpdateSystemConfig', () { final configData = {'maintenanceMode': true}; blocTest( 'emits [Loading, SystemConfigLoaded, SystemSettingsSuccess] on success', build: () { when(mockUpdateSettings(configData)) .thenAnswer((_) async => const SystemConfigModel(maintenanceMode: true)); return bloc; }, act: (b) => b.add(UpdateSystemConfig(configData)), expect: () => [ isA(), isA(), isA().having( (s) => s.message, 'message', 'Configuration mise à jour', ), ], ); blocTest( 'emits [Loading, SystemSettingsError] on update failure', build: () { when(mockUpdateSettings(any)).thenThrow(Exception('update error')); return bloc; }, act: (b) => b.add(UpdateSystemConfig({})), expect: () => [isA(), isA()], ); }); // ── LoadCacheStats ──────────────────────────────────────────────────────── group('LoadCacheStats', () { blocTest( 'emits [Loading, CacheStatsLoaded] on success', build: () { when(mockGetCacheStats()).thenAnswer((_) async => fakeCacheStats); return bloc; }, act: (b) => b.add(LoadCacheStats()), expect: () => [ isA(), isA().having( (s) => s.stats.hitRate, 'hitRate', 0.85, ), ], ); blocTest( 'emits [Loading, SystemSettingsError] on cache stats failure', build: () { when(mockGetCacheStats()).thenThrow(Exception('cache error')); return bloc; }, act: (b) => b.add(LoadCacheStats()), expect: () => [isA(), isA()], ); }); // ── LoadSystemMetrics ───────────────────────────────────────────────────── group('LoadSystemMetrics', () { blocTest( 'emits [Loading, SystemMetricsLoaded] on success', build: () { when(mockRepository.getMetrics()).thenAnswer((_) async => fakeMetrics); return bloc; }, act: (b) => b.add(LoadSystemMetrics()), expect: () => [ isA(), isA().having( (s) => s.metrics.cpuUsagePercent, 'cpuUsagePercent', 30.0, ), ], ); blocTest( 'emits [Loading, SystemSettingsError] on metrics failure', build: () { when(mockRepository.getMetrics()).thenThrow(Exception('metrics error')); return bloc; }, act: (b) => b.add(LoadSystemMetrics()), expect: () => [isA(), isA()], ); }); // ── ClearCache ──────────────────────────────────────────────────────────── group('ClearCache', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockClearCache()).thenAnswer((_) async {}); return bloc; }, act: (b) => b.add(ClearCache()), expect: () => [ isA(), isA().having( (s) => s.message, 'message', 'Cache vidé avec succès', ), ], ); blocTest( 'emits [Loading, SystemSettingsError] on clear cache failure', build: () { when(mockClearCache()).thenThrow(Exception('clear failed')); return bloc; }, act: (b) => b.add(ClearCache()), expect: () => [isA(), isA()], ); }); // ── TestDatabaseConnection ──────────────────────────────────────────────── group('TestDatabaseConnection', () { blocTest( 'emits [Loading, SystemSettingsSuccess] when testDatabase returns success=true', build: () { when(mockRepository.testDatabase()).thenAnswer( (_) async => {'success': true, 'message': 'Connexion OK'}, ); return bloc; }, act: (b) => b.add(TestDatabaseConnection()), expect: () => [ isA(), isA().having( (s) => s.message, 'message', 'Connexion OK', ), ], ); blocTest( 'emits [Loading, SystemSettingsError] when testDatabase returns success=false', build: () { when(mockRepository.testDatabase()).thenAnswer( (_) async => {'success': false, 'message': 'Connection refused'}, ); return bloc; }, act: (b) => b.add(TestDatabaseConnection()), expect: () => [ isA(), isA().having( (s) => s.error, 'error', 'Connection refused', ), ], ); blocTest( 'emits [Loading, SystemSettingsError] on exception', build: () { when(mockRepository.testDatabase()).thenThrow(Exception('db error')); return bloc; }, act: (b) => b.add(TestDatabaseConnection()), expect: () => [isA(), isA()], ); }); // ── TestEmailConfiguration ──────────────────────────────────────────────── group('TestEmailConfiguration', () { blocTest( 'emits [Loading, SystemSettingsSuccess] when email test returns success=true', build: () { when(mockRepository.testEmail()).thenAnswer( (_) async => {'success': true, 'message': 'Email envoyé'}, ); return bloc; }, act: (b) => b.add(TestEmailConfiguration()), expect: () => [ isA(), isA().having( (s) => s.message, 'message', 'Email envoyé', ), ], ); blocTest( 'emits [Loading, SystemSettingsError] when email test fails', build: () { when(mockRepository.testEmail()).thenThrow(Exception('smtp error')); return bloc; }, act: (b) => b.add(TestEmailConfiguration()), expect: () => [isA(), isA()], ); }); // ── ResetSystemConfig ───────────────────────────────────────────────────── group('ResetSystemConfig', () { blocTest( 'emits [Loading, SystemConfigLoaded, SystemSettingsSuccess] on reset success', build: () { when(mockResetSettings()).thenAnswer((_) async => fakeConfig); return bloc; }, act: (b) => b.add(ResetSystemConfig()), expect: () => [ isA(), isA(), isA().having( (s) => s.message, 'message', 'Configuration réinitialisée', ), ], ); blocTest( 'emits [Loading, SystemSettingsError] on reset failure', build: () { when(mockResetSettings()).thenThrow(Exception('reset error')); return bloc; }, act: (b) => b.add(ResetSystemConfig()), expect: () => [isA(), isA()], ); }); // ── Repository-delegated actions ────────────────────────────────────────── group('OptimizeDatabase', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.optimizeDatabase()) .thenAnswer((_) async => {'message': 'Base de données optimisée'}); return bloc; }, act: (b) => b.add(OptimizeDatabase()), expect: () => [ isA(), isA().having( (s) => s.message, 'message', 'Base de données optimisée', ), ], ); blocTest( 'emits [Loading, SystemSettingsError] on failure', build: () { when(mockRepository.optimizeDatabase()).thenThrow(Exception('optimize error')); return bloc; }, act: (b) => b.add(OptimizeDatabase()), expect: () => [isA(), isA()], ); }); group('ForceGlobalLogout', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.forceGlobalLogout()) .thenAnswer((_) async => {'message': 'Déconnexion globale déclenchée'}); return bloc; }, act: (b) => b.add(ForceGlobalLogout()), expect: () => [ isA(), isA(), ], ); }); group('CleanupSessions', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.cleanupSessions()) .thenAnswer((_) async => {'message': 'Sessions nettoyées'}); return bloc; }, act: (b) => b.add(CleanupSessions()), expect: () => [isA(), isA()], ); }); group('CleanOldLogs', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.cleanOldLogs()) .thenAnswer((_) async => {'message': 'Logs nettoyés'}); return bloc; }, act: (b) => b.add(CleanOldLogs()), expect: () => [isA(), isA()], ); }); group('ExportLogs', () { blocTest( 'emits [Loading, SystemSettingsSuccess] with count message', build: () { when(mockRepository.exportLogs()) .thenAnswer((_) async => {'count': 42}); return bloc; }, act: (b) => b.add(ExportLogs()), expect: () => [ isA(), isA().having( (s) => s.message, 'message', '42 log(s) exporté(s)', ), ], ); }); group('PurgeExpiredData', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.purgeExpiredData()) .thenAnswer((_) async => {'message': 'Données purgées'}); return bloc; }, act: (b) => b.add(PurgeExpiredData()), expect: () => [isA(), isA()], ); }); group('ScheduleMaintenance', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.scheduleMaintenance( scheduledAt: anyNamed('scheduledAt'), reason: anyNamed('reason'), )).thenAnswer((_) async => {'message': 'Maintenance planifiée'}); return bloc; }, act: (b) => b.add(const ScheduleMaintenance( scheduledAt: '2026-05-01T02:00:00', reason: 'Mise à jour', )), expect: () => [ isA(), isA().having( (s) => s.message, 'message', 'Maintenance planifiée', ), ], ); }); group('CreateBackup (settings)', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.createBackup()) .thenAnswer((_) async => {'message': 'Sauvegarde créée'}); return bloc; }, act: (b) => b.add(CreateBackup()), expect: () => [isA(), isA()], ); }); group('GenerateAuditReport', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.generateAuditReport()) .thenAnswer((_) async => {'message': "Rapport d'audit généré"}); return bloc; }, act: (b) => b.add(GenerateAuditReport()), expect: () => [isA(), isA()], ); }); group('GenerateUsageReport', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.generateUsageReport()) .thenAnswer((_) async => {'message': 'Rapport généré'}); return bloc; }, act: (b) => b.add(GenerateUsageReport()), expect: () => [isA(), isA()], ); }); group('ExportGDPRData', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.exportGDPRData()) .thenAnswer((_) async => {'message': 'Export RGPD initié'}); return bloc; }, act: (b) => b.add(ExportGDPRData()), expect: () => [isA(), isA()], ); }); group('CheckUpdates', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.checkUpdates()) .thenAnswer((_) async => {'message': 'Vérification terminée'}); return bloc; }, act: (b) => b.add(CheckUpdates()), expect: () => [isA(), isA()], ); }); group('EmergencyMaintenance', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.emergencyMaintenance()) .thenAnswer((_) async => {'message': "Maintenance d'urgence activée"}); return bloc; }, act: (b) => b.add(EmergencyMaintenance()), expect: () => [isA(), isA()], ); }); group('AnalyzePerformance', () { blocTest( 'emits [Loading, SystemSettingsSuccess] on success', build: () { when(mockRepository.analyzePerformance()) .thenAnswer((_) async => {'message': 'Analyse terminée'}); return bloc; }, act: (b) => b.add(AnalyzePerformance()), expect: () => [isA(), isA()], ); }); }