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:
dahoud
2026-04-21 12:42:35 +00:00
parent 33f5b5a707
commit 37db88672b
142 changed files with 27599 additions and 16068 deletions

View File

@@ -0,0 +1,483 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/core/error/failures.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/entities/transaction_approval.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/approve_transaction.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/get_approval_by_id.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/get_pending_approvals.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/reject_transaction.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/presentation/bloc/approval_bloc.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/presentation/bloc/approval_event.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/presentation/bloc/approval_state.dart';
@GenerateMocks([
GetPendingApprovals,
GetApprovalById,
ApproveTransaction,
RejectTransaction,
])
import 'approval_bloc_test.mocks.dart';
// ---------------------------------------------------------------------------
// Fixtures
// ---------------------------------------------------------------------------
TransactionApproval _buildApproval({
String id = 'appr-1',
ApprovalStatus status = ApprovalStatus.pending,
}) =>
TransactionApproval(
id: id,
transactionId: 'tx-1',
transactionType: TransactionType.contribution,
amount: 50000,
requesterId: 'user-1',
requesterName: 'Alice Diallo',
requiredLevel: ApprovalLevel.level1,
status: status,
createdAt: DateTime(2026, 4, 1),
);
final _pendingApproval = _buildApproval();
final _approvedApproval =
_buildApproval(status: ApprovalStatus.approved);
final _rejectedApproval =
_buildApproval(status: ApprovalStatus.rejected);
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
void main() {
late MockGetPendingApprovals mockGetPendingApprovals;
late MockGetApprovalById mockGetApprovalById;
late MockApproveTransaction mockApproveTransaction;
late MockRejectTransaction mockRejectTransaction;
setUp(() {
mockGetPendingApprovals = MockGetPendingApprovals();
mockGetApprovalById = MockGetApprovalById();
mockApproveTransaction = MockApproveTransaction();
mockRejectTransaction = MockRejectTransaction();
});
ApprovalBloc buildBloc() => ApprovalBloc(
getPendingApprovals: mockGetPendingApprovals,
getApprovalById: mockGetApprovalById,
approveTransaction: mockApproveTransaction,
rejectTransaction: mockRejectTransaction,
);
// ─── initial state ───────────────────────────────────────────────────────
test('initial state is ApprovalInitial', () {
final bloc = buildBloc();
expect(bloc.state, isA<ApprovalInitial>());
bloc.close();
});
// ─── LoadPendingApprovals ────────────────────────────────────────────────
group('LoadPendingApprovals', () {
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalsLoading, ApprovalsLoaded] when approvals exist',
build: () {
when(mockGetPendingApprovals(organizationId: anyNamed('organizationId')))
.thenAnswer((_) async => Right([_pendingApproval]));
return buildBloc();
},
act: (b) =>
b.add(const LoadPendingApprovals(organizationId: 'org-1')),
expect: () => [
const ApprovalsLoading(),
ApprovalsLoaded(
approvals: [_pendingApproval],
pendingCount: 1,
),
],
verify: (_) {
verify(mockGetPendingApprovals(organizationId: 'org-1')).called(1);
},
);
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalsLoading, ApprovalsLoaded] with correct pendingCount',
build: () {
when(mockGetPendingApprovals(organizationId: anyNamed('organizationId')))
.thenAnswer((_) async => Right([
_pendingApproval,
_buildApproval(id: 'appr-2'),
_buildApproval(id: 'appr-3'),
]));
return buildBloc();
},
act: (b) => b.add(const LoadPendingApprovals()),
expect: () => [
const ApprovalsLoading(),
predicate<ApprovalState>(
(s) => s is ApprovalsLoaded && s.pendingCount == 3,
'ApprovalsLoaded with pendingCount == 3',
),
],
);
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalsLoading, ApprovalsEmpty] when list is empty',
build: () {
when(mockGetPendingApprovals(organizationId: anyNamed('organizationId')))
.thenAnswer((_) async => const Right([]));
return buildBloc();
},
act: (b) => b.add(const LoadPendingApprovals(organizationId: 'org-1')),
expect: () => [
const ApprovalsLoading(),
const ApprovalsEmpty(),
],
);
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalsLoading, ApprovalError] on failure',
build: () {
when(mockGetPendingApprovals(organizationId: anyNamed('organizationId')))
.thenAnswer((_) async =>
const Left(ServerFailure('server error')));
return buildBloc();
},
act: (b) => b.add(const LoadPendingApprovals(organizationId: 'org-1')),
expect: () => [
const ApprovalsLoading(),
const ApprovalError('server error'),
],
);
blocTest<ApprovalBloc, ApprovalState>(
'works without organizationId (null)',
build: () {
when(mockGetPendingApprovals(organizationId: null))
.thenAnswer((_) async => Right([_pendingApproval]));
return buildBloc();
},
act: (b) => b.add(const LoadPendingApprovals()),
expect: () => [
const ApprovalsLoading(),
isA<ApprovalsLoaded>(),
],
);
});
// ─── LoadApprovalById ────────────────────────────────────────────────────
group('LoadApprovalById', () {
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalsLoading, ApprovalDetailLoaded] on success',
build: () {
when(mockGetApprovalById('appr-1'))
.thenAnswer((_) async => Right(_pendingApproval));
return buildBloc();
},
act: (b) => b.add(const LoadApprovalById('appr-1')),
expect: () => [
const ApprovalsLoading(),
ApprovalDetailLoaded(_pendingApproval),
],
verify: (_) {
verify(mockGetApprovalById('appr-1')).called(1);
},
);
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalsLoading, ApprovalError] when not found',
build: () {
when(mockGetApprovalById(any))
.thenAnswer((_) async =>
const Left(NotFoundFailure('Approbation introuvable')));
return buildBloc();
},
act: (b) => b.add(const LoadApprovalById('unknown-id')),
expect: () => [
const ApprovalsLoading(),
const ApprovalError('Approbation introuvable'),
],
);
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalsLoading, ApprovalError] on network failure',
build: () {
when(mockGetApprovalById(any))
.thenAnswer((_) async =>
const Left(NetworkFailure('net')));
return buildBloc();
},
act: (b) => b.add(const LoadApprovalById('appr-1')),
expect: () => [
const ApprovalsLoading(),
const ApprovalError('net'),
],
);
});
// ─── ApproveTransactionEvent ─────────────────────────────────────────────
group('ApproveTransactionEvent', () {
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalActionInProgress(approve), TransactionApproved] on success',
build: () {
when(mockApproveTransaction(
approvalId: anyNamed('approvalId'),
comment: anyNamed('comment'),
)).thenAnswer((_) async => Right(_approvedApproval));
return buildBloc();
},
act: (b) => b.add(
const ApproveTransactionEvent(
approvalId: 'appr-1',
comment: 'OK pour moi',
),
),
expect: () => [
const ApprovalActionInProgress('approve'),
TransactionApproved(approval: _approvedApproval),
],
verify: (_) {
verify(mockApproveTransaction(
approvalId: 'appr-1',
comment: 'OK pour moi',
)).called(1);
},
);
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalActionInProgress(approve), TransactionApproved] without comment',
build: () {
when(mockApproveTransaction(
approvalId: anyNamed('approvalId'),
comment: anyNamed('comment'),
)).thenAnswer((_) async => Right(_approvedApproval));
return buildBloc();
},
act: (b) => b.add(
const ApproveTransactionEvent(approvalId: 'appr-1'),
),
expect: () => [
const ApprovalActionInProgress('approve'),
isA<TransactionApproved>(),
],
);
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalActionInProgress(approve), ApprovalError] on failure',
build: () {
when(mockApproveTransaction(
approvalId: anyNamed('approvalId'),
comment: anyNamed('comment'),
)).thenAnswer(
(_) async => const Left(ForbiddenFailure('Droits insuffisants')));
return buildBloc();
},
act: (b) => b.add(
const ApproveTransactionEvent(approvalId: 'appr-1'),
),
expect: () => [
const ApprovalActionInProgress('approve'),
const ApprovalError('Droits insuffisants'),
],
);
blocTest<ApprovalBloc, ApprovalState>(
'TransactionApproved has correct default message',
build: () {
when(mockApproveTransaction(
approvalId: anyNamed('approvalId'),
comment: anyNamed('comment'),
)).thenAnswer((_) async => Right(_approvedApproval));
return buildBloc();
},
act: (b) =>
b.add(const ApproveTransactionEvent(approvalId: 'appr-1')),
expect: () => [
const ApprovalActionInProgress('approve'),
predicate<ApprovalState>(
(s) =>
s is TransactionApproved &&
s.message == 'Transaction approuvée avec succès',
'TransactionApproved with correct message',
),
],
);
});
// ─── RejectTransactionEvent ──────────────────────────────────────────────
group('RejectTransactionEvent', () {
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalActionInProgress(reject), TransactionRejected] on success',
build: () {
when(mockRejectTransaction(
approvalId: anyNamed('approvalId'),
reason: anyNamed('reason'),
)).thenAnswer((_) async => Right(_rejectedApproval));
return buildBloc();
},
act: (b) => b.add(
const RejectTransactionEvent(
approvalId: 'appr-1',
reason: 'Montant incorrect',
),
),
expect: () => [
const ApprovalActionInProgress('reject'),
TransactionRejected(approval: _rejectedApproval),
],
verify: (_) {
verify(mockRejectTransaction(
approvalId: 'appr-1',
reason: 'Montant incorrect',
)).called(1);
},
);
blocTest<ApprovalBloc, ApprovalState>(
'emits [ApprovalActionInProgress(reject), ApprovalError] on failure',
build: () {
when(mockRejectTransaction(
approvalId: anyNamed('approvalId'),
reason: anyNamed('reason'),
)).thenAnswer((_) async =>
const Left(ValidationFailure('ID approbation requis')));
return buildBloc();
},
act: (b) => b.add(
const RejectTransactionEvent(
approvalId: 'appr-1',
reason: 'bad data',
),
),
expect: () => [
const ApprovalActionInProgress('reject'),
const ApprovalError('ID approbation requis'),
],
);
blocTest<ApprovalBloc, ApprovalState>(
'TransactionRejected has correct default message',
build: () {
when(mockRejectTransaction(
approvalId: anyNamed('approvalId'),
reason: anyNamed('reason'),
)).thenAnswer((_) async => Right(_rejectedApproval));
return buildBloc();
},
act: (b) => b.add(
const RejectTransactionEvent(
approvalId: 'appr-1',
reason: 'Raison valide',
),
),
expect: () => [
const ApprovalActionInProgress('reject'),
predicate<ApprovalState>(
(s) =>
s is TransactionRejected &&
s.message == 'Transaction rejetée avec succès',
'TransactionRejected with correct message',
),
],
);
});
// ─── RefreshApprovals ────────────────────────────────────────────────────
group('RefreshApprovals', () {
blocTest<ApprovalBloc, ApprovalState>(
'emits ApprovalsLoaded without ApprovalsLoading during refresh',
build: () {
when(mockGetPendingApprovals(
organizationId: anyNamed('organizationId'),
)).thenAnswer((_) async => Right([_pendingApproval]));
return buildBloc();
},
// No seed — run from ApprovalInitial. RefreshApprovals skips ApprovalsLoading.
act: (b) => b.add(const RefreshApprovals(organizationId: 'org-1')),
// No ApprovalsLoading emitted — goes straight to result
expect: () => [
ApprovalsLoaded(
approvals: [_pendingApproval],
pendingCount: 1,
),
],
);
blocTest<ApprovalBloc, ApprovalState>(
'emits ApprovalsEmpty when refresh returns empty list',
build: () {
when(mockGetPendingApprovals(
organizationId: anyNamed('organizationId'),
)).thenAnswer((_) async => const Right([]));
return buildBloc();
},
act: (b) => b.add(const RefreshApprovals()),
expect: () => [const ApprovalsEmpty()],
);
blocTest<ApprovalBloc, ApprovalState>(
'emits ApprovalError on refresh failure',
build: () {
when(mockGetPendingApprovals(
organizationId: anyNamed('organizationId'),
)).thenAnswer((_) async =>
const Left(ServerFailure('serveur indisponible')));
return buildBloc();
},
act: (b) => b.add(const RefreshApprovals()),
expect: () => [const ApprovalError('serveur indisponible')],
);
});
// ─── State equality ──────────────────────────────────────────────────────
group('State equality / props', () {
test('ApprovalsLoaded equality', () {
final s1 = ApprovalsLoaded(
approvals: [_pendingApproval],
pendingCount: 1,
);
final s2 = ApprovalsLoaded(
approvals: [_pendingApproval],
pendingCount: 1,
);
expect(s1, equals(s2));
});
test('ApprovalActionInProgress equality', () {
expect(
const ApprovalActionInProgress('approve'),
equals(const ApprovalActionInProgress('approve')),
);
expect(
const ApprovalActionInProgress('approve'),
isNot(equals(const ApprovalActionInProgress('reject'))),
);
});
test('TransactionApproval.isPending is true for pending status', () {
expect(_pendingApproval.isPending, isTrue);
});
test('TransactionApproval.isCompleted is false for pending', () {
expect(_pendingApproval.isCompleted, isFalse);
});
test('TransactionApproval.requiredApprovals matches level', () {
expect(_pendingApproval.requiredApprovals, equals(1)); // level1
});
test('ApprovalsEmpty has default message', () {
const s = ApprovalsEmpty();
expect(s.message, 'Aucune approbation en attente');
});
});
}

View File

@@ -0,0 +1,220 @@
// Mocks generated by Mockito 5.4.6 from annotations
// in unionflow_mobile_apps/test/features/finance_workflow/bloc/approval_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:dartz/dartz.dart' as _i3;
import 'package:mockito/mockito.dart' as _i1;
import 'package:unionflow_mobile_apps/core/error/failures.dart' as _i6;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/entities/transaction_approval.dart'
as _i7;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/repositories/finance_workflow_repository.dart'
as _i2;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/approve_transaction.dart'
as _i9;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/get_approval_by_id.dart'
as _i8;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/get_pending_approvals.dart'
as _i4;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/reject_transaction.dart'
as _i10;
// 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: must_be_immutable
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class
// ignore_for_file: invalid_use_of_internal_member
class _FakeFinanceWorkflowRepository_0 extends _i1.SmartFake
implements _i2.FinanceWorkflowRepository {
_FakeFinanceWorkflowRepository_0(Object parent, Invocation parentInvocation)
: super(parent, parentInvocation);
}
class _FakeEither_1<L, R> extends _i1.SmartFake implements _i3.Either<L, R> {
_FakeEither_1(Object parent, Invocation parentInvocation)
: super(parent, parentInvocation);
}
/// A class which mocks [GetPendingApprovals].
///
/// See the documentation for Mockito's code generation for more information.
class MockGetPendingApprovals extends _i1.Mock
implements _i4.GetPendingApprovals {
MockGetPendingApprovals() {
_i1.throwOnMissingStub(this);
}
@override
_i2.FinanceWorkflowRepository get repository =>
(super.noSuchMethod(
Invocation.getter(#repository),
returnValue: _FakeFinanceWorkflowRepository_0(
this,
Invocation.getter(#repository),
),
)
as _i2.FinanceWorkflowRepository);
@override
_i5.Future<_i3.Either<_i6.Failure, List<_i7.TransactionApproval>>> call({
String? organizationId,
}) =>
(super.noSuchMethod(
Invocation.method(#call, [], {#organizationId: organizationId}),
returnValue:
_i5.Future<
_i3.Either<_i6.Failure, List<_i7.TransactionApproval>>
>.value(
_FakeEither_1<_i6.Failure, List<_i7.TransactionApproval>>(
this,
Invocation.method(#call, [], {
#organizationId: organizationId,
}),
),
),
)
as _i5.Future<
_i3.Either<_i6.Failure, List<_i7.TransactionApproval>>
>);
}
/// A class which mocks [GetApprovalById].
///
/// See the documentation for Mockito's code generation for more information.
class MockGetApprovalById extends _i1.Mock implements _i8.GetApprovalById {
MockGetApprovalById() {
_i1.throwOnMissingStub(this);
}
@override
_i2.FinanceWorkflowRepository get repository =>
(super.noSuchMethod(
Invocation.getter(#repository),
returnValue: _FakeFinanceWorkflowRepository_0(
this,
Invocation.getter(#repository),
),
)
as _i2.FinanceWorkflowRepository);
@override
_i5.Future<_i3.Either<_i6.Failure, _i7.TransactionApproval>> call(
String? approvalId,
) =>
(super.noSuchMethod(
Invocation.method(#call, [approvalId]),
returnValue:
_i5.Future<
_i3.Either<_i6.Failure, _i7.TransactionApproval>
>.value(
_FakeEither_1<_i6.Failure, _i7.TransactionApproval>(
this,
Invocation.method(#call, [approvalId]),
),
),
)
as _i5.Future<_i3.Either<_i6.Failure, _i7.TransactionApproval>>);
}
/// A class which mocks [ApproveTransaction].
///
/// See the documentation for Mockito's code generation for more information.
class MockApproveTransaction extends _i1.Mock
implements _i9.ApproveTransaction {
MockApproveTransaction() {
_i1.throwOnMissingStub(this);
}
@override
_i2.FinanceWorkflowRepository get repository =>
(super.noSuchMethod(
Invocation.getter(#repository),
returnValue: _FakeFinanceWorkflowRepository_0(
this,
Invocation.getter(#repository),
),
)
as _i2.FinanceWorkflowRepository);
@override
_i5.Future<_i3.Either<_i6.Failure, _i7.TransactionApproval>> call({
required String? approvalId,
String? comment,
}) =>
(super.noSuchMethod(
Invocation.method(#call, [], {
#approvalId: approvalId,
#comment: comment,
}),
returnValue:
_i5.Future<
_i3.Either<_i6.Failure, _i7.TransactionApproval>
>.value(
_FakeEither_1<_i6.Failure, _i7.TransactionApproval>(
this,
Invocation.method(#call, [], {
#approvalId: approvalId,
#comment: comment,
}),
),
),
)
as _i5.Future<_i3.Either<_i6.Failure, _i7.TransactionApproval>>);
}
/// A class which mocks [RejectTransaction].
///
/// See the documentation for Mockito's code generation for more information.
class MockRejectTransaction extends _i1.Mock implements _i10.RejectTransaction {
MockRejectTransaction() {
_i1.throwOnMissingStub(this);
}
@override
_i2.FinanceWorkflowRepository get repository =>
(super.noSuchMethod(
Invocation.getter(#repository),
returnValue: _FakeFinanceWorkflowRepository_0(
this,
Invocation.getter(#repository),
),
)
as _i2.FinanceWorkflowRepository);
@override
_i5.Future<_i3.Either<_i6.Failure, _i7.TransactionApproval>> call({
required String? approvalId,
required String? reason,
}) =>
(super.noSuchMethod(
Invocation.method(#call, [], {
#approvalId: approvalId,
#reason: reason,
}),
returnValue:
_i5.Future<
_i3.Either<_i6.Failure, _i7.TransactionApproval>
>.value(
_FakeEither_1<_i6.Failure, _i7.TransactionApproval>(
this,
Invocation.method(#call, [], {
#approvalId: approvalId,
#reason: reason,
}),
),
),
)
as _i5.Future<_i3.Either<_i6.Failure, _i7.TransactionApproval>>);
}

View File

@@ -0,0 +1,742 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/core/error/failures.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/entities/budget.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/create_budget.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/get_budget_by_id.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/get_budget_tracking.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/get_budgets.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/presentation/bloc/budget_bloc.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/presentation/bloc/budget_event.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/presentation/bloc/budget_state.dart';
@GenerateMocks([
GetBudgets,
GetBudgetById,
CreateBudget,
GetBudgetTracking,
])
import 'budget_bloc_test.mocks.dart';
// ---------------------------------------------------------------------------
// Fixtures
// ---------------------------------------------------------------------------
final _now = DateTime(2026, 4, 20);
Budget _buildBudget({
String id = 'budget-1',
BudgetStatus status = BudgetStatus.active,
String orgId = 'org-1',
double totalPlanned = 100000,
double totalRealized = 60000,
}) =>
Budget(
id: id,
name: 'Budget Avril 2026',
organizationId: orgId,
period: BudgetPeriod.monthly,
year: 2026,
month: 4,
status: status,
totalPlanned: totalPlanned,
totalRealized: totalRealized,
createdBy: 'user-1',
createdAt: _now,
startDate: DateTime(2026, 4, 1),
endDate: DateTime(2026, 4, 30),
);
final _budget = _buildBudget();
const _budgetLine = BudgetLine(
id: 'line-1',
category: BudgetCategory.contributions,
name: 'Cotisations membres',
amountPlanned: 50000,
);
final _tracking = <String, dynamic>{
'totalPlanned': 100000,
'totalRealized': 60000,
'realizationRate': 60.0,
};
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
void main() {
late MockGetBudgets mockGetBudgets;
late MockGetBudgetById mockGetBudgetById;
late MockCreateBudget mockCreateBudget;
late MockGetBudgetTracking mockGetBudgetTracking;
setUp(() {
mockGetBudgets = MockGetBudgets();
mockGetBudgetById = MockGetBudgetById();
mockCreateBudget = MockCreateBudget();
mockGetBudgetTracking = MockGetBudgetTracking();
});
BudgetBloc buildBloc() => BudgetBloc(
getBudgets: mockGetBudgets,
getBudgetById: mockGetBudgetById,
createBudget: mockCreateBudget,
getBudgetTracking: mockGetBudgetTracking,
);
// ─── initial state ───────────────────────────────────────────────────────
test('initial state is BudgetInitial', () {
final bloc = buildBloc();
expect(bloc.state, isA<BudgetInitial>());
bloc.close();
});
// ─── LoadBudgets ─────────────────────────────────────────────────────────
group('LoadBudgets', () {
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetsLoaded] on success',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async => Right([_budget]));
return buildBloc();
},
act: (b) => b.add(
const LoadBudgets(organizationId: 'org-1'),
),
expect: () => [
const BudgetsLoading(),
BudgetsLoaded(budgets: [_budget]),
],
verify: (_) {
verify(mockGetBudgets(
organizationId: 'org-1',
status: null,
year: null,
)).called(1);
},
);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetsEmpty] when list is empty',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async => const Right([]));
return buildBloc();
},
act: (b) => b.add(const LoadBudgets()),
expect: () => [
const BudgetsLoading(),
const BudgetsEmpty(),
],
);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetError] on failure',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async =>
const Left(ServerFailure('server error')));
return buildBloc();
},
act: (b) => b.add(const LoadBudgets(organizationId: 'org-1')),
expect: () => [
const BudgetsLoading(),
const BudgetError('server error'),
],
);
blocTest<BudgetBloc, BudgetState>(
'passes status and year filters to use case',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async => Right([_budget]));
return buildBloc();
},
act: (b) => b.add(
const LoadBudgets(
organizationId: 'org-1',
status: BudgetStatus.active,
year: 2026,
),
),
verify: (_) {
verify(mockGetBudgets(
organizationId: 'org-1',
status: BudgetStatus.active,
year: 2026,
)).called(1);
},
);
blocTest<BudgetBloc, BudgetState>(
'BudgetsLoaded preserves filterStatus and filterYear',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async => Right([_budget]));
return buildBloc();
},
act: (b) => b.add(
const LoadBudgets(
organizationId: 'org-1',
status: BudgetStatus.draft,
year: 2025,
),
),
expect: () => [
const BudgetsLoading(),
BudgetsLoaded(
budgets: [_budget],
filterStatus: BudgetStatus.draft,
filterYear: 2025,
),
],
);
});
// ─── LoadBudgetById ───────────────────────────────────────────────────────
group('LoadBudgetById', () {
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetDetailLoaded] on success',
build: () {
when(mockGetBudgetById('budget-1'))
.thenAnswer((_) async => Right(_budget));
return buildBloc();
},
act: (b) => b.add(const LoadBudgetById('budget-1')),
expect: () => [
const BudgetsLoading(),
BudgetDetailLoaded(_budget),
],
verify: (_) {
verify(mockGetBudgetById('budget-1')).called(1);
},
);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetError] when not found',
build: () {
when(mockGetBudgetById(any))
.thenAnswer((_) async =>
const Left(NotFoundFailure('Budget introuvable')));
return buildBloc();
},
act: (b) => b.add(const LoadBudgetById('unknown')),
expect: () => [
const BudgetsLoading(),
const BudgetError('Budget introuvable'),
],
);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetError] on network failure',
build: () {
when(mockGetBudgetById(any))
.thenAnswer((_) async => const Left(NetworkFailure('net')));
return buildBloc();
},
act: (b) => b.add(const LoadBudgetById('budget-1')),
expect: () => [
const BudgetsLoading(),
const BudgetError('net'),
],
);
});
// ─── CreateBudgetEvent ────────────────────────────────────────────────────
group('CreateBudgetEvent', () {
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetActionInProgress(create), BudgetCreated] on success',
build: () {
when(mockCreateBudget(
name: anyNamed('name'),
description: anyNamed('description'),
organizationId: anyNamed('organizationId'),
period: anyNamed('period'),
year: anyNamed('year'),
month: anyNamed('month'),
lines: anyNamed('lines'),
)).thenAnswer((_) async => Right(_budget));
return buildBloc();
},
act: (b) => b.add(
const CreateBudgetEvent(
name: 'Budget Avril 2026',
organizationId: 'org-1',
period: BudgetPeriod.monthly,
year: 2026,
month: 4,
lines: [_budgetLine],
),
),
expect: () => [
const BudgetActionInProgress('create'),
BudgetCreated(budget: _budget),
],
verify: (_) {
verify(mockCreateBudget(
name: 'Budget Avril 2026',
description: null,
organizationId: 'org-1',
period: BudgetPeriod.monthly,
year: 2026,
month: 4,
lines: const [_budgetLine],
)).called(1);
},
);
blocTest<BudgetBloc, BudgetState>(
'BudgetCreated has correct default message',
build: () {
when(mockCreateBudget(
name: anyNamed('name'),
description: anyNamed('description'),
organizationId: anyNamed('organizationId'),
period: anyNamed('period'),
year: anyNamed('year'),
month: anyNamed('month'),
lines: anyNamed('lines'),
)).thenAnswer((_) async => Right(_budget));
return buildBloc();
},
act: (b) => b.add(
const CreateBudgetEvent(
name: 'Budget',
organizationId: 'org-1',
period: BudgetPeriod.annual,
year: 2026,
lines: [_budgetLine],
),
),
expect: () => [
const BudgetActionInProgress('create'),
predicate<BudgetState>(
(s) => s is BudgetCreated && s.message == 'Budget créé avec succès',
'BudgetCreated with correct message',
),
],
);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetActionInProgress(create), BudgetError] on validation failure',
build: () {
when(mockCreateBudget(
name: anyNamed('name'),
description: anyNamed('description'),
organizationId: anyNamed('organizationId'),
period: anyNamed('period'),
year: anyNamed('year'),
month: anyNamed('month'),
lines: anyNamed('lines'),
)).thenAnswer((_) async =>
const Left(ValidationFailure('Nom du budget requis')));
return buildBloc();
},
act: (b) => b.add(
const CreateBudgetEvent(
name: '',
organizationId: 'org-1',
period: BudgetPeriod.monthly,
year: 2026,
month: 4,
lines: [_budgetLine],
),
),
expect: () => [
const BudgetActionInProgress('create'),
const BudgetError('Nom du budget requis'),
],
);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetActionInProgress(create), BudgetError] on server failure',
build: () {
when(mockCreateBudget(
name: anyNamed('name'),
description: anyNamed('description'),
organizationId: anyNamed('organizationId'),
period: anyNamed('period'),
year: anyNamed('year'),
month: anyNamed('month'),
lines: anyNamed('lines'),
)).thenAnswer((_) async =>
const Left(ServerFailure('server error')));
return buildBloc();
},
act: (b) => b.add(
const CreateBudgetEvent(
name: 'Budget valide',
organizationId: 'org-1',
period: BudgetPeriod.annual,
year: 2026,
lines: [_budgetLine],
),
),
expect: () => [
const BudgetActionInProgress('create'),
const BudgetError('server error'),
],
);
});
// ─── LoadBudgetTracking ───────────────────────────────────────────────────
group('LoadBudgetTracking', () {
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetTrackingLoaded] on success',
build: () {
when(mockGetBudgetById('budget-1'))
.thenAnswer((_) async => Right(_budget));
when(mockGetBudgetTracking(budgetId: 'budget-1'))
.thenAnswer((_) async => Right(_tracking));
return buildBloc();
},
act: (b) => b.add(const LoadBudgetTracking('budget-1')),
expect: () => [
const BudgetsLoading(),
BudgetTrackingLoaded(budget: _budget, tracking: _tracking),
],
verify: (_) {
verify(mockGetBudgetById('budget-1')).called(1);
verify(mockGetBudgetTracking(budgetId: 'budget-1')).called(1);
},
);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetError] when getBudgetById fails',
build: () {
when(mockGetBudgetById(any))
.thenAnswer((_) async =>
const Left(NotFoundFailure('Budget introuvable')));
return buildBloc();
},
act: (b) => b.add(const LoadBudgetTracking('unknown')),
expect: () => [
const BudgetsLoading(),
const BudgetError('Budget introuvable'),
],
verify: (_) {
// Tracking should NOT be called if budget fetch fails
verifyNever(mockGetBudgetTracking(budgetId: anyNamed('budgetId')));
},
);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetError] when getTracking fails',
build: () {
when(mockGetBudgetById('budget-1'))
.thenAnswer((_) async => Right(_budget));
when(mockGetBudgetTracking(budgetId: anyNamed('budgetId')))
.thenAnswer((_) async =>
const Left(ServerFailure('tracking error')));
return buildBloc();
},
act: (b) => b.add(const LoadBudgetTracking('budget-1')),
expect: () => [
const BudgetsLoading(),
const BudgetError('tracking error'),
],
);
});
// ─── RefreshBudgets ───────────────────────────────────────────────────────
group('RefreshBudgets', () {
blocTest<BudgetBloc, BudgetState>(
'emits BudgetsLoaded without BudgetsLoading during refresh',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async => Right([_budget]));
return buildBloc();
},
act: (b) =>
b.add(const RefreshBudgets(organizationId: 'org-1')),
// RefreshBudgets does NOT emit BudgetsLoading — goes straight to result
expect: () => [
BudgetsLoaded(budgets: [_budget]),
],
);
blocTest<BudgetBloc, BudgetState>(
'emits BudgetsEmpty on refresh when list is empty',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async => const Right([]));
return buildBloc();
},
act: (b) => b.add(const RefreshBudgets()),
expect: () => [const BudgetsEmpty()],
);
blocTest<BudgetBloc, BudgetState>(
'emits BudgetError on refresh failure',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async =>
const Left(ServerFailure('refresh error')));
return buildBloc();
},
act: (b) =>
b.add(const RefreshBudgets(organizationId: 'org-1')),
expect: () => [const BudgetError('refresh error')],
);
});
// ─── FilterBudgets ────────────────────────────────────────────────────────
group('FilterBudgets', () {
final activeBudget =
_buildBudget(id: 'budget-active', status: BudgetStatus.active);
final draftBudget =
_buildBudget(id: 'budget-draft', status: BudgetStatus.draft);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetsLoaded] with filter applied',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: BudgetStatus.active,
year: anyNamed('year'),
)).thenAnswer((_) async => Right([activeBudget]));
return buildBloc();
},
act: (b) => b.add(
const FilterBudgets(status: BudgetStatus.active),
),
expect: () => [
const BudgetsLoading(),
BudgetsLoaded(
budgets: [activeBudget],
filterStatus: BudgetStatus.active,
),
],
verify: (_) {
verify(mockGetBudgets(
organizationId: null, // no loaded state, so null
status: BudgetStatus.active,
year: null,
)).called(1);
},
);
blocTest<BudgetBloc, BudgetState>(
'uses organizationId from loaded state for filter',
build: () {
// First call (LoadBudgets) returns activeBudget with orgId='org-1'
when(mockGetBudgets(
organizationId: 'org-1',
status: null,
year: null,
)).thenAnswer((_) async => Right([activeBudget]));
// Second call (FilterBudgets) returns draftBudget
when(mockGetBudgets(
organizationId: 'org-1',
status: BudgetStatus.draft,
year: null,
)).thenAnswer((_) async => Right([draftBudget]));
return buildBloc();
},
act: (b) async {
b.add(const LoadBudgets(organizationId: 'org-1'));
// Wait until BudgetsLoaded so FilterBudgets sees org-1 in state
await b.stream.firstWhere((s) => s is BudgetsLoaded);
b.add(const FilterBudgets(status: BudgetStatus.draft));
},
expect: () => [
const BudgetsLoading(),
BudgetsLoaded(budgets: [activeBudget]),
const BudgetsLoading(),
BudgetsLoaded(
budgets: [draftBudget],
filterStatus: BudgetStatus.draft,
),
],
verify: (_) {
// organizationId extracted from first budget in loaded state
verify(mockGetBudgets(
organizationId: 'org-1',
status: BudgetStatus.draft,
year: null,
)).called(1);
},
);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetsEmpty] when filter returns no results',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async => const Right([]));
return buildBloc();
},
act: (b) =>
b.add(const FilterBudgets(status: BudgetStatus.cancelled)),
expect: () => [
const BudgetsLoading(),
const BudgetsEmpty(),
],
);
blocTest<BudgetBloc, BudgetState>(
'emits [BudgetsLoading, BudgetError] on filter failure',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async =>
const Left(ServerFailure('filter error')));
return buildBloc();
},
act: (b) => b.add(const FilterBudgets(year: 2025)),
expect: () => [
const BudgetsLoading(),
const BudgetError('filter error'),
],
);
blocTest<BudgetBloc, BudgetState>(
'filters with year only',
build: () {
when(mockGetBudgets(
organizationId: anyNamed('organizationId'),
status: anyNamed('status'),
year: anyNamed('year'),
)).thenAnswer((_) async => Right([_budget]));
return buildBloc();
},
act: (b) => b.add(const FilterBudgets(year: 2026)),
verify: (_) {
verify(mockGetBudgets(
organizationId: null,
status: null,
year: 2026,
)).called(1);
},
);
});
// ─── State equality / Budget entity ──────────────────────────────────────
group('Budget entity computations', () {
test('realizationRate is correct', () {
expect(_budget.realizationRate, closeTo(60.0, 0.01));
});
test('variance is correct', () {
expect(_budget.variance, closeTo(-40000, 0.01));
});
test('isOverBudget is false when realized < planned', () {
expect(_budget.isOverBudget, isFalse);
});
test('isOverBudget is true when realized > planned', () {
final over = _buildBudget(totalPlanned: 100000, totalRealized: 120000);
expect(over.isOverBudget, isTrue);
});
test('isActive is true for active budget', () {
expect(_budget.isActive, isTrue);
});
test('realizationRate is 0 when totalPlanned is 0', () {
final zeroBudget = _buildBudget(totalPlanned: 0, totalRealized: 0);
expect(zeroBudget.realizationRate, equals(0.0));
});
test('varianceRate is 0 when totalPlanned is 0', () {
final zeroBudget = _buildBudget(totalPlanned: 0, totalRealized: 0);
expect(zeroBudget.varianceRate, equals(0.0));
});
});
group('BudgetLine entity computations', () {
test('realizationRate is correct', () {
const line = BudgetLine(
id: 'l',
category: BudgetCategory.events,
name: 'Test',
amountPlanned: 10000,
amountRealized: 7500,
);
expect(line.realizationRate, closeTo(75.0, 0.01));
});
test('isOverBudget is false when realized <= planned', () {
expect(_budgetLine.isOverBudget, isFalse);
});
test('realizationRate is 0 when amountPlanned is 0', () {
const line = BudgetLine(
id: 'l',
category: BudgetCategory.other,
name: 'Zero',
amountPlanned: 0,
);
expect(line.realizationRate, equals(0.0));
});
});
group('State equality', () {
test('BudgetsLoaded equality', () {
final s1 = BudgetsLoaded(budgets: [_budget]);
final s2 = BudgetsLoaded(budgets: [_budget]);
expect(s1, equals(s2));
});
test('BudgetError equality', () {
const e1 = BudgetError('msg');
const e2 = BudgetError('msg');
expect(e1, equals(e2));
});
test('BudgetsEmpty has default message', () {
const s = BudgetsEmpty();
expect(s.message, 'Aucun budget trouvé');
});
test('BudgetActionInProgress equality', () {
expect(
const BudgetActionInProgress('create'),
equals(const BudgetActionInProgress('create')),
);
});
});
}

View File

@@ -0,0 +1,220 @@
// Mocks generated by Mockito 5.4.6 from annotations
// in unionflow_mobile_apps/test/features/finance_workflow/bloc/budget_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:dartz/dartz.dart' as _i3;
import 'package:mockito/mockito.dart' as _i1;
import 'package:unionflow_mobile_apps/core/error/failures.dart' as _i6;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/entities/budget.dart'
as _i7;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/repositories/finance_workflow_repository.dart'
as _i2;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/create_budget.dart'
as _i9;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/get_budget_by_id.dart'
as _i8;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/get_budget_tracking.dart'
as _i10;
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/get_budgets.dart'
as _i4;
// 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: must_be_immutable
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class
// ignore_for_file: invalid_use_of_internal_member
class _FakeFinanceWorkflowRepository_0 extends _i1.SmartFake
implements _i2.FinanceWorkflowRepository {
_FakeFinanceWorkflowRepository_0(Object parent, Invocation parentInvocation)
: super(parent, parentInvocation);
}
class _FakeEither_1<L, R> extends _i1.SmartFake implements _i3.Either<L, R> {
_FakeEither_1(Object parent, Invocation parentInvocation)
: super(parent, parentInvocation);
}
/// A class which mocks [GetBudgets].
///
/// See the documentation for Mockito's code generation for more information.
class MockGetBudgets extends _i1.Mock implements _i4.GetBudgets {
MockGetBudgets() {
_i1.throwOnMissingStub(this);
}
@override
_i2.FinanceWorkflowRepository get repository =>
(super.noSuchMethod(
Invocation.getter(#repository),
returnValue: _FakeFinanceWorkflowRepository_0(
this,
Invocation.getter(#repository),
),
)
as _i2.FinanceWorkflowRepository);
@override
_i5.Future<_i3.Either<_i6.Failure, List<_i7.Budget>>> call({
String? organizationId,
_i7.BudgetStatus? status,
int? year,
}) =>
(super.noSuchMethod(
Invocation.method(#call, [], {
#organizationId: organizationId,
#status: status,
#year: year,
}),
returnValue:
_i5.Future<_i3.Either<_i6.Failure, List<_i7.Budget>>>.value(
_FakeEither_1<_i6.Failure, List<_i7.Budget>>(
this,
Invocation.method(#call, [], {
#organizationId: organizationId,
#status: status,
#year: year,
}),
),
),
)
as _i5.Future<_i3.Either<_i6.Failure, List<_i7.Budget>>>);
}
/// A class which mocks [GetBudgetById].
///
/// See the documentation for Mockito's code generation for more information.
class MockGetBudgetById extends _i1.Mock implements _i8.GetBudgetById {
MockGetBudgetById() {
_i1.throwOnMissingStub(this);
}
@override
_i2.FinanceWorkflowRepository get repository =>
(super.noSuchMethod(
Invocation.getter(#repository),
returnValue: _FakeFinanceWorkflowRepository_0(
this,
Invocation.getter(#repository),
),
)
as _i2.FinanceWorkflowRepository);
@override
_i5.Future<_i3.Either<_i6.Failure, _i7.Budget>> call(String? budgetId) =>
(super.noSuchMethod(
Invocation.method(#call, [budgetId]),
returnValue: _i5.Future<_i3.Either<_i6.Failure, _i7.Budget>>.value(
_FakeEither_1<_i6.Failure, _i7.Budget>(
this,
Invocation.method(#call, [budgetId]),
),
),
)
as _i5.Future<_i3.Either<_i6.Failure, _i7.Budget>>);
}
/// A class which mocks [CreateBudget].
///
/// See the documentation for Mockito's code generation for more information.
class MockCreateBudget extends _i1.Mock implements _i9.CreateBudget {
MockCreateBudget() {
_i1.throwOnMissingStub(this);
}
@override
_i2.FinanceWorkflowRepository get repository =>
(super.noSuchMethod(
Invocation.getter(#repository),
returnValue: _FakeFinanceWorkflowRepository_0(
this,
Invocation.getter(#repository),
),
)
as _i2.FinanceWorkflowRepository);
@override
_i5.Future<_i3.Either<_i6.Failure, _i7.Budget>> call({
required String? name,
String? description,
required String? organizationId,
required _i7.BudgetPeriod? period,
required int? year,
int? month,
required List<_i7.BudgetLine>? lines,
}) =>
(super.noSuchMethod(
Invocation.method(#call, [], {
#name: name,
#description: description,
#organizationId: organizationId,
#period: period,
#year: year,
#month: month,
#lines: lines,
}),
returnValue: _i5.Future<_i3.Either<_i6.Failure, _i7.Budget>>.value(
_FakeEither_1<_i6.Failure, _i7.Budget>(
this,
Invocation.method(#call, [], {
#name: name,
#description: description,
#organizationId: organizationId,
#period: period,
#year: year,
#month: month,
#lines: lines,
}),
),
),
)
as _i5.Future<_i3.Either<_i6.Failure, _i7.Budget>>);
}
/// A class which mocks [GetBudgetTracking].
///
/// See the documentation for Mockito's code generation for more information.
class MockGetBudgetTracking extends _i1.Mock implements _i10.GetBudgetTracking {
MockGetBudgetTracking() {
_i1.throwOnMissingStub(this);
}
@override
_i2.FinanceWorkflowRepository get repository =>
(super.noSuchMethod(
Invocation.getter(#repository),
returnValue: _FakeFinanceWorkflowRepository_0(
this,
Invocation.getter(#repository),
),
)
as _i2.FinanceWorkflowRepository);
@override
_i5.Future<_i3.Either<_i6.Failure, Map<String, dynamic>>> call({
required String? budgetId,
}) =>
(super.noSuchMethod(
Invocation.method(#call, [], {#budgetId: budgetId}),
returnValue:
_i5.Future<_i3.Either<_i6.Failure, Map<String, dynamic>>>.value(
_FakeEither_1<_i6.Failure, Map<String, dynamic>>(
this,
Invocation.method(#call, [], {#budgetId: budgetId}),
),
),
)
as _i5.Future<_i3.Either<_i6.Failure, Map<String, dynamic>>>);
}