/// 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 approvals = json.decode(response.body); expect(approvals, isA(), 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 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 budgets = json.decode(response.body); expect(budgets, isA(), 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 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)); }); }); }