376 lines
9.4 KiB
Go
376 lines
9.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"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) (*models.Job, error)
|
|
getJobByIDFunc func(id int) (*models.Job, error)
|
|
updateJobFunc func(id int, req dto.UpdateJobRequest) (*models.Job, error)
|
|
deleteJobFunc func(id int) 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) (*models.Job, error) {
|
|
if m.createJobFunc != nil {
|
|
return m.createJobFunc(req)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockJobService) GetJobByID(id int) (*models.Job, error) {
|
|
if m.getJobByIDFunc != nil {
|
|
return m.getJobByIDFunc(id)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockJobService) UpdateJob(id int, req dto.UpdateJobRequest) (*models.Job, error) {
|
|
if m.updateJobFunc != nil {
|
|
return m.updateJobFunc(id, req)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockJobService) DeleteJob(id int) error {
|
|
if m.deleteJobFunc != nil {
|
|
return m.deleteJobFunc(id)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// JobServiceInterface defines the interface for job service operations
|
|
type JobServiceInterface interface {
|
|
GetJobs(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error)
|
|
CreateJob(req dto.CreateJobRequest) (*models.Job, error)
|
|
GetJobByID(id int) (*models.Job, error)
|
|
UpdateJob(id int, req dto.UpdateJobRequest) (*models.Job, error)
|
|
DeleteJob(id int) error
|
|
}
|
|
|
|
// testableJobHandler wraps an interface for testing
|
|
type testableJobHandler struct {
|
|
service JobServiceInterface
|
|
}
|
|
|
|
func newTestableJobHandler(service JobServiceInterface) *testableJobHandler {
|
|
return &testableJobHandler{service: service}
|
|
}
|
|
|
|
func (h *testableJobHandler) GetJobs(w http.ResponseWriter, r *http.Request) {
|
|
jobs, total, err := h.service.GetJobs(dto.JobFilterQuery{})
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
response := dto.PaginatedResponse{
|
|
Data: jobs,
|
|
Pagination: dto.Pagination{
|
|
Total: total,
|
|
},
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
func (h *testableJobHandler) CreateJob(w http.ResponseWriter, r *http.Request) {
|
|
var req dto.CreateJobRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
job, err := h.service.CreateJob(req)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(job)
|
|
}
|
|
|
|
func (h *testableJobHandler) GetJobByID(w http.ResponseWriter, r *http.Request) {
|
|
job, err := h.service.GetJobByID(1) // simplified for testing
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(job)
|
|
}
|
|
|
|
func (h *testableJobHandler) DeleteJob(w http.ResponseWriter, r *http.Request) {
|
|
if err := h.service.DeleteJob(1); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
|
|
// =============================================================================
|
|
// Test Cases
|
|
// =============================================================================
|
|
|
|
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,
|
|
CompanyID: 1,
|
|
Title: "Software Engineer",
|
|
Status: "open",
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
},
|
|
CompanyName: "TestCorp",
|
|
},
|
|
{
|
|
Job: models.Job{
|
|
ID: 2,
|
|
CompanyID: 1,
|
|
Title: "DevOps Engineer",
|
|
Status: "open",
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
},
|
|
CompanyName: "TestCorp",
|
|
},
|
|
}, 2, nil
|
|
},
|
|
}
|
|
|
|
handler := newTestableJobHandler(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_Empty(t *testing.T) {
|
|
mockService := &mockJobService{
|
|
getJobsFunc: func(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error) {
|
|
return []models.JobWithCompany{}, 0, nil
|
|
},
|
|
}
|
|
|
|
handler := newTestableJobHandler(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, 0, response.Pagination.Total)
|
|
}
|
|
|
|
func TestGetJobs_Error(t *testing.T) {
|
|
mockService := &mockJobService{
|
|
getJobsFunc: func(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error) {
|
|
return nil, 0, assert.AnError
|
|
},
|
|
}
|
|
|
|
handler := newTestableJobHandler(mockService)
|
|
req := httptest.NewRequest("GET", "/jobs", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handler.GetJobs(rr, req)
|
|
|
|
assert.Equal(t, http.StatusInternalServerError, rr.Code)
|
|
}
|
|
|
|
func TestCreateJob_Success(t *testing.T) {
|
|
mockService := &mockJobService{
|
|
createJobFunc: func(req dto.CreateJobRequest) (*models.Job, error) {
|
|
return &models.Job{
|
|
ID: 1,
|
|
CompanyID: req.CompanyID,
|
|
Title: req.Title,
|
|
Description: req.Description,
|
|
Status: req.Status,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
handler := newTestableJobHandler(mockService)
|
|
|
|
jobReq := dto.CreateJobRequest{
|
|
CompanyID: 1,
|
|
Title: "Backend Developer",
|
|
Description: "Build awesome APIs",
|
|
Status: "open",
|
|
}
|
|
body, _ := json.Marshal(jobReq)
|
|
|
|
req := httptest.NewRequest("POST", "/jobs", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
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)
|
|
}
|
|
|
|
func TestCreateJob_InvalidJSON(t *testing.T) {
|
|
mockService := &mockJobService{}
|
|
|
|
handler := newTestableJobHandler(mockService)
|
|
|
|
req := httptest.NewRequest("POST", "/jobs", bytes.NewReader([]byte("invalid json")))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rr := httptest.NewRecorder()
|
|
|
|
handler.CreateJob(rr, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, rr.Code)
|
|
}
|
|
|
|
func TestCreateJob_ServiceError(t *testing.T) {
|
|
mockService := &mockJobService{
|
|
createJobFunc: func(req dto.CreateJobRequest) (*models.Job, error) {
|
|
return nil, assert.AnError
|
|
},
|
|
}
|
|
|
|
handler := newTestableJobHandler(mockService)
|
|
|
|
jobReq := dto.CreateJobRequest{
|
|
CompanyID: 1,
|
|
Title: "Backend Developer",
|
|
Description: "Build awesome APIs",
|
|
Status: "open",
|
|
}
|
|
body, _ := json.Marshal(jobReq)
|
|
|
|
req := httptest.NewRequest("POST", "/jobs", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
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 int) (*models.Job, error) {
|
|
return &models.Job{
|
|
ID: id,
|
|
CompanyID: 1,
|
|
Title: "Software Engineer",
|
|
Description: "Great job opportunity",
|
|
Status: "open",
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
handler := newTestableJobHandler(mockService)
|
|
|
|
req := httptest.NewRequest("GET", "/jobs/1", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handler.GetJobByID(rr, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rr.Code)
|
|
|
|
var job models.Job
|
|
err := json.Unmarshal(rr.Body.Bytes(), &job)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "Software Engineer", job.Title)
|
|
}
|
|
|
|
func TestGetJobByID_NotFound(t *testing.T) {
|
|
mockService := &mockJobService{
|
|
getJobByIDFunc: func(id int) (*models.Job, error) {
|
|
return nil, assert.AnError
|
|
},
|
|
}
|
|
|
|
handler := newTestableJobHandler(mockService)
|
|
|
|
req := httptest.NewRequest("GET", "/jobs/999", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handler.GetJobByID(rr, req)
|
|
|
|
assert.Equal(t, http.StatusNotFound, rr.Code)
|
|
}
|
|
|
|
func TestDeleteJob_Success(t *testing.T) {
|
|
mockService := &mockJobService{
|
|
deleteJobFunc: func(id int) error {
|
|
return nil
|
|
},
|
|
}
|
|
|
|
handler := newTestableJobHandler(mockService)
|
|
|
|
req := httptest.NewRequest("DELETE", "/jobs/1", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handler.DeleteJob(rr, req)
|
|
|
|
assert.Equal(t, http.StatusNoContent, rr.Code)
|
|
}
|
|
|
|
func TestDeleteJob_Error(t *testing.T) {
|
|
mockService := &mockJobService{
|
|
deleteJobFunc: func(id int) error {
|
|
return assert.AnError
|
|
},
|
|
}
|
|
|
|
handler := newTestableJobHandler(mockService)
|
|
|
|
req := httptest.NewRequest("DELETE", "/jobs/1", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handler.DeleteJob(rr, req)
|
|
|
|
assert.Equal(t, http.StatusInternalServerError, rr.Code)
|
|
}
|