package api import ( "net/http" "net/http/httptest" "strings" "testing" ) func TestHealth(t *testing.T) { r := New(Dependencies{AuthMgr: NewStubAuthManager()}) srv := httptest.NewServer(r) defer srv.Close() resp, err := http.Get(srv.URL + "/api/v1/health") if err != nil { t.Fatalf("GET health: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("expected 200, got %d", resp.StatusCode) } } func TestLoginAndProtectedEndpoint(t *testing.T) { auth := NewStubAuthManager() r := New(Dependencies{AuthMgr: auth}) srv := httptest.NewServer(r) defer srv.Close() // Login resp, err := http.Post(srv.URL+"/api/v1/auth/login", "application/json", strings.NewReader(`{"username":"admin","password":"admin"}`)) if err != nil { t.Fatalf("POST login: %v", err) } resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("login: expected 200, got %d", resp.StatusCode) } // Use token for protected endpoint req, _ := http.NewRequest("GET", srv.URL+"/api/v1/sessions", nil) req.Header.Set("Authorization", "Bearer stub-token-admin") resp2, err := http.DefaultClient.Do(req) if err != nil { t.Fatalf("GET sessions: %v", err) } resp2.Body.Close() if resp2.StatusCode != http.StatusOK { t.Errorf("sessions: expected 200, got %d", resp2.StatusCode) } } func TestLoginWrongPassword(t *testing.T) { auth := NewStubAuthManager() r := New(Dependencies{AuthMgr: auth}) srv := httptest.NewServer(r) defer srv.Close() resp, err := http.Post(srv.URL+"/api/v1/auth/login", "application/json", strings.NewReader(`{"username":"admin","password":"wrong"}`)) if err != nil { t.Fatalf("POST login: %v", err) } resp.Body.Close() if resp.StatusCode != http.StatusUnauthorized { t.Errorf("expected 401, got %d", resp.StatusCode) } } func TestProtectedWithoutToken(t *testing.T) { auth := NewStubAuthManager() r := New(Dependencies{AuthMgr: auth}) srv := httptest.NewServer(r) defer srv.Close() resp, err := http.Get(srv.URL + "/api/v1/sessions") if err != nil { t.Fatalf("GET sessions: %v", err) } resp.Body.Close() if resp.StatusCode != http.StatusUnauthorized { t.Errorf("expected 401, got %d", resp.StatusCode) } } func TestStubAuthManager(t *testing.T) { m := NewStubAuthManager() // Login as admin token, err := m.Login("admin", "admin") if err != nil { t.Fatalf("Login: %v", err) } uid, uname, role, err := m.ValidateToken(token) if err != nil { t.Fatalf("ValidateToken: %v", err) } if uid != "admin" || uname != "admin" || role != "admin" { t.Errorf("wrong claims: %s/%s/%s", uid, uname, role) } // Invalid token _, _, _, err = m.ValidateToken("bogus") if err == nil { t.Error("expected error for bogus token") } } func TestCORSHeaders(t *testing.T) { r := New(Dependencies{AuthMgr: NewStubAuthManager()}) srv := httptest.NewServer(r) defer srv.Close() req, _ := http.NewRequest("OPTIONS", srv.URL+"/api/v1/health", nil) req.Header.Set("Origin", "http://localhost:5173") resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatalf("OPTIONS: %v", err) } resp.Body.Close() if resp.StatusCode != http.StatusNoContent { t.Errorf("expected 204 for OPTIONS, got %d", resp.StatusCode) } if resp.Header.Get("Access-Control-Allow-Origin") == "" { t.Error("expected CORS header") } }