refactoring

This commit is contained in:
dahoud
2026-03-31 09:14:47 +00:00
parent 9bfffeeebe
commit 5383df6dcb
200 changed files with 11192 additions and 7063 deletions

View File

@@ -0,0 +1,330 @@
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/members/bloc/membres_bloc.dart';
import 'package:unionflow_mobile_apps/features/members/bloc/membres_event.dart';
import 'package:unionflow_mobile_apps/features/members/bloc/membres_state.dart';
import 'package:unionflow_mobile_apps/features/members/domain/usecases/get_members.dart';
import 'package:unionflow_mobile_apps/features/members/domain/usecases/get_member_by_id.dart';
import 'package:unionflow_mobile_apps/features/members/domain/usecases/create_member.dart';
import 'package:unionflow_mobile_apps/features/members/domain/usecases/update_member.dart';
import 'package:unionflow_mobile_apps/features/members/domain/usecases/delete_member.dart';
import 'package:unionflow_mobile_apps/features/members/domain/usecases/search_members.dart';
import 'package:unionflow_mobile_apps/features/members/domain/usecases/get_member_stats.dart';
import 'package:unionflow_mobile_apps/features/members/domain/repositories/membre_repository.dart';
import 'package:unionflow_mobile_apps/shared/models/membre_search_result.dart';
import 'package:unionflow_mobile_apps/shared/models/membre_search_criteria.dart';
@GenerateMocks([
GetMembers,
GetMemberById,
CreateMember,
UpdateMember,
DeleteMember,
SearchMembers,
GetMemberStats,
IMembreRepository,
])
import 'membres_bloc_test.mocks.dart';
void main() {
late MembresBloc bloc;
late MockGetMembers mockGetMembers;
late MockGetMemberById mockGetMemberById;
late MockCreateMember mockCreateMember;
late MockUpdateMember mockUpdateMember;
late MockDeleteMember mockDeleteMember;
late MockSearchMembers mockSearchMembers;
late MockGetMemberStats mockGetMemberStats;
late MockIMembreRepository mockRepository;
const orgId = 'org-123';
MembreSearchResult emptyResult() => MembreSearchResult(
membres: [],
totalElements: 0,
totalPages: 0,
currentPage: 0,
pageSize: 20,
numberOfElements: 0,
hasNext: false,
hasPrevious: false,
isFirst: true,
isLast: true,
criteria: MembreSearchCriteria(),
executionTimeMs: 0,
);
setUp(() {
mockGetMembers = MockGetMembers();
mockGetMemberById = MockGetMemberById();
mockCreateMember = MockCreateMember();
mockUpdateMember = MockUpdateMember();
mockDeleteMember = MockDeleteMember();
mockSearchMembers = MockSearchMembers();
mockGetMemberStats = MockGetMemberStats();
mockRepository = MockIMembreRepository();
bloc = MembresBloc(
mockGetMembers,
mockGetMemberById,
mockCreateMember,
mockUpdateMember,
mockDeleteMember,
mockSearchMembers,
mockGetMemberStats,
mockRepository,
);
});
tearDown(() => bloc.close());
// ─────────────────────────────────────────────────────────────────────────
// SuperAdmin — accès global sans filtre organisation
// ─────────────────────────────────────────────────────────────────────────
group('LoadMembres — SuperAdmin (sans organisationId)', () {
blocTest<MembresBloc, MembresState>(
'appelle GetMembers sans filtre org et émet MembresLoaded sans organisationId',
build: () {
when(mockGetMembers(
page: anyNamed('page'),
size: anyNamed('size'),
recherche: anyNamed('recherche'),
)).thenAnswer((_) async => emptyResult());
return bloc;
},
act: (b) => b.add(const LoadMembres()),
expect: () => [
const MembresLoading(),
isA<MembresLoaded>().having(
(s) => s.organisationId,
'organisationId',
isNull,
),
],
verify: (_) {
// GetMembers doit être appelé avec les bons paramètres
final captured = verify(mockGetMembers(
page: captureAnyNamed('page'),
size: captureAnyNamed('size'),
recherche: captureAnyNamed('recherche'),
)).captured;
expect(captured[0], equals(0)); // page
expect(captured[1], equals(20)); // size
expect(captured[2], isNull); // recherche
// SearchMembers ne doit jamais être appelé
verifyNever(mockSearchMembers(
criteria: anyNamed('criteria'),
page: anyNamed('page'),
size: anyNamed('size'),
));
},
);
blocTest<MembresBloc, MembresState>(
'transmet le terme de recherche à GetMembers',
build: () {
when(mockGetMembers(
page: anyNamed('page'),
size: anyNamed('size'),
recherche: anyNamed('recherche'),
)).thenAnswer((_) async => emptyResult());
return bloc;
},
act: (b) => b.add(const LoadMembres(recherche: 'Jean')),
expect: () => [
const MembresLoading(),
isA<MembresLoaded>(),
],
verify: (_) {
final captured = verify(mockGetMembers(
page: captureAnyNamed('page'),
size: captureAnyNamed('size'),
recherche: captureAnyNamed('recherche'),
)).captured;
expect(captured[2], equals('Jean')); // recherche
verifyNever(mockSearchMembers(
criteria: anyNamed('criteria'),
page: anyNamed('page'),
size: anyNamed('size'),
));
},
);
});
// ─────────────────────────────────────────────────────────────────────────
// OrgAdmin — accès limité à son organisation
// ─────────────────────────────────────────────────────────────────────────
group('LoadMembres — OrgAdmin (avec organisationId)', () {
blocTest<MembresBloc, MembresState>(
'appelle SearchMembers avec organisationIds et émet MembresLoaded avec organisationId',
build: () {
when(mockSearchMembers(
criteria: anyNamed('criteria'),
page: anyNamed('page'),
size: anyNamed('size'),
)).thenAnswer((_) async => emptyResult());
return bloc;
},
act: (b) => b.add(const LoadMembres(organisationId: orgId)),
expect: () => [
const MembresLoading(),
isA<MembresLoaded>().having(
(s) => s.organisationId,
'organisationId',
equals(orgId),
),
],
verify: (_) {
// GetMembers ne doit JAMAIS être appelé pour un OrgAdmin
verifyNever(mockGetMembers(
page: anyNamed('page'),
size: anyNamed('size'),
recherche: anyNamed('recherche'),
));
// SearchMembers doit être appelé avec l'organisationId dans les critères
final captured = verify(mockSearchMembers(
criteria: captureAnyNamed('criteria'),
page: captureAnyNamed('page'),
size: captureAnyNamed('size'),
)).captured;
final criteria = captured[0] as MembreSearchCriteria;
expect(criteria.organisationIds, equals([orgId]));
expect(criteria.query, isNull);
expect(captured[1], equals(0)); // page
expect(captured[2], equals(20)); // size
},
);
blocTest<MembresBloc, MembresState>(
'OrgAdmin avec recherche : ajoute query aux critères',
build: () {
when(mockSearchMembers(
criteria: anyNamed('criteria'),
page: anyNamed('page'),
size: anyNamed('size'),
)).thenAnswer((_) async => emptyResult());
return bloc;
},
act: (b) => b.add(const LoadMembres(organisationId: orgId, recherche: 'Dupont')),
expect: () => [
const MembresLoading(),
isA<MembresLoaded>(),
],
verify: (_) {
final captured = verify(mockSearchMembers(
criteria: captureAnyNamed('criteria'),
page: captureAnyNamed('page'),
size: captureAnyNamed('size'),
)).captured;
final criteria = captured[0] as MembreSearchCriteria;
expect(criteria.organisationIds, equals([orgId]));
expect(criteria.query, equals('Dupont'));
},
);
blocTest<MembresBloc, MembresState>(
'OrgAdmin avec recherche vide : query non transmis',
build: () {
when(mockSearchMembers(
criteria: anyNamed('criteria'),
page: anyNamed('page'),
size: anyNamed('size'),
)).thenAnswer((_) async => emptyResult());
return bloc;
},
act: (b) => b.add(const LoadMembres(organisationId: orgId, recherche: '')),
expect: () => [const MembresLoading(), isA<MembresLoaded>()],
verify: (_) {
final captured = verify(mockSearchMembers(
criteria: captureAnyNamed('criteria'),
page: captureAnyNamed('page'),
size: captureAnyNamed('size'),
)).captured;
final criteria = captured[0] as MembreSearchCriteria;
expect(criteria.organisationIds, equals([orgId]));
// recherche vide → query null (pas transmis aux critères)
expect(criteria.query, isNull);
},
);
blocTest<MembresBloc, MembresState>(
'OrgAdmin : la pagination conserve l organisationId',
build: () {
when(mockSearchMembers(
criteria: anyNamed('criteria'),
page: anyNamed('page'),
size: anyNamed('size'),
)).thenAnswer((_) async => emptyResult());
return bloc;
},
act: (b) => b.add(const LoadMembres(organisationId: orgId, page: 1)),
expect: () => [
const MembresLoading(),
isA<MembresLoaded>().having(
(s) => s.organisationId,
'organisationId',
equals(orgId),
),
],
verify: (_) {
final captured = verify(mockSearchMembers(
criteria: captureAnyNamed('criteria'),
page: captureAnyNamed('page'),
size: captureAnyNamed('size'),
)).captured;
expect(captured[1], equals(1)); // page = 1
},
);
});
// ─────────────────────────────────────────────────────────────────────────
// MembresLoaded.copyWith
// ─────────────────────────────────────────────────────────────────────────
group('MembresLoaded.copyWith', () {
test('preserve organisationId si non fourni', () {
const state = MembresLoaded(
membres: [],
totalElements: 5,
totalPages: 1,
organisationId: orgId,
);
final copy = state.copyWith(totalElements: 10);
expect(copy.organisationId, equals(orgId));
expect(copy.totalElements, equals(10));
});
test('met à jour organisationId si fourni', () {
const state = MembresLoaded(
membres: [],
totalElements: 5,
totalPages: 1,
organisationId: orgId,
);
final copy = state.copyWith(organisationId: 'new-org');
expect(copy.organisationId, equals('new-org'));
});
test('organisationId null par défaut si non défini', () {
const state = MembresLoaded(
membres: [],
totalElements: 0,
totalPages: 0,
);
expect(state.organisationId, isNull);
final copy = state.copyWith(totalElements: 1, totalPages: 1);
expect(copy.organisationId, isNull);
});
});
}

View File

@@ -0,0 +1,497 @@
// Mocks generated by Mockito 5.4.4 from annotations
// in unionflow_mobile_apps/test/features/members/bloc/membres_bloc_test.dart.
// Do not manually edit this file.
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i5;
import 'package:mockito/mockito.dart' as _i1;
import 'package:unionflow_mobile_apps/features/members/data/models/membre_complete_model.dart'
as _i3;
import 'package:unionflow_mobile_apps/features/members/domain/repositories/membre_repository.dart'
as _i13;
import 'package:unionflow_mobile_apps/features/members/domain/usecases/create_member.dart'
as _i7;
import 'package:unionflow_mobile_apps/features/members/domain/usecases/delete_member.dart'
as _i9;
import 'package:unionflow_mobile_apps/features/members/domain/usecases/get_member_by_id.dart'
as _i6;
import 'package:unionflow_mobile_apps/features/members/domain/usecases/get_member_stats.dart'
as _i12;
import 'package:unionflow_mobile_apps/features/members/domain/usecases/get_members.dart'
as _i4;
import 'package:unionflow_mobile_apps/features/members/domain/usecases/search_members.dart'
as _i10;
import 'package:unionflow_mobile_apps/features/members/domain/usecases/update_member.dart'
as _i8;
import 'package:unionflow_mobile_apps/shared/models/membre_search_criteria.dart'
as _i11;
import 'package:unionflow_mobile_apps/shared/models/membre_search_result.dart'
as _i2;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: deprecated_member_use
// ignore_for_file: deprecated_member_use_from_same_package
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class
class _FakeMembreSearchResult_0 extends _i1.SmartFake
implements _i2.MembreSearchResult {
_FakeMembreSearchResult_0(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
class _FakeMembreCompletModel_1 extends _i1.SmartFake
implements _i3.MembreCompletModel {
_FakeMembreCompletModel_1(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
/// A class which mocks [GetMembers].
///
/// See the documentation for Mockito's code generation for more information.
class MockGetMembers extends _i1.Mock implements _i4.GetMembers {
MockGetMembers() {
_i1.throwOnMissingStub(this);
}
@override
_i5.Future<_i2.MembreSearchResult> call({
int? page = 0,
int? size = 20,
String? recherche,
}) =>
(super.noSuchMethod(
Invocation.method(
#call,
[],
{
#page: page,
#size: size,
#recherche: recherche,
},
),
returnValue:
_i5.Future<_i2.MembreSearchResult>.value(_FakeMembreSearchResult_0(
this,
Invocation.method(
#call,
[],
{
#page: page,
#size: size,
#recherche: recherche,
},
),
)),
) as _i5.Future<_i2.MembreSearchResult>);
}
/// A class which mocks [GetMemberById].
///
/// See the documentation for Mockito's code generation for more information.
class MockGetMemberById extends _i1.Mock implements _i6.GetMemberById {
MockGetMemberById() {
_i1.throwOnMissingStub(this);
}
@override
_i5.Future<_i3.MembreCompletModel?> call(String? id) => (super.noSuchMethod(
Invocation.method(
#call,
[id],
),
returnValue: _i5.Future<_i3.MembreCompletModel?>.value(),
) as _i5.Future<_i3.MembreCompletModel?>);
}
/// A class which mocks [CreateMember].
///
/// See the documentation for Mockito's code generation for more information.
class MockCreateMember extends _i1.Mock implements _i7.CreateMember {
MockCreateMember() {
_i1.throwOnMissingStub(this);
}
@override
_i5.Future<_i3.MembreCompletModel> call(_i3.MembreCompletModel? membre) =>
(super.noSuchMethod(
Invocation.method(
#call,
[membre],
),
returnValue:
_i5.Future<_i3.MembreCompletModel>.value(_FakeMembreCompletModel_1(
this,
Invocation.method(
#call,
[membre],
),
)),
) as _i5.Future<_i3.MembreCompletModel>);
}
/// A class which mocks [UpdateMember].
///
/// See the documentation for Mockito's code generation for more information.
class MockUpdateMember extends _i1.Mock implements _i8.UpdateMember {
MockUpdateMember() {
_i1.throwOnMissingStub(this);
}
@override
_i5.Future<_i3.MembreCompletModel> call(
String? id,
_i3.MembreCompletModel? membre,
) =>
(super.noSuchMethod(
Invocation.method(
#call,
[
id,
membre,
],
),
returnValue:
_i5.Future<_i3.MembreCompletModel>.value(_FakeMembreCompletModel_1(
this,
Invocation.method(
#call,
[
id,
membre,
],
),
)),
) as _i5.Future<_i3.MembreCompletModel>);
}
/// A class which mocks [DeleteMember].
///
/// See the documentation for Mockito's code generation for more information.
class MockDeleteMember extends _i1.Mock implements _i9.DeleteMember {
MockDeleteMember() {
_i1.throwOnMissingStub(this);
}
@override
_i5.Future<void> call(String? id) => (super.noSuchMethod(
Invocation.method(
#call,
[id],
),
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);
}
/// A class which mocks [SearchMembers].
///
/// See the documentation for Mockito's code generation for more information.
class MockSearchMembers extends _i1.Mock implements _i10.SearchMembers {
MockSearchMembers() {
_i1.throwOnMissingStub(this);
}
@override
_i5.Future<_i2.MembreSearchResult> call({
required _i11.MembreSearchCriteria? criteria,
int? page = 0,
int? size = 20,
}) =>
(super.noSuchMethod(
Invocation.method(
#call,
[],
{
#criteria: criteria,
#page: page,
#size: size,
},
),
returnValue:
_i5.Future<_i2.MembreSearchResult>.value(_FakeMembreSearchResult_0(
this,
Invocation.method(
#call,
[],
{
#criteria: criteria,
#page: page,
#size: size,
},
),
)),
) as _i5.Future<_i2.MembreSearchResult>);
}
/// A class which mocks [GetMemberStats].
///
/// See the documentation for Mockito's code generation for more information.
class MockGetMemberStats extends _i1.Mock implements _i12.GetMemberStats {
MockGetMemberStats() {
_i1.throwOnMissingStub(this);
}
@override
_i5.Future<Map<String, dynamic>> call() => (super.noSuchMethod(
Invocation.method(
#call,
[],
),
returnValue:
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i5.Future<Map<String, dynamic>>);
}
/// A class which mocks [IMembreRepository].
///
/// See the documentation for Mockito's code generation for more information.
class MockIMembreRepository extends _i1.Mock implements _i13.IMembreRepository {
MockIMembreRepository() {
_i1.throwOnMissingStub(this);
}
@override
_i5.Future<_i2.MembreSearchResult> getMembres({
int? page = 0,
int? size = 20,
String? recherche,
}) =>
(super.noSuchMethod(
Invocation.method(
#getMembres,
[],
{
#page: page,
#size: size,
#recherche: recherche,
},
),
returnValue:
_i5.Future<_i2.MembreSearchResult>.value(_FakeMembreSearchResult_0(
this,
Invocation.method(
#getMembres,
[],
{
#page: page,
#size: size,
#recherche: recherche,
},
),
)),
) as _i5.Future<_i2.MembreSearchResult>);
@override
_i5.Future<_i3.MembreCompletModel?> getMembreById(String? id) =>
(super.noSuchMethod(
Invocation.method(
#getMembreById,
[id],
),
returnValue: _i5.Future<_i3.MembreCompletModel?>.value(),
) as _i5.Future<_i3.MembreCompletModel?>);
@override
_i5.Future<_i3.MembreCompletModel> createMembre(
_i3.MembreCompletModel? membre) =>
(super.noSuchMethod(
Invocation.method(
#createMembre,
[membre],
),
returnValue:
_i5.Future<_i3.MembreCompletModel>.value(_FakeMembreCompletModel_1(
this,
Invocation.method(
#createMembre,
[membre],
),
)),
) as _i5.Future<_i3.MembreCompletModel>);
@override
_i5.Future<_i3.MembreCompletModel> updateMembre(
String? id,
_i3.MembreCompletModel? membre,
) =>
(super.noSuchMethod(
Invocation.method(
#updateMembre,
[
id,
membre,
],
),
returnValue:
_i5.Future<_i3.MembreCompletModel>.value(_FakeMembreCompletModel_1(
this,
Invocation.method(
#updateMembre,
[
id,
membre,
],
),
)),
) as _i5.Future<_i3.MembreCompletModel>);
@override
_i5.Future<void> deleteMembre(String? id) => (super.noSuchMethod(
Invocation.method(
#deleteMembre,
[id],
),
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);
@override
_i5.Future<_i3.MembreCompletModel> activateMembre(String? id) =>
(super.noSuchMethod(
Invocation.method(
#activateMembre,
[id],
),
returnValue:
_i5.Future<_i3.MembreCompletModel>.value(_FakeMembreCompletModel_1(
this,
Invocation.method(
#activateMembre,
[id],
),
)),
) as _i5.Future<_i3.MembreCompletModel>);
@override
_i5.Future<_i3.MembreCompletModel> deactivateMembre(String? id) =>
(super.noSuchMethod(
Invocation.method(
#deactivateMembre,
[id],
),
returnValue:
_i5.Future<_i3.MembreCompletModel>.value(_FakeMembreCompletModel_1(
this,
Invocation.method(
#deactivateMembre,
[id],
),
)),
) as _i5.Future<_i3.MembreCompletModel>);
@override
_i5.Future<_i2.MembreSearchResult> searchMembres({
required _i11.MembreSearchCriteria? criteria,
int? page = 0,
int? size = 20,
}) =>
(super.noSuchMethod(
Invocation.method(
#searchMembres,
[],
{
#criteria: criteria,
#page: page,
#size: size,
},
),
returnValue:
_i5.Future<_i2.MembreSearchResult>.value(_FakeMembreSearchResult_0(
this,
Invocation.method(
#searchMembres,
[],
{
#criteria: criteria,
#page: page,
#size: size,
},
),
)),
) as _i5.Future<_i2.MembreSearchResult>);
@override
_i5.Future<_i2.MembreSearchResult> getActiveMembers({
int? page = 0,
int? size = 20,
}) =>
(super.noSuchMethod(
Invocation.method(
#getActiveMembers,
[],
{
#page: page,
#size: size,
},
),
returnValue:
_i5.Future<_i2.MembreSearchResult>.value(_FakeMembreSearchResult_0(
this,
Invocation.method(
#getActiveMembers,
[],
{
#page: page,
#size: size,
},
),
)),
) as _i5.Future<_i2.MembreSearchResult>);
@override
_i5.Future<_i2.MembreSearchResult> getBureauMembers({
int? page = 0,
int? size = 20,
}) =>
(super.noSuchMethod(
Invocation.method(
#getBureauMembers,
[],
{
#page: page,
#size: size,
},
),
returnValue:
_i5.Future<_i2.MembreSearchResult>.value(_FakeMembreSearchResult_0(
this,
Invocation.method(
#getBureauMembers,
[],
{
#page: page,
#size: size,
},
),
)),
) as _i5.Future<_i2.MembreSearchResult>);
@override
_i5.Future<Map<String, dynamic>> getMembresStats() => (super.noSuchMethod(
Invocation.method(
#getMembresStats,
[],
),
returnValue:
_i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i5.Future<Map<String, dynamic>>);
}