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:
@@ -0,0 +1,67 @@
|
||||
/// Tests unitaires pour ClearCache use case
|
||||
library clear_cache_test;
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/domain/repositories/system_config_repository.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/domain/usecases/clear_cache.dart';
|
||||
|
||||
@GenerateMocks([ISystemConfigRepository])
|
||||
import 'clear_cache_test.mocks.dart';
|
||||
|
||||
void main() {
|
||||
late ClearCache useCase;
|
||||
late MockISystemConfigRepository mockRepository;
|
||||
|
||||
setUp(() {
|
||||
mockRepository = MockISystemConfigRepository();
|
||||
useCase = ClearCache(mockRepository);
|
||||
});
|
||||
|
||||
group('ClearCache Use Case', () {
|
||||
test('should clear cache successfully', () async {
|
||||
// Arrange
|
||||
when(mockRepository.clearCache())
|
||||
.thenAnswer((_) async => Future.value());
|
||||
|
||||
// Act
|
||||
await useCase();
|
||||
|
||||
// Assert
|
||||
verify(mockRepository.clearCache());
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
});
|
||||
|
||||
test('should complete without error when cache is empty', () async {
|
||||
// Arrange
|
||||
when(mockRepository.clearCache())
|
||||
.thenAnswer((_) async => Future.value());
|
||||
|
||||
// Act
|
||||
await useCase();
|
||||
|
||||
// Assert
|
||||
verify(mockRepository.clearCache()).called(1);
|
||||
});
|
||||
|
||||
test('should throw exception when clear operation fails', () async {
|
||||
// Arrange
|
||||
when(mockRepository.clearCache())
|
||||
.thenThrow(Exception('Clear operation failed'));
|
||||
|
||||
// Act & Assert
|
||||
expect(() => useCase(), throwsA(isA<Exception>()));
|
||||
verify(mockRepository.clearCache());
|
||||
});
|
||||
|
||||
test('should throw exception when permission denied', () async {
|
||||
// Arrange
|
||||
when(mockRepository.clearCache())
|
||||
.thenThrow(Exception('Permission denied'));
|
||||
|
||||
// Act & Assert
|
||||
expect(() => useCase(), throwsException);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/// Tests unitaires pour GetCacheStats use case
|
||||
library get_cache_stats_test;
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/domain/repositories/system_config_repository.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/domain/usecases/get_cache_stats.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/data/models/cache_stats_model.dart';
|
||||
|
||||
@GenerateMocks([ISystemConfigRepository])
|
||||
import 'get_cache_stats_test.mocks.dart';
|
||||
|
||||
void main() {
|
||||
late GetCacheStats useCase;
|
||||
late MockISystemConfigRepository mockRepository;
|
||||
|
||||
setUp(() {
|
||||
mockRepository = MockISystemConfigRepository();
|
||||
useCase = GetCacheStats(mockRepository);
|
||||
});
|
||||
|
||||
group('GetCacheStats Use Case', () {
|
||||
final tCacheStats = CacheStatsModel(
|
||||
totalEntries: 1000,
|
||||
hits: 850,
|
||||
misses: 150,
|
||||
hitRate: 0.85,
|
||||
totalSizeBytes: 1024 * 1024 * 50, // 50 MB
|
||||
);
|
||||
|
||||
test('should return cache statistics', () async {
|
||||
// Arrange
|
||||
when(mockRepository.getCacheStats()).thenAnswer((_) async => tCacheStats);
|
||||
|
||||
// Act
|
||||
final result = await useCase();
|
||||
|
||||
// Assert
|
||||
expect(result, equals(tCacheStats));
|
||||
expect(result.totalEntries, equals(1000));
|
||||
expect(result.hitRate, equals(0.85));
|
||||
verify(mockRepository.getCacheStats());
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
});
|
||||
|
||||
test('should handle empty cache', () async {
|
||||
// Arrange
|
||||
final emptyCacheStats = CacheStatsModel(
|
||||
totalEntries: 0,
|
||||
hits: 0,
|
||||
misses: 0,
|
||||
hitRate: 0.0,
|
||||
totalSizeBytes: 0,
|
||||
);
|
||||
when(mockRepository.getCacheStats())
|
||||
.thenAnswer((_) async => emptyCacheStats);
|
||||
|
||||
// Act
|
||||
final result = await useCase();
|
||||
|
||||
// Assert
|
||||
expect(result.totalEntries, equals(0));
|
||||
expect(result.hitRate, equals(0.0));
|
||||
});
|
||||
|
||||
test('should handle low hit rate cache', () async {
|
||||
// Arrange
|
||||
final lowHitCacheStats = CacheStatsModel(
|
||||
totalEntries: 100,
|
||||
hits: 20,
|
||||
misses: 80,
|
||||
hitRate: 0.20,
|
||||
totalSizeBytes: 1024 * 100,
|
||||
);
|
||||
when(mockRepository.getCacheStats())
|
||||
.thenAnswer((_) async => lowHitCacheStats);
|
||||
|
||||
// Act
|
||||
final result = await useCase();
|
||||
|
||||
// Assert
|
||||
expect(result.hitRate, lessThan(0.5));
|
||||
expect(result.misses!, greaterThan(result.hits!));
|
||||
});
|
||||
|
||||
test('should throw exception when stats retrieval fails', () async {
|
||||
// Arrange
|
||||
when(mockRepository.getCacheStats())
|
||||
.thenThrow(Exception('Stats unavailable'));
|
||||
|
||||
// Act & Assert
|
||||
expect(() => useCase(), throwsException);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/// Tests unitaires pour GetSettings use case
|
||||
library get_settings_test;
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/domain/repositories/system_config_repository.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/domain/usecases/get_settings.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/data/models/system_config_model.dart';
|
||||
|
||||
@GenerateMocks([ISystemConfigRepository])
|
||||
import 'get_settings_test.mocks.dart';
|
||||
|
||||
void main() {
|
||||
late GetSettings useCase;
|
||||
late MockISystemConfigRepository mockRepository;
|
||||
|
||||
setUp(() {
|
||||
mockRepository = MockISystemConfigRepository();
|
||||
useCase = GetSettings(mockRepository);
|
||||
});
|
||||
|
||||
group('GetSettings Use Case', () {
|
||||
final tConfig = SystemConfigModel(
|
||||
applicationName: 'UnionFlow',
|
||||
version: '1.0.0',
|
||||
maintenanceMode: false,
|
||||
defaultLanguage: 'fr',
|
||||
timezone: 'Europe/Paris',
|
||||
sessionTimeoutMinutes: 30,
|
||||
);
|
||||
|
||||
test('should return system configuration', () async {
|
||||
// Arrange
|
||||
when(mockRepository.getConfig()).thenAnswer((_) async => tConfig);
|
||||
|
||||
// Act
|
||||
final result = await useCase();
|
||||
|
||||
// Assert
|
||||
expect(result, equals(tConfig));
|
||||
expect(result.applicationName, equals('UnionFlow'));
|
||||
expect(result.maintenanceMode, isFalse);
|
||||
verify(mockRepository.getConfig());
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
});
|
||||
|
||||
test('should return config with all optional fields', () async {
|
||||
// Arrange
|
||||
final fullConfig = SystemConfigModel(
|
||||
applicationName: 'UnionFlow',
|
||||
version: '1.0.0',
|
||||
maintenanceMode: false,
|
||||
defaultLanguage: 'fr',
|
||||
timezone: 'Europe/Paris',
|
||||
networkTimeout: 30000,
|
||||
sessionTimeoutMinutes: 60,
|
||||
twoFactorAuthEnabled: true,
|
||||
);
|
||||
when(mockRepository.getConfig()).thenAnswer((_) async => fullConfig);
|
||||
|
||||
// Act
|
||||
final result = await useCase();
|
||||
|
||||
// Assert
|
||||
expect(result.networkTimeout, equals(30000));
|
||||
expect(result.twoFactorAuthEnabled, isTrue);
|
||||
});
|
||||
|
||||
test('should handle minimal config', () async {
|
||||
// Arrange
|
||||
final minimalConfig = SystemConfigModel(
|
||||
applicationName: 'UnionFlow',
|
||||
);
|
||||
when(mockRepository.getConfig()).thenAnswer((_) async => minimalConfig);
|
||||
|
||||
// Act
|
||||
final result = await useCase();
|
||||
|
||||
// Assert
|
||||
expect(result.applicationName, isNotNull);
|
||||
verify(mockRepository.getConfig());
|
||||
});
|
||||
|
||||
test('should throw exception when config retrieval fails', () async {
|
||||
// Arrange
|
||||
when(mockRepository.getConfig())
|
||||
.thenThrow(Exception('Config not found'));
|
||||
|
||||
// Act & Assert
|
||||
expect(() => useCase(), throwsException);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/// Tests unitaires pour ResetSettings use case
|
||||
library reset_settings_test;
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/domain/repositories/system_config_repository.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/domain/usecases/reset_settings.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/data/models/system_config_model.dart';
|
||||
|
||||
@GenerateMocks([ISystemConfigRepository])
|
||||
import 'reset_settings_test.mocks.dart';
|
||||
|
||||
void main() {
|
||||
late ResetSettings useCase;
|
||||
late MockISystemConfigRepository mockRepository;
|
||||
|
||||
setUp(() {
|
||||
mockRepository = MockISystemConfigRepository();
|
||||
useCase = ResetSettings(mockRepository);
|
||||
});
|
||||
|
||||
group('ResetSettings Use Case', () {
|
||||
final tDefaultConfig = SystemConfigModel(
|
||||
applicationName: 'UnionFlow',
|
||||
version: '1.0.0',
|
||||
maintenanceMode: false,
|
||||
defaultLanguage: 'fr',
|
||||
timezone: 'Europe/Paris',
|
||||
);
|
||||
|
||||
test('should reset configuration to default values', () async {
|
||||
// Arrange
|
||||
when(mockRepository.resetConfig()).thenAnswer((_) async => tDefaultConfig);
|
||||
|
||||
// Act
|
||||
final result = await useCase();
|
||||
|
||||
// Assert
|
||||
expect(result, equals(tDefaultConfig));
|
||||
expect(result.maintenanceMode, isFalse);
|
||||
expect(result.applicationName, equals('UnionFlow'));
|
||||
expect(result.version, equals('1.0.0'));
|
||||
verify(mockRepository.resetConfig());
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
});
|
||||
|
||||
test('should handle fallback when reset endpoint fails', () async {
|
||||
// Arrange
|
||||
when(mockRepository.resetConfig()).thenAnswer((_) async => tDefaultConfig);
|
||||
|
||||
// Act
|
||||
final result = await useCase();
|
||||
|
||||
// Assert
|
||||
expect(result, isNotNull);
|
||||
expect(result.applicationName, equals('UnionFlow'));
|
||||
verify(mockRepository.resetConfig());
|
||||
});
|
||||
|
||||
test('should throw exception when all reset strategies fail', () async {
|
||||
// Arrange
|
||||
when(mockRepository.resetConfig()).thenThrow(Exception('Reset failed'));
|
||||
|
||||
// Act & Assert
|
||||
expect(
|
||||
() => useCase(),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
verify(mockRepository.resetConfig());
|
||||
});
|
||||
|
||||
test('should return valid config with minimal required fields', () async {
|
||||
// Arrange
|
||||
final tMinimalConfig = SystemConfigModel(
|
||||
applicationName: 'UnionFlow',
|
||||
version: '1.0.0',
|
||||
maintenanceMode: false,
|
||||
);
|
||||
when(mockRepository.resetConfig()).thenAnswer((_) async => tMinimalConfig);
|
||||
|
||||
// Act
|
||||
final result = await useCase();
|
||||
|
||||
// Assert
|
||||
expect(result.applicationName, isNotNull);
|
||||
expect(result.version, isNotNull);
|
||||
expect(result.maintenanceMode, isNotNull);
|
||||
verify(mockRepository.resetConfig());
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/// Tests unitaires pour UpdateSettings use case
|
||||
library update_settings_test;
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/domain/repositories/system_config_repository.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/domain/usecases/update_settings.dart';
|
||||
import 'package:unionflow_mobile_apps/features/settings/data/models/system_config_model.dart';
|
||||
|
||||
@GenerateMocks([ISystemConfigRepository])
|
||||
import 'update_settings_test.mocks.dart';
|
||||
|
||||
void main() {
|
||||
late UpdateSettings useCase;
|
||||
late MockISystemConfigRepository mockRepository;
|
||||
|
||||
setUp(() {
|
||||
mockRepository = MockISystemConfigRepository();
|
||||
useCase = UpdateSettings(mockRepository);
|
||||
});
|
||||
|
||||
group('UpdateSettings Use Case', () {
|
||||
final tConfigMap = {
|
||||
'applicationName': 'UnionFlow Updated',
|
||||
'maintenanceMode': true,
|
||||
'sessionTimeoutMinutes': 45,
|
||||
};
|
||||
|
||||
final tUpdatedConfig = SystemConfigModel(
|
||||
applicationName: 'UnionFlow Updated',
|
||||
maintenanceMode: true,
|
||||
sessionTimeoutMinutes: 45,
|
||||
);
|
||||
|
||||
test('should update configuration successfully', () async {
|
||||
// Arrange
|
||||
when(mockRepository.updateConfig(tConfigMap))
|
||||
.thenAnswer((_) async => tUpdatedConfig);
|
||||
|
||||
// Act
|
||||
final result = await useCase(tConfigMap);
|
||||
|
||||
// Assert
|
||||
expect(result, equals(tUpdatedConfig));
|
||||
expect(result.applicationName, equals('UnionFlow Updated'));
|
||||
expect(result.maintenanceMode, isTrue);
|
||||
verify(mockRepository.updateConfig(tConfigMap));
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
});
|
||||
|
||||
test('should update partial configuration', () async {
|
||||
// Arrange
|
||||
final partialConfig = {'maintenanceMode': false};
|
||||
final expectedResult = SystemConfigModel(maintenanceMode: false);
|
||||
when(mockRepository.updateConfig(partialConfig))
|
||||
.thenAnswer((_) async => expectedResult);
|
||||
|
||||
// Act
|
||||
final result = await useCase(partialConfig);
|
||||
|
||||
// Assert
|
||||
expect(result.maintenanceMode, isFalse);
|
||||
verify(mockRepository.updateConfig(partialConfig));
|
||||
});
|
||||
|
||||
test('should handle empty config map', () async {
|
||||
// Arrange
|
||||
final emptyConfig = <String, dynamic>{};
|
||||
final expectedResult = SystemConfigModel();
|
||||
when(mockRepository.updateConfig(emptyConfig))
|
||||
.thenAnswer((_) async => expectedResult);
|
||||
|
||||
// Act
|
||||
final result = await useCase(emptyConfig);
|
||||
|
||||
// Assert
|
||||
expect(result, isNotNull);
|
||||
verify(mockRepository.updateConfig(emptyConfig));
|
||||
});
|
||||
|
||||
test('should throw exception when update fails', () async {
|
||||
// Arrange
|
||||
when(mockRepository.updateConfig(any))
|
||||
.thenThrow(Exception('Update failed'));
|
||||
|
||||
// Act & Assert
|
||||
expect(() => useCase(tConfigMap), throwsException);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user