package test import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "gitea.avt.data-center.id/othman.suseno/atlas/internal/httpapp" ) // TestServer provides an integration test server type TestServer struct { App *httpapp.App Server *httptest.Server Client *http.Client AuthToken string } // NewTestServer creates a new test server func NewTestServer(t *testing.T) *TestServer { // Use absolute paths or create templates directory for tests templatesDir := "web/templates" staticDir := "web/static" app, err := httpapp.New(httpapp.Config{ Addr: ":0", // Use random port TemplatesDir: templatesDir, StaticDir: staticDir, DatabasePath: "", // Empty = in-memory mode (no database) }) if err != nil { t.Fatalf("create test app: %v", err) } server := httptest.NewServer(app.Router()) return &TestServer{ App: app, Server: server, Client: &http.Client{}, } } // Close shuts down the test server func (ts *TestServer) Close() { ts.Server.Close() ts.App.StopScheduler() } // Login performs a login and stores the auth token func (ts *TestServer) Login(t *testing.T, username, password string) { reqBody := map[string]string{ "username": username, "password": password, } body, _ := json.Marshal(reqBody) req, err := http.NewRequest("POST", ts.Server.URL+"/api/v1/auth/login", bytes.NewReader(body)) if err != nil { t.Fatalf("create login request: %v", err) } req.Header.Set("Content-Type", "application/json") resp, err := ts.Client.Do(req) if err != nil { t.Fatalf("login request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("login failed with status %d", resp.StatusCode) } var result map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { t.Fatalf("decode login response: %v", err) } if token, ok := result["token"].(string); ok { ts.AuthToken = token } else { t.Fatal("no token in login response") } } // Get performs an authenticated GET request func (ts *TestServer) Get(t *testing.T, path string) *http.Response { req, err := http.NewRequest("GET", ts.Server.URL+path, nil) if err != nil { t.Fatalf("create GET request: %v", err) } if ts.AuthToken != "" { req.Header.Set("Authorization", "Bearer "+ts.AuthToken) } resp, err := ts.Client.Do(req) if err != nil { t.Fatalf("GET request: %v", err) } return resp } // Post performs an authenticated POST request func (ts *TestServer) Post(t *testing.T, path string, body interface{}) *http.Response { bodyBytes, err := json.Marshal(body) if err != nil { t.Fatalf("marshal request body: %v", err) } req, err := http.NewRequest("POST", ts.Server.URL+path, bytes.NewReader(bodyBytes)) if err != nil { t.Fatalf("create POST request: %v", err) } req.Header.Set("Content-Type", "application/json") if ts.AuthToken != "" { req.Header.Set("Authorization", "Bearer "+ts.AuthToken) } resp, err := ts.Client.Do(req) if err != nil { t.Fatalf("POST request: %v", err) } return resp } // TestHealthCheck tests the health check endpoint func TestHealthCheck(t *testing.T) { ts := NewTestServer(t) defer ts.Close() resp := ts.Get(t, "/healthz") if resp.StatusCode != http.StatusOK { t.Errorf("expected status 200, got %d", resp.StatusCode) } } // TestLogin tests the login endpoint func TestLogin(t *testing.T) { ts := NewTestServer(t) defer ts.Close() // Test with default admin credentials ts.Login(t, "admin", "admin") if ts.AuthToken == "" { t.Error("expected auth token after login") } } // TestUnauthorizedAccess tests that protected endpoints require authentication func TestUnauthorizedAccess(t *testing.T) { ts := NewTestServer(t) defer ts.Close() resp := ts.Get(t, "/api/v1/pools") if resp.StatusCode != http.StatusUnauthorized { t.Errorf("expected status 401, got %d", resp.StatusCode) } } // TestAuthenticatedAccess tests that authenticated requests work func TestAuthenticatedAccess(t *testing.T) { ts := NewTestServer(t) defer ts.Close() ts.Login(t, "admin", "admin") resp := ts.Get(t, "/api/v1/pools") if resp.StatusCode != http.StatusOK { t.Errorf("expected status 200, got %d", resp.StatusCode) } }