Refactor backend to use string IDs for Job, Company, and Application

This commit is contained in:
Tiago Yamamoto 2025-12-23 14:46:17 -03:00
parent 5e714eeb4f
commit 0e265e64b8
16 changed files with 128 additions and 157 deletions

View file

@ -123,12 +123,7 @@ func (h *AdminHandlers) ListCompanies(w http.ResponseWriter, r *http.Request) {
} }
func (h *AdminHandlers) UpdateCompanyStatus(w http.ResponseWriter, r *http.Request) { func (h *AdminHandlers) UpdateCompanyStatus(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id") id := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid company ID", http.StatusBadRequest)
return
}
var req UpdateCompanyStatusRequest var req UpdateCompanyStatusRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@ -182,12 +177,7 @@ func (h *AdminHandlers) ListJobs(w http.ResponseWriter, r *http.Request) {
} }
func (h *AdminHandlers) UpdateJobStatus(w http.ResponseWriter, r *http.Request) { func (h *AdminHandlers) UpdateJobStatus(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id") id := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid job ID", http.StatusBadRequest)
return
}
var req UpdateJobStatusRequest var req UpdateJobStatusRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@ -211,12 +201,7 @@ func (h *AdminHandlers) UpdateJobStatus(w http.ResponseWriter, r *http.Request)
} }
func (h *AdminHandlers) DuplicateJob(w http.ResponseWriter, r *http.Request) { func (h *AdminHandlers) DuplicateJob(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id") id := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid job ID", http.StatusBadRequest)
return
}
job, err := h.adminService.DuplicateJob(r.Context(), id) job, err := h.adminService.DuplicateJob(r.Context(), id)
if err != nil { if err != nil {

View file

@ -2,7 +2,7 @@ package dto
// CreateJobRequest represents the request to create a new job // CreateJobRequest represents the request to create a new job
type CreateJobRequest struct { type CreateJobRequest struct {
CompanyID int `json:"companyId" validate:"required"` CompanyID string `json:"companyId" validate:"required"`
Title string `json:"title" validate:"required,min=5,max=255"` Title string `json:"title" validate:"required,min=5,max=255"`
Description string `json:"description" validate:"required,min=20"` Description string `json:"description" validate:"required,min=20"`
SalaryMin *float64 `json:"salaryMin,omitempty"` SalaryMin *float64 `json:"salaryMin,omitempty"`
@ -41,8 +41,8 @@ type UpdateJobRequest struct {
// CreateApplicationRequest represents a job application (guest or logged user) // CreateApplicationRequest represents a job application (guest or logged user)
type CreateApplicationRequest struct { type CreateApplicationRequest struct {
JobID int `json:"jobId" validate:"required"` JobID string `json:"jobId" validate:"required"`
UserID *int `json:"userId,omitempty"` UserID *string `json:"userId,omitempty"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
Phone *string `json:"phone,omitempty"` Phone *string `json:"phone,omitempty"`
LineID *string `json:"lineId,omitempty"` LineID *string `json:"lineId,omitempty"`
@ -95,8 +95,8 @@ type UpdateCompanyRequest struct {
// AssignUserToCompanyRequest represents assigning a user to a company // AssignUserToCompanyRequest represents assigning a user to a company
type AssignUserToCompanyRequest struct { type AssignUserToCompanyRequest struct {
UserID int `json:"userId" validate:"required"` UserID string `json:"userId" validate:"required"`
CompanyID int `json:"companyId" validate:"required"` CompanyID string `json:"companyId" validate:"required"`
Role string `json:"role" validate:"required,oneof=companyAdmin recruiter"` Role string `json:"role" validate:"required,oneof=companyAdmin recruiter"`
Permissions map[string]interface{} `json:"permissions,omitempty"` Permissions map[string]interface{} `json:"permissions,omitempty"`
} }
@ -110,7 +110,7 @@ type PaginationQuery struct {
// JobFilterQuery represents job filtering parameters // JobFilterQuery represents job filtering parameters
type JobFilterQuery struct { type JobFilterQuery struct {
PaginationQuery PaginationQuery
CompanyID *int `form:"companyId"` CompanyID *string `form:"companyId"`
RegionID *int `form:"regionId"` RegionID *int `form:"regionId"`
CityID *int `form:"cityId"` CityID *int `form:"cityId"`
EmploymentType *string `form:"employmentType"` EmploymentType *string `form:"employmentType"`

View file

@ -3,7 +3,6 @@ package handlers
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"strconv"
"github.com/rede5/gohorsejobs/backend/internal/dto" "github.com/rede5/gohorsejobs/backend/internal/dto"
"github.com/rede5/gohorsejobs/backend/internal/services" "github.com/rede5/gohorsejobs/backend/internal/services"
@ -59,16 +58,11 @@ func (h *ApplicationHandler) CreateApplication(w http.ResponseWriter, r *http.Re
// @Router /api/v1/applications [get] // @Router /api/v1/applications [get]
func (h *ApplicationHandler) GetApplications(w http.ResponseWriter, r *http.Request) { func (h *ApplicationHandler) GetApplications(w http.ResponseWriter, r *http.Request) {
// For now, simple get by Job ID query param // For now, simple get by Job ID query param
jobIDStr := r.URL.Query().Get("jobId") jobID := r.URL.Query().Get("jobId")
if jobIDStr == "" { if jobID == "" {
http.Error(w, "jobId is required", http.StatusBadRequest) http.Error(w, "jobId is required", http.StatusBadRequest)
return return
} }
jobID, err := strconv.Atoi(jobIDStr)
if err != nil {
http.Error(w, "Invalid jobId", http.StatusBadRequest)
return
}
apps, err := h.Service.GetApplications(jobID) apps, err := h.Service.GetApplications(jobID)
if err != nil { if err != nil {
@ -92,12 +86,7 @@ func (h *ApplicationHandler) GetApplications(w http.ResponseWriter, r *http.Requ
// @Failure 404 {string} string "Not Found" // @Failure 404 {string} string "Not Found"
// @Router /api/v1/applications/{id} [get] // @Router /api/v1/applications/{id} [get]
func (h *ApplicationHandler) GetApplicationByID(w http.ResponseWriter, r *http.Request) { func (h *ApplicationHandler) GetApplicationByID(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id") id := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid application ID", http.StatusBadRequest)
return
}
app, err := h.Service.GetApplicationByID(id) app, err := h.Service.GetApplicationByID(id)
if err != nil { if err != nil {
@ -122,12 +111,7 @@ func (h *ApplicationHandler) GetApplicationByID(w http.ResponseWriter, r *http.R
// @Failure 500 {string} string "Internal Server Error" // @Failure 500 {string} string "Internal Server Error"
// @Router /api/v1/applications/{id}/status [put] // @Router /api/v1/applications/{id}/status [put]
func (h *ApplicationHandler) UpdateApplicationStatus(w http.ResponseWriter, r *http.Request) { func (h *ApplicationHandler) UpdateApplicationStatus(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id") id := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid application ID", http.StatusBadRequest)
return
}
var req dto.UpdateApplicationStatusRequest var req dto.UpdateApplicationStatusRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {

View file

@ -16,9 +16,9 @@ import (
// mockApplicationService is a mock implementation for testing // mockApplicationService is a mock implementation for testing
type mockApplicationService struct { type mockApplicationService struct {
createApplicationFunc func(req dto.CreateApplicationRequest) (*models.Application, error) createApplicationFunc func(req dto.CreateApplicationRequest) (*models.Application, error)
getApplicationsFunc func(jobID int) ([]models.Application, error) getApplicationsFunc func(jobID string) ([]models.Application, error)
getApplicationByIDFunc func(id int) (*models.Application, error) getApplicationByIDFunc func(id string) (*models.Application, error)
updateApplicationStatusFunc func(id int, req dto.UpdateApplicationStatusRequest) (*models.Application, error) updateApplicationStatusFunc func(id string, req dto.UpdateApplicationStatusRequest) (*models.Application, error)
} }
func (m *mockApplicationService) CreateApplication(req dto.CreateApplicationRequest) (*models.Application, error) { func (m *mockApplicationService) CreateApplication(req dto.CreateApplicationRequest) (*models.Application, error) {
@ -28,21 +28,21 @@ func (m *mockApplicationService) CreateApplication(req dto.CreateApplicationRequ
return nil, nil return nil, nil
} }
func (m *mockApplicationService) GetApplications(jobID int) ([]models.Application, error) { func (m *mockApplicationService) GetApplications(jobID string) ([]models.Application, error) {
if m.getApplicationsFunc != nil { if m.getApplicationsFunc != nil {
return m.getApplicationsFunc(jobID) return m.getApplicationsFunc(jobID)
} }
return nil, nil return nil, nil
} }
func (m *mockApplicationService) GetApplicationByID(id int) (*models.Application, error) { func (m *mockApplicationService) GetApplicationByID(id string) (*models.Application, error) {
if m.getApplicationByIDFunc != nil { if m.getApplicationByIDFunc != nil {
return m.getApplicationByIDFunc(id) return m.getApplicationByIDFunc(id)
} }
return nil, nil return nil, nil
} }
func (m *mockApplicationService) UpdateApplicationStatus(id int, req dto.UpdateApplicationStatusRequest) (*models.Application, error) { func (m *mockApplicationService) UpdateApplicationStatus(id string, req dto.UpdateApplicationStatusRequest) (*models.Application, error) {
if m.updateApplicationStatusFunc != nil { if m.updateApplicationStatusFunc != nil {
return m.updateApplicationStatusFunc(id, req) return m.updateApplicationStatusFunc(id, req)
} }
@ -52,9 +52,9 @@ func (m *mockApplicationService) UpdateApplicationStatus(id int, req dto.UpdateA
// ApplicationServiceInterface defines the interface for application service // ApplicationServiceInterface defines the interface for application service
type ApplicationServiceInterface interface { type ApplicationServiceInterface interface {
CreateApplication(req dto.CreateApplicationRequest) (*models.Application, error) CreateApplication(req dto.CreateApplicationRequest) (*models.Application, error)
GetApplications(jobID int) ([]models.Application, error) GetApplications(jobID string) ([]models.Application, error)
GetApplicationByID(id int) (*models.Application, error) GetApplicationByID(id string) (*models.Application, error)
UpdateApplicationStatus(id int, req dto.UpdateApplicationStatusRequest) (*models.Application, error) UpdateApplicationStatus(id string, req dto.UpdateApplicationStatusRequest) (*models.Application, error)
} }
// testableApplicationHandler wraps an interface for testing // testableApplicationHandler wraps an interface for testing
@ -84,7 +84,8 @@ func (h *testableApplicationHandler) CreateApplication(w http.ResponseWriter, r
json.NewEncoder(w).Encode(app) json.NewEncoder(w).Encode(app)
} }
func (h *testableApplicationHandler) GetApplications(w http.ResponseWriter, r *http.Request, jobID int) { func (h *testableApplicationHandler) GetApplications(w http.ResponseWriter, r *http.Request) {
jobID := r.URL.Query().Get("jobId")
apps, err := h.service.GetApplications(jobID) apps, err := h.service.GetApplications(jobID)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@ -95,7 +96,12 @@ func (h *testableApplicationHandler) GetApplications(w http.ResponseWriter, r *h
json.NewEncoder(w).Encode(apps) json.NewEncoder(w).Encode(apps)
} }
func (h *testableApplicationHandler) GetApplicationByID(w http.ResponseWriter, r *http.Request, id int) { func (h *testableApplicationHandler) GetApplicationByID(w http.ResponseWriter, r *http.Request) {
// In real handler we use path value, here we might need to simulate or just call service
// For unit test of handler logic usually we mock the router or simply pass arguments if method signature allows.
// But check original handler: it extracts from r.PathValue("id").
// In tests using httptest.NewRequest with Go 1.22 routing, we need to set path values.
id := r.PathValue("id")
app, err := h.service.GetApplicationByID(id) app, err := h.service.GetApplicationByID(id)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusNotFound) http.Error(w, err.Error(), http.StatusNotFound)
@ -106,7 +112,8 @@ func (h *testableApplicationHandler) GetApplicationByID(w http.ResponseWriter, r
json.NewEncoder(w).Encode(app) json.NewEncoder(w).Encode(app)
} }
func (h *testableApplicationHandler) UpdateApplicationStatus(w http.ResponseWriter, r *http.Request, id int) { func (h *testableApplicationHandler) UpdateApplicationStatus(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
var req dto.UpdateApplicationStatusRequest var req dto.UpdateApplicationStatusRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
@ -134,7 +141,7 @@ func TestCreateApplication_Success(t *testing.T) {
mockService := &mockApplicationService{ mockService := &mockApplicationService{
createApplicationFunc: func(req dto.CreateApplicationRequest) (*models.Application, error) { createApplicationFunc: func(req dto.CreateApplicationRequest) (*models.Application, error) {
return &models.Application{ return &models.Application{
ID: 1, ID: "1",
JobID: req.JobID, JobID: req.JobID,
Name: &name, Name: &name,
Email: &email, Email: &email,
@ -148,7 +155,7 @@ func TestCreateApplication_Success(t *testing.T) {
handler := newTestableApplicationHandler(mockService) handler := newTestableApplicationHandler(mockService)
appReq := dto.CreateApplicationRequest{ appReq := dto.CreateApplicationRequest{
JobID: 1, JobID: "1",
Name: &name, Name: &name,
Email: &email, Email: &email,
} }
@ -166,7 +173,7 @@ func TestCreateApplication_Success(t *testing.T) {
err := json.Unmarshal(rr.Body.Bytes(), &app) err := json.Unmarshal(rr.Body.Bytes(), &app)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "pending", app.Status) assert.Equal(t, "pending", app.Status)
assert.Equal(t, 1, app.JobID) assert.Equal(t, "1", app.JobID)
} }
func TestCreateApplication_InvalidJSON(t *testing.T) { func TestCreateApplication_InvalidJSON(t *testing.T) {
@ -192,7 +199,7 @@ func TestCreateApplication_ServiceError(t *testing.T) {
handler := newTestableApplicationHandler(mockService) handler := newTestableApplicationHandler(mockService)
appReq := dto.CreateApplicationRequest{JobID: 1} appReq := dto.CreateApplicationRequest{JobID: "1"}
body, _ := json.Marshal(appReq) body, _ := json.Marshal(appReq)
req := httptest.NewRequest("POST", "/applications", bytes.NewReader(body)) req := httptest.NewRequest("POST", "/applications", bytes.NewReader(body))
@ -209,17 +216,17 @@ func TestGetApplications_Success(t *testing.T) {
name2 := "Jane Smith" name2 := "Jane Smith"
mockService := &mockApplicationService{ mockService := &mockApplicationService{
getApplicationsFunc: func(jobID int) ([]models.Application, error) { getApplicationsFunc: func(jobID string) ([]models.Application, error) {
return []models.Application{ return []models.Application{
{ {
ID: 1, ID: "1",
JobID: jobID, JobID: jobID,
Name: &name1, Name: &name1,
Status: "pending", Status: "pending",
CreatedAt: time.Now(), CreatedAt: time.Now(),
}, },
{ {
ID: 2, ID: "2",
JobID: jobID, JobID: jobID,
Name: &name2, Name: &name2,
Status: "reviewed", Status: "reviewed",
@ -234,7 +241,7 @@ func TestGetApplications_Success(t *testing.T) {
req := httptest.NewRequest("GET", "/applications?jobId=1", nil) req := httptest.NewRequest("GET", "/applications?jobId=1", nil)
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.GetApplications(rr, req, 1) handler.GetApplications(rr, req)
assert.Equal(t, http.StatusOK, rr.Code) assert.Equal(t, http.StatusOK, rr.Code)
@ -246,7 +253,7 @@ func TestGetApplications_Success(t *testing.T) {
func TestGetApplications_Empty(t *testing.T) { func TestGetApplications_Empty(t *testing.T) {
mockService := &mockApplicationService{ mockService := &mockApplicationService{
getApplicationsFunc: func(jobID int) ([]models.Application, error) { getApplicationsFunc: func(jobID string) ([]models.Application, error) {
return []models.Application{}, nil return []models.Application{}, nil
}, },
} }
@ -256,14 +263,14 @@ func TestGetApplications_Empty(t *testing.T) {
req := httptest.NewRequest("GET", "/applications?jobId=1", nil) req := httptest.NewRequest("GET", "/applications?jobId=1", nil)
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.GetApplications(rr, req, 1) handler.GetApplications(rr, req)
assert.Equal(t, http.StatusOK, rr.Code) assert.Equal(t, http.StatusOK, rr.Code)
} }
func TestGetApplications_Error(t *testing.T) { func TestGetApplications_Error(t *testing.T) {
mockService := &mockApplicationService{ mockService := &mockApplicationService{
getApplicationsFunc: func(jobID int) ([]models.Application, error) { getApplicationsFunc: func(jobID string) ([]models.Application, error) {
return nil, assert.AnError return nil, assert.AnError
}, },
} }
@ -273,7 +280,7 @@ func TestGetApplications_Error(t *testing.T) {
req := httptest.NewRequest("GET", "/applications?jobId=1", nil) req := httptest.NewRequest("GET", "/applications?jobId=1", nil)
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.GetApplications(rr, req, 1) handler.GetApplications(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code) assert.Equal(t, http.StatusInternalServerError, rr.Code)
} }
@ -282,10 +289,10 @@ func TestGetApplicationByID_Success(t *testing.T) {
name := "John Doe" name := "John Doe"
mockService := &mockApplicationService{ mockService := &mockApplicationService{
getApplicationByIDFunc: func(id int) (*models.Application, error) { getApplicationByIDFunc: func(id string) (*models.Application, error) {
return &models.Application{ return &models.Application{
ID: id, ID: id,
JobID: 1, JobID: "1",
Name: &name, Name: &name,
Status: "pending", Status: "pending",
CreatedAt: time.Now(), CreatedAt: time.Now(),
@ -296,21 +303,22 @@ func TestGetApplicationByID_Success(t *testing.T) {
handler := newTestableApplicationHandler(mockService) handler := newTestableApplicationHandler(mockService)
req := httptest.NewRequest("GET", "/applications/1", nil) req := httptest.NewRequest("GET", "/applications/1", nil)
req.SetPathValue("id", "1") // Go 1.22 feature
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.GetApplicationByID(rr, req, 1) handler.GetApplicationByID(rr, req)
assert.Equal(t, http.StatusOK, rr.Code) assert.Equal(t, http.StatusOK, rr.Code)
var app models.Application var app models.Application
err := json.Unmarshal(rr.Body.Bytes(), &app) err := json.Unmarshal(rr.Body.Bytes(), &app)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 1, app.ID) assert.Equal(t, "1", app.ID)
} }
func TestGetApplicationByID_NotFound(t *testing.T) { func TestGetApplicationByID_NotFound(t *testing.T) {
mockService := &mockApplicationService{ mockService := &mockApplicationService{
getApplicationByIDFunc: func(id int) (*models.Application, error) { getApplicationByIDFunc: func(id string) (*models.Application, error) {
return nil, assert.AnError return nil, assert.AnError
}, },
} }
@ -318,9 +326,10 @@ func TestGetApplicationByID_NotFound(t *testing.T) {
handler := newTestableApplicationHandler(mockService) handler := newTestableApplicationHandler(mockService)
req := httptest.NewRequest("GET", "/applications/999", nil) req := httptest.NewRequest("GET", "/applications/999", nil)
req.SetPathValue("id", "999")
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.GetApplicationByID(rr, req, 999) handler.GetApplicationByID(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code) assert.Equal(t, http.StatusNotFound, rr.Code)
} }
@ -329,10 +338,10 @@ func TestUpdateApplicationStatus_Success(t *testing.T) {
name := "John Doe" name := "John Doe"
mockService := &mockApplicationService{ mockService := &mockApplicationService{
updateApplicationStatusFunc: func(id int, req dto.UpdateApplicationStatusRequest) (*models.Application, error) { updateApplicationStatusFunc: func(id string, req dto.UpdateApplicationStatusRequest) (*models.Application, error) {
return &models.Application{ return &models.Application{
ID: id, ID: id,
JobID: 1, JobID: "1",
Name: &name, Name: &name,
Status: req.Status, Status: req.Status,
CreatedAt: time.Now(), CreatedAt: time.Now(),
@ -349,10 +358,11 @@ func TestUpdateApplicationStatus_Success(t *testing.T) {
body, _ := json.Marshal(statusReq) body, _ := json.Marshal(statusReq)
req := httptest.NewRequest("PUT", "/applications/1/status", bytes.NewReader(body)) req := httptest.NewRequest("PUT", "/applications/1/status", bytes.NewReader(body))
req.SetPathValue("id", "1")
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.UpdateApplicationStatus(rr, req, 1) handler.UpdateApplicationStatus(rr, req)
assert.Equal(t, http.StatusOK, rr.Code) assert.Equal(t, http.StatusOK, rr.Code)
@ -368,17 +378,18 @@ func TestUpdateApplicationStatus_InvalidJSON(t *testing.T) {
handler := newTestableApplicationHandler(mockService) handler := newTestableApplicationHandler(mockService)
req := httptest.NewRequest("PUT", "/applications/1/status", bytes.NewReader([]byte("invalid"))) req := httptest.NewRequest("PUT", "/applications/1/status", bytes.NewReader([]byte("invalid")))
req.SetPathValue("id", "1")
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.UpdateApplicationStatus(rr, req, 1) handler.UpdateApplicationStatus(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code) assert.Equal(t, http.StatusBadRequest, rr.Code)
} }
func TestUpdateApplicationStatus_Error(t *testing.T) { func TestUpdateApplicationStatus_Error(t *testing.T) {
mockService := &mockApplicationService{ mockService := &mockApplicationService{
updateApplicationStatusFunc: func(id int, req dto.UpdateApplicationStatusRequest) (*models.Application, error) { updateApplicationStatusFunc: func(id string, req dto.UpdateApplicationStatusRequest) (*models.Application, error) {
return nil, assert.AnError return nil, assert.AnError
}, },
} }
@ -389,10 +400,11 @@ func TestUpdateApplicationStatus_Error(t *testing.T) {
body, _ := json.Marshal(statusReq) body, _ := json.Marshal(statusReq)
req := httptest.NewRequest("PUT", "/applications/1/status", bytes.NewReader(body)) req := httptest.NewRequest("PUT", "/applications/1/status", bytes.NewReader(body))
req.SetPathValue("id", "1")
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.UpdateApplicationStatus(rr, req, 1) handler.UpdateApplicationStatus(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code) assert.Equal(t, http.StatusInternalServerError, rr.Code)
} }

View file

@ -40,7 +40,7 @@ func NewJobHandler(service *services.JobService) *JobHandler {
func (h *JobHandler) GetJobs(w http.ResponseWriter, r *http.Request) { func (h *JobHandler) GetJobs(w http.ResponseWriter, r *http.Request) {
page, _ := strconv.Atoi(r.URL.Query().Get("page")) page, _ := strconv.Atoi(r.URL.Query().Get("page"))
limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
companyID, _ := strconv.Atoi(r.URL.Query().Get("companyId")) companyID := r.URL.Query().Get("companyId")
isFeaturedStr := r.URL.Query().Get("featured") isFeaturedStr := r.URL.Query().Get("featured")
// Extraction of filters // Extraction of filters
@ -55,7 +55,7 @@ func (h *JobHandler) GetJobs(w http.ResponseWriter, r *http.Request) {
Limit: limit, Limit: limit,
}, },
} }
if companyID > 0 { if companyID != "" {
filter.CompanyID = &companyID filter.CompanyID = &companyID
} }
if isFeaturedStr == "true" { if isFeaturedStr == "true" {
@ -137,13 +137,7 @@ func (h *JobHandler) CreateJob(w http.ResponseWriter, r *http.Request) {
// @Failure 404 {string} string "Not Found" // @Failure 404 {string} string "Not Found"
// @Router /api/v1/jobs/{id} [get] // @Router /api/v1/jobs/{id} [get]
func (h *JobHandler) GetJobByID(w http.ResponseWriter, r *http.Request) { func (h *JobHandler) GetJobByID(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id") // Go 1.22+ routing id := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid job ID", http.StatusBadRequest)
return
}
job, err := h.Service.GetJobByID(id) job, err := h.Service.GetJobByID(id)
if err != nil { if err != nil {
@ -168,12 +162,7 @@ func (h *JobHandler) GetJobByID(w http.ResponseWriter, r *http.Request) {
// @Failure 500 {string} string "Internal Server Error" // @Failure 500 {string} string "Internal Server Error"
// @Router /api/v1/jobs/{id} [put] // @Router /api/v1/jobs/{id} [put]
func (h *JobHandler) UpdateJob(w http.ResponseWriter, r *http.Request) { func (h *JobHandler) UpdateJob(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id") id := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid job ID", http.StatusBadRequest)
return
}
var req dto.UpdateJobRequest var req dto.UpdateJobRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@ -203,12 +192,7 @@ func (h *JobHandler) UpdateJob(w http.ResponseWriter, r *http.Request) {
// @Failure 500 {string} string "Internal Server Error" // @Failure 500 {string} string "Internal Server Error"
// @Router /api/v1/jobs/{id} [delete] // @Router /api/v1/jobs/{id} [delete]
func (h *JobHandler) DeleteJob(w http.ResponseWriter, r *http.Request) { func (h *JobHandler) DeleteJob(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id") id := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid job ID", http.StatusBadRequest)
return
}
if err := h.Service.DeleteJob(id); err != nil { if err := h.Service.DeleteJob(id); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

View file

@ -17,9 +17,9 @@ import (
type mockJobService struct { type mockJobService struct {
getJobsFunc func(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error) getJobsFunc func(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error)
createJobFunc func(req dto.CreateJobRequest) (*models.Job, error) createJobFunc func(req dto.CreateJobRequest) (*models.Job, error)
getJobByIDFunc func(id int) (*models.Job, error) getJobByIDFunc func(id string) (*models.Job, error)
updateJobFunc func(id int, req dto.UpdateJobRequest) (*models.Job, error) updateJobFunc func(id string, req dto.UpdateJobRequest) (*models.Job, error)
deleteJobFunc func(id int) error deleteJobFunc func(id string) error
} }
func (m *mockJobService) GetJobs(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error) { func (m *mockJobService) GetJobs(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error) {
@ -36,21 +36,21 @@ func (m *mockJobService) CreateJob(req dto.CreateJobRequest) (*models.Job, error
return nil, nil return nil, nil
} }
func (m *mockJobService) GetJobByID(id int) (*models.Job, error) { func (m *mockJobService) GetJobByID(id string) (*models.Job, error) {
if m.getJobByIDFunc != nil { if m.getJobByIDFunc != nil {
return m.getJobByIDFunc(id) return m.getJobByIDFunc(id)
} }
return nil, nil return nil, nil
} }
func (m *mockJobService) UpdateJob(id int, req dto.UpdateJobRequest) (*models.Job, error) { func (m *mockJobService) UpdateJob(id string, req dto.UpdateJobRequest) (*models.Job, error) {
if m.updateJobFunc != nil { if m.updateJobFunc != nil {
return m.updateJobFunc(id, req) return m.updateJobFunc(id, req)
} }
return nil, nil return nil, nil
} }
func (m *mockJobService) DeleteJob(id int) error { func (m *mockJobService) DeleteJob(id string) error {
if m.deleteJobFunc != nil { if m.deleteJobFunc != nil {
return m.deleteJobFunc(id) return m.deleteJobFunc(id)
} }
@ -61,9 +61,9 @@ func (m *mockJobService) DeleteJob(id int) error {
type JobServiceInterface interface { type JobServiceInterface interface {
GetJobs(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error) GetJobs(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error)
CreateJob(req dto.CreateJobRequest) (*models.Job, error) CreateJob(req dto.CreateJobRequest) (*models.Job, error)
GetJobByID(id int) (*models.Job, error) GetJobByID(id string) (*models.Job, error)
UpdateJob(id int, req dto.UpdateJobRequest) (*models.Job, error) UpdateJob(id string, req dto.UpdateJobRequest) (*models.Job, error)
DeleteJob(id int) error DeleteJob(id string) error
} }
// testableJobHandler wraps an interface for testing // testableJobHandler wraps an interface for testing
@ -112,7 +112,8 @@ func (h *testableJobHandler) CreateJob(w http.ResponseWriter, r *http.Request) {
} }
func (h *testableJobHandler) GetJobByID(w http.ResponseWriter, r *http.Request) { func (h *testableJobHandler) GetJobByID(w http.ResponseWriter, r *http.Request) {
job, err := h.service.GetJobByID(1) // simplified for testing id := r.PathValue("id")
job, err := h.service.GetJobByID(id)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusNotFound) http.Error(w, err.Error(), http.StatusNotFound)
return return
@ -123,7 +124,8 @@ func (h *testableJobHandler) GetJobByID(w http.ResponseWriter, r *http.Request)
} }
func (h *testableJobHandler) DeleteJob(w http.ResponseWriter, r *http.Request) { func (h *testableJobHandler) DeleteJob(w http.ResponseWriter, r *http.Request) {
if err := h.service.DeleteJob(1); err != nil { id := r.PathValue("id")
if err := h.service.DeleteJob(id); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -140,8 +142,8 @@ func TestGetJobs_Success(t *testing.T) {
return []models.JobWithCompany{ return []models.JobWithCompany{
{ {
Job: models.Job{ Job: models.Job{
ID: 1, ID: "1",
CompanyID: 1, CompanyID: "1",
Title: "Software Engineer", Title: "Software Engineer",
Status: "open", Status: "open",
CreatedAt: time.Now(), CreatedAt: time.Now(),
@ -151,8 +153,8 @@ func TestGetJobs_Success(t *testing.T) {
}, },
{ {
Job: models.Job{ Job: models.Job{
ID: 2, ID: "2",
CompanyID: 1, CompanyID: "1",
Title: "DevOps Engineer", Title: "DevOps Engineer",
Status: "open", Status: "open",
CreatedAt: time.Now(), CreatedAt: time.Now(),
@ -219,7 +221,7 @@ func TestCreateJob_Success(t *testing.T) {
mockService := &mockJobService{ mockService := &mockJobService{
createJobFunc: func(req dto.CreateJobRequest) (*models.Job, error) { createJobFunc: func(req dto.CreateJobRequest) (*models.Job, error) {
return &models.Job{ return &models.Job{
ID: 1, ID: "1",
CompanyID: req.CompanyID, CompanyID: req.CompanyID,
Title: req.Title, Title: req.Title,
Description: req.Description, Description: req.Description,
@ -233,7 +235,7 @@ func TestCreateJob_Success(t *testing.T) {
handler := newTestableJobHandler(mockService) handler := newTestableJobHandler(mockService)
jobReq := dto.CreateJobRequest{ jobReq := dto.CreateJobRequest{
CompanyID: 1, CompanyID: "1",
Title: "Backend Developer", Title: "Backend Developer",
Description: "Build awesome APIs", Description: "Build awesome APIs",
Status: "open", Status: "open",
@ -278,7 +280,7 @@ func TestCreateJob_ServiceError(t *testing.T) {
handler := newTestableJobHandler(mockService) handler := newTestableJobHandler(mockService)
jobReq := dto.CreateJobRequest{ jobReq := dto.CreateJobRequest{
CompanyID: 1, CompanyID: "1",
Title: "Backend Developer", Title: "Backend Developer",
Description: "Build awesome APIs", Description: "Build awesome APIs",
Status: "open", Status: "open",
@ -296,10 +298,10 @@ func TestCreateJob_ServiceError(t *testing.T) {
func TestGetJobByID_Success(t *testing.T) { func TestGetJobByID_Success(t *testing.T) {
mockService := &mockJobService{ mockService := &mockJobService{
getJobByIDFunc: func(id int) (*models.Job, error) { getJobByIDFunc: func(id string) (*models.Job, error) {
return &models.Job{ return &models.Job{
ID: id, ID: id,
CompanyID: 1, CompanyID: "1",
Title: "Software Engineer", Title: "Software Engineer",
Description: "Great job opportunity", Description: "Great job opportunity",
Status: "open", Status: "open",
@ -312,6 +314,7 @@ func TestGetJobByID_Success(t *testing.T) {
handler := newTestableJobHandler(mockService) handler := newTestableJobHandler(mockService)
req := httptest.NewRequest("GET", "/jobs/1", nil) req := httptest.NewRequest("GET", "/jobs/1", nil)
req.SetPathValue("id", "1")
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.GetJobByID(rr, req) handler.GetJobByID(rr, req)
@ -326,7 +329,7 @@ func TestGetJobByID_Success(t *testing.T) {
func TestGetJobByID_NotFound(t *testing.T) { func TestGetJobByID_NotFound(t *testing.T) {
mockService := &mockJobService{ mockService := &mockJobService{
getJobByIDFunc: func(id int) (*models.Job, error) { getJobByIDFunc: func(id string) (*models.Job, error) {
return nil, assert.AnError return nil, assert.AnError
}, },
} }
@ -334,6 +337,7 @@ func TestGetJobByID_NotFound(t *testing.T) {
handler := newTestableJobHandler(mockService) handler := newTestableJobHandler(mockService)
req := httptest.NewRequest("GET", "/jobs/999", nil) req := httptest.NewRequest("GET", "/jobs/999", nil)
req.SetPathValue("id", "999")
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.GetJobByID(rr, req) handler.GetJobByID(rr, req)
@ -343,7 +347,7 @@ func TestGetJobByID_NotFound(t *testing.T) {
func TestDeleteJob_Success(t *testing.T) { func TestDeleteJob_Success(t *testing.T) {
mockService := &mockJobService{ mockService := &mockJobService{
deleteJobFunc: func(id int) error { deleteJobFunc: func(id string) error {
return nil return nil
}, },
} }
@ -351,6 +355,7 @@ func TestDeleteJob_Success(t *testing.T) {
handler := newTestableJobHandler(mockService) handler := newTestableJobHandler(mockService)
req := httptest.NewRequest("DELETE", "/jobs/1", nil) req := httptest.NewRequest("DELETE", "/jobs/1", nil)
req.SetPathValue("id", "1")
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.DeleteJob(rr, req) handler.DeleteJob(rr, req)
@ -360,7 +365,7 @@ func TestDeleteJob_Success(t *testing.T) {
func TestDeleteJob_Error(t *testing.T) { func TestDeleteJob_Error(t *testing.T) {
mockService := &mockJobService{ mockService := &mockJobService{
deleteJobFunc: func(id int) error { deleteJobFunc: func(id string) error {
return assert.AnError return assert.AnError
}, },
} }
@ -368,6 +373,7 @@ func TestDeleteJob_Error(t *testing.T) {
handler := newTestableJobHandler(mockService) handler := newTestableJobHandler(mockService)
req := httptest.NewRequest("DELETE", "/jobs/1", nil) req := httptest.NewRequest("DELETE", "/jobs/1", nil)
req.SetPathValue("id", "1")
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
handler.DeleteJob(rr, req) handler.DeleteJob(rr, req)

View file

@ -4,9 +4,9 @@ import "time"
// Application represents a job application (from user or guest) // Application represents a job application (from user or guest)
type Application struct { type Application struct {
ID int `json:"id" db:"id"` ID string `json:"id" db:"id"`
JobID int `json:"jobId" db:"job_id"` JobID string `json:"jobId" db:"job_id"`
UserID *int `json:"userId,omitempty" db:"user_id"` // NULL for guest applications UserID *string `json:"userId,omitempty" db:"user_id"` // NULL for guest applications
// Applicant Info (for guest applications) // Applicant Info (for guest applications)
Name *string `json:"name,omitempty" db:"name"` Name *string `json:"name,omitempty" db:"name"`
@ -33,7 +33,7 @@ type Application struct {
type ApplicationWithDetails struct { type ApplicationWithDetails struct {
Application Application
JobTitle string `json:"jobTitle"` JobTitle string `json:"jobTitle"`
CompanyID int `json:"companyId"` CompanyID string `json:"companyId"`
CompanyName string `json:"companyName"` CompanyName string `json:"companyName"`
ApplicantName string `json:"applicantName"` // From user or guest name ApplicantName string `json:"applicantName"` // From user or guest name
ApplicantPhone *string `json:"applicantPhone,omitempty"` ApplicantPhone *string `json:"applicantPhone,omitempty"`

View file

@ -4,7 +4,7 @@ import "time"
// Company represents an employer organization // Company represents an employer organization
type Company struct { type Company struct {
ID int `json:"id" db:"id"` ID string `json:"id" db:"id"`
Name string `json:"name" db:"name"` Name string `json:"name" db:"name"`
Slug string `json:"slug" db:"slug"` Slug string `json:"slug" db:"slug"`
Type string `json:"type" db:"type"` Type string `json:"type" db:"type"`

View file

@ -4,9 +4,9 @@ import "time"
// FavoriteJob represents a saved/favorited job by a user // FavoriteJob represents a saved/favorited job by a user
type FavoriteJob struct { type FavoriteJob struct {
ID int `json:"id" db:"id"` ID string `json:"id" db:"id"`
UserID int `json:"userId" db:"user_id"` UserID string `json:"userId" db:"user_id"`
JobID int `json:"jobId" db:"job_id"` JobID string `json:"jobId" db:"job_id"`
CreatedAt time.Time `json:"createdAt" db:"created_at"` CreatedAt time.Time `json:"createdAt" db:"created_at"`
} }
@ -14,7 +14,7 @@ type FavoriteJob struct {
type FavoriteJobWithDetails struct { type FavoriteJobWithDetails struct {
FavoriteJob FavoriteJob
JobTitle string `json:"jobTitle"` JobTitle string `json:"jobTitle"`
CompanyID int `json:"companyId"` CompanyID string `json:"companyId"`
CompanyName string `json:"companyName"` CompanyName string `json:"companyName"`
CompanyLogoURL *string `json:"companyLogoUrl,omitempty"` CompanyLogoURL *string `json:"companyLogoUrl,omitempty"`
Location *string `json:"location,omitempty"` Location *string `json:"location,omitempty"`

View file

@ -4,9 +4,9 @@ import "time"
// Job represents a job posting // Job represents a job posting
type Job struct { type Job struct {
ID int `json:"id" db:"id"` ID string `json:"id" db:"id"`
CompanyID int `json:"companyId" db:"company_id"` CompanyID string `json:"companyId" db:"company_id"`
CreatedBy int `json:"createdBy" db:"created_by"` CreatedBy string `json:"createdBy" db:"created_by"`
// Job Details // Job Details
Title string `json:"title" db:"title"` Title string `json:"title" db:"title"`

View file

@ -4,7 +4,7 @@ import "time"
// User represents a system user (SuperAdmin, CompanyAdmin, Recruiter, or JobSeeker) // User represents a system user (SuperAdmin, CompanyAdmin, Recruiter, or JobSeeker)
type User struct { type User struct {
ID int `json:"id" db:"id"` ID string `json:"id" db:"id"`
Identifier string `json:"identifier" db:"identifier"` Identifier string `json:"identifier" db:"identifier"`
PasswordHash string `json:"-" db:"password_hash"` // Never expose password hash in JSON PasswordHash string `json:"-" db:"password_hash"` // Never expose password hash in JSON
Role string `json:"role" db:"role"` // superadmin, companyAdmin, recruiter, jobSeeker Role string `json:"role" db:"role"` // superadmin, companyAdmin, recruiter, jobSeeker
@ -28,7 +28,7 @@ type User struct {
// UserResponse is the public representation of a user (without sensitive data) // UserResponse is the public representation of a user (without sensitive data)
type UserResponse struct { type UserResponse struct {
ID int `json:"id"` ID string `json:"id"`
Identifier string `json:"identifier"` Identifier string `json:"identifier"`
Role string `json:"role"` Role string `json:"role"`
FullName string `json:"fullName"` FullName string `json:"fullName"`

View file

@ -8,9 +8,9 @@ import (
// UserCompany represents the N:M relationship between users and companies // UserCompany represents the N:M relationship between users and companies
type UserCompany struct { type UserCompany struct {
ID int `json:"id" db:"id"` ID string `json:"id" db:"id"`
UserID int `json:"userId" db:"user_id"` UserID string `json:"userId" db:"user_id"`
CompanyID int `json:"companyId" db:"company_id"` CompanyID string `json:"companyId" db:"company_id"`
Role string `json:"role" db:"role"` // companyAdmin, recruiter Role string `json:"role" db:"role"` // companyAdmin, recruiter
Permissions JSONMap `json:"permissions,omitempty" db:"permissions"` Permissions JSONMap `json:"permissions,omitempty" db:"permissions"`
CreatedAt time.Time `json:"createdAt" db:"created_at"` CreatedAt time.Time `json:"createdAt" db:"created_at"`

View file

@ -69,7 +69,7 @@ func (s *AdminService) ListCompanies(ctx context.Context, verified *bool) ([]mod
return companies, nil return companies, nil
} }
func (s *AdminService) UpdateCompanyStatus(ctx context.Context, id int, active *bool, verified *bool) (*models.Company, error) { func (s *AdminService) UpdateCompanyStatus(ctx context.Context, id string, active *bool, verified *bool) (*models.Company, error) {
company, err := s.getCompanyByID(ctx, id) company, err := s.getCompanyByID(ctx, id)
if err != nil { if err != nil {
return nil, err return nil, err
@ -96,7 +96,7 @@ func (s *AdminService) UpdateCompanyStatus(ctx context.Context, id int, active *
return company, nil return company, nil
} }
func (s *AdminService) DuplicateJob(ctx context.Context, id int) (*models.Job, error) { func (s *AdminService) DuplicateJob(ctx context.Context, id string) (*models.Job, error) {
query := ` query := `
SELECT company_id, created_by, title, description, salary_min, salary_max, salary_type, SELECT company_id, created_by, title, description, salary_min, salary_max, salary_type,
employment_type, work_mode, working_hours, location, region_id, city_id, employment_type, work_mode, working_hours, location, region_id, city_id,
@ -446,7 +446,7 @@ func isActiveApplicationStatus(status string) bool {
} }
} }
func (s *AdminService) getCompanyByID(ctx context.Context, id int) (*models.Company, error) { func (s *AdminService) getCompanyByID(ctx context.Context, id string) (*models.Company, error) {
query := ` query := `
SELECT id, name, slug, type, document, address, region_id, city_id, phone, email, website, logo_url, description, active, verified, created_at, updated_at SELECT id, name, slug, type, document, address, region_id, city_id, phone, email, website, logo_url, description, active, verified, created_at, updated_at
FROM companies WHERE id = $1 FROM companies WHERE id = $1

View file

@ -54,7 +54,7 @@ func (s *ApplicationService) CreateApplication(req dto.CreateApplicationRequest)
return app, nil return app, nil
} }
func (s *ApplicationService) GetApplications(jobID int) ([]models.Application, error) { func (s *ApplicationService) GetApplications(jobID string) ([]models.Application, error) {
// Simple get by Job ID // Simple get by Job ID
query := ` query := `
SELECT id, job_id, user_id, name, phone, line_id, whatsapp, email, SELECT id, job_id, user_id, name, phone, line_id, whatsapp, email,
@ -81,7 +81,7 @@ func (s *ApplicationService) GetApplications(jobID int) ([]models.Application, e
return apps, nil return apps, nil
} }
func (s *ApplicationService) GetApplicationByID(id int) (*models.Application, error) { func (s *ApplicationService) GetApplicationByID(id string) (*models.Application, error) {
var a models.Application var a models.Application
query := ` query := `
SELECT id, job_id, user_id, name, phone, line_id, whatsapp, email, SELECT id, job_id, user_id, name, phone, line_id, whatsapp, email,
@ -98,7 +98,7 @@ func (s *ApplicationService) GetApplicationByID(id int) (*models.Application, er
return &a, nil return &a, nil
} }
func (s *ApplicationService) UpdateApplicationStatus(id int, req dto.UpdateApplicationStatusRequest) (*models.Application, error) { func (s *ApplicationService) UpdateApplicationStatus(id string, req dto.UpdateApplicationStatusRequest) (*models.Application, error) {
query := ` query := `
UPDATE applications SET status = $1, updated_at = NOW() UPDATE applications SET status = $1, updated_at = NOW()
WHERE id = $2 WHERE id = $2

View file

@ -173,7 +173,7 @@ func (s *JobService) GetJobs(filter dto.JobFilterQuery) ([]models.JobWithCompany
return jobs, total, nil return jobs, total, nil
} }
func (s *JobService) GetJobByID(id int) (*models.Job, error) { func (s *JobService) GetJobByID(id string) (*models.Job, error) {
var j models.Job var j models.Job
query := ` query := `
SELECT id, company_id, title, description, salary_min, salary_max, salary_type, SELECT id, company_id, title, description, salary_min, salary_max, salary_type,
@ -192,7 +192,7 @@ func (s *JobService) GetJobByID(id int) (*models.Job, error) {
return &j, nil return &j, nil
} }
func (s *JobService) UpdateJob(id int, req dto.UpdateJobRequest) (*models.Job, error) { func (s *JobService) UpdateJob(id string, req dto.UpdateJobRequest) (*models.Job, error) {
var setClauses []string var setClauses []string
var args []interface{} var args []interface{}
argId := 1 argId := 1
@ -232,7 +232,7 @@ func (s *JobService) UpdateJob(id int, req dto.UpdateJobRequest) (*models.Job, e
return s.GetJobByID(id) return s.GetJobByID(id)
} }
func (s *JobService) DeleteJob(id int) error { func (s *JobService) DeleteJob(id string) error {
_, err := s.DB.Exec("DELETE FROM jobs WHERE id = $1", id) _, err := s.DB.Exec("DELETE FROM jobs WHERE id = $1", id)
return err return err
} }

View file

@ -29,21 +29,21 @@ func TestCreateJob(t *testing.T) {
{ {
name: "Success", name: "Success",
req: dto.CreateJobRequest{ req: dto.CreateJobRequest{
CompanyID: 1, CompanyID: "1",
Title: "Go Developer", Title: "Go Developer",
Status: "published", Status: "published",
}, },
mockRun: func() { mockRun: func() {
mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO jobs`)). mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO jobs`)).
WithArgs(1, "Go Developer", sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), "published", sqlmock.AnyArg(), sqlmock.AnyArg()). WithArgs("1", "Go Developer", sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), "published", sqlmock.AnyArg(), sqlmock.AnyArg()).
WillReturnRows(sqlmock.NewRows([]string{"id", "created_at", "updated_at"}).AddRow(100, time.Now(), time.Now())) WillReturnRows(sqlmock.NewRows([]string{"id", "created_at", "updated_at"}).AddRow("100", time.Now(), time.Now()))
}, },
wantErr: false, wantErr: false,
}, },
{ {
name: "DB Error", name: "DB Error",
req: dto.CreateJobRequest{ req: dto.CreateJobRequest{
CompanyID: 1, CompanyID: "1",
Title: "Go Developer", Title: "Go Developer",
}, },
mockRun: func() { mockRun: func() {
@ -63,7 +63,7 @@ func TestCreateJob(t *testing.T) {
return return
} }
if !tt.wantErr { if !tt.wantErr {
assert.Equal(t, 100, got.ID) assert.Equal(t, "100", got.ID)
} }
}) })
} }
@ -99,10 +99,10 @@ func TestGetJobs(t *testing.T) {
FROM jobs j`)). FROM jobs j`)).
WillReturnRows(sqlmock.NewRows([]string{ WillReturnRows(sqlmock.NewRows([]string{
"id", "company_id", "title", "description", "salary_min", "salary_max", "salary_type", "id", "company_id", "title", "description", "salary_min", "salary_max", "salary_type",
"employment_type", "location", "status", "is_featured", "created_at", "updated_at", "employment_type", "work_mode", "location", "status", "is_featured", "created_at", "updated_at",
"company_name", "company_logo_url", "region_name", "city_name", "company_name", "company_logo_url", "region_name", "city_name",
}).AddRow( }).AddRow(
1, 10, "Dev", "Desc", 100, 200, "m", "ft", "Remote", "open", false, time.Now(), time.Now(), "1", "10", "Dev", "Desc", 100, 200, "m", "ft", "Remote", "Remote", "open", false, time.Now(), time.Now(),
"Acme", "url", "Region", "City", "Acme", "url", "Region", "City",
)) ))