Refactor backend to use string IDs for Job, Company, and Application
This commit is contained in:
parent
5e714eeb4f
commit
0e265e64b8
16 changed files with 128 additions and 157 deletions
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue