feat: BLoC tests complets + sécurité production + freerasp 7.5.1 migration
## Tests BLoC (Task P2.4 Mobile) - 25 nouveaux fichiers *_bloc_test.dart + mocks générés (build_runner) - Features couvertes : authentication, admin_users, adhesions, backup, communication/messaging, contributions, dashboard, finance (approval/budget), events, explore/network, feed, logs_monitoring, notifications, onboarding, organizations (switcher/types/CRUD), profile, reports, settings, solidarity - ~380 tests, > 80% coverage BLoCs ## Sécurité Production (Task P2.2) - lib/core/security/app_integrity_service.dart (freerasp 7.5.1) - Migration API breaking changes freerasp 7.5.1 : - onRootDetected → onPrivilegedAccess - onDebuggerDetected → onDebug - onSignatureDetected → onAppIntegrity - onHookDetected → onHooks - onEmulatorDetected → onSimulator - onUntrustedInstallationSourceDetected → onUnofficialStore - onDeviceBindingDetected → onDeviceBinding - onObfuscationIssuesDetected → onObfuscationIssues - Talsec.start() split → start() + attachListener() - const AndroidConfig/IOSConfig → final (constructors call ConfigVerifier) - supportedAlternativeStores → supportedStores ## Pubspec - bloc_test: ^9.1.7 → ^10.0.0 (compat flutter_bloc ^9.0.0) - freerasp 7.5.1 ## Config - android/app/build.gradle : ajustements release - lib/core/config/environment.dart : URLs API actualisées - lib/main.dart + app_router : intégrations sécurité/BLoC ## Cleanup - Suppression docs intermédiaires (TACHES_*.md, TASK_*_COMPLETION_REPORT.md, TESTS_UNITAIRES_PROGRESS.md) - .g.dart régénérés (json_serializable) - .mocks.dart régénérés (mockito) ## Résultat - 142 fichiers, +27 596 insertions - Toutes les tâches P2 mobile complétées Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
685
test/features/events/bloc/evenements_bloc_test.dart
Normal file
685
test/features/events/bloc/evenements_bloc_test.dart
Normal file
@@ -0,0 +1,685 @@
|
||||
/// Tests unitaires pour EvenementsBloc
|
||||
library evenements_bloc_test;
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'package:unionflow_mobile_apps/features/events/bloc/evenements_bloc.dart';
|
||||
import 'package:unionflow_mobile_apps/features/events/bloc/evenements_event.dart';
|
||||
import 'package:unionflow_mobile_apps/features/events/bloc/evenements_state.dart';
|
||||
import 'package:unionflow_mobile_apps/features/events/data/models/evenement_model.dart';
|
||||
import 'package:unionflow_mobile_apps/features/events/data/repositories/evenement_repository_impl.dart';
|
||||
import 'package:unionflow_mobile_apps/features/events/domain/repositories/evenement_repository.dart';
|
||||
import 'package:unionflow_mobile_apps/features/events/domain/usecases/get_events.dart';
|
||||
import 'package:unionflow_mobile_apps/features/events/domain/usecases/get_event_by_id.dart';
|
||||
import 'package:unionflow_mobile_apps/features/events/domain/usecases/create_event.dart' as uc;
|
||||
import 'package:unionflow_mobile_apps/features/events/domain/usecases/update_event.dart' as uc;
|
||||
import 'package:unionflow_mobile_apps/features/events/domain/usecases/delete_event.dart' as uc;
|
||||
import 'package:unionflow_mobile_apps/features/events/domain/usecases/register_for_event.dart';
|
||||
import 'package:unionflow_mobile_apps/features/events/domain/usecases/cancel_registration.dart';
|
||||
import 'package:unionflow_mobile_apps/features/events/domain/usecases/get_event_participants.dart';
|
||||
|
||||
@GenerateMocks([
|
||||
GetEvents,
|
||||
GetEventById,
|
||||
uc.CreateEvent,
|
||||
uc.UpdateEvent,
|
||||
uc.DeleteEvent,
|
||||
RegisterForEvent,
|
||||
CancelRegistration,
|
||||
GetEventParticipants,
|
||||
IEvenementRepository,
|
||||
])
|
||||
import 'evenements_bloc_test.mocks.dart';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
final _now = DateTime(2025, 6, 15);
|
||||
final _later = DateTime(2025, 6, 16);
|
||||
|
||||
EvenementModel _makeEvenement({int id = 1, String titre = 'Réunion mensuelle'}) =>
|
||||
EvenementModel(
|
||||
id: id,
|
||||
titre: titre,
|
||||
dateDebut: _now,
|
||||
dateFin: _later,
|
||||
);
|
||||
|
||||
EvenementSearchResult _makeSearchResult(List<EvenementModel> items) =>
|
||||
EvenementSearchResult(
|
||||
evenements: items,
|
||||
total: items.length,
|
||||
page: 0,
|
||||
size: 20,
|
||||
totalPages: items.isEmpty ? 0 : 1,
|
||||
);
|
||||
|
||||
DioException _makeDioException(int statusCode) => DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: statusCode,
|
||||
),
|
||||
type: DioExceptionType.badResponse,
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void main() {
|
||||
late MockGetEvents mockGetEvents;
|
||||
late MockGetEventById mockGetEventById;
|
||||
late MockCreateEvent mockCreateEvent;
|
||||
late MockUpdateEvent mockUpdateEvent;
|
||||
late MockDeleteEvent mockDeleteEvent;
|
||||
late MockRegisterForEvent mockRegisterForEvent;
|
||||
late MockCancelRegistration mockCancelRegistration;
|
||||
late MockGetEventParticipants mockGetEventParticipants;
|
||||
late MockIEvenementRepository mockRepository;
|
||||
|
||||
EvenementsBloc buildBloc() => EvenementsBloc(
|
||||
mockGetEvents,
|
||||
mockGetEventById,
|
||||
mockCreateEvent,
|
||||
mockUpdateEvent,
|
||||
mockDeleteEvent,
|
||||
mockRegisterForEvent,
|
||||
mockCancelRegistration,
|
||||
mockGetEventParticipants,
|
||||
mockRepository,
|
||||
);
|
||||
|
||||
setUp(() {
|
||||
mockGetEvents = MockGetEvents();
|
||||
mockGetEventById = MockGetEventById();
|
||||
mockCreateEvent = MockCreateEvent();
|
||||
mockUpdateEvent = MockUpdateEvent();
|
||||
mockDeleteEvent = MockDeleteEvent();
|
||||
mockRegisterForEvent = MockRegisterForEvent();
|
||||
mockCancelRegistration = MockCancelRegistration();
|
||||
mockGetEventParticipants = MockGetEventParticipants();
|
||||
mockRepository = MockIEvenementRepository();
|
||||
});
|
||||
|
||||
// ---- initial state -------------------------------------------------------
|
||||
|
||||
test('initial state is EvenementsInitial', () {
|
||||
final bloc = buildBloc();
|
||||
expect(bloc.state, isA<EvenementsInitial>());
|
||||
bloc.close();
|
||||
});
|
||||
|
||||
// ---- LoadEvenements ------------------------------------------------------
|
||||
|
||||
group('LoadEvenements', () {
|
||||
final evenement = _makeEvenement();
|
||||
final searchResult = _makeSearchResult([evenement]);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Loaded] on success',
|
||||
build: () {
|
||||
when(mockGetEvents(
|
||||
page: anyNamed('page'),
|
||||
size: anyNamed('size'),
|
||||
recherche: anyNamed('recherche')))
|
||||
.thenAnswer((_) async => searchResult);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenements()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsLoaded>()
|
||||
.having((s) => s.evenements.length, 'count', 1)
|
||||
.having((s) => s.total, 'total', 1),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Refreshing, Loaded] when refresh=true and state is EvenementsLoaded',
|
||||
build: () {
|
||||
when(mockGetEvents(
|
||||
page: anyNamed('page'),
|
||||
size: anyNamed('size'),
|
||||
recherche: anyNamed('recherche')))
|
||||
.thenAnswer((_) async => _makeSearchResult([]));
|
||||
return buildBloc();
|
||||
},
|
||||
seed: () => EvenementsLoaded(
|
||||
evenements: [evenement],
|
||||
total: 1,
|
||||
totalPages: 1,
|
||||
),
|
||||
act: (b) => b.add(const LoadEvenements(refresh: true)),
|
||||
expect: () => [
|
||||
isA<EvenementsRefreshing>()
|
||||
.having((s) => s.currentEvenements.length, 'current', 1),
|
||||
isA<EvenementsLoaded>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on generic exception',
|
||||
build: () {
|
||||
when(mockGetEvents(
|
||||
page: anyNamed('page'),
|
||||
size: anyNamed('size'),
|
||||
recherche: anyNamed('recherche')))
|
||||
.thenThrow(Exception('network'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenements()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, NetworkError] on DioException (non-auth)',
|
||||
build: () {
|
||||
when(mockGetEvents(
|
||||
page: anyNamed('page'),
|
||||
size: anyNamed('size'),
|
||||
recherche: anyNamed('recherche')))
|
||||
.thenThrow(_makeDioException(500));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenements()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsNetworkError>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, EvenementsError] on 401 DioException',
|
||||
build: () {
|
||||
when(mockGetEvents(
|
||||
page: anyNamed('page'),
|
||||
size: anyNamed('size'),
|
||||
recherche: anyNamed('recherche')))
|
||||
.thenThrow(_makeDioException(401));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenements()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'uses recherche parameter',
|
||||
build: () {
|
||||
when(mockGetEvents(
|
||||
page: anyNamed('page'),
|
||||
size: anyNamed('size'),
|
||||
recherche: 'gala'))
|
||||
.thenAnswer((_) async => _makeSearchResult([evenement]));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenements(recherche: 'gala')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsLoaded>(),
|
||||
],
|
||||
verify: (_) => verify(mockGetEvents(
|
||||
page: anyNamed('page'),
|
||||
size: anyNamed('size'),
|
||||
recherche: 'gala')),
|
||||
);
|
||||
});
|
||||
|
||||
// ---- LoadEvenementById ---------------------------------------------------
|
||||
|
||||
group('LoadEvenementById', () {
|
||||
final evenement = _makeEvenement();
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, EvenementDetailLoaded] when found',
|
||||
build: () {
|
||||
when(mockGetEventById.call(any)).thenAnswer((_) async => evenement);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementById('1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementDetailLoaded>()
|
||||
.having((s) => s.evenement.id, 'id', 1),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, EvenementsError(404)] when not found (null)',
|
||||
build: () {
|
||||
when(mockGetEventById.call(any)).thenAnswer((_) async => null);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementById('missing')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>()
|
||||
.having((s) => s.code, 'code', '404'),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on exception',
|
||||
build: () {
|
||||
when(mockGetEventById.call(any)).thenThrow(Exception('server error'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementById('1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ---- CreateEvenement -----------------------------------------------------
|
||||
|
||||
group('CreateEvenement', () {
|
||||
final evenement = _makeEvenement(id: 99);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, EvenementCreated] on success',
|
||||
build: () {
|
||||
when(mockCreateEvent.call(any)).thenAnswer((_) async => evenement);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(CreateEvenement(evenement)),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementCreated>()
|
||||
.having((s) => s.evenement.id, 'id', 99),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, EvenementsValidationError] on 400 DioException',
|
||||
build: () {
|
||||
final dioEx = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 400,
|
||||
data: {
|
||||
'errors': {'titre': 'obligatoire'}
|
||||
},
|
||||
),
|
||||
type: DioExceptionType.badResponse,
|
||||
);
|
||||
when(mockCreateEvent.call(any)).thenThrow(dioEx);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(CreateEvenement(evenement)),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsValidationError>()
|
||||
.having((s) => s.code, 'code', '400'),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on generic exception',
|
||||
build: () {
|
||||
when(mockCreateEvent.call(any)).thenThrow(Exception('creation failed'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(CreateEvenement(evenement)),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ---- UpdateEvenement -----------------------------------------------------
|
||||
|
||||
group('UpdateEvenement', () {
|
||||
final evenement = _makeEvenement();
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, EvenementUpdated] on success',
|
||||
build: () {
|
||||
when(mockUpdateEvent.call(any, any)).thenAnswer((_) async => evenement);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(UpdateEvenement('1', evenement)),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementUpdated>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, ValidationError] on 400',
|
||||
build: () {
|
||||
final dioEx = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 400,
|
||||
data: {'errors': {}},
|
||||
),
|
||||
type: DioExceptionType.badResponse,
|
||||
);
|
||||
when(mockUpdateEvent.call(any, any)).thenThrow(dioEx);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(UpdateEvenement('1', evenement)),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsValidationError>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on generic exception',
|
||||
build: () {
|
||||
when(mockUpdateEvent.call(any, any))
|
||||
.thenThrow(Exception('update failed'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(UpdateEvenement('1', evenement)),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ---- DeleteEvenement -----------------------------------------------------
|
||||
|
||||
group('DeleteEvenement', () {
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, EvenementDeleted] on success',
|
||||
build: () {
|
||||
when(mockDeleteEvent.call(any)).thenAnswer((_) async => null);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const DeleteEvenement('1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementDeleted>()
|
||||
.having((s) => s.id, 'id', '1'),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on failure',
|
||||
build: () {
|
||||
when(mockDeleteEvent.call(any)).thenThrow(Exception('delete failed'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const DeleteEvenement('1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ---- LoadEvenementsAVenir ------------------------------------------------
|
||||
|
||||
group('LoadEvenementsAVenir', () {
|
||||
final result = _makeSearchResult([_makeEvenement()]);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Loaded] on success',
|
||||
build: () {
|
||||
when(mockRepository.getEvenementsAVenir(
|
||||
page: anyNamed('page'), size: anyNamed('size')))
|
||||
.thenAnswer((_) async => result);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementsAVenir()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsLoaded>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, NetworkError] on DioException',
|
||||
build: () {
|
||||
when(mockRepository.getEvenementsAVenir(
|
||||
page: anyNamed('page'), size: anyNamed('size')))
|
||||
.thenThrow(_makeDioException(503));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementsAVenir()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsNetworkError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ---- LoadEvenementsEnCours -----------------------------------------------
|
||||
|
||||
group('LoadEvenementsEnCours', () {
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Loaded] on success',
|
||||
build: () {
|
||||
when(mockRepository.getEvenementsEnCours(
|
||||
page: anyNamed('page'), size: anyNamed('size')))
|
||||
.thenAnswer((_) async => _makeSearchResult([_makeEvenement()]));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementsEnCours()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsLoaded>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on generic exception',
|
||||
build: () {
|
||||
when(mockRepository.getEvenementsEnCours(
|
||||
page: anyNamed('page'), size: anyNamed('size')))
|
||||
.thenThrow(Exception('server error'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementsEnCours()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ---- LoadEvenementsPasses ------------------------------------------------
|
||||
|
||||
group('LoadEvenementsPasses', () {
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Loaded] on success',
|
||||
build: () {
|
||||
when(mockRepository.getEvenementsPasses(
|
||||
page: anyNamed('page'), size: anyNamed('size')))
|
||||
.thenAnswer((_) async => _makeSearchResult([_makeEvenement()]));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementsPasses()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsLoaded>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on generic exception',
|
||||
build: () {
|
||||
when(mockRepository.getEvenementsPasses(
|
||||
page: anyNamed('page'), size: anyNamed('size')))
|
||||
.thenThrow(Exception('server error'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementsPasses()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ---- InscrireEvenement ---------------------------------------------------
|
||||
|
||||
group('InscrireEvenement', () {
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, EvenementInscrit] on success',
|
||||
build: () {
|
||||
when(mockRegisterForEvent.call(any)).thenAnswer((_) async => null);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const InscrireEvenement('evt-1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementInscrit>()
|
||||
.having((s) => s.evenementId, 'evenementId', 'evt-1'),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on failure',
|
||||
build: () {
|
||||
when(mockRegisterForEvent.call(any))
|
||||
.thenThrow(Exception('registration failed'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const InscrireEvenement('evt-1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, NetworkError] on non-auth DioException',
|
||||
build: () {
|
||||
when(mockRegisterForEvent.call(any))
|
||||
.thenThrow(_makeDioException(409));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const InscrireEvenement('evt-1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsNetworkError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ---- DesinscrireEvenement ------------------------------------------------
|
||||
|
||||
group('DesinscrireEvenement', () {
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, EvenementDesinscrit] on success',
|
||||
build: () {
|
||||
when(mockCancelRegistration.call(any)).thenAnswer((_) async => null);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const DesinscrireEvenement('evt-1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementDesinscrit>()
|
||||
.having((s) => s.evenementId, 'evenementId', 'evt-1'),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on failure',
|
||||
build: () {
|
||||
when(mockCancelRegistration.call(any))
|
||||
.thenThrow(Exception('cancel failed'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const DesinscrireEvenement('evt-1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ---- LoadParticipants ----------------------------------------------------
|
||||
|
||||
group('LoadParticipants', () {
|
||||
final participants = [
|
||||
{'id': 'm1', 'nom': 'Dupont', 'statut': 'CONFIRME'},
|
||||
{'id': 'm2', 'nom': 'Martin', 'statut': 'EN_ATTENTE'},
|
||||
];
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, ParticipantsLoaded] on success',
|
||||
build: () {
|
||||
when(mockGetEventParticipants.call(any))
|
||||
.thenAnswer((_) async => participants);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadParticipants('evt-1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<ParticipantsLoaded>()
|
||||
.having((s) => s.evenementId, 'evenementId', 'evt-1')
|
||||
.having((s) => s.participants.length, 'count', 2),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on failure',
|
||||
build: () {
|
||||
when(mockGetEventParticipants.call(any))
|
||||
.thenThrow(Exception('participants error'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadParticipants('evt-1')),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ---- LoadEvenementsStats -------------------------------------------------
|
||||
|
||||
group('LoadEvenementsStats', () {
|
||||
final stats = {'total': 20, 'aVenir': 5, 'enCours': 3};
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, EvenementsStatsLoaded] on success',
|
||||
build: () {
|
||||
when(mockRepository.getEvenementsStats())
|
||||
.thenAnswer((_) async => stats);
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementsStats()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsStatsLoaded>()
|
||||
.having((s) => s.stats['total'], 'total', 20),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<EvenementsBloc, EvenementsState>(
|
||||
'emits [Loading, Error] on exception',
|
||||
build: () {
|
||||
when(mockRepository.getEvenementsStats())
|
||||
.thenThrow(Exception('stats error'));
|
||||
return buildBloc();
|
||||
},
|
||||
act: (b) => b.add(const LoadEvenementsStats()),
|
||||
expect: () => [
|
||||
isA<EvenementsLoading>(),
|
||||
isA<EvenementsError>(),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user