package summary import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" ) func TestPushClient_Push(t *testing.T) { var received MetricsPayload server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { t.Errorf("expected POST, got %s", r.Method) } if ct := r.Header.Get("Content-Type"); ct != "application/json" { t.Errorf("expected Content-Type application/json, got %s", ct) } if err := json.NewDecoder(r.Body).Decode(&received); err != nil { t.Errorf("failed to decode body: %v", err) } w.WriteHeader(http.StatusCreated) })) defer server.Close() client := NewPushClient(server.URL) client.ctx = ExecutionContext{ Organization: "test-org", Repository: "test-repo", Workflow: "ci.yml", Job: "build", RunID: "12345", } summary := &RunSummary{ StartTime: time.Now().Add(-time.Minute), EndTime: time.Now(), DurationSeconds: 60.0, SampleCount: 10, CPUTotal: StatSummary{Peak: 80.0, Avg: 50.0, P95: 75.0}, } err := client.Push(context.Background(), summary) if err != nil { t.Fatalf("Push() error = %v", err) } if received.Execution.Organization != "test-org" { t.Errorf("Organization = %q, want %q", received.Execution.Organization, "test-org") } if received.Execution.RunID != "12345" { t.Errorf("RunID = %q, want %q", received.Execution.RunID, "12345") } if received.Summary.SampleCount != 10 { t.Errorf("SampleCount = %d, want %d", received.Summary.SampleCount, 10) } } func TestPushClient_Push_NilSummary(t *testing.T) { client := NewPushClient("http://localhost:9999") err := client.Push(context.Background(), nil) if err != nil { t.Errorf("Push(nil) error = %v, want nil", err) } } func TestPushClient_Push_ServerError(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) defer server.Close() client := NewPushClient(server.URL) client.ctx = ExecutionContext{RunID: "test"} err := client.Push(context.Background(), &RunSummary{}) if err == nil { t.Error("Push() expected error for 500 response, got nil") } } func TestPushClient_Push_ConnectionError(t *testing.T) { client := NewPushClient("http://localhost:1") // Invalid port client.ctx = ExecutionContext{RunID: "test"} err := client.Push(context.Background(), &RunSummary{}) if err == nil { t.Error("Push() expected error for connection failure, got nil") } } func TestExecutionContextFromEnv(t *testing.T) { // Save and restore env origVars := map[string]string{ "GITHUB_REPOSITORY_OWNER": "", "GITHUB_REPOSITORY": "", "GITHUB_WORKFLOW": "", "GITHUB_JOB": "", "GITHUB_RUN_ID": "", } for k := range origVars { origVars[k] = getEnvWithFallback(k) } defer func() { for k, v := range origVars { if v == "" { t.Setenv(k, "") } } }() t.Setenv("GITHUB_REPOSITORY_OWNER", "my-org") t.Setenv("GITHUB_REPOSITORY", "my-org/my-repo") t.Setenv("GITHUB_WORKFLOW", "CI") t.Setenv("GITHUB_JOB", "test") t.Setenv("GITHUB_RUN_ID", "999") ctx := ExecutionContextFromEnv() if ctx.Organization != "my-org" { t.Errorf("Organization = %q, want %q", ctx.Organization, "my-org") } if ctx.Repository != "my-org/my-repo" { t.Errorf("Repository = %q, want %q", ctx.Repository, "my-org/my-repo") } if ctx.Workflow != "CI" { t.Errorf("Workflow = %q, want %q", ctx.Workflow, "CI") } if ctx.Job != "test" { t.Errorf("Job = %q, want %q", ctx.Job, "test") } if ctx.RunID != "999" { t.Errorf("RunID = %q, want %q", ctx.RunID, "999") } } func TestExecutionContextFromEnv_GiteaFallback(t *testing.T) { t.Setenv("GITHUB_RUN_ID", "") t.Setenv("GITEA_RUN_ID", "gitea-123") ctx := ExecutionContextFromEnv() if ctx.RunID != "gitea-123" { t.Errorf("RunID = %q, want %q (Gitea fallback)", ctx.RunID, "gitea-123") } } func TestPushClient_ExecutionContext(t *testing.T) { client := NewPushClient("http://example.com") client.ctx = ExecutionContext{ Organization: "org", Repository: "repo", RunID: "run", } ctx := client.ExecutionContext() if ctx.Organization != "org" { t.Errorf("Organization = %q, want %q", ctx.Organization, "org") } }