package main import ( "context" "encoding/json" "net/http/httptest" "slices" "strings" "testing" "time" ) func TestQueueReportsQueuedRunningAndPosted(t *testing.T) { f := newFixture(t) q := newQueue(f.rev, 4, f.rev.log, f.metrics) if r := q.Enqueue("konairius/homelab", 7, "abc123"); r != "queued" { t.Fatalf("enqueue: %s", r) } if st, _ := f.status.Get("konairius/homelab", 7); st.Phase != PhaseQueued || st.Position != 1 { t.Errorf("before the worker runs: %+v, want queued at position 1", st) } q.Start(context.Background(), 1) q.Stop(5 * time.Second) want := []string{ "abc123 pending: queued", "abc123 pending: review running: mistral:mistral-large-4-0", "abc123 success: reviewed: request changes", } if got := f.commitStatuses(); !slices.Equal(got, want) { t.Errorf("commit statuses\n got %q\nwant %q", got, want) } st, _ := f.status.Get("konairius/homelab", 7) if st.Phase != PhasePosted || st.URL != "https://gitea/pr/7#review" || st.Head != "abc123" { t.Errorf("after the review: %+v", st) } } func TestAFailedReviewIsReportedAsAnError(t *testing.T) { f := newFixture(t) f.answer = `{"outputs":[{"type":"message.output","content":"I could not do it."}]}` q := newQueue(f.rev, 4, f.rev.log, f.metrics) q.Enqueue("konairius/homelab", 7, "abc123") q.Start(context.Background(), 1) q.Stop(5 * time.Second) got := f.commitStatuses() if last := got[len(got)-1]; last != "abc123 error: review failed: the agent's answer holds no report" { t.Errorf("last commit status %q", last) } if st, _ := f.status.Get("konairius/homelab", 7); st.Phase != PhaseFailed || st.Detail != "the agent's answer holds no report" { t.Errorf("after the failure: %+v", st) } } func TestARetryIsReported(t *testing.T) { f := newFixture(t) f.convStatus = []int{429} if err := f.rev.Review(context.Background(), "konairius/homelab", 7); err != nil { t.Fatal(err) } got := f.commitStatuses() if !slices.ContainsFunc(got, func(s string) bool { return strings.HasPrefix(s, "abc123 pending: waiting to retry: HTTP 429, retry 1/2 in ") }) { t.Errorf("no retry status in %q", got) } } func TestSkipsResolveTheQueuedStatus(t *testing.T) { f := newFixture(t) f.title = "WIP: x" if err := f.rev.Review(context.Background(), "konairius/homelab", 7); err != nil { t.Fatal(err) } f.title = "feat: x" f.reviews = `[{"body":"","state":"APPROVED","html_url":"https://gitea/r/1","user":{"login":"review-bot"}}]` if err := f.rev.Review(context.Background(), "konairius/homelab", 7); err != nil { t.Fatal(err) } want := []string{"abc123 success: skipped: wip", "abc123 success: reviewed: approve"} if got := f.commitStatuses(); !slices.Equal(got, want) { t.Errorf("commit statuses\n got %q\nwant %q", got, want) } } func TestAStaleHeadIsMarkedSuperseded(t *testing.T) { f := newFixture(t) f.pushAfter = "def456" if err := f.rev.Review(context.Background(), "konairius/homelab", 7); err != nil { t.Fatal(err) } got := f.commitStatuses() if !slices.Contains(got, "abc123 warning: superseded: new head def456") || got[len(got)-1] != "def456 success: reviewed: request changes" { t.Errorf("commit statuses %q", got) } } func TestStatusEndpoints(t *testing.T) { f := newFixture(t) q := newQueue(f.rev, 4, f.rev.log, f.metrics) s := &Server{cfg: f.cfg, queue: q, reviewer: f.rev, metrics: f.metrics, log: f.rev.log} q.Enqueue("konairius/homelab", 7, "abc123") get := func(path string) *httptest.ResponseRecorder { rec := httptest.NewRecorder() s.routes().ServeHTTP(rec, httptest.NewRequest("GET", path, nil)) return rec } var list []PRStatus if rec := get("/status"); rec.Code != 200 || json.Unmarshal(rec.Body.Bytes(), &list) != nil || len(list) != 1 || list[0].Phase != PhaseQueued || list[0].Position != 1 { t.Errorf("/status: %d %s", rec.Code, rec.Body) } var one PRStatus if rec := get("/status/konairius/homelab/7"); rec.Code != 200 || json.Unmarshal(rec.Body.Bytes(), &one) != nil || one.Repo != "konairius/homelab" || one.Number != 7 || one.Head != "abc123" { t.Errorf("/status/konairius/homelab/7: %d %s", rec.Code, rec.Body) } if rec := get("/status/konairius/homelab/8"); rec.Code != 404 { t.Errorf("untracked PR: %d, want 404", rec.Code) } if rec := get("/status/konairius/homelab/x"); rec.Code != 400 { t.Errorf("bad number: %d, want 400", rec.Code) } } func TestFinishedReviewsAreForgottenAfterADay(t *testing.T) { f := newFixture(t) now := time.Now() f.status.now = func() time.Time { return now } f.status.Set("konairius/homelab", 7, "", PhasePosted, "comment", "") f.status.Set("konairius/homelab", 8, "", PhaseQueued, "", "") now = now.Add(keepFinished + time.Minute) got := f.status.List() if len(got) != 1 || got[0].Number != 8 { t.Errorf("after a day: %+v, want only the queued #8", got) } }