package push import ( "context" "encoding/json" "net/http" "net/http/httptest" "sync/atomic" "testing" "advisor/internal/provider" ) func sampleFindings() []provider.Finding { return []provider.Finding{ {Kind: "diagnosis", Severity: provider.SevDegraded, Summary: "1 job stalled", Evidence: []string{"73937053183639552", "unknown(0)"}, Confidence: 1.0}, } } func TestReport_PostsShapeAndAuth(t *testing.T) { var gotAuth, gotPath string var body map[string]any srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotAuth = r.Header.Get("Authorization") gotPath = r.URL.Path _ = json.NewDecoder(r.Body).Decode(&body) w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"code":200,"msg":"Success","data":{"published":1}}`)) })) defer srv.Close() p := New(srv.URL, "xce_ak_test") if err := p.Report(context.Background(), "pend_stall", "2026-10-19T08:00:00Z", sampleFindings()); err != nil { t.Fatalf("Report: %v", err) } if gotPath != "/xce/v1/advisor/findings" { t.Errorf("path = %q", gotPath) } if gotAuth != "xce_ak_test" { t.Errorf("auth = %q", gotAuth) } if body["scenario"] != "pend_stall" { t.Errorf("scenario = %v", body["scenario"]) } fs, ok := body["findings"].([]any) if !ok || len(fs) != 1 { t.Fatalf("findings = %v", body["findings"]) } } func TestReport_RetriesOnceThenSucceeds(t *testing.T) { var calls atomic.Int32 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if calls.Add(1) == 1 { w.WriteHeader(http.StatusBadGateway) return } _, _ = w.Write([]byte(`{"code":200,"msg":"Success","data":{}}`)) })) defer srv.Close() p := New(srv.URL, "tok") if err := p.Report(context.Background(), "pend_stall", "now", sampleFindings()); err != nil { t.Fatalf("retry after 502 should succeed: %v", err) } if got := calls.Load(); got != 2 { t.Fatalf("calls = %d, want 2", got) } } func TestReport_GivesUpAfterTwoFailures(t *testing.T) { var calls atomic.Int32 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { calls.Add(1) w.WriteHeader(http.StatusServiceUnavailable) })) defer srv.Close() p := New(srv.URL, "tok") if err := p.Report(context.Background(), "pend_stall", "now", sampleFindings()); err == nil { t.Fatal("expected error after both attempts failed") } if got := calls.Load(); got != 2 { t.Fatalf("calls = %d, want exactly 2 (one retry, no loop)", got) } } func TestReport_EmptyFindingsAndUnconfiguredAreNoOps(t *testing.T) { // nil pusher and empty findings must not touch the network. p := New("", "tok") if err := p.Report(context.Background(), "s", "now", nil); err != nil { t.Fatalf("empty findings: %v", err) } var q *Pusher if err := q.Report(context.Background(), "s", "now", sampleFindings()); err != nil { t.Fatalf("nil pusher: %v", err) } }