From 53ffffe9d4634cb9438539487b56a19f9d041f59 Mon Sep 17 00:00:00 2001 From: Jean Jacques Avril Date: Sat, 4 Jan 2025 22:08:33 +0000 Subject: [PATCH] test: implemented test for all services in go --- .../tests/mocks/mock_project_data_source.go | 3 +- .../mocks/mock_project_task_data_source.go | 3 +- .../tests/services/project_service_test.go | 149 ++++++++++++++++++ .../services/project_task_service_test.go | 149 ++++++++++++++++++ backend-go/tests/services/service_test.go | 106 +++++++++++++ .../tests/services/time_entry_service_test.go | 148 +++++++++++++++++ 6 files changed, 554 insertions(+), 4 deletions(-) create mode 100644 backend-go/tests/services/project_service_test.go create mode 100644 backend-go/tests/services/project_task_service_test.go create mode 100644 backend-go/tests/services/service_test.go create mode 100644 backend-go/tests/services/time_entry_service_test.go diff --git a/backend-go/tests/mocks/mock_project_data_source.go b/backend-go/tests/mocks/mock_project_data_source.go index 541b1fd..f252a17 100644 --- a/backend-go/tests/mocks/mock_project_data_source.go +++ b/backend-go/tests/mocks/mock_project_data_source.go @@ -1,7 +1,6 @@ package mocks import ( - "actatempus_backend/internal/domain/data" "actatempus_backend/internal/domain/entities" impl "actatempus_backend/internal/infrastructure/data" @@ -19,7 +18,7 @@ type MockProjectDataSource struct { mu sync.RWMutex } -func NewMockProjectDataSource() data.ProjectDataSource { +func NewMockProjectDataSource() *MockProjectDataSource { return &MockProjectDataSource{ Store: make(map[string]*entities.Project), } diff --git a/backend-go/tests/mocks/mock_project_task_data_source.go b/backend-go/tests/mocks/mock_project_task_data_source.go index 2da9f0f..4fd9a90 100644 --- a/backend-go/tests/mocks/mock_project_task_data_source.go +++ b/backend-go/tests/mocks/mock_project_task_data_source.go @@ -1,7 +1,6 @@ package mocks import ( - "actatempus_backend/internal/domain/data" "actatempus_backend/internal/domain/entities" impl "actatempus_backend/internal/infrastructure/data" "context" @@ -18,7 +17,7 @@ type MockProjectTaskDataSource struct { mu sync.RWMutex } -func NewMockProjectTaskDataSource() data.ProjectTaskDataSource { +func NewMockProjectTaskDataSource() *MockProjectTaskDataSource { return &MockProjectTaskDataSource{ Store: make(map[string]*entities.ProjectTask), } diff --git a/backend-go/tests/services/project_service_test.go b/backend-go/tests/services/project_service_test.go new file mode 100644 index 0000000..40133cd --- /dev/null +++ b/backend-go/tests/services/project_service_test.go @@ -0,0 +1,149 @@ +package services_test + +import ( + "actatempus_backend/internal/application/repository" + "actatempus_backend/internal/application/services" + "actatempus_backend/internal/application/services/dto" + "actatempus_backend/internal/domain/entities" + "fmt" + + "github.com/gin-gonic/gin" + + "actatempus_backend/tests/mocks" + "bytes" + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + E "github.com/IBM/fp-go/either" + F "github.com/IBM/fp-go/function" + "github.com/stretchr/testify/assert" +) + +func TestProjectService(t *testing.T) { + // Setup + gin.SetMode(gin.TestMode) + mockAuthRepo := mocks.NewMockAuthRepository() + projectDS := mocks.NewMockProjectDataSource() + projectRepo := repository.NewProjectRepository(projectDS) + projectService := services.NewProjectService(mockAuthRepo, projectRepo) + + router := gin.Default() + router.RedirectTrailingSlash = false + api := router.Group("/api/projects") + projectService.RegisterRoutes(api) + + populateProject := func(userID, name, description string) entities.Project { + newProject, _ := E.Unwrap(F.Pipe1( + entities.ProjectCreate{ + UserID: userID, + Name: name, + Description: &description, + }, + projectRepo.Create(context.Background()), + )) + return newProject + } + + t.Run("CreateProject - Success", func(t *testing.T) { + projectCreateDTO := dto.ProjectCreateDTO{ + UserID: "user123", + Name: "Test Project", + Description: StringPtr("Test project description"), + } + + body, _ := json.Marshal(projectCreateDTO) + req := httptest.NewRequest(http.MethodPost, "/api/projects/", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusCreated, rec.Code) + var response dto.ProjectDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, projectCreateDTO.Name, response.Name) + assert.Equal(t, *projectCreateDTO.Description, *response.Description) + assert.Equal(t, projectCreateDTO.UserID, response.UserID) + }) + + t.Run("GetProjectByID - Success", func(t *testing.T) { + project := populateProject("user123", "Sample Project", "Sample description") + + req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/projects/%s", project.ID), nil) + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response dto.ProjectDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, project.Name, response.Name) + assert.Equal(t, *project.Description, *response.Description) + }) + + t.Run("GetAllProjects - Success", func(t *testing.T) { + projectDS.Clear() + populateProject("user123", "Project 1", "Description 1") + populateProject("user456", "Project 2", "Description 2") + + req := httptest.NewRequest(http.MethodGet, "/api/projects/", nil) + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response []dto.ProjectDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Len(t, response, 2) + }) + + t.Run("UpdateProject - Success", func(t *testing.T) { + project := populateProject("user123", "Old Project", "Old description") + + projectUpdateDTO := dto.ProjectUpdateDTO{ + Name: StringPtr("Updated Project"), + Description: StringPtr("Updated description"), + } + + body, _ := json.Marshal(projectUpdateDTO) + req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/projects/%s", project.ID), bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response dto.ProjectDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, *projectUpdateDTO.Name, response.Name) + assert.Equal(t, *projectUpdateDTO.Description, *response.Description) + }) + + t.Run("DeleteProject - Success", func(t *testing.T) { + project := populateProject("user123", "To Be Deleted", "Description to be deleted") + + req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/projects/%s", project.ID), nil) + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response dto.ProjectDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "To Be Deleted", response.Name) + assert.Equal(t, "Description to be deleted", *response.Description) + }) +} diff --git a/backend-go/tests/services/project_task_service_test.go b/backend-go/tests/services/project_task_service_test.go new file mode 100644 index 0000000..9cc4ae7 --- /dev/null +++ b/backend-go/tests/services/project_task_service_test.go @@ -0,0 +1,149 @@ +package services_test + +import ( + "actatempus_backend/internal/application/repository" + "actatempus_backend/internal/application/services" + "actatempus_backend/internal/application/services/dto" + "actatempus_backend/internal/domain/entities" + "fmt" + + "github.com/gin-gonic/gin" + + "actatempus_backend/tests/mocks" + "bytes" + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + E "github.com/IBM/fp-go/either" + F "github.com/IBM/fp-go/function" + "github.com/stretchr/testify/assert" +) + +func TestProjectTaskService(t *testing.T) { + // Setup + gin.SetMode(gin.TestMode) + mockAuthRepo := mocks.NewMockAuthRepository() + projectTaskDS := mocks.NewMockProjectTaskDataSource() + projectTaskRepo := repository.NewProjectTaskRepository(projectTaskDS) + projectTaskService := services.NewProjectTaskService(mockAuthRepo, projectTaskRepo) + + router := gin.Default() + router.RedirectTrailingSlash = false + api := router.Group("/api/project-tasks") + projectTaskService.RegisterRoutes(api) + + populateTask := func(projectID, name, description string) entities.ProjectTask { + newTask, _ := E.Unwrap(F.Pipe1( + entities.ProjectTaskCreate{ + ProjectID: projectID, + Name: name, + Description: &description, + }, + projectTaskRepo.Create(context.Background()), + )) + return newTask + } + + t.Run("CreateTask - Success", func(t *testing.T) { + taskCreateDTO := dto.ProjectTaskCreateDTO{ + ProjectID: "project123", + Name: "Test Task", + Description: StringPtr("Test task description"), + } + + body, _ := json.Marshal(taskCreateDTO) + req := httptest.NewRequest(http.MethodPost, "/api/project-tasks/", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusCreated, rec.Code) + var response dto.ProjectTaskDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, taskCreateDTO.Name, response.Name) + assert.Equal(t, *taskCreateDTO.Description, *response.Description) + assert.Equal(t, taskCreateDTO.ProjectID, response.ProjectID) + }) + + t.Run("GetTaskByID - Success", func(t *testing.T) { + task := populateTask("project123", "Test Task", "Sample description") + + req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/project-tasks/%s", task.ID), nil) + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response dto.ProjectTaskDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, task.Name, response.Name) + assert.Equal(t, *task.Description, *response.Description) + }) + + t.Run("GetAllTasks - Success", func(t *testing.T) { + projectTaskDS.Clear() + populateTask("project123", "Task 1", "Description 1") + populateTask("project456", "Task 2", "Description 2") + + req := httptest.NewRequest(http.MethodGet, "/api/project-tasks/", nil) + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response []dto.ProjectTaskDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Len(t, response, 2) + }) + + t.Run("UpdateTask - Success", func(t *testing.T) { + task := populateTask("project123", "Old Task", "Old description") + + taskUpdateDTO := dto.ProjectTaskUpdateDTO{ + Name: StringPtr("Updated Task"), + Description: StringPtr("Updated description"), + } + + body, _ := json.Marshal(taskUpdateDTO) + req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/project-tasks/%s", task.ID), bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response dto.ProjectTaskDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, *taskUpdateDTO.Name, response.Name) + assert.Equal(t, *taskUpdateDTO.Description, *response.Description) + }) + + t.Run("DeleteTask - Success", func(t *testing.T) { + task := populateTask("project123", "To Be Deleted", "Description to be deleted") + + req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/project-tasks/%s", task.ID), nil) + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response dto.ProjectTaskDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "To Be Deleted", response.Name) + assert.Equal(t, "Description to be deleted", *response.Description) + }) +} diff --git a/backend-go/tests/services/service_test.go b/backend-go/tests/services/service_test.go new file mode 100644 index 0000000..21f8b19 --- /dev/null +++ b/backend-go/tests/services/service_test.go @@ -0,0 +1,106 @@ +package services_test + +import ( + "actatempus_backend/internal/application/repository" + "actatempus_backend/internal/application/services" + "actatempus_backend/internal/application/services/dto" + "actatempus_backend/internal/domain/entities" + + "github.com/gin-gonic/gin" + + "actatempus_backend/tests/mocks" + "bytes" + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + E "github.com/IBM/fp-go/either" + F "github.com/IBM/fp-go/function" + "github.com/stretchr/testify/assert" +) + +func TestAuthService(t *testing.T) { + // Setup + gin.SetMode(gin.TestMode) + authRepo := repository.NewInMemoryAuthRepositoryImpl("secret") + userDS := mocks.NewMockUserDataSource() + userRepo := repository.NewUserRepository(userDS) + authService := services.NewAuthService(authRepo, userRepo) + + router := gin.Default() + router.RedirectTrailingSlash = false + api := router.Group("/api/auth") + authService.RegisterRoutes(api) + + populateUser := func(email, password, name string) entities.User { + newUser, _ := E.Unwrap(F.Pipe1( + entities.UserCreate{ + Email: email, + Password: password, + Name: name, + }, + userRepo.Create(context.Background()), + )) + return newUser + } + + t.Run("Login - Success", func(t *testing.T) { + user := populateUser("test.user@example.com", "password123", "Test User") + + loginRequest := dto.LoginRequestDTO{ + Email: user.Email, + Password: "password123", + } + + body, _ := json.Marshal(loginRequest) + req := httptest.NewRequest(http.MethodPost, "/api/auth/login", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response dto.TokenResponseDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.NotEmpty(t, response.Token) + }) + + t.Run("Validate - Success", func(t *testing.T) { + user := populateUser("validate.user@example.com", "password123", "Validate User") + token, _ := E.Unwrap(F.Pipe1(user.ID, authRepo.GenerateToken(context.Background()))) + + req := httptest.NewRequest(http.MethodPost, "/api/auth/validate", nil) + req.Header.Set("Cookie", "session_token="+token) + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response map[string]interface{} + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Token validated", response["message"]) + assert.Equal(t, user.ID, response["user_id"]) + }) + + t.Run("Logout - Success", func(t *testing.T) { + user := populateUser("logout.user@example.com", "password123", "Logout User") + token, _ := E.Unwrap(F.Pipe1(user.ID, authRepo.GenerateToken(context.Background()))) + + req := httptest.NewRequest(http.MethodPost, "/api/auth/logout", nil) + req.Header.Set("Cookie", "session_token="+token) + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response map[string]interface{} + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Token revoked", response["message"]) + assert.Equal(t, user.ID, response["user_id"]) + }) +} diff --git a/backend-go/tests/services/time_entry_service_test.go b/backend-go/tests/services/time_entry_service_test.go new file mode 100644 index 0000000..423774d --- /dev/null +++ b/backend-go/tests/services/time_entry_service_test.go @@ -0,0 +1,148 @@ +package services_test + +import ( + "actatempus_backend/internal/application/repository" + "actatempus_backend/internal/application/services" + "actatempus_backend/internal/application/services/dto" + "actatempus_backend/internal/domain/entities" + "fmt" + "time" + + "github.com/gin-gonic/gin" + + "actatempus_backend/tests/mocks" + "bytes" + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + E "github.com/IBM/fp-go/either" + F "github.com/IBM/fp-go/function" + "github.com/stretchr/testify/assert" +) + +func TestTimeEntryService(t *testing.T) { + // Setup + gin.SetMode(gin.TestMode) + mockAuthRepo := mocks.NewMockAuthRepository() + timeEntryDS := mocks.NewMockTimeEntryDataSource() + timeEntryRepo := repository.NewTimeEntryRepository(timeEntryDS) + timeEntryService := services.NewTimeEntryService(mockAuthRepo, timeEntryRepo) + + router := gin.Default() + router.RedirectTrailingSlash = false + api := router.Group("/api/time-entries") + timeEntryService.RegisterRoutes(api) + + populateTimeEntry := func(userID, projectID, description string) entities.TimeEntry { + newEntry, _ := E.Unwrap(F.Pipe1( + entities.TimeEntryCreate{ + UserID: userID, + ProjectID: projectID, + Description: &description, + StartTime: time.Now(), + }, + timeEntryRepo.Create(context.Background()), + )) + return newEntry + } + + t.Run("CreateTimeEntry - Success", func(t *testing.T) { + timeEntryCreateDTO := dto.TimeEntryCreateDTO{ + UserID: "user123", + ProjectID: "project123", + Description: StringPtr("Test time entry"), + StartTime: "2021-01-01T00:00:00Z", + } + + body, _ := json.Marshal(timeEntryCreateDTO) + req := httptest.NewRequest(http.MethodPost, "/api/time-entries/", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusCreated, rec.Code) + var response dto.TimeEntryDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, *timeEntryCreateDTO.Description, *response.Description) + assert.Equal(t, timeEntryCreateDTO.UserID, response.UserID) + assert.Equal(t, timeEntryCreateDTO.ProjectID, response.ProjectID) + }) + + t.Run("GetTimeEntryByID - Success", func(t *testing.T) { + timeEntry := populateTimeEntry("user123", "project123", "Sample description") + + req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/time-entries/%s", timeEntry.ID), nil) + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response dto.TimeEntryDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, timeEntry.Description, response.Description) + }) + + t.Run("GetAllTimeEntries - Success", func(t *testing.T) { + timeEntryDS.Clear() + populateTimeEntry("user123", "project123", "Entry 1") + populateTimeEntry("user456", "project456", "Entry 2") + + req := httptest.NewRequest(http.MethodGet, "/api/time-entries/", nil) + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response []dto.TimeEntryDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Len(t, response, 2) + }) + + t.Run("UpdateTimeEntry - Success", func(t *testing.T) { + timeEntry := populateTimeEntry("user123", "project123", "Old description") + + timeEntryUpdateDTO := dto.TimeEntryUpdateDTO{ + Description: StringPtr("Updated description"), + } + + body, _ := json.Marshal(timeEntryUpdateDTO) + req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/time-entries/%s", timeEntry.ID), bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response dto.TimeEntryDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, *timeEntryUpdateDTO.Description, *response.Description) + }) + + t.Run("DeleteTimeEntry - Success", func(t *testing.T) { + timeEntry := populateTimeEntry("user123", "project123", "To be deleted") + + req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/time-entries/%s", timeEntry.ID), nil) + req.Header.Set("Cookie", "session_token=fake_test_token") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + var response dto.TimeEntryDTO + err := json.Unmarshal(rec.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "To be deleted", *response.Description) + }) +}