//go:build integration // +build integration package integration import ( "bytes" "encoding/json" "io" "net/http" "net/http/httptest" "os" "testing" "time" "github.com/golang-jwt/jwt/v5" "github.com/rede5/gohorsejobs/backend/internal/database" "github.com/rede5/gohorsejobs/backend/internal/router" ) var testServer *httptest.Server func TestMain(m *testing.M) { setTestEnv() database.InitDB() testServer = httptest.NewServer(router.NewRouter()) defer testServer.Close() code := m.Run() cleanupTestData() os.Exit(code) } func setTestEnv() { if os.Getenv("DB_HOST") == "" { os.Setenv("DB_HOST", "db-60059.dc-sp-1.absamcloud.com") } if os.Getenv("DB_USER") == "" { os.Setenv("DB_USER", "yuki") } if os.Getenv("DB_PASSWORD") == "" { os.Setenv("DB_PASSWORD", "xl1zfmr6e9bb") } if os.Getenv("DB_NAME") == "" { os.Setenv("DB_NAME", "gohorsejobs_dev") } if os.Getenv("DB_PORT") == "" { os.Setenv("DB_PORT", "26868") } if os.Getenv("DB_SSLMODE") == "" { os.Setenv("DB_SSLMODE", "require") } if os.Getenv("JWT_SECRET") == "" { os.Setenv("JWT_SECRET", "gohorse-super-secret-key-2024-production") } } func cleanupTestData() { if database.DB != nil { database.DB.Exec("DELETE FROM applications WHERE id > 0") database.DB.Exec("DELETE FROM jobs WHERE title LIKE 'Int Test%'") database.DB.Exec("DELETE FROM companies WHERE name LIKE 'Int Test%'") database.DB.Exec("DELETE FROM users WHERE full_name LIKE 'Int Test%'") } } // Helpers type testClient struct { baseURL string token string } func newTestClient() *testClient { return &testClient{baseURL: testServer.URL} } func (c *testClient) setAuthToken(token string) { c.token = token } func (c *testClient) doRequest(method, path string, body interface{}) (*http.Response, error) { var reqBody io.Reader if body != nil { jsonData, err := json.Marshal(body) if err != nil { return nil, err } reqBody = bytes.NewReader(jsonData) } req, err := http.NewRequest(method, c.baseURL+path, reqBody) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") if c.token != "" { req.Header.Set("Authorization", "Bearer "+c.token) } return http.DefaultClient.Do(req) } func (c *testClient) get(path string) (*http.Response, error) { return c.doRequest("GET", path, nil) } func (c *testClient) post(path string, body interface{}) (*http.Response, error) { return c.doRequest("POST", path, body) } func (c *testClient) delete(path string) (*http.Response, error) { return c.doRequest("DELETE", path, nil) } func createAuthToken(t *testing.T, userID, tenantID string, roles []string) string { t.Helper() secret := os.Getenv("JWT_SECRET") claims := jwt.MapClaims{ "sub": userID, "tenant": tenantID, "roles": roles, "iss": "gohorse-jobs", "exp": time.Now().Add(time.Hour).Unix(), "iat": time.Now().Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) str, err := token.SignedString([]byte(secret)) if err != nil { t.Fatalf("Failed to sign token: %v", err) } return str } func setupTestCompanyAndUser(t *testing.T) (companyID, userID string) { t.Helper() // User err := database.DB.QueryRow(` INSERT INTO users (identifier, password_hash, role, full_name, email, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, NOW(), NOW()) ON CONFLICT (identifier) DO UPDATE SET full_name = $4 RETURNING id`, "int-test-user", "hash", "superadmin", "Int Test User", "int@test.com", ).Scan(&userID) if err != nil { t.Fatalf("Failed setup user: %v", err) } database.DB.Exec("INSERT INTO user_roles (user_id, role) VALUES ($1, 'superadmin') ON CONFLICT DO NOTHING", userID) // Company err = database.DB.QueryRow(` INSERT INTO companies (name, slug, type, active, verified, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, NOW(), NOW()) ON CONFLICT (slug) DO UPDATE SET name = $1 RETURNING id`, "Int Test Company", "int-test-company", "employer", true, true, ).Scan(&companyID) if err != nil { t.Fatalf("Failed setup company: %v", err) } return companyID, userID } func createTestCandidate(t *testing.T) string { var id string err := database.DB.QueryRow(` INSERT INTO users (identifier, password_hash, role, full_name, email, status, created_at, updated_at) VALUES ('int_cand_' || gen_random_uuid(), 'hash', 'candidate', 'Int Test Cand', 'cand@int.com', 'active', NOW(), NOW()) RETURNING id `).Scan(&id) if err != nil { t.Fatalf("Failed create candidate: %v", err) } database.DB.Exec("INSERT INTO user_roles (user_id, role) VALUES ($1, 'candidate')", id) return id }