package handlers import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "github.com/go-chi/chi/v5" "github.com/orchard9/rdev/internal/domain" ) // mockDiagnosticsGetter implements DiagnosticsGetter for testing. type mockDiagnosticsGetter struct { diagnostics *domain.ProjectDiagnostics err error } func (m *mockDiagnosticsGetter) GetDiagnostics(_ context.Context, projectID string) (*domain.ProjectDiagnostics, error) { if m.err != nil { return nil, m.err } if m.diagnostics != nil { return m.diagnostics, nil } // Return default healthy diagnostics return &domain.ProjectDiagnostics{ ProjectID: projectID, GeneratedAt: time.Now().UTC(), Summary: domain.DiagnosticsSummaryHealthy, Issues: []domain.DiagnosticIssue{}, }, nil } func TestDiagnosticsHandler_GetDiagnostics_Success(t *testing.T) { getter := &mockDiagnosticsGetter{} projects := newMockProjectRepo() h := NewDiagnosticsHandler(getter, projects, nil) // Create router with chi to handle URL params r := chi.NewRouter() r.Use(testAdminAuth) // Add auth context for tests h.Mount(r) req := httptest.NewRequest("GET", "/projects/test-project/diagnostics/", nil) rec := httptest.NewRecorder() r.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Errorf("GetDiagnostics() status = %d, want %d; body = %s", rec.Code, http.StatusOK, rec.Body.String()) } var resp map[string]any if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { t.Fatalf("failed to decode response: %v", err) } data, ok := resp["data"].(map[string]any) if !ok { t.Fatalf("response missing data field") } if data["project_id"] != "test-project" { t.Errorf("project_id = %q, want %q", data["project_id"], "test-project") } if data["summary"] != domain.DiagnosticsSummaryHealthy { t.Errorf("summary = %q, want %q", data["summary"], domain.DiagnosticsSummaryHealthy) } } func TestDiagnosticsHandler_GetDiagnostics_WithIssues(t *testing.T) { getter := &mockDiagnosticsGetter{ diagnostics: &domain.ProjectDiagnostics{ ProjectID: "unhealthy-project", GeneratedAt: time.Now().UTC(), Summary: domain.DiagnosticsSummaryUnhealthy, Issues: []domain.DiagnosticIssue{ { Severity: domain.DiagnosticSeverityError, Source: domain.DiagnosticSourceCI, Message: "CI build #42 failed", }, { Severity: domain.DiagnosticSeverityWarning, Source: domain.DiagnosticSourceRegistry, Message: "Container registry slow", }, }, }, } projects := newMockProjectRepo() h := NewDiagnosticsHandler(getter, projects, nil) r := chi.NewRouter() r.Use(testAdminAuth) // Add auth context for tests h.Mount(r) req := httptest.NewRequest("GET", "/projects/unhealthy-project/diagnostics/", nil) rec := httptest.NewRecorder() r.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Errorf("GetDiagnostics() status = %d, want %d", rec.Code, http.StatusOK) } var resp map[string]any if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { t.Fatalf("failed to decode response: %v", err) } data, ok := resp["data"].(map[string]any) if !ok { t.Fatalf("response missing data field") } if data["summary"] != domain.DiagnosticsSummaryUnhealthy { t.Errorf("summary = %q, want %q", data["summary"], domain.DiagnosticsSummaryUnhealthy) } issues, ok := data["issues"].([]any) if !ok { t.Fatalf("response missing issues field") } if len(issues) != 2 { t.Errorf("issues count = %d, want %d", len(issues), 2) } } func TestDiagnosticsHandler_GetDiagnostics_MissingProjectID(t *testing.T) { getter := &mockDiagnosticsGetter{} projects := newMockProjectRepo() h := NewDiagnosticsHandler(getter, projects, nil) // Direct call without chi router to test missing projectId req := httptest.NewRequest("GET", "/projects//diagnostics/", nil) rec := httptest.NewRecorder() h.GetDiagnostics(rec, req) if rec.Code != http.StatusBadRequest { t.Errorf("GetDiagnostics() status = %d, want %d", rec.Code, http.StatusBadRequest) } }