From 582b2aeef441d7743058f3af19c54053b2d2cce1 Mon Sep 17 00:00:00 2001 From: Jean Jacques Avril Date: Wed, 8 Jan 2025 20:33:59 +0000 Subject: [PATCH] feat: even more dart tests --- .../test/mocks/mock_auth_repository.dart | 16 ++ .../project_repository_tests.dart | 2 +- .../project_task_repository_tests.dart | 2 +- .../time_entries_repository_tests.dart | 2 +- .../user_repository_tests.dart | 2 +- .../auth_service_test.dart} | 4 +- .../service_test/project_service_test.dart | 156 +++++++++++++ .../project_task_service_test.dart | 156 +++++++++++++ .../service_test/time_entry_service_test.dart | 176 +++++++++++++++ .../test/service_test/user_service_test.dart | 206 ++++++++++++++++++ 10 files changed, 716 insertions(+), 6 deletions(-) create mode 100644 backend-dart/test/mocks/mock_auth_repository.dart rename backend-dart/test/{ => repository_test}/project_repository_tests.dart (99%) rename backend-dart/test/{ => repository_test}/project_task_repository_tests.dart (99%) rename backend-dart/test/{ => repository_test}/time_entries_repository_tests.dart (99%) rename backend-dart/test/{ => repository_test}/user_repository_tests.dart (99%) rename backend-dart/test/{server_test.dart => service_test/auth_service_test.dart} (98%) create mode 100644 backend-dart/test/service_test/project_service_test.dart create mode 100644 backend-dart/test/service_test/project_task_service_test.dart create mode 100644 backend-dart/test/service_test/time_entry_service_test.dart create mode 100644 backend-dart/test/service_test/user_service_test.dart diff --git a/backend-dart/test/mocks/mock_auth_repository.dart b/backend-dart/test/mocks/mock_auth_repository.dart new file mode 100644 index 0000000..6fd6ab0 --- /dev/null +++ b/backend-dart/test/mocks/mock_auth_repository.dart @@ -0,0 +1,16 @@ +import 'package:backend_dart/domain/errors/error.dart'; +import 'package:backend_dart/domain/repository/auth_repository.dart'; +import 'package:fpdart/fpdart.dart'; + +class MockAuthRepositoryImpl implements AuthRepository { + @override + TaskEither generateToken(String userId) => + TaskEither.right('token'); + + @override + TaskEither validateToken(String token) => + TaskEither.right('test_userId'); + + @override + TaskEither revokeToken(String token) => TaskEither.right(null); +} diff --git a/backend-dart/test/project_repository_tests.dart b/backend-dart/test/repository_test/project_repository_tests.dart similarity index 99% rename from backend-dart/test/project_repository_tests.dart rename to backend-dart/test/repository_test/project_repository_tests.dart index fd77f44..6c2238f 100644 --- a/backend-dart/test/project_repository_tests.dart +++ b/backend-dart/test/repository_test/project_repository_tests.dart @@ -4,7 +4,7 @@ import 'package:backend_dart/domain/repository/project_repository.dart'; import 'package:fpdart/fpdart.dart'; import 'package:test/test.dart'; -import 'mocks/mock_database.dart'; +import '../mocks/mock_database.dart'; void main() { late MockDatabase database; diff --git a/backend-dart/test/project_task_repository_tests.dart b/backend-dart/test/repository_test/project_task_repository_tests.dart similarity index 99% rename from backend-dart/test/project_task_repository_tests.dart rename to backend-dart/test/repository_test/project_task_repository_tests.dart index 3f655b9..ae647ed 100644 --- a/backend-dart/test/project_task_repository_tests.dart +++ b/backend-dart/test/repository_test/project_task_repository_tests.dart @@ -4,7 +4,7 @@ import 'package:backend_dart/domain/repository/project_task_repository.dart'; import 'package:fpdart/fpdart.dart'; import 'package:test/test.dart'; -import 'mocks/mock_database.dart'; +import '../mocks/mock_database.dart'; void main() { late MockDatabase database; diff --git a/backend-dart/test/time_entries_repository_tests.dart b/backend-dart/test/repository_test/time_entries_repository_tests.dart similarity index 99% rename from backend-dart/test/time_entries_repository_tests.dart rename to backend-dart/test/repository_test/time_entries_repository_tests.dart index 65d663f..7ebc0dc 100644 --- a/backend-dart/test/time_entries_repository_tests.dart +++ b/backend-dart/test/repository_test/time_entries_repository_tests.dart @@ -4,7 +4,7 @@ import 'package:backend_dart/domain/repository/time_entry_repository.dart'; import 'package:fpdart/fpdart.dart'; import 'package:test/test.dart'; -import 'mocks/mock_database.dart'; +import '../mocks/mock_database.dart'; void main() { late MockDatabase database; diff --git a/backend-dart/test/user_repository_tests.dart b/backend-dart/test/repository_test/user_repository_tests.dart similarity index 99% rename from backend-dart/test/user_repository_tests.dart rename to backend-dart/test/repository_test/user_repository_tests.dart index c9a30c9..2d4ffde 100644 --- a/backend-dart/test/user_repository_tests.dart +++ b/backend-dart/test/repository_test/user_repository_tests.dart @@ -5,7 +5,7 @@ import 'package:backend_dart/domain/repository/user_repository.dart'; import 'package:fpdart/fpdart.dart'; import 'package:test/test.dart'; -import 'mocks/mock_database.dart'; +import '../mocks/mock_database.dart'; void main() { late MockDatabase database; diff --git a/backend-dart/test/server_test.dart b/backend-dart/test/service_test/auth_service_test.dart similarity index 98% rename from backend-dart/test/server_test.dart rename to backend-dart/test/service_test/auth_service_test.dart index 44023f1..bc6b472 100644 --- a/backend-dart/test/server_test.dart +++ b/backend-dart/test/service_test/auth_service_test.dart @@ -10,8 +10,8 @@ import 'package:riverpod/riverpod.dart'; import 'package:shelf/shelf.dart'; import 'package:test/test.dart'; -import 'extensions.dart'; -import 'mocks/mock_database.dart'; +import '../extensions.dart'; +import '../mocks/mock_database.dart'; void main() { late MockDatabase database; diff --git a/backend-dart/test/service_test/project_service_test.dart b/backend-dart/test/service_test/project_service_test.dart new file mode 100644 index 0000000..ca1329d --- /dev/null +++ b/backend-dart/test/service_test/project_service_test.dart @@ -0,0 +1,156 @@ +import 'dart:convert'; + +import 'package:backend_dart/application/repository/provider.dart'; +import 'package:backend_dart/application/service/dto/project_dto.dart'; +import 'package:backend_dart/domain/entities/project.dart'; +import 'package:backend_dart/infrastructure/data/database_provider.dart'; +import 'package:backend_dart/interfaces/http/router.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:shelf/shelf.dart'; +import 'package:test/test.dart'; + +import '../extensions.dart'; +import '../mocks/mock_auth_repository.dart'; +import '../mocks/mock_database.dart'; + +void main() { + late MockDatabase database; + late Handler testHandler; + late MockAuthRepositoryImpl authRepository; + + setUpAll(() async { + database = MockDatabase(); + authRepository = MockAuthRepositoryImpl(); + final container = ProviderContainer( + overrides: [ + databaseProvider.overrideWithValue(database), + authProvider.overrideWithValue(authRepository), + ], + ); + testHandler = Pipeline() + .addMiddleware(logRequests()) + .addHandler(getRouter(container).call); + }); + + setUp(() async { + database.clear(); + }); + + test('ListProjects', () async { + await database.projects + .create(ProjectCreate( + name: 'Project 1', + userId: 'user1', + )) + .run(); + await database.projects + .create(ProjectCreate( + name: 'Project 2', + userId: 'user2', + )) + .run(); + + final response = await testHandler(Request( + 'GET', + Uri.parse('http://localhost/api/projects/'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody, isA()); + expect(responseBody.length, 2); + }); + + test('FetchProject', () async { + final project = await database.projects + .create(ProjectCreate( + name: 'Project 1', + userId: 'user1', + )) + .get; + + final response = await testHandler(Request( + 'GET', + Uri.parse('http://localhost/api/projects/${project!.id}'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['id'], project.id); + expect(responseBody['name'], 'Project 1'); + expect(responseBody['userId'], 'user1'); + }); + + test('CreateProject', () async { + final projectCreateDto = ProjectCreateDto( + name: 'New Project', + userId: 'user1', + ); + + final response = await testHandler(Request( + 'POST', + Uri.parse('http://localhost/api/projects/'), + headers: { + 'Content-Type': 'application/json', + 'Cookie': 'session_token=dummy', + }, + body: jsonEncode(projectCreateDto.toJson()), + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['name'], 'New Project'); + expect(responseBody['userId'], 'user1'); + }); + + test('UpdateProject', () async { + final project = await database.projects + .create(ProjectCreate( + name: 'Project 1', + userId: 'user1', + )) + .get; + + final projectUpdateDto = ProjectUpdateDto( + name: 'Updated Project', + ); + + final response = await testHandler(Request( + 'PUT', + Uri.parse('http://localhost/api/projects/${project!.id}'), + headers: { + 'Content-Type': 'application/json', + 'Cookie': 'session_token=dummy', + }, + body: jsonEncode(projectUpdateDto.toJson()), + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['name'], 'Updated Project'); + }); + + test('DeleteProject', () async { + final project = await database.projects + .create(ProjectCreate( + name: 'Project to Delete', + userId: 'user1', + )) + .get; + + final response = await testHandler(Request( + 'DELETE', + Uri.parse('http://localhost/api/projects/${project!.id}'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['id'], project.id); + + final deletedProject = await database.projects.findById(project.id).get; + expect(deletedProject, isNull); + }); +} diff --git a/backend-dart/test/service_test/project_task_service_test.dart b/backend-dart/test/service_test/project_task_service_test.dart new file mode 100644 index 0000000..9209adb --- /dev/null +++ b/backend-dart/test/service_test/project_task_service_test.dart @@ -0,0 +1,156 @@ +import 'dart:convert'; + +import 'package:backend_dart/application/repository/provider.dart'; +import 'package:backend_dart/application/service/dto/project_task_dto.dart'; +import 'package:backend_dart/domain/entities/project_task.dart'; +import 'package:backend_dart/infrastructure/data/database_provider.dart'; +import 'package:backend_dart/interfaces/http/router.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:shelf/shelf.dart'; +import 'package:test/test.dart'; + +import '../extensions.dart'; +import '../mocks/mock_auth_repository.dart'; +import '../mocks/mock_database.dart'; + +void main() { + late MockDatabase database; + late Handler testHandler; + late MockAuthRepositoryImpl authRepository; + + setUpAll(() async { + database = MockDatabase(); + authRepository = MockAuthRepositoryImpl(); + final container = ProviderContainer( + overrides: [ + databaseProvider.overrideWithValue(database), + authProvider.overrideWithValue(authRepository), + ], + ); + testHandler = Pipeline() + .addMiddleware(logRequests()) + .addHandler(getRouter(container).call); + }); + + setUp(() async { + database.clear(); + }); + + test('ListTasks', () async { + await database.tasks + .create(ProjectTaskCreate( + name: 'Task 1', + projectId: 'project1', + )) + .run(); + await database.tasks + .create(ProjectTaskCreate( + name: 'Task 2', + projectId: 'project2', + )) + .run(); + + final response = await testHandler(Request( + 'GET', + Uri.parse('http://localhost/api/project-tasks/'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody, isA()); + expect(responseBody.length, 2); + }); + + test('FetchTask', () async { + final task = await database.tasks + .create(ProjectTaskCreate( + name: 'Task 1', + projectId: 'project1', + )) + .get; + + final response = await testHandler(Request( + 'GET', + Uri.parse('http://localhost/api/project-tasks/${task!.id}'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['id'], task.id); + expect(responseBody['name'], 'Task 1'); + expect(responseBody['projectId'], 'project1'); + }); + + test('CreateTask', () async { + final taskCreateDto = ProjectTaskCreateDto( + name: 'Task 1', + projectId: 'project1', + ); + + final response = await testHandler(Request( + 'POST', + Uri.parse('http://localhost/api/project-tasks/'), + headers: { + 'Content-Type': 'application/json', + 'Cookie': 'session_token=dummy', + }, + body: jsonEncode(taskCreateDto.toJson()), + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['name'], 'Task 1'); + expect(responseBody['projectId'], 'project1'); + }); + + test('UpdateTask', () async { + final task = await database.tasks + .create(ProjectTaskCreate( + name: 'Task 1', + projectId: 'project1', + )) + .get; + + final taskUpdateDto = ProjectTaskUpdateDto( + name: 'Updated Task', + ); + + final response = await testHandler(Request( + 'PUT', + Uri.parse('http://localhost/api/project-tasks/${task!.id}'), + headers: { + 'Content-Type': 'application/json', + 'Cookie': 'session_token=dummy', + }, + body: jsonEncode(taskUpdateDto.toJson()), + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['name'], 'Updated Task'); + }); + + test('DeleteTask', () async { + final task = await database.tasks + .create(ProjectTaskCreate( + name: 'Task 1', + projectId: 'project1', + )) + .get; + + final response = await testHandler(Request( + 'DELETE', + Uri.parse('http://localhost/api/project-tasks/${task!.id}'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['id'], task.id); + + final deletedTask = await database.tasks.findById(task.id).get; + expect(deletedTask, isNull); + }); +} diff --git a/backend-dart/test/service_test/time_entry_service_test.dart b/backend-dart/test/service_test/time_entry_service_test.dart new file mode 100644 index 0000000..7725885 --- /dev/null +++ b/backend-dart/test/service_test/time_entry_service_test.dart @@ -0,0 +1,176 @@ +import 'dart:convert'; + +import 'package:backend_dart/application/repository/provider.dart'; +import 'package:backend_dart/application/service/dto/time_entry_dto.dart'; +import 'package:backend_dart/application/service/dto/user_dto.dart'; +import 'package:backend_dart/domain/entities/time_entry.dart'; +import 'package:backend_dart/domain/entities/user.dart'; +import 'package:backend_dart/domain/errors/error.dart'; +import 'package:backend_dart/infrastructure/data/database_provider.dart'; +import 'package:backend_dart/interfaces/http/router.dart'; +import 'package:fpdart/fpdart.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:shelf/shelf.dart'; +import 'package:test/test.dart'; + +import '../extensions.dart'; +import '../mocks/mock_auth_repository.dart'; +import '../mocks/mock_database.dart'; + +void main() { + late MockDatabase database; + late Handler testHandler; + late MockAuthRepositoryImpl authRepository; + + setUpAll( + () async { + database = MockDatabase(); + authRepository = MockAuthRepositoryImpl(); + final container = ProviderContainer( + overrides: [ + databaseProvider.overrideWithValue(database), + authProvider.overrideWithValue(authRepository), + ], + ); + testHandler = Pipeline() + .addMiddleware(logRequests()) // Logs all incoming requests + .addHandler(getRouter(container).call); + }, + ); + + setUp( + () async { + database.clear(); // Clear database before each test + }, + ); + + test('ListEntries', () async { + // Prepopulate database with entries + await database.timeEntries.create(TimeEntryCreate( + startTime: DateTime.parse('2023-01-01T08:00:00Z'), + endTime: DateTime.parse('2023-01-01T16:00:00Z'), + userId: 'user1', + projectId: 'project1', + )).run(); + await database.timeEntries.create(TimeEntryCreate( + startTime: DateTime.parse('2023-01-02T09:00:00Z'), + endTime: DateTime.parse('2023-01-02T17:00:00Z'), + userId: 'user2', + projectId: 'project2', + )).run(); + + final response = await testHandler(Request( + 'GET', + Uri.parse('http://localhost/api/time-entries/'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody, isA()); + expect(responseBody.length, 2); + }); + + test('FetchEntry', () async { + final entry = await database.timeEntries + .create(TimeEntryCreate( + startTime: DateTime.parse('2023-01-01T08:00:00Z'), + endTime: DateTime.parse('2023-01-01T16:00:00Z'), + userId: 'user1', + projectId: 'project1', + )) + .get; + + final response = await testHandler(Request( + 'GET', + Uri.parse('http://localhost/api/time-entries/${entry!.id}'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['id'], entry.id); + expect(responseBody['userId'], 'user1'); + expect(responseBody['projectId'], 'project1'); + }); + + test('CreateEntry', () async { + final entryCreateDto = TimeEntryCreateDto( + startTime: DateTime.parse('2023-01-01T08:00:00Z'), + endTime: DateTime.parse('2023-01-01T16:00:00Z'), + userId: 'user1', + projectId: 'project1', + ); + + final response = await testHandler(Request( + 'POST', + Uri.parse('http://localhost/api/time-entries/'), + headers: { + 'Content-Type': 'application/json', + 'Cookie': 'session_token=dummy' + }, + body: jsonEncode(entryCreateDto.toJson()), + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['userId'], 'user1'); + expect(responseBody['projectId'], 'project1'); + }); + + test('UpdateEntry', () async { + final entry = await database.timeEntries + .create(TimeEntryCreate( + startTime: DateTime.parse('2023-01-01T08:00:00Z'), + endTime: DateTime.parse('2023-01-01T16:00:00Z'), + userId: 'user1', + projectId: 'project1', + )) + .get; + + final entryUpdateDto = TimeEntryUpdateDto( + endTime: DateTime.parse('2023-01-01T17:00:00Z'), + description: 'Updated entry', + ); + + final response = await testHandler(Request( + 'PUT', + Uri.parse('http://localhost/api/time-entries/${entry!.id}'), + headers: { + 'Content-Type': 'application/json', + 'Cookie': 'session_token=dummy' + }, + body: jsonEncode(entryUpdateDto.toJson()), + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['description'], 'Updated entry'); + expect(responseBody['endTime'], + DateTime.parse('2023-01-01T17:00:00Z').toIso8601String()); + }); + + test('DeleteEntry', () async { + final entry = await database.timeEntries + .create(TimeEntryCreate( + startTime: DateTime.parse('2023-01-01T08:00:00Z'), + endTime: DateTime.parse('2023-01-01T16:00:00Z'), + userId: 'user1', + projectId: 'project1', + )) + .get; + + final response = await testHandler(Request( + 'DELETE', + Uri.parse('http://localhost/api/time-entries/${entry!.id}'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + expect(response.statusCode, 200); + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['id'], entry.id); + + final deletedEntry = await database.timeEntries.findById(entry.id).get; + expect(deletedEntry, isNull); + }); +} diff --git a/backend-dart/test/service_test/user_service_test.dart b/backend-dart/test/service_test/user_service_test.dart new file mode 100644 index 0000000..9407e11 --- /dev/null +++ b/backend-dart/test/service_test/user_service_test.dart @@ -0,0 +1,206 @@ +import 'dart:convert'; + +import 'package:backend_dart/application/repository/provider.dart'; +import 'package:backend_dart/application/service/dto/user_dto.dart'; +import 'package:backend_dart/domain/entities/user.dart'; +import 'package:backend_dart/domain/errors/error.dart'; +import 'package:backend_dart/infrastructure/data/database_provider.dart'; +import 'package:backend_dart/interfaces/http/router.dart'; +import 'package:fpdart/fpdart.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:shelf/shelf.dart'; +import 'package:test/test.dart'; + +import '../extensions.dart'; +import '../mocks/mock_auth_repository.dart'; +import '../mocks/mock_database.dart'; + +void main() { + late MockDatabase database; + late Handler testHandler; + late MockAuthRepositoryImpl authRepository; + + late TaskEither defaultUser; + setUpAll( + () async { + database = MockDatabase(); + authRepository = MockAuthRepositoryImpl(); + final container = ProviderContainer( + overrides: [ + // Add any overrides here + databaseProvider.overrideWithValue(database), + authProvider.overrideWithValue(authRepository), + ], + ); + // Load the router from the router file + testHandler = Pipeline() + .addMiddleware(logRequests()) // Logs all incoming requests + .addHandler(getRouter(container).call); + }, + ); + + setUp( + () async { + database.clear(); // Clear the database before each test + }, + ); + + test('CreateUser', () async { + final userCreate = UserCreateDto( + email: 'user@example.com', + password: 'password', + name: 'John Doe1', + ); + + final responseCreate = await testHandler(Request( + 'POST', + Uri.parse('http://localhost/api/users/'), + headers: { + 'Content-Type': 'application/json', + 'Cookie': 'session_token=dummy', + }, + body: jsonEncode(userCreate.toJson()), + )); + + // Check the response status code + expect(responseCreate.statusCode, 200, reason: 'User creation failed'); + + // Parse the response body + final responseBody = jsonDecode(await responseCreate.readAsString()); + expect(responseBody['email'], userCreate.email, reason: 'Email mismatch'); + expect(responseBody['name'], userCreate.name, reason: 'Name mismatch'); + expect(responseBody.containsKey('id'), isTrue, reason: 'ID is missing'); + }); + + test('GetUser', () async { + // Create a user directly in the mock database + final newUser = await database.users + .create(UserCreate( + email: 'user@example.com', + password: 'password', + name: 'John Doe1', + )) + .get; + + if (newUser == null) { + fail('User creation failed'); + } + + // Make a GET request to fetch the created user + final responseGet = await testHandler(Request( + 'GET', + Uri.parse('http://localhost/api/users/${newUser.id}'), + headers: { + 'Cookie': 'session_token=dummy', + }, + )); + + // Check the response status code + expect(responseGet.statusCode, 200, reason: 'Failed to fetch user'); + + // Parse the response body + final responseBody = jsonDecode(await responseGet.readAsString()); + expect(responseBody['email'], newUser.email, reason: 'Email mismatch'); + expect(responseBody['name'], newUser.name, reason: 'Name mismatch'); + expect(responseBody['id'], newUser.id, reason: 'ID mismatch'); + }); + + test('ListUsers', () async { + // Prepopulate database with multiple users + await database.users + .create(UserCreate( + email: 'user1@example.com', + password: 'password', + name: 'User One', + )) + .run(); + await database.users + .create(UserCreate( + email: 'user2@example.com', + password: 'password', + name: 'User Two', + )) + .run(); + + // Perform GET request to list all users + final response = await testHandler(Request( + 'GET', + Uri.parse('http://localhost/api/users/'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + // Validate response status + expect(response.statusCode, 200, reason: 'Failed to fetch user list'); + + // Validate response body + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody, isA(), reason: 'Response should be a list'); + expect(responseBody.length, 2, reason: 'User list count mismatch'); + expect(responseBody[0]['email'], 'user1@example.com'); + expect(responseBody[1]['email'], 'user2@example.com'); + }); + + test('UpdateUser', () async { + // Prepopulate database with a user + final user = await database.users + .create(UserCreate( + email: 'update@example.com', + password: 'password', + name: 'Update User', + )) + .get; + + // Prepare update payload + final userUpdate = {'name': 'Updated Name', 'email': 'updated@example.com'}; + + // Perform PUT request to update user + final response = await testHandler(Request( + 'PUT', + Uri.parse('http://localhost/api/users/${user!.id}'), + headers: { + 'Content-Type': 'application/json', + 'Cookie': 'session_token=dummy', + }, + body: jsonEncode(userUpdate), + )); + + // Validate response status + expect(response.statusCode, 200, reason: 'Failed to update user'); + + // Validate response body + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['id'], user.id); + expect(responseBody['name'], 'Updated Name'); + expect(responseBody['email'], 'updated@example.com'); + }); + + test('DeleteUser', () async { + // Prepopulate database with a user + final user = await database.users + .create(UserCreate( + email: 'delete@example.com', + password: 'password', + name: 'Delete User', + )) + .get; + + // Perform DELETE request to delete user + final response = await testHandler(Request( + 'DELETE', + Uri.parse('http://localhost/api/users/${user!.id}'), + headers: {'Cookie': 'session_token=dummy'}, + )); + + // Validate response status + expect(response.statusCode, 200, reason: 'Failed to delete user'); + + // Validate response body + final responseBody = jsonDecode(await response.readAsString()); + expect(responseBody['id'], user.id); + expect(responseBody['email'], 'delete@example.com'); + + // Ensure user is deleted from database + final deletedUser = await database.users.findById(user.id).get; + expect(deletedUser, isNull, reason: 'User was not deleted'); + }); +}