feat: WebSocket temps réel + Finance Workflow + corrections

- Task #6: WebSocket /ws/dashboard + Kafka events (5 topics)
  * Backend: KafkaEventProducer, KafkaEventConsumer
  * Mobile: WebSocketService (reconnection, heartbeat, typed events)
  * DashboardBloc: Auto-refresh depuis WebSocket events

- Finance Workflow: approbations + budgets (backend + mobile)
  * Backend: entities, services, resources, migrations Flyway V6
  * Mobile: features finance_workflow complète avec BLoC

- Corrections DI: interfaces IRepository partout
  * IProfileRepository, IOrganizationRepository, IMembreRepository
  * GetIt configuré avec @injectable

- Spec-Kit: constitution + templates mis à jour
  * .specify/memory/constitution.md enrichie
  * Templates agent, plan, spec, tasks, checklist

- Nettoyage: fichiers temporaires supprimés

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 02:12:17 +00:00
parent bbc409de9d
commit e8ad874015
635 changed files with 58160 additions and 20674 deletions

View File

@@ -0,0 +1,65 @@
/// Tests unitaires pour CancelRegistration use case
library cancel_registration_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/features/events/domain/repositories/evenement_repository.dart';
import 'package:unionflow_mobile_apps/features/events/domain/usecases/cancel_registration.dart';
@GenerateMocks([IEvenementRepository])
import 'cancel_registration_test.mocks.dart';
void main() {
late CancelRegistration useCase;
late MockIEvenementRepository mockRepository;
setUp(() {
mockRepository = MockIEvenementRepository();
useCase = CancelRegistration(mockRepository);
});
group('CancelRegistration Use Case', () {
const tEventId = 'event123';
test('should cancel registration successfully', () async {
// Arrange
when(mockRepository.desinscrireEvenement(tEventId))
.thenAnswer((_) async => Future.value());
// Act
await useCase(tEventId);
// Assert
verify(mockRepository.desinscrireEvenement(tEventId));
verifyNoMoreInteractions(mockRepository);
});
test('should throw exception when event not found', () async {
// Arrange
when(mockRepository.desinscrireEvenement(any))
.thenThrow(Exception('Événement non trouvé'));
// Act & Assert
expect(() => useCase('nonexistent'), throwsA(isA<Exception>()));
});
test('should throw exception when not registered', () async {
// Arrange
when(mockRepository.desinscrireEvenement(any))
.thenThrow(Exception('Vous n\'êtes pas inscrit à cet événement'));
// Act & Assert
expect(() => useCase(tEventId), throwsA(isA<Exception>()));
});
test('should throw exception when cancellation fails', () async {
// Arrange
when(mockRepository.desinscrireEvenement(any))
.thenThrow(Exception('Erreur de désinscription'));
// Act & Assert
expect(() => useCase(tEventId), throwsException);
});
});
}

View File

@@ -0,0 +1,118 @@
/// Tests unitaires pour CreateEvent use case
library create_event_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/features/events/domain/repositories/evenement_repository.dart';
import 'package:unionflow_mobile_apps/features/events/domain/usecases/create_event.dart';
import 'package:unionflow_mobile_apps/features/events/data/models/evenement_model.dart';
@GenerateMocks([IEvenementRepository])
import 'create_event_test.mocks.dart';
void main() {
late CreateEvent useCase;
late MockIEvenementRepository mockRepository;
setUp(() {
mockRepository = MockIEvenementRepository();
useCase = CreateEvent(mockRepository);
});
group('CreateEvent Use Case', () {
final tNewEvent = EvenementModel(
titre: 'Nouvelle Réunion',
description: 'Réunion mensuelle du comité',
dateDebut: DateTime(2025, 1, 15, 10, 0),
dateFin: DateTime(2025, 1, 15, 12, 0),
lieu: 'Salle de réunion',
type: TypeEvenement.reunion,
statut: StatutEvenement.planifie,
);
final tCreatedEvent = EvenementModel(
id: 456,
titre: 'Nouvelle Réunion',
description: 'Réunion mensuelle du comité',
dateDebut: DateTime(2025, 1, 15, 10, 0),
dateFin: DateTime(2025, 1, 15, 12, 0),
lieu: 'Salle de réunion',
type: TypeEvenement.reunion,
statut: StatutEvenement.planifie,
);
test('should create event successfully', () async {
// Arrange
when(mockRepository.createEvenement(tNewEvent))
.thenAnswer((_) async => tCreatedEvent);
// Act
final result = await useCase(tNewEvent);
// Assert
expect(result, equals(tCreatedEvent));
expect(result.id, isNotNull);
expect(result.id, equals(456));
expect(result.titre, equals('Nouvelle Réunion'));
verify(mockRepository.createEvenement(tNewEvent));
verifyNoMoreInteractions(mockRepository);
});
test('should create public event with registration', () async {
// Arrange
final publicEvent = EvenementModel(
titre: 'Conférence Publique',
dateDebut: DateTime(2025, 2, 1, 14, 0),
dateFin: DateTime(2025, 2, 1, 17, 0),
type: TypeEvenement.conference,
statut: StatutEvenement.planifie,
estPublic: true,
inscriptionRequise: true,
maxParticipants: 200,
);
final createdPublic = publicEvent.copyWith(id: 789);
when(mockRepository.createEvenement(publicEvent))
.thenAnswer((_) async => createdPublic);
// Act
final result = await useCase(publicEvent);
// Assert
expect(result.estPublic, isTrue);
expect(result.inscriptionRequise, isTrue);
expect(result.maxParticipants, equals(200));
});
test('should create event with cost', () async {
// Arrange
final paidEvent = EvenementModel(
titre: 'Séminaire Payant',
dateDebut: DateTime(2025, 3, 1, 9, 0),
dateFin: DateTime(2025, 3, 1, 18, 0),
type: TypeEvenement.seminaire,
statut: StatutEvenement.planifie,
cout: 50000.0,
devise: 'XOF',
);
when(mockRepository.createEvenement(any))
.thenAnswer((_) async => paidEvent.copyWith(id: 999));
// Act
final result = await useCase(paidEvent);
// Assert
expect(result.cout, equals(50000.0));
expect(result.devise, equals('XOF'));
});
test('should throw exception when creation fails', () async {
// Arrange
when(mockRepository.createEvenement(any))
.thenThrow(Exception('Validation error'));
// Act & Assert
expect(() => useCase(tNewEvent), throwsException);
});
});
}

View File

@@ -0,0 +1,66 @@
/// Tests unitaires pour DeleteEvent use case
library delete_event_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/features/events/domain/repositories/evenement_repository.dart';
import 'package:unionflow_mobile_apps/features/events/domain/usecases/delete_event.dart';
@GenerateMocks([IEvenementRepository])
import 'delete_event_test.mocks.dart';
void main() {
late DeleteEvent useCase;
late MockIEvenementRepository mockRepository;
setUp(() {
mockRepository = MockIEvenementRepository();
useCase = DeleteEvent(mockRepository);
});
group('DeleteEvent Use Case', () {
const tEventId = 'event123';
test('should delete event successfully', () async {
// Arrange
when(mockRepository.deleteEvenement(tEventId))
.thenAnswer((_) async => Future.value());
// Act
await useCase(tEventId);
// Assert
verify(mockRepository.deleteEvenement(tEventId));
verifyNoMoreInteractions(mockRepository);
});
test('should throw exception when event not found', () async {
// Arrange
when(mockRepository.deleteEvenement(any))
.thenThrow(Exception('Événement non trouvé'));
// Act & Assert
expect(() => useCase('nonexistent'), throwsA(isA<Exception>()));
verify(mockRepository.deleteEvenement('nonexistent'));
});
test('should throw exception when event has participants', () async {
// Arrange
when(mockRepository.deleteEvenement(any))
.thenThrow(Exception('Impossible de supprimer un événement avec des participants'));
// Act & Assert
expect(() => useCase(tEventId), throwsA(isA<Exception>()));
});
test('should throw exception when deletion fails', () async {
// Arrange
when(mockRepository.deleteEvenement(any))
.thenThrow(Exception('Erreur de suppression'));
// Act & Assert
expect(() => useCase(tEventId), throwsException);
});
});
}

View File

@@ -0,0 +1,95 @@
/// Tests unitaires pour GetEventById use case
library get_event_by_id_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/features/events/domain/repositories/evenement_repository.dart';
import 'package:unionflow_mobile_apps/features/events/domain/usecases/get_event_by_id.dart';
import 'package:unionflow_mobile_apps/features/events/data/models/evenement_model.dart';
@GenerateMocks([IEvenementRepository])
import 'get_event_by_id_test.mocks.dart';
void main() {
late GetEventById useCase;
late MockIEvenementRepository mockRepository;
setUp(() {
mockRepository = MockIEvenementRepository();
useCase = GetEventById(mockRepository);
});
group('GetEventById Use Case', () {
const tEventId = 'event123';
final tEvent = EvenementModel(
id: 123,
titre: 'Assemblée Générale 2024',
description: 'Assemblée générale annuelle de l\'organisation',
dateDebut: DateTime(2024, 12, 15, 14, 0),
dateFin: DateTime(2024, 12, 15, 18, 0),
lieu: 'Salle des Congrès',
adresse: '123 Rue de la République',
ville: 'Dakar',
type: TypeEvenement.assembleeGenerale,
statut: StatutEvenement.confirme,
priorite: PrioriteEvenement.haute,
participantsActuels: 45,
maxParticipants: 100,
estPublic: true,
inscriptionRequise: true,
);
test('should return event by id', () async {
// Arrange
when(mockRepository.getEvenementById(tEventId))
.thenAnswer((_) async => tEvent);
// Act
final result = await useCase(tEventId);
// Assert
expect(result, equals(tEvent));
expect(result?.id, equals(123));
expect(result?.titre, equals('Assemblée Générale 2024'));
verify(mockRepository.getEvenementById(tEventId));
verifyNoMoreInteractions(mockRepository);
});
test('should return event with all details populated', () async {
// Arrange
when(mockRepository.getEvenementById(tEventId))
.thenAnswer((_) async => tEvent);
// Act
final result = await useCase(tEventId);
// Assert
expect(result?.lieu, isNotNull);
expect(result?.adresse, isNotNull);
expect(result?.participantsActuels, equals(45));
expect(result?.maxParticipants, equals(100));
});
test('should return null when event not found', () async {
// Arrange
when(mockRepository.getEvenementById(any))
.thenAnswer((_) async => null);
// Act
final result = await useCase('nonexistent');
// Assert
expect(result, isNull);
});
test('should throw exception when repository fails', () async {
// Arrange
when(mockRepository.getEvenementById(any))
.thenThrow(Exception('Database error'));
// Act & Assert
expect(() => useCase(tEventId), throwsException);
});
});
}

View File

@@ -0,0 +1,102 @@
/// Tests unitaires pour GetEventParticipants use case
library get_event_participants_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/features/events/domain/repositories/evenement_repository.dart';
import 'package:unionflow_mobile_apps/features/events/domain/usecases/get_event_participants.dart';
@GenerateMocks([IEvenementRepository])
import 'get_event_participants_test.mocks.dart';
void main() {
late GetEventParticipants useCase;
late MockIEvenementRepository mockRepository;
setUp(() {
mockRepository = MockIEvenementRepository();
useCase = GetEventParticipants(mockRepository);
});
group('GetEventParticipants Use Case', () {
const tEventId = 'event123';
final tParticipantsList = [
{
'id': 'membre1',
'nom': 'Dupont',
'prenom': 'Jean',
'email': 'jean.dupont@example.com',
'dateInscription': '2024-11-01T10:00:00Z',
'statut': 'CONFIRME',
},
{
'id': 'membre2',
'nom': 'Martin',
'prenom': 'Marie',
'email': 'marie.martin@example.com',
'dateInscription': '2024-11-02T14:00:00Z',
'statut': 'EN_ATTENTE',
},
{
'id': 'membre3',
'nom': 'Diallo',
'prenom': 'Amadou',
'email': 'amadou.diallo@example.com',
'dateInscription': '2024-11-03T09:00:00Z',
'statut': 'CONFIRME',
},
];
test('should return list of event participants', () async {
// Arrange
when(mockRepository.getParticipants(tEventId))
.thenAnswer((_) async => tParticipantsList);
// Act
final result = await useCase(tEventId);
// Assert
expect(result, equals(tParticipantsList));
expect(result.length, equals(3));
expect(result[0]['nom'], equals('Dupont'));
expect(result[0]['statut'], equals('CONFIRME'));
verify(mockRepository.getParticipants(tEventId));
verifyNoMoreInteractions(mockRepository);
});
test('should return participants with different statuses', () async {
// Arrange
when(mockRepository.getParticipants(tEventId))
.thenAnswer((_) async => tParticipantsList);
// Act
final result = await useCase(tEventId);
// Assert
expect(result.where((p) => p['statut'] == 'CONFIRME').length, equals(2));
expect(result.where((p) => p['statut'] == 'EN_ATTENTE').length, equals(1));
});
test('should return empty list when no participants', () async {
// Arrange
when(mockRepository.getParticipants(tEventId))
.thenAnswer((_) async => []);
// Act
final result = await useCase(tEventId);
// Assert
expect(result, isEmpty);
});
test('should throw exception when event not found', () async {
// Arrange
when(mockRepository.getParticipants(any))
.thenThrow(Exception('Événement non trouvé'));
// Act & Assert
expect(() => useCase('nonexistent'), throwsException);
});
});
}

View File

@@ -0,0 +1,138 @@
/// Tests unitaires pour GetEvents use case
library get_events_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.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/data/models/evenement_model.dart';
import 'package:unionflow_mobile_apps/features/events/data/repositories/evenement_repository_impl.dart';
@GenerateMocks([IEvenementRepository])
import 'get_events_test.mocks.dart';
void main() {
late GetEvents useCase;
late MockIEvenementRepository mockRepository;
setUp(() {
mockRepository = MockIEvenementRepository();
useCase = GetEvents(mockRepository);
});
group('GetEvents Use Case', () {
final tEventsList = [
EvenementModel(
id: 1,
titre: 'Assemblée Générale 2024',
description: 'Assemblée générale annuelle',
dateDebut: DateTime(2024, 12, 15, 14, 0),
dateFin: DateTime(2024, 12, 15, 18, 0),
lieu: 'Salle des Congrès',
type: TypeEvenement.assembleeGenerale,
statut: StatutEvenement.confirme,
priorite: PrioriteEvenement.haute,
participantsActuels: 45,
maxParticipants: 100,
),
EvenementModel(
id: 2,
titre: 'Formation Leadership',
description: 'Formation sur le leadership',
dateDebut: DateTime(2024, 12, 20, 9, 0),
dateFin: DateTime(2024, 12, 20, 17, 0),
lieu: 'Centre de Formation',
type: TypeEvenement.formation,
statut: StatutEvenement.planifie,
participantsActuels: 15,
maxParticipants: 30,
),
];
final tSearchResult = EvenementSearchResult(
evenements: tEventsList,
total: 2,
page: 0,
size: 20,
totalPages: 1,
);
test('should return paginated list of events', () async {
// Arrange
when(mockRepository.getEvenements(
page: anyNamed('page'),
size: anyNamed('size'),
recherche: anyNamed('recherche'),
)).thenAnswer((_) async => tSearchResult);
// Act
final result = await useCase(page: 0, size: 20);
// Assert
expect(result, equals(tSearchResult));
expect(result.evenements.length, equals(2));
expect(result.total, equals(2));
verify(mockRepository.getEvenements(page: 0, size: 20));
verifyNoMoreInteractions(mockRepository);
});
test('should filter events by search query', () async {
// Arrange
final filteredResult = EvenementSearchResult(
evenements: [tEventsList[0]],
total: 1,
page: 0,
size: 20,
totalPages: 1,
);
when(mockRepository.getEvenements(
page: 0,
size: 20,
recherche: 'Assemblée',
)).thenAnswer((_) async => filteredResult);
// Act
final result = await useCase(page: 0, size: 20, recherche: 'Assemblée');
// Assert
expect(result.evenements.length, equals(1));
expect(result.evenements.first.titre, contains('Assemblée'));
});
test('should return empty result when no events exist', () async {
// Arrange
final emptyResult = EvenementSearchResult(
evenements: [],
total: 0,
page: 0,
size: 20,
totalPages: 0,
);
when(mockRepository.getEvenements(
page: anyNamed('page'),
size: anyNamed('size'),
recherche: anyNamed('recherche'),
)).thenAnswer((_) async => emptyResult);
// Act
final result = await useCase(page: 0, size: 20);
// Assert
expect(result.evenements, isEmpty);
expect(result.total, equals(0));
});
test('should throw exception when repository fails', () async {
// Arrange
when(mockRepository.getEvenements(
page: anyNamed('page'),
size: anyNamed('size'),
recherche: anyNamed('recherche'),
)).thenThrow(Exception('Network error'));
// Act & Assert
expect(() => useCase(page: 0, size: 20), throwsException);
});
});
}

View File

@@ -0,0 +1,121 @@
/// Tests unitaires pour GetMyRegistrations use case
library get_my_registrations_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/features/events/domain/repositories/evenement_repository.dart';
import 'package:unionflow_mobile_apps/features/events/domain/usecases/get_my_registrations.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';
@GenerateMocks([IEvenementRepository])
import 'get_my_registrations_test.mocks.dart';
void main() {
late GetMyRegistrations useCase;
late MockIEvenementRepository mockRepository;
setUp(() {
mockRepository = MockIEvenementRepository();
useCase = GetMyRegistrations(mockRepository);
});
group('GetMyRegistrations Use Case', () {
final tRegisteredEvents = [
EvenementModel(
id: 1,
titre: 'Formation Leadership',
dateDebut: DateTime(2024, 12, 20, 9, 0),
dateFin: DateTime(2024, 12, 20, 17, 0),
type: TypeEvenement.formation,
statut: StatutEvenement.confirme,
inscriptionRequise: true,
participantsActuels: 15,
),
EvenementModel(
id: 2,
titre: 'Séminaire Annuel',
dateDebut: DateTime(2025, 1, 10, 14, 0),
dateFin: DateTime(2025, 1, 10, 18, 0),
type: TypeEvenement.seminaire,
statut: StatutEvenement.planifie,
inscriptionRequise: true,
participantsActuels: 30,
),
];
final tSearchResult = EvenementSearchResult(
evenements: tRegisteredEvents,
total: 2,
page: 0,
size: 20,
totalPages: 1,
);
test('should return list of registered events', () async {
// Arrange
when(mockRepository.getEvenementsAVenir(page: anyNamed('page'), size: anyNamed('size')))
.thenAnswer((_) async => tSearchResult);
// Act
final result = await useCase(page: 0, size: 20);
// Assert
expect(result, equals(tSearchResult));
expect(result.evenements.length, equals(2));
expect(result.total, equals(2));
verify(mockRepository.getEvenementsAVenir(page: 0, size: 20));
verifyNoMoreInteractions(mockRepository);
});
test('should return events with custom page size', () async {
// Arrange
final smallResult = EvenementSearchResult(
evenements: [tRegisteredEvents[0]],
total: 2,
page: 0,
size: 1,
totalPages: 2,
);
when(mockRepository.getEvenementsAVenir(page: 0, size: 1))
.thenAnswer((_) async => smallResult);
// Act
final result = await useCase(page: 0, size: 1);
// Assert
expect(result.evenements.length, equals(1));
expect(result.size, equals(1));
});
test('should return empty result when no registrations', () async {
// Arrange
final emptyResult = EvenementSearchResult(
evenements: [],
total: 0,
page: 0,
size: 20,
totalPages: 0,
);
when(mockRepository.getEvenementsAVenir(page: anyNamed('page'), size: anyNamed('size')))
.thenAnswer((_) async => emptyResult);
// Act
final result = await useCase(page: 0, size: 20);
// Assert
expect(result.evenements, isEmpty);
expect(result.total, equals(0));
});
test('should throw exception when repository fails', () async {
// Arrange
when(mockRepository.getEvenementsAVenir(page: anyNamed('page'), size: anyNamed('size')))
.thenThrow(Exception('Network error'));
// Act & Assert
expect(() => useCase(page: 0, size: 20), throwsException);
});
});
}

View File

@@ -0,0 +1,65 @@
/// Tests unitaires pour RegisterForEvent use case
library register_for_event_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/features/events/domain/repositories/evenement_repository.dart';
import 'package:unionflow_mobile_apps/features/events/domain/usecases/register_for_event.dart';
@GenerateMocks([IEvenementRepository])
import 'register_for_event_test.mocks.dart';
void main() {
late RegisterForEvent useCase;
late MockIEvenementRepository mockRepository;
setUp(() {
mockRepository = MockIEvenementRepository();
useCase = RegisterForEvent(mockRepository);
});
group('RegisterForEvent Use Case', () {
const tEventId = 'event123';
test('should register for event successfully', () async {
// Arrange
when(mockRepository.inscrireEvenement(tEventId))
.thenAnswer((_) async => Future.value());
// Act
await useCase(tEventId);
// Assert
verify(mockRepository.inscrireEvenement(tEventId));
verifyNoMoreInteractions(mockRepository);
});
test('should throw exception when event not found', () async {
// Arrange
when(mockRepository.inscrireEvenement(any))
.thenThrow(Exception('Événement non trouvé'));
// Act & Assert
expect(() => useCase('nonexistent'), throwsA(isA<Exception>()));
});
test('should throw exception when already registered', () async {
// Arrange
when(mockRepository.inscrireEvenement(any))
.thenThrow(Exception('Vous êtes déjà inscrit à cet événement'));
// Act & Assert
expect(() => useCase(tEventId), throwsA(isA<Exception>()));
});
test('should throw exception when event is full', () async {
// Arrange
when(mockRepository.inscrireEvenement(any))
.thenThrow(Exception('Événement complet'));
// Act & Assert
expect(() => useCase(tEventId), throwsException);
});
});
}

View File

@@ -0,0 +1,67 @@
/// Tests unitaires pour SubmitEventFeedback use case
library submit_event_feedback_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/features/events/domain/repositories/evenement_repository.dart';
import 'package:unionflow_mobile_apps/features/events/domain/usecases/submit_event_feedback.dart';
@GenerateMocks([IEvenementRepository])
import 'submit_event_feedback_test.mocks.dart';
void main() {
late SubmitEventFeedback useCase;
late MockIEvenementRepository mockRepository;
setUp(() {
mockRepository = MockIEvenementRepository();
useCase = SubmitEventFeedback(mockRepository);
});
group('SubmitEventFeedback Use Case', () {
const tEventId = 'event123';
const tNote = 5;
const tCommentaire = 'Excellent événement, très enrichissant!';
test('should throw UnimplementedError as endpoint not available', () async {
// Act & Assert
expect(
() => useCase(evenementId: tEventId, note: tNote),
throwsA(isA<UnimplementedError>()),
);
});
test('should throw UnimplementedError with feedback message', () async {
// Act & Assert
expect(
() => useCase(
evenementId: tEventId,
note: tNote,
commentaire: tCommentaire,
),
throwsA(isA<UnimplementedError>()),
);
});
test('should throw UnimplementedError for minimum rating', () async {
// Act & Assert
expect(
() => useCase(evenementId: tEventId, note: 1),
throwsA(isA<UnimplementedError>()),
);
});
test('should throw UnimplementedError for maximum rating', () async {
// Act & Assert
expect(
() => useCase(
evenementId: tEventId,
note: 5,
commentaire: 'Parfait!',
),
throwsA(isA<UnimplementedError>()),
);
});
});
}

View File

@@ -0,0 +1,104 @@
/// Tests unitaires pour UpdateEvent use case
library update_event_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/features/events/domain/repositories/evenement_repository.dart';
import 'package:unionflow_mobile_apps/features/events/domain/usecases/update_event.dart';
import 'package:unionflow_mobile_apps/features/events/data/models/evenement_model.dart';
@GenerateMocks([IEvenementRepository])
import 'update_event_test.mocks.dart';
void main() {
late UpdateEvent useCase;
late MockIEvenementRepository mockRepository;
setUp(() {
mockRepository = MockIEvenementRepository();
useCase = UpdateEvent(mockRepository);
});
group('UpdateEvent Use Case', () {
const tEventId = 'event123';
final tUpdatedEvent = EvenementModel(
id: 123,
titre: 'Assemblée Générale 2024 - Modifiée',
description: 'Assemblée générale annuelle (mise à jour)',
dateDebut: DateTime(2024, 12, 16, 14, 0),
dateFin: DateTime(2024, 12, 16, 18, 0),
lieu: 'Nouveau lieu',
type: TypeEvenement.assembleeGenerale,
statut: StatutEvenement.confirme,
);
test('should update event successfully', () async {
// Arrange
when(mockRepository.updateEvenement(tEventId, tUpdatedEvent))
.thenAnswer((_) async => tUpdatedEvent);
// Act
final result = await useCase(tEventId, tUpdatedEvent);
// Assert
expect(result, equals(tUpdatedEvent));
expect(result.titre, contains('Modifiée'));
expect(result.lieu, equals('Nouveau lieu'));
verify(mockRepository.updateEvenement(tEventId, tUpdatedEvent));
verifyNoMoreInteractions(mockRepository);
});
test('should update event status', () async {
// Arrange
final statusUpdate = EvenementModel(
id: 123,
titre: 'Événement',
dateDebut: DateTime(2024, 12, 15, 14, 0),
dateFin: DateTime(2024, 12, 15, 18, 0),
type: TypeEvenement.reunion,
statut: StatutEvenement.annule,
);
when(mockRepository.updateEvenement(tEventId, statusUpdate))
.thenAnswer((_) async => statusUpdate);
// Act
final result = await useCase(tEventId, statusUpdate);
// Assert
expect(result.statut, equals(StatutEvenement.annule));
});
test('should update event capacity', () async {
// Arrange
final capacityUpdate = EvenementModel(
id: 123,
titre: 'Événement',
dateDebut: DateTime(2024, 12, 15, 14, 0),
dateFin: DateTime(2024, 12, 15, 18, 0),
type: TypeEvenement.formation,
statut: StatutEvenement.planifie,
maxParticipants: 50,
participantsActuels: 25,
);
when(mockRepository.updateEvenement(tEventId, capacityUpdate))
.thenAnswer((_) async => capacityUpdate);
// Act
final result = await useCase(tEventId, capacityUpdate);
// Assert
expect(result.maxParticipants, equals(50));
expect(result.participantsActuels, equals(25));
});
test('should throw exception when update fails', () async {
// Arrange
when(mockRepository.updateEvenement(any, any))
.thenThrow(Exception('Update failed'));
// Act & Assert
expect(() => useCase(tEventId, tUpdatedEvent), throwsException);
});
});
}