Flux source URL, flux-git rotator verify repo, review-bot REPOS, pipeline API, clone and push URLs, the heating-can source label and the review-bot and kuma-sync Go module paths. Merge right after the transfer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
141 lines
4.7 KiB
Go
141 lines
4.7 KiB
Go
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":"<!-- pr-review:sha=abc123 -->","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)
|
|
}
|
|
}
|