gohorsejobs/backend/internal/handlers/job_handler_test.go

252 lines
6.7 KiB
Go

package handlers
import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
"github.com/rede5/gohorsejobs/backend/internal/api/middleware"
"github.com/rede5/gohorsejobs/backend/internal/dto"
"github.com/rede5/gohorsejobs/backend/internal/models"
"github.com/stretchr/testify/assert"
)
// mockJobService is a mock implementation of the job service for testing
type mockJobService struct {
getJobsFunc func(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error)
createJobFunc func(req dto.CreateJobRequest, createdBy string) (*models.Job, error)
getJobByIDFunc func(id string) (*models.Job, error)
updateJobFunc func(id string, req dto.UpdateJobRequest) (*models.Job, error)
deleteJobFunc func(id string) error
}
func (m *mockJobService) GetJobs(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error) {
if m.getJobsFunc != nil {
return m.getJobsFunc(filter)
}
return nil, 0, nil
}
func (m *mockJobService) CreateJob(req dto.CreateJobRequest, createdBy string) (*models.Job, error) {
if m.createJobFunc != nil {
return m.createJobFunc(req, createdBy)
}
return nil, nil
}
func (m *mockJobService) GetJobByID(id string) (*models.Job, error) {
if m.getJobByIDFunc != nil {
return m.getJobByIDFunc(id)
}
return nil, nil
}
func (m *mockJobService) UpdateJob(id string, req dto.UpdateJobRequest) (*models.Job, error) {
if m.updateJobFunc != nil {
return m.updateJobFunc(id, req)
}
return nil, nil
}
func (m *mockJobService) DeleteJob(id string) error {
if m.deleteJobFunc != nil {
return m.deleteJobFunc(id)
}
return nil
}
func TestGetJobs_Success(t *testing.T) {
mockService := &mockJobService{
getJobsFunc: func(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error) {
return []models.JobWithCompany{
{
Job: models.Job{ID: "1", Title: "Software Engineer", Status: "open"},
CompanyName: "TestCorp",
},
{
Job: models.Job{ID: "2", Title: "DevOps", Status: "open"},
CompanyName: "TestCorp",
},
}, 2, nil
},
}
handler := NewJobHandler(mockService)
req := httptest.NewRequest("GET", "/jobs", nil)
rr := httptest.NewRecorder()
handler.GetJobs(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
var response dto.PaginatedResponse
err := json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, 2, response.Pagination.Total)
}
func TestGetJobs_ParsesCareerjetAliases(t *testing.T) {
mockService := &mockJobService{
getJobsFunc: func(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error) {
if assert.NotNil(t, filter.Search) {
assert.Equal(t, "software engineer", *filter.Search)
}
if assert.NotNil(t, filter.Location) {
assert.Equal(t, "Sao Paulo", *filter.Location)
}
if assert.NotNil(t, filter.LocationSearch) {
assert.Equal(t, "Sao Paulo", *filter.LocationSearch)
}
return []models.JobWithCompany{}, 0, nil
},
}
handler := NewJobHandler(mockService)
req := httptest.NewRequest("GET", "/jobs?s=software+engineer&l=Sao+Paulo", nil)
rr := httptest.NewRecorder()
handler.GetJobs(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
}
func TestCreateJob_Success(t *testing.T) {
mockService := &mockJobService{
createJobFunc: func(req dto.CreateJobRequest, createdBy string) (*models.Job, error) {
assert.Equal(t, "user-123", createdBy)
if assert.NotNil(t, req.WorkMode) {
assert.Equal(t, "remote", *req.WorkMode)
}
return &models.Job{
ID: "1",
Title: req.Title,
Status: "open",
WorkMode: req.WorkMode,
}, nil
},
}
handler := NewJobHandler(mockService)
jobReq := dto.CreateJobRequest{
CompanyID: "1",
Title: "Backend Developer",
Description: "Build awesome APIs",
Status: "open",
WorkMode: func() *string { s := "remote"; return &s }(),
}
body, _ := json.Marshal(jobReq)
req := httptest.NewRequest("POST", "/jobs", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
// Inject Context
ctx := context.WithValue(req.Context(), middleware.ContextUserID, "user-123")
req = req.WithContext(ctx)
rr := httptest.NewRecorder()
handler.CreateJob(rr, req)
assert.Equal(t, http.StatusCreated, rr.Code)
var job models.Job
err := json.Unmarshal(rr.Body.Bytes(), &job)
assert.NoError(t, err)
assert.Equal(t, "Backend Developer", job.Title)
if assert.NotNil(t, job.WorkMode) {
assert.Equal(t, "remote", *job.WorkMode)
}
}
func TestCreateJob_ServiceError(t *testing.T) {
mockService := &mockJobService{
createJobFunc: func(req dto.CreateJobRequest, createdBy string) (*models.Job, error) {
return nil, errors.New("db error")
},
}
handler := NewJobHandler(mockService)
jobReq := dto.CreateJobRequest{
Title: "Failing Job",
}
body, _ := json.Marshal(jobReq)
req := httptest.NewRequest("POST", "/jobs", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
ctx := context.WithValue(req.Context(), middleware.ContextUserID, "user-123")
req = req.WithContext(ctx)
rr := httptest.NewRecorder()
handler.CreateJob(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)
}
func TestGetJobByID_Success(t *testing.T) {
mockService := &mockJobService{
getJobByIDFunc: func(id string) (*models.Job, error) {
return &models.Job{ID: id, Title: "Software Engineer"}, nil
},
}
handler := NewJobHandler(mockService)
req := httptest.NewRequest("GET", "/jobs/1", nil)
req.SetPathValue("id", "1")
rr := httptest.NewRecorder()
handler.GetJobByID(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
}
func TestDeleteJob_Success(t *testing.T) {
mockService := &mockJobService{
deleteJobFunc: func(id string) error {
return nil
},
}
handler := NewJobHandler(mockService)
req := httptest.NewRequest("DELETE", "/jobs/1", nil)
req.SetPathValue("id", "1")
rr := httptest.NewRecorder()
handler.DeleteJob(rr, req)
assert.Equal(t, http.StatusNoContent, rr.Code)
}
func TestUpdateJob_Success(t *testing.T) {
workMode := "hybrid"
mockService := &mockJobService{
updateJobFunc: func(id string, req dto.UpdateJobRequest) (*models.Job, error) {
if assert.NotNil(t, req.WorkMode) {
assert.Equal(t, workMode, *req.WorkMode)
}
return &models.Job{ID: id, Title: "Updated"}, nil
},
}
handler := NewJobHandler(mockService)
reqBody, _ := json.Marshal(dto.UpdateJobRequest{Title: func() *string { s := "Updated"; return &s }()}) // Inline pointer helper not clean but works or define var
// Cleaner
title := "Updated"
reqBody, _ = json.Marshal(dto.UpdateJobRequest{Title: &title, WorkMode: &workMode})
req := httptest.NewRequest("PUT", "/jobs/1", bytes.NewReader(reqBody))
req.SetPathValue("id", "1")
rr := httptest.NewRecorder()
handler.UpdateJob(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
}