Files
unionflow-mobile-apps/integration_test/finance_workflow_integration_test.dart
dahoud d094d6db9c Initial commit: unionflow-mobile-apps
Application Flutter complète (sans build artifacts).

Signed-off-by: lions dev Team
2026-03-15 16:30:08 +00:00

311 lines
10 KiB
Dart
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// Tests d'intégration pour Finance Workflow (API-only)
library finance_workflow_integration_test;
import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'helpers/test_config.dart';
import 'helpers/auth_helper.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
late http.Client client;
late AuthHelper authHelper;
setUpAll(() async {
print('\n🚀 Démarrage des tests d\'intégration Finance Workflow\n');
client = http.Client();
authHelper = AuthHelper(client);
// Authentification en tant qu'ORG_ADMIN
final authenticated = await authHelper.authenticateAsOrgAdmin();
expect(authenticated, true, reason: 'Authentification doit réussir');
print('✅ Setup terminé - Token obtenu\n');
});
tearDownAll(() {
client.close();
print('\n✅ Tests d\'intégration Finance Workflow terminés\n');
});
group('Finance Workflow - Approbations', () {
test('GET /api/finance/approvals/pending - Récupérer approbations en attente',
() async {
// Arrange
final url = Uri.parse(
'${TestConfig.apiBaseUrl}/api/finance/approvals/pending',
).replace(queryParameters: {
'organizationId': TestConfig.testOrganizationId,
});
// Act
final response = await client.get(url, headers: authHelper.getAuthHeaders());
// Assert
expect(response.statusCode, 200, reason: 'HTTP 200 OK attendu');
final List<dynamic> approvals = json.decode(response.body);
expect(approvals, isA<List>(), reason: 'Réponse doit être une liste');
print('✅ GET pending approvals: ${approvals.length} approbations trouvées');
await Future.delayed(Duration(milliseconds: TestConfig.delayBetweenTests));
});
test('GET /api/finance/approvals/{id} - Récupérer approbation par ID',
() async {
// Arrange - Récupère d'abord la liste pour avoir un ID
final listUrl = Uri.parse(
'${TestConfig.apiBaseUrl}/api/finance/approvals/pending',
).replace(queryParameters: {
'organizationId': TestConfig.testOrganizationId,
});
final listResponse = await client.get(listUrl, headers: authHelper.getAuthHeaders());
expect(listResponse.statusCode, 200);
final List<dynamic> approvals = json.decode(listResponse.body);
if (approvals.isEmpty) {
print('⚠️ Aucune approbation en attente - test ignoré');
return;
}
final approvalId = approvals.first['id'];
// Act - Récupère l'approbation par ID
final url = Uri.parse(
'${TestConfig.apiBaseUrl}/api/finance/approvals/$approvalId',
);
final response = await client.get(url, headers: authHelper.getAuthHeaders());
// Assert
expect(response.statusCode, 200, reason: 'HTTP 200 OK attendu');
final approval = json.decode(response.body);
expect(approval['id'], equals(approvalId), reason: 'ID doit correspondre');
print('✅ GET approval by ID: ${approval['id']}');
await Future.delayed(Duration(milliseconds: TestConfig.delayBetweenTests));
});
test('POST /api/finance/approvals/{id}/approve - Approuver transaction',
() async {
// Note: Ce test nécessite une approbation en statut "pending"
// Pour éviter de modifier l'état en prod, ce test est informatif
print(' Test approve transaction - Simulé (évite modification en prod)');
print(' Endpoint: POST /api/finance/approvals/{id}/approve');
print(' Body: { "comment": "Approved by integration test" }');
print(' Expected: HTTP 200, statut=approved');
await Future.delayed(Duration(milliseconds: TestConfig.delayBetweenTests));
});
test('POST /api/finance/approvals/{id}/reject - Rejeter transaction',
() async {
// Note: Ce test nécessite une approbation en statut "pending"
// Pour éviter de modifier l'état en prod, ce test est informatif
print(' Test reject transaction - Simulé (évite modification en prod)');
print(' Endpoint: POST /api/finance/approvals/{id}/reject');
print(' Body: { "reason": "Rejected by integration test" }');
print(' Expected: HTTP 200, statut=rejected');
await Future.delayed(Duration(milliseconds: TestConfig.delayBetweenTests));
});
});
group('Finance Workflow - Budgets', () {
String? createdBudgetId;
test('GET /api/finance/budgets - Récupérer liste budgets',
() async {
// Arrange
final url = Uri.parse(
'${TestConfig.apiBaseUrl}/api/finance/budgets',
).replace(queryParameters: {
'organizationId': TestConfig.testOrganizationId,
});
// Act
final response = await client.get(url, headers: authHelper.getAuthHeaders());
// Assert
expect(response.statusCode, 200, reason: 'HTTP 200 OK attendu');
final List<dynamic> budgets = json.decode(response.body);
expect(budgets, isA<List>(), reason: 'Réponse doit être une liste');
print('✅ GET budgets: ${budgets.length} budgets trouvés');
await Future.delayed(Duration(milliseconds: TestConfig.delayBetweenTests));
});
test('POST /api/finance/budgets - Créer un budget',
() async {
// Arrange
final url = Uri.parse('${TestConfig.apiBaseUrl}/api/finance/budgets');
final requestBody = {
'name': 'Budget Test Intégration ${DateTime.now().millisecondsSinceEpoch}',
'description': 'Budget créé par test d\'intégration',
'organizationId': TestConfig.testOrganizationId,
'period': 'ANNUAL',
'year': DateTime.now().year,
'lines': [
{
'category': 'CONTRIBUTIONS',
'name': 'Cotisations',
'amountPlanned': 1000000.0,
'description': 'Revenus cotisations',
},
{
'category': 'SAVINGS',
'name': 'Épargne',
'amountPlanned': 500000.0,
'description': 'Collecte épargne',
},
],
};
// Act
final response = await client.post(
url,
headers: authHelper.getAuthHeaders(),
body: json.encode(requestBody),
);
// Assert
expect(response.statusCode, inInclusiveRange(200, 201),
reason: 'HTTP 200/201 attendu');
final budget = json.decode(response.body);
expect(budget['id'], isNotNull, reason: 'ID budget doit être présent');
expect(budget['name'], contains('Budget Test Intégration'),
reason: 'Nom doit correspondre');
createdBudgetId = budget['id'];
print('✅ POST create budget: ${budget['id']} - ${budget['name']}');
await Future.delayed(Duration(milliseconds: TestConfig.delayBetweenTests));
});
test('GET /api/finance/budgets/{id} - Récupérer budget par ID',
() async {
// Arrange - Utilise le budget créé précédemment ou récupère un existant
String budgetId;
if (createdBudgetId != null) {
budgetId = createdBudgetId!;
} else {
// Récupère un budget existant
final listUrl = Uri.parse(
'${TestConfig.apiBaseUrl}/api/finance/budgets',
).replace(queryParameters: {
'organizationId': TestConfig.testOrganizationId,
});
final listResponse = await client.get(listUrl, headers: authHelper.getAuthHeaders());
expect(listResponse.statusCode, 200);
final List<dynamic> budgets = json.decode(listResponse.body);
if (budgets.isEmpty) {
print('⚠️ Aucun budget trouvé - test ignoré');
return;
}
budgetId = budgets.first['id'];
}
// Act
final url = Uri.parse('${TestConfig.apiBaseUrl}/api/finance/budgets/$budgetId');
final response = await client.get(url, headers: authHelper.getAuthHeaders());
// Assert
expect(response.statusCode, 200, reason: 'HTTP 200 OK attendu');
final budget = json.decode(response.body);
expect(budget['id'], equals(budgetId), reason: 'ID doit correspondre');
expect(budget['lines'], isNotNull, reason: 'Lignes budgétaires doivent être présentes');
print('✅ GET budget by ID: ${budget['id']} - ${budget['name']}');
print(' Lignes budgétaires: ${budget['lines'].length}');
await Future.delayed(Duration(milliseconds: TestConfig.delayBetweenTests));
});
});
group('Finance Workflow - Tests négatifs', () {
test('GET approbation inexistante - Doit retourner 404',
() async {
// Arrange
final fakeId = '00000000-0000-0000-0000-000000000000';
final url = Uri.parse(
'${TestConfig.apiBaseUrl}/api/finance/approvals/$fakeId',
);
// Act
final response = await client.get(url, headers: authHelper.getAuthHeaders());
// Assert
expect(response.statusCode, 404, reason: 'HTTP 404 Not Found attendu');
print('✅ Test négatif: 404 pour approbation inexistante');
await Future.delayed(Duration(milliseconds: TestConfig.delayBetweenTests));
});
test('GET budget inexistant - Doit retourner 404',
() async {
// Arrange
final fakeId = '00000000-0000-0000-0000-000000000000';
final url = Uri.parse(
'${TestConfig.apiBaseUrl}/api/finance/budgets/$fakeId',
);
// Act
final response = await client.get(url, headers: authHelper.getAuthHeaders());
// Assert
expect(response.statusCode, 404, reason: 'HTTP 404 Not Found attendu');
print('✅ Test négatif: 404 pour budget inexistant');
await Future.delayed(Duration(milliseconds: TestConfig.delayBetweenTests));
});
test('POST budget sans authentication - Doit retourner 401',
() async {
// Arrange
final url = Uri.parse('${TestConfig.apiBaseUrl}/api/finance/budgets');
final requestBody = {
'name': 'Budget Sans Auth',
'organizationId': TestConfig.testOrganizationId,
'period': 'ANNUAL',
'year': 2026,
'lines': [],
};
// Act - Sans token d'authentification
final response = await client.post(
url,
headers: {'Content-Type': 'application/json'},
body: json.encode(requestBody),
);
// Assert
expect(response.statusCode, 401, reason: 'HTTP 401 Unauthorized attendu');
print('✅ Test négatif: 401 pour requête non authentifiée');
await Future.delayed(Duration(milliseconds: TestConfig.delayBetweenTests));
});
});
}