330 lines
8.3 KiB
Go
330 lines
8.3 KiB
Go
//go:build e2e
|
|
// +build e2e
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rede5/gohorsejobs/backend/internal/database"
|
|
"github.com/rede5/gohorsejobs/backend/internal/dto"
|
|
"github.com/rede5/gohorsejobs/backend/internal/models"
|
|
)
|
|
|
|
// setupTestJobForApplications creates a test company, user, and job for application tests
|
|
func setupTestJobForApplications(t *testing.T) (companyID, userID, jobID int) {
|
|
t.Helper()
|
|
|
|
// Create user first
|
|
userQuery := `
|
|
INSERT INTO users (identifier, password_hash, role, full_name, created_at, updated_at)
|
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
ON CONFLICT (identifier) DO UPDATE SET password_hash = $2
|
|
RETURNING id
|
|
`
|
|
err := database.DB.QueryRow(
|
|
userQuery,
|
|
"e2e-test-apps-user",
|
|
"hashedpassword",
|
|
"superadmin",
|
|
"E2E Test Apps User",
|
|
time.Now(),
|
|
time.Now(),
|
|
).Scan(&userID)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test user: %v", err)
|
|
}
|
|
|
|
// Create company
|
|
companyQuery := `
|
|
INSERT INTO companies (name, slug, type, active, verified, created_at, updated_at)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
ON CONFLICT (slug) DO UPDATE SET name = $1
|
|
RETURNING id
|
|
`
|
|
err = database.DB.QueryRow(
|
|
companyQuery,
|
|
"E2E Test Company for Apps",
|
|
"e2e-test-company-apps",
|
|
"employer",
|
|
true,
|
|
false,
|
|
time.Now(),
|
|
time.Now(),
|
|
).Scan(&companyID)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test company: %v", err)
|
|
}
|
|
|
|
// Create job
|
|
jobQuery := `
|
|
INSERT INTO jobs (company_id, created_by, title, description, status, visa_support, created_at, updated_at)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
RETURNING id
|
|
`
|
|
err = database.DB.QueryRow(
|
|
jobQuery,
|
|
companyID,
|
|
userID,
|
|
"E2E Test Job for Applications",
|
|
"Test job to receive applications",
|
|
"open",
|
|
false,
|
|
time.Now(),
|
|
time.Now(),
|
|
).Scan(&jobID)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test job: %v", err)
|
|
}
|
|
|
|
return companyID, userID, jobID
|
|
}
|
|
|
|
// cleanupTestJobForApplications removes test data
|
|
func cleanupTestJobForApplications(t *testing.T, companyID, userID, jobID int) {
|
|
t.Helper()
|
|
database.DB.Exec("DELETE FROM applications WHERE job_id = $1", jobID)
|
|
database.DB.Exec("DELETE FROM jobs WHERE id = $1", jobID)
|
|
database.DB.Exec("DELETE FROM companies WHERE id = $1", companyID)
|
|
}
|
|
|
|
// TestE2E_Applications_CRUD tests the complete application flow
|
|
func TestE2E_Applications_CRUD(t *testing.T) {
|
|
client := newTestClient()
|
|
companyID, userID, jobID := setupTestJobForApplications(t)
|
|
defer cleanupTestJobForApplications(t, companyID, userID, jobID)
|
|
|
|
var createdAppID int
|
|
|
|
// =====================
|
|
// 1. CREATE APPLICATION
|
|
// =====================
|
|
t.Run("CreateApplication", func(t *testing.T) {
|
|
name := "John Doe E2E Test"
|
|
email := "john.e2e@test.com"
|
|
phone := "+81-90-1234-5678"
|
|
message := "I am interested in this position. This is an E2E test application."
|
|
|
|
appReq := dto.CreateApplicationRequest{
|
|
JobID: jobID,
|
|
Name: &name,
|
|
Email: &email,
|
|
Phone: &phone,
|
|
Message: &message,
|
|
}
|
|
|
|
resp, err := client.post("/applications", appReq)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create application: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusCreated {
|
|
t.Errorf("Expected status 201, got %d", resp.StatusCode)
|
|
}
|
|
|
|
var app models.Application
|
|
if err := parseJSON(resp, &app); err != nil {
|
|
t.Fatalf("Failed to parse response: %v", err)
|
|
}
|
|
|
|
if app.Status != "pending" {
|
|
t.Errorf("Expected status 'pending', got '%s'", app.Status)
|
|
}
|
|
|
|
if app.JobID != jobID {
|
|
t.Errorf("Expected jobID %d, got %d", jobID, app.JobID)
|
|
}
|
|
|
|
createdAppID = app.ID
|
|
t.Logf("Created application with ID: %d", createdAppID)
|
|
})
|
|
|
|
// =====================
|
|
// 2. GET APPLICATION BY ID
|
|
// =====================
|
|
t.Run("GetApplicationByID", func(t *testing.T) {
|
|
if createdAppID == 0 {
|
|
t.Skip("No application was created")
|
|
}
|
|
|
|
resp, err := client.get(fmt.Sprintf("/applications/%d", createdAppID))
|
|
if err != nil {
|
|
t.Fatalf("Failed to get application: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Errorf("Expected status 200, got %d", resp.StatusCode)
|
|
}
|
|
|
|
var app models.Application
|
|
if err := parseJSON(resp, &app); err != nil {
|
|
t.Fatalf("Failed to parse response: %v", err)
|
|
}
|
|
|
|
if app.ID != createdAppID {
|
|
t.Errorf("Expected application ID %d, got %d", createdAppID, app.ID)
|
|
}
|
|
})
|
|
|
|
// =====================
|
|
// 3. LIST APPLICATIONS BY JOB
|
|
// =====================
|
|
t.Run("ListApplicationsByJob", func(t *testing.T) {
|
|
resp, err := client.get(fmt.Sprintf("/applications?jobId=%d", jobID))
|
|
if err != nil {
|
|
t.Fatalf("Failed to list applications: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Errorf("Expected status 200, got %d", resp.StatusCode)
|
|
}
|
|
|
|
var apps []models.Application
|
|
if err := parseJSON(resp, &apps); err != nil {
|
|
t.Fatalf("Failed to parse response: %v", err)
|
|
}
|
|
|
|
if len(apps) < 1 {
|
|
t.Error("Expected at least 1 application")
|
|
}
|
|
})
|
|
|
|
// =====================
|
|
// 4. UPDATE APPLICATION STATUS
|
|
// =====================
|
|
t.Run("UpdateStatusToReviewed", func(t *testing.T) {
|
|
if createdAppID == 0 {
|
|
t.Skip("No application was created")
|
|
}
|
|
|
|
statusReq := dto.UpdateApplicationStatusRequest{
|
|
Status: "reviewed",
|
|
}
|
|
|
|
resp, err := client.put(fmt.Sprintf("/applications/%d/status", createdAppID), statusReq)
|
|
if err != nil {
|
|
t.Fatalf("Failed to update status: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Errorf("Expected status 200, got %d", resp.StatusCode)
|
|
}
|
|
|
|
var app models.Application
|
|
if err := parseJSON(resp, &app); err != nil {
|
|
t.Fatalf("Failed to parse response: %v", err)
|
|
}
|
|
|
|
if app.Status != "reviewed" {
|
|
t.Errorf("Expected status 'reviewed', got '%s'", app.Status)
|
|
}
|
|
})
|
|
|
|
// =====================
|
|
// 5. UPDATE TO HIRED
|
|
// =====================
|
|
t.Run("UpdateStatusToHired", func(t *testing.T) {
|
|
if createdAppID == 0 {
|
|
t.Skip("No application was created")
|
|
}
|
|
|
|
statusReq := dto.UpdateApplicationStatusRequest{
|
|
Status: "hired",
|
|
}
|
|
|
|
resp, err := client.put(fmt.Sprintf("/applications/%d/status", createdAppID), statusReq)
|
|
if err != nil {
|
|
t.Fatalf("Failed to update status: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Errorf("Expected status 200, got %d", resp.StatusCode)
|
|
}
|
|
|
|
var app models.Application
|
|
if err := parseJSON(resp, &app); err != nil {
|
|
t.Fatalf("Failed to parse response: %v", err)
|
|
}
|
|
|
|
if app.Status != "hired" {
|
|
t.Errorf("Expected status 'hired', got '%s'", app.Status)
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestE2E_Applications_MultipleApplicants tests multiple applications for the same job
|
|
func TestE2E_Applications_MultipleApplicants(t *testing.T) {
|
|
client := newTestClient()
|
|
companyID, userID, jobID := setupTestJobForApplications(t)
|
|
defer cleanupTestJobForApplications(t, companyID, userID, jobID)
|
|
|
|
// Create 3 applications
|
|
applicants := []string{"Alice", "Bob", "Charlie"}
|
|
for _, name := range applicants {
|
|
appName := name + " E2E Test"
|
|
email := name + "@e2etest.com"
|
|
appReq := dto.CreateApplicationRequest{
|
|
JobID: jobID,
|
|
Name: &appName,
|
|
Email: &email,
|
|
}
|
|
resp, _ := client.post("/applications", appReq)
|
|
resp.Body.Close()
|
|
}
|
|
|
|
// List all applications for job
|
|
resp, err := client.get(fmt.Sprintf("/applications?jobId=%d", jobID))
|
|
if err != nil {
|
|
t.Fatalf("Failed to list applications: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var apps []models.Application
|
|
if err := parseJSON(resp, &apps); err != nil {
|
|
t.Fatalf("Failed to parse response: %v", err)
|
|
}
|
|
|
|
if len(apps) != 3 {
|
|
t.Errorf("Expected 3 applications, got %d", len(apps))
|
|
}
|
|
}
|
|
|
|
// TestE2E_Applications_Errors tests error handling
|
|
func TestE2E_Applications_Errors(t *testing.T) {
|
|
client := newTestClient()
|
|
|
|
t.Run("GetNonExistentApplication", func(t *testing.T) {
|
|
resp, err := client.get("/applications/999999")
|
|
if err != nil {
|
|
t.Fatalf("Failed to make request: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusNotFound {
|
|
t.Errorf("Expected status 404, got %d", resp.StatusCode)
|
|
}
|
|
})
|
|
|
|
t.Run("ListApplicationsMissingJobId", func(t *testing.T) {
|
|
resp, err := client.get("/applications")
|
|
if err != nil {
|
|
t.Fatalf("Failed to make request: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusBadRequest {
|
|
t.Errorf("Expected status 400, got %d", resp.StatusCode)
|
|
}
|
|
})
|
|
}
|