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:
253
test/features/logs/bloc/logs_monitoring_bloc_test.dart
Normal file
253
test/features/logs/bloc/logs_monitoring_bloc_test.dart
Normal file
@@ -0,0 +1,253 @@
|
||||
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/logs/presentation/bloc/logs_monitoring_bloc.dart';
|
||||
import 'package:unionflow_mobile_apps/features/logs/data/repositories/logs_monitoring_repository.dart';
|
||||
import 'package:unionflow_mobile_apps/features/logs/data/models/system_log_model.dart';
|
||||
import 'package:unionflow_mobile_apps/features/logs/data/models/system_metrics_model.dart';
|
||||
import 'package:unionflow_mobile_apps/features/logs/data/models/system_alert_model.dart';
|
||||
|
||||
@GenerateMocks([LogsMonitoringRepository])
|
||||
import 'logs_monitoring_bloc_test.mocks.dart';
|
||||
|
||||
void main() {
|
||||
late LogsMonitoringBloc bloc;
|
||||
late MockLogsMonitoringRepository mockRepository;
|
||||
|
||||
// ── Fixtures ──────────────────────────────────────────────────────────────
|
||||
|
||||
SystemLogModel fakeLog({String id = 'log-1', String level = 'INFO'}) =>
|
||||
SystemLogModel(id: id, level: level, source: 'API', message: 'Test log');
|
||||
|
||||
SystemMetricsModel fakeMetrics() => const SystemMetricsModel(
|
||||
cpuUsagePercent: 45.0,
|
||||
memoryUsagePercent: 60.0,
|
||||
activeConnections: 10,
|
||||
);
|
||||
|
||||
SystemAlertModel fakeAlert({String id = 'alert-1', bool acknowledged = false}) =>
|
||||
SystemAlertModel(id: id, level: 'WARNING', title: 'CPU High', acknowledged: acknowledged);
|
||||
|
||||
setUp(() {
|
||||
mockRepository = MockLogsMonitoringRepository();
|
||||
bloc = LogsMonitoringBloc(mockRepository);
|
||||
});
|
||||
|
||||
tearDown(() => bloc.close());
|
||||
|
||||
// ── Initial state ─────────────────────────────────────────────────────────
|
||||
|
||||
test('initial state is LogsMonitoringInitial', () {
|
||||
expect(bloc.state, isA<LogsMonitoringInitial>());
|
||||
});
|
||||
|
||||
// ── SearchLogs ────────────────────────────────────────────────────────────
|
||||
|
||||
group('SearchLogs', () {
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, LogsLoaded] on success with default parameters',
|
||||
build: () {
|
||||
when(mockRepository.searchLogs(
|
||||
level: anyNamed('level'),
|
||||
source: anyNamed('source'),
|
||||
searchQuery: anyNamed('searchQuery'),
|
||||
timeRange: anyNamed('timeRange'),
|
||||
)).thenAnswer((_) async => [fakeLog(), fakeLog(id: 'log-2')]);
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(SearchLogs()),
|
||||
expect: () => [
|
||||
isA<LogsMonitoringLoading>(),
|
||||
isA<LogsLoaded>().having((s) => s.logs.length, 'logs.length', 2),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, LogsLoaded] with filtered parameters',
|
||||
build: () {
|
||||
when(mockRepository.searchLogs(
|
||||
level: 'ERROR',
|
||||
source: 'API',
|
||||
searchQuery: 'timeout',
|
||||
timeRange: 'Dernières 1h',
|
||||
)).thenAnswer((_) async => [fakeLog(level: 'ERROR')]);
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(SearchLogs(
|
||||
level: 'ERROR',
|
||||
source: 'API',
|
||||
searchQuery: 'timeout',
|
||||
timeRange: 'Dernières 1h',
|
||||
)),
|
||||
expect: () => [
|
||||
isA<LogsMonitoringLoading>(),
|
||||
isA<LogsLoaded>().having(
|
||||
(s) => s.logs.first.level,
|
||||
'level',
|
||||
'ERROR',
|
||||
),
|
||||
],
|
||||
verify: (_) {
|
||||
verify(mockRepository.searchLogs(
|
||||
level: 'ERROR',
|
||||
source: 'API',
|
||||
searchQuery: 'timeout',
|
||||
timeRange: 'Dernières 1h',
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, LogsMonitoringError] on failure',
|
||||
build: () {
|
||||
when(mockRepository.searchLogs(
|
||||
level: anyNamed('level'),
|
||||
source: anyNamed('source'),
|
||||
searchQuery: anyNamed('searchQuery'),
|
||||
timeRange: anyNamed('timeRange'),
|
||||
)).thenThrow(Exception('network error'));
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(SearchLogs()),
|
||||
expect: () => [
|
||||
isA<LogsMonitoringLoading>(),
|
||||
isA<LogsMonitoringError>().having(
|
||||
(s) => s.error,
|
||||
'error',
|
||||
contains('Erreur'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, LogsLoaded] with empty result',
|
||||
build: () {
|
||||
when(mockRepository.searchLogs(
|
||||
level: anyNamed('level'),
|
||||
source: anyNamed('source'),
|
||||
searchQuery: anyNamed('searchQuery'),
|
||||
timeRange: anyNamed('timeRange'),
|
||||
)).thenAnswer((_) async => []);
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(SearchLogs()),
|
||||
expect: () => [
|
||||
isA<LogsMonitoringLoading>(),
|
||||
isA<LogsLoaded>().having((s) => s.logs, 'logs', isEmpty),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ── LoadMetrics ───────────────────────────────────────────────────────────
|
||||
|
||||
group('LoadMetrics', () {
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, MetricsLoaded] on success',
|
||||
build: () {
|
||||
when(mockRepository.getMetrics()).thenAnswer((_) async => fakeMetrics());
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(LoadMetrics()),
|
||||
expect: () => [
|
||||
isA<LogsMonitoringLoading>(),
|
||||
isA<MetricsLoaded>().having(
|
||||
(s) => s.metrics.cpuUsagePercent,
|
||||
'cpuUsagePercent',
|
||||
45.0,
|
||||
),
|
||||
],
|
||||
verify: (_) => verify(mockRepository.getMetrics()).called(1),
|
||||
);
|
||||
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, LogsMonitoringError] on metrics failure',
|
||||
build: () {
|
||||
when(mockRepository.getMetrics()).thenThrow(Exception('metrics error'));
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(LoadMetrics()),
|
||||
expect: () => [isA<LogsMonitoringLoading>(), isA<LogsMonitoringError>()],
|
||||
);
|
||||
});
|
||||
|
||||
// ── LoadAlerts ────────────────────────────────────────────────────────────
|
||||
|
||||
group('LoadAlerts', () {
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, AlertsLoaded] on success',
|
||||
build: () {
|
||||
when(mockRepository.getAlerts())
|
||||
.thenAnswer((_) async => [fakeAlert(), fakeAlert(id: 'alert-2')]);
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(LoadAlerts()),
|
||||
expect: () => [
|
||||
isA<LogsMonitoringLoading>(),
|
||||
isA<AlertsLoaded>().having((s) => s.alerts.length, 'alerts.length', 2),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, LogsMonitoringError] on alerts failure',
|
||||
build: () {
|
||||
when(mockRepository.getAlerts()).thenThrow(Exception('alerts error'));
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(LoadAlerts()),
|
||||
expect: () => [isA<LogsMonitoringLoading>(), isA<LogsMonitoringError>()],
|
||||
);
|
||||
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, AlertsLoaded] with empty list',
|
||||
build: () {
|
||||
when(mockRepository.getAlerts()).thenAnswer((_) async => []);
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(LoadAlerts()),
|
||||
expect: () => [
|
||||
isA<LogsMonitoringLoading>(),
|
||||
isA<AlertsLoaded>().having((s) => s.alerts, 'alerts', isEmpty),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// ── AcknowledgeAlert ──────────────────────────────────────────────────────
|
||||
|
||||
group('AcknowledgeAlert', () {
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, AlertsLoaded, LogsMonitoringSuccess] on acknowledge success',
|
||||
build: () {
|
||||
when(mockRepository.acknowledgeAlert('alert-1')).thenAnswer((_) async {});
|
||||
when(mockRepository.getAlerts())
|
||||
.thenAnswer((_) async => [fakeAlert(acknowledged: true)]);
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(AcknowledgeAlert('alert-1')),
|
||||
expect: () => [
|
||||
isA<LogsMonitoringLoading>(),
|
||||
isA<AlertsLoaded>(),
|
||||
isA<LogsMonitoringSuccess>().having(
|
||||
(s) => s.message,
|
||||
'message',
|
||||
'Alerte acquittée',
|
||||
),
|
||||
],
|
||||
verify: (_) {
|
||||
verify(mockRepository.acknowledgeAlert('alert-1')).called(1);
|
||||
verify(mockRepository.getAlerts()).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
blocTest<LogsMonitoringBloc, LogsMonitoringState>(
|
||||
'emits [Loading, LogsMonitoringError] on acknowledge failure',
|
||||
build: () {
|
||||
when(mockRepository.acknowledgeAlert(any)).thenThrow(Exception('ack error'));
|
||||
return bloc;
|
||||
},
|
||||
act: (b) => b.add(AcknowledgeAlert('alert-1')),
|
||||
expect: () => [isA<LogsMonitoringLoading>(), isA<LogsMonitoringError>()],
|
||||
);
|
||||
});
|
||||
}
|
||||
99
test/features/logs/bloc/logs_monitoring_bloc_test.mocks.dart
Normal file
99
test/features/logs/bloc/logs_monitoring_bloc_test.mocks.dart
Normal file
@@ -0,0 +1,99 @@
|
||||
// Mocks generated by Mockito 5.4.6 from annotations
|
||||
// in unionflow_mobile_apps/test/features/logs/bloc/logs_monitoring_bloc_test.dart.
|
||||
// Do not manually edit this file.
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:async' as _i4;
|
||||
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:unionflow_mobile_apps/features/logs/data/models/system_alert_model.dart'
|
||||
as _i6;
|
||||
import 'package:unionflow_mobile_apps/features/logs/data/models/system_log_model.dart'
|
||||
as _i5;
|
||||
import 'package:unionflow_mobile_apps/features/logs/data/models/system_metrics_model.dart'
|
||||
as _i2;
|
||||
import 'package:unionflow_mobile_apps/features/logs/data/repositories/logs_monitoring_repository.dart'
|
||||
as _i3;
|
||||
|
||||
// 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 _FakeSystemMetricsModel_0 extends _i1.SmartFake
|
||||
implements _i2.SystemMetricsModel {
|
||||
_FakeSystemMetricsModel_0(Object parent, Invocation parentInvocation)
|
||||
: super(parent, parentInvocation);
|
||||
}
|
||||
|
||||
/// A class which mocks [LogsMonitoringRepository].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockLogsMonitoringRepository extends _i1.Mock
|
||||
implements _i3.LogsMonitoringRepository {
|
||||
MockLogsMonitoringRepository() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i4.Future<List<_i5.SystemLogModel>> searchLogs({
|
||||
String? level,
|
||||
String? source,
|
||||
String? searchQuery,
|
||||
String? timeRange,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#searchLogs, [], {
|
||||
#level: level,
|
||||
#source: source,
|
||||
#searchQuery: searchQuery,
|
||||
#timeRange: timeRange,
|
||||
}),
|
||||
returnValue: _i4.Future<List<_i5.SystemLogModel>>.value(
|
||||
<_i5.SystemLogModel>[],
|
||||
),
|
||||
)
|
||||
as _i4.Future<List<_i5.SystemLogModel>>);
|
||||
|
||||
@override
|
||||
_i4.Future<_i2.SystemMetricsModel> getMetrics() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#getMetrics, []),
|
||||
returnValue: _i4.Future<_i2.SystemMetricsModel>.value(
|
||||
_FakeSystemMetricsModel_0(
|
||||
this,
|
||||
Invocation.method(#getMetrics, []),
|
||||
),
|
||||
),
|
||||
)
|
||||
as _i4.Future<_i2.SystemMetricsModel>);
|
||||
|
||||
@override
|
||||
_i4.Future<List<_i6.SystemAlertModel>> getAlerts() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#getAlerts, []),
|
||||
returnValue: _i4.Future<List<_i6.SystemAlertModel>>.value(
|
||||
<_i6.SystemAlertModel>[],
|
||||
),
|
||||
)
|
||||
as _i4.Future<List<_i6.SystemAlertModel>>);
|
||||
|
||||
@override
|
||||
_i4.Future<void> acknowledgeAlert(String? alertId) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#acknowledgeAlert, [alertId]),
|
||||
returnValue: _i4.Future<void>.value(),
|
||||
returnValueForMissingStub: _i4.Future<void>.value(),
|
||||
)
|
||||
as _i4.Future<void>);
|
||||
}
|
||||
Reference in New Issue
Block a user