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/backup/presentation/bloc/backup_bloc.dart'; import 'package:unionflow_mobile_apps/features/backup/data/repositories/backup_repository.dart'; import 'package:unionflow_mobile_apps/features/backup/data/models/backup_model.dart'; import 'package:unionflow_mobile_apps/features/backup/data/models/backup_config_model.dart'; @GenerateMocks([BackupRepository]) import 'backup_bloc_test.mocks.dart'; void main() { late BackupBloc bloc; late MockBackupRepository mockRepository; // ── Fixtures ────────────────────────────────────────────────────────────── BackupModel fakeBackup({String id = 'bk-1', String name = 'Test'}) => BackupModel( id: id, name: name, type: 'MANUAL', status: 'COMPLETED', sizeBytes: 1024, sizeFormatted: '1 KB', ); BackupConfigModel fakeConfig() => const BackupConfigModel( autoBackupEnabled: true, frequency: 'DAILY', retentionDays: 30, totalBackups: 5, ); setUp(() { mockRepository = MockBackupRepository(); bloc = BackupBloc(mockRepository); }); tearDown(() => bloc.close()); // ── Initial state ───────────────────────────────────────────────────────── test('initial state is BackupInitial', () { expect(bloc.state, isA()); }); // ── LoadBackups ─────────────────────────────────────────────────────────── group('LoadBackups', () { blocTest( 'emits [BackupLoading, BackupsLoaded] on success', build: () { when(mockRepository.getAll()) .thenAnswer((_) async => [fakeBackup()]); return bloc; }, act: (b) => b.add(LoadBackups()), expect: () => [ isA(), isA().having( (s) => s.backups.length, 'backups.length', 1, ), ], verify: (_) => verify(mockRepository.getAll()).called(1), ); blocTest( 'emits [BackupLoading, BackupError] on failure', build: () { when(mockRepository.getAll()).thenThrow(Exception('network error')); return bloc; }, act: (b) => b.add(LoadBackups()), expect: () => [ isA(), isA().having( (s) => s.error, 'error', contains('Erreur'), ), ], ); blocTest( 'emits [BackupLoading, BackupsLoaded] with empty list', build: () { when(mockRepository.getAll()).thenAnswer((_) async => []); return bloc; }, act: (b) => b.add(LoadBackups()), expect: () => [ isA(), isA().having((s) => s.backups, 'backups', isEmpty), ], ); }); // ── CreateBackup ────────────────────────────────────────────────────────── group('CreateBackup', () { blocTest( 'emits [BackupLoading, BackupsLoaded, BackupSuccess] on success', build: () { when(mockRepository.create('Ma sauvegarde', description: anyNamed('description'))) .thenAnswer((_) async => fakeBackup()); when(mockRepository.getAll()) .thenAnswer((_) async => [fakeBackup()]); return bloc; }, act: (b) => b.add(CreateBackup('Ma sauvegarde', description: 'desc')), expect: () => [ isA(), isA(), isA().having( (s) => s.message, 'message', 'Sauvegarde créée', ), ], verify: (_) { verify(mockRepository.create('Ma sauvegarde', description: 'desc')).called(1); verify(mockRepository.getAll()).called(1); }, ); blocTest( 'emits [BackupLoading, BackupError] on create failure', build: () { when(mockRepository.create(any, description: anyNamed('description'))) .thenThrow(Exception('Server error')); return bloc; }, act: (b) => b.add(CreateBackup('Test')), expect: () => [ isA(), isA(), ], ); }); // ── RestoreBackup ───────────────────────────────────────────────────────── group('RestoreBackup', () { blocTest( 'emits [BackupLoading, BackupSuccess] on restore success', build: () { when(mockRepository.restore('bk-1')).thenAnswer((_) async {}); return bloc; }, act: (b) => b.add(RestoreBackup('bk-1')), expect: () => [ isA(), isA().having( (s) => s.message, 'message', 'Restauration en cours', ), ], verify: (_) => verify(mockRepository.restore('bk-1')).called(1), ); blocTest( 'emits [BackupLoading, BackupError] on restore failure', build: () { when(mockRepository.restore(any)).thenThrow(Exception('restore failed')); return bloc; }, act: (b) => b.add(RestoreBackup('bk-1')), expect: () => [isA(), isA()], ); }); // ── DeleteBackup ────────────────────────────────────────────────────────── group('DeleteBackup', () { blocTest( 'emits [BackupLoading, BackupsLoaded, BackupSuccess] on delete success', build: () { when(mockRepository.delete('bk-1')).thenAnswer((_) async {}); when(mockRepository.getAll()).thenAnswer((_) async => []); return bloc; }, act: (b) => b.add(DeleteBackup('bk-1')), expect: () => [ isA(), isA(), isA().having( (s) => s.message, 'message', 'Sauvegarde supprimée', ), ], ); blocTest( 'emits [BackupLoading, BackupError] on delete failure', build: () { when(mockRepository.delete(any)).thenThrow(Exception('delete failed')); return bloc; }, act: (b) => b.add(DeleteBackup('bk-1')), expect: () => [isA(), isA()], ); }); // ── LoadBackupConfig ────────────────────────────────────────────────────── group('LoadBackupConfig', () { blocTest( 'emits [BackupLoading, BackupConfigLoaded] on success', build: () { when(mockRepository.getConfig()).thenAnswer((_) async => fakeConfig()); return bloc; }, act: (b) => b.add(LoadBackupConfig()), expect: () => [ isA(), isA().having( (s) => s.config.autoBackupEnabled, 'autoBackupEnabled', true, ), ], ); blocTest( 'emits [BackupLoading, BackupError] on config load failure', build: () { when(mockRepository.getConfig()).thenThrow(Exception('config error')); return bloc; }, act: (b) => b.add(LoadBackupConfig()), expect: () => [isA(), isA()], ); }); // ── UpdateBackupConfig ──────────────────────────────────────────────────── group('UpdateBackupConfig', () { final configMap = {'autoBackupEnabled': false, 'frequency': 'WEEKLY'}; blocTest( 'emits [BackupLoading, BackupConfigLoaded, BackupSuccess] on success', build: () { when(mockRepository.updateConfig(configMap)) .thenAnswer((_) async => const BackupConfigModel(autoBackupEnabled: false, frequency: 'WEEKLY')); return bloc; }, act: (b) => b.add(UpdateBackupConfig(configMap)), expect: () => [ isA(), isA(), isA().having( (s) => s.message, 'message', 'Configuration mise à jour', ), ], ); blocTest( 'emits [BackupLoading, BackupError] on update failure', build: () { when(mockRepository.updateConfig(any)).thenThrow(Exception('update error')); return bloc; }, act: (b) => b.add(UpdateBackupConfig({})), expect: () => [isA(), isA()], ); }); }