import 'dart:convert'; import 'package:afterwork/core/errors/exceptions.dart'; import 'package:afterwork/data/datasources/user_remote_data_source.dart'; import 'package:afterwork/data/models/user_model.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:mocktail/mocktail.dart'; // Mock classes class MockHttpClient extends Mock implements http.Client {} void main() { late UserRemoteDataSource dataSource; late MockHttpClient mockHttpClient; setUpAll(() { // Register fallback values for mocktail registerFallbackValue(Uri.parse('http://example.com')); registerFallbackValue({}); }); setUp(() { mockHttpClient = MockHttpClient(); dataSource = UserRemoteDataSource(mockHttpClient); }); group('UserRemoteDataSource', () { final tUserJson = { 'userId': 'user123', 'email': 'test@example.com', 'firstName': 'John', 'lastName': 'Doe', 'profileImageUrl': 'https://test.com/profile.jpg', }; final tUserModel = UserModel.fromJson(tUserJson); group('authenticateUser', () { const tEmail = 'test@example.com'; const tPassword = 'password123'; test('should return UserModel when authentication is successful', () async { // Arrange when(() => mockHttpClient.post(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer( (_) async => http.Response( jsonEncode(tUserJson), 200, ), ); // Act final result = await dataSource.authenticateUser(tEmail, tPassword); // Assert expect(result, isA()); expect(result.userId, 'user123'); expect(result.email, tEmail); verify(() => mockHttpClient.post(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .called(1); }); test('should throw Exception when userId is missing', () async { // Arrange final userJsonWithoutId = Map.from(tUserJson)..remove('userId'); when(() => mockHttpClient.post(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer( (_) async => http.Response( jsonEncode(userJsonWithoutId), 200, ), ); // Act & Assert expect( () => dataSource.authenticateUser(tEmail, tPassword), throwsA(isA()), ); }); test('should throw UnauthorizedException when status code is 401', () async { // Arrange when(() => mockHttpClient.post(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer((_) async => http.Response('Unauthorized', 401)); // Act & Assert expect( () => dataSource.authenticateUser(tEmail, tPassword), throwsA(predicate((e) => e.toString().contains('UnauthorizedException'))), ); }); test('should throw ServerExceptionWithMessage when status code is not 200 or 401', () async { // Arrange when(() => mockHttpClient.post(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer((_) async => http.Response('Error', 500)); // Act & Assert expect( () => dataSource.authenticateUser(tEmail, tPassword), throwsA(predicate((e) => e.toString().contains('ServerException'))), ); }); test('should send correct request body', () async { // Arrange when(() => mockHttpClient.post(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer( (_) async => http.Response(jsonEncode(tUserJson), 200), ); // Act await dataSource.authenticateUser(tEmail, tPassword); // Assert final captured = verify(() => mockHttpClient.post( any(), headers: any(named: 'headers'), body: captureAny(named: 'body'), )).captured; final body = jsonDecode(captured.first as String) as Map; expect(body['email'], tEmail); expect(body['motDePasse'], tPassword); }); }); group('getUser', () { const tUserId = 'user123'; test('should return UserModel when API call is successful', () async { // Arrange when(() => mockHttpClient.get(any())).thenAnswer( (_) async => http.Response( jsonEncode(tUserJson), 200, ), ); // Act final result = await dataSource.getUser(tUserId); // Assert expect(result, isA()); expect(result.userId, tUserId); verify(() => mockHttpClient.get(any())).called(1); }); test('should throw UserNotFoundException when status code is 404', () async { // Arrange when(() => mockHttpClient.get(any())).thenAnswer( (_) async => http.Response('Not found', 404), ); // Act & Assert expect( () => dataSource.getUser(tUserId), throwsA(predicate((e) => e.toString().contains('UserNotFoundException'))), ); }); test('should throw ServerException when status code is not 200 or 404', () async { // Arrange when(() => mockHttpClient.get(any())).thenAnswer( (_) async => http.Response('Error', 500), ); // Act & Assert expect( () => dataSource.getUser(tUserId), throwsA(predicate((e) => e.toString().contains('ServerException'))), ); }); }); group('createUser', () { test('should return created UserModel when API call is successful', () async { // Arrange when(() => mockHttpClient.post(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer( (_) async => http.Response( jsonEncode(tUserJson), 201, ), ); // Act final result = await dataSource.createUser(tUserModel); // Assert expect(result, isA()); expect(result.userId, 'user123'); verify(() => mockHttpClient.post(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .called(1); }); test('should throw ConflictException when status code is 409', () async { // Arrange when(() => mockHttpClient.post(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer((_) async => http.Response('Conflict', 409)); // Act & Assert expect( () => dataSource.createUser(tUserModel), throwsA(predicate((e) => e.toString().contains('ConflictException'))), ); }); test('should throw ServerException when status code is not 201 or 409', () async { // Arrange when(() => mockHttpClient.post(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer((_) async => http.Response('Error', 500)); // Act & Assert expect( () => dataSource.createUser(tUserModel), throwsA(predicate((e) => e.toString().contains('ServerException'))), ); }); }); group('updateUser', () { test('should return updated UserModel when API call is successful', () async { // Arrange when(() => mockHttpClient.put(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer( (_) async => http.Response( jsonEncode(tUserJson), 200, ), ); // Act final result = await dataSource.updateUser(tUserModel); // Assert expect(result, isA()); expect(result.userId, 'user123'); verify(() => mockHttpClient.put(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .called(1); }); test('should throw UserNotFoundException when status code is 404', () async { // Arrange when(() => mockHttpClient.put(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer((_) async => http.Response('Not found', 404)); // Act & Assert expect( () => dataSource.updateUser(tUserModel), throwsA(predicate((e) => e.toString().contains('UserNotFoundException'))), ); }); test('should throw ServerException when status code is not 200 or 404', () async { // Arrange when(() => mockHttpClient.put(any(), headers: any(named: 'headers'), body: any(named: 'body'))) .thenAnswer((_) async => http.Response('Error', 500)); // Act & Assert expect( () => dataSource.updateUser(tUserModel), throwsA(predicate((e) => e.toString().contains('ServerException'))), ); }); }); group('deleteUser', () { const tUserId = 'user123'; test('should delete user successfully when API call is successful', () async { // Arrange when(() => mockHttpClient.delete(any())).thenAnswer( (_) async => http.Response('', 204), ); // Act await dataSource.deleteUser(tUserId); // Assert verify(() => mockHttpClient.delete(any())).called(1); }); test('should throw ServerException when status code is not 204', () async { // Arrange when(() => mockHttpClient.delete(any())).thenAnswer( (_) async => http.Response('Error', 500), ); // Act & Assert expect( () => dataSource.deleteUser(tUserId), throwsA(predicate((e) => e.toString().contains('ServerException'))), ); }); }); }); }