gohorsejobs/backend/tests/e2e/applications_e2e_test.go

331 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 string) {
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 string) {
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 string
// =====================
// 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("/api/v1/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 %s, got %s", jobID, app.JobID)
}
createdAppID = app.ID
t.Logf("Created application with ID: %s", createdAppID)
})
// =====================
// 2. GET APPLICATION BY ID
// =====================
t.Run("GetApplicationByID", func(t *testing.T) {
if createdAppID == "" {
t.Skip("No application was created")
}
resp, err := client.get(fmt.Sprintf("/api/v1/applications/%s", 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 %s, got %s", createdAppID, app.ID)
}
})
// =====================
// 3. LIST APPLICATIONS BY JOB
// =====================
t.Run("ListApplicationsByJob", func(t *testing.T) {
resp, err := client.get(fmt.Sprintf("/api/v1/applications?jobId=%s", 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 == "" {
t.Skip("No application was created")
}
statusReq := dto.UpdateApplicationStatusRequest{
Status: "reviewed",
}
resp, err := client.put(fmt.Sprintf("/api/v1/applications/%s/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 == "" {
t.Skip("No application was created")
}
statusReq := dto.UpdateApplicationStatusRequest{
Status: "hired",
}
resp, err := client.put(fmt.Sprintf("/api/v1/applications/%s/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("/api/v1/applications", appReq)
resp.Body.Close()
}
// List all applications for job
resp, err := client.get(fmt.Sprintf("/api/v1/applications?jobId=%s", 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("/api/v1/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("/api/v1/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)
}
})
}