package gitea import ( "fmt" "net/http" "net/http/httptest" "strings" "testing" "time" ) func TestTasksParsesAndSorts(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !strings.Contains(r.URL.Path, "/repos/brasse/agent-tools/actions/tasks") { t.Errorf("unexpected path %s", r.URL.Path) } if r.Header.Get("Authorization") != "token tok" { t.Errorf("token header missing") } fmt.Fprint(w, `{"workflow_runs":[ {"id":110,"name":"build","run_number":5,"status":"success"}, {"id":111,"name":"build-release","run_number":6,"status":"running"}]}`) })) defer srv.Close() tasks, err := New(srv.URL, "brasse", "tok").Tasks("agent-tools", 10) if err != nil { t.Fatal(err) } if len(tasks) != 2 || tasks[0].ID != 111 { t.Errorf("tasks not sorted newest first: %+v", tasks) } if !tasks[0].Active() || tasks[1].Active() { t.Error("Active() wrong") } } func TestPrivateRepoHint(t *testing.T) { srv := httptest.NewServer(http.NotFoundHandler()) defer srv.Close() _, err := New(srv.URL, "brasse", "").Tasks("infra-Doc", 5) if err == nil || !strings.Contains(err.Error(), "token") { t.Errorf("404 should hint about tokens, got %v", err) } } func TestLogWebRejectsHTML(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "login page") })) defer srv.Close() _, err := New(srv.URL, "brasse", "").LogWeb("x", 1, 0) if err == nil || !strings.Contains(err.Error(), "HTML") { t.Errorf("HTML response should error, got %v", err) } } func TestZstPathHexBucket(t *testing.T) { // verified against the real Pi5 layout: task 53 -> 35/53.log.zst cases := map[int64]string{ 53: "/logs/brasse/FitnessDroid/35/53.log.zst", 52: "/logs/brasse/FitnessDroid/34/52.log.zst", 86: "/logs/brasse/FitnessDroid/56/86.log.zst", 2: "/logs/brasse/FitnessDroid/02/2.log.zst", 258: "/logs/brasse/FitnessDroid/02/258.log.zst", } for id, want := range cases { if got := ZstPath("/logs", "brasse", "FitnessDroid", id); got != want { t.Errorf("ZstPath(%d) = %s, want %s", id, got, want) } } } func TestWaitRunFinishes(t *testing.T) { calls := 0 fetch := func(repo string) ([]Task, error) { calls++ status := "running" if calls >= 3 { status = "success" } return []Task{ {ID: 2, RunNumber: 7, Status: status}, {ID: 1, RunNumber: 6, Status: "failure"}, // older run must not matter }, nil } res, err := WaitRun("x", fetch, time.Millisecond, time.Second, nil) if err != nil { t.Fatal(err) } if !res.Done || !res.AllOK || res.Attempts != 3 { t.Errorf("unexpected: %+v", res) } } func TestWaitRunRedFailure(t *testing.T) { fetch := func(repo string) ([]Task, error) { return []Task{{ID: 2, RunNumber: 7, Status: "failure"}}, nil } res, err := WaitRun("x", fetch, time.Millisecond, time.Second, nil) if err != nil { t.Fatal(err) } if !res.Done || res.AllOK { t.Errorf("failure must give Done && !AllOK: %+v", res) } } func TestWaitRunTimeout(t *testing.T) { fetch := func(repo string) ([]Task, error) { return []Task{{ID: 2, RunNumber: 7, Status: "running"}}, nil } res, err := WaitRun("x", fetch, 5*time.Millisecond, 15*time.Millisecond, nil) if err != nil { t.Fatal(err) } if res.Done { t.Errorf("should have timed out: %+v", res) } } func TestWaitQuietCountsAcrossRepos(t *testing.T) { step := 0 fetch := func(repo string) ([]Task, error) { // step 0: both repos busy; step >= 1: only one if repo == "a" && step > 0 { return []Task{{ID: 1, Status: "success"}}, nil } return []Task{{ID: 2, Status: "running"}}, nil } log := func(string) { step++ } ok, err := WaitQuiet([]string{"a", "b"}, 1, fetch, time.Millisecond, time.Second, log) if err != nil { t.Fatal(err) } if !ok { t.Error("should reach quiet state") } } func TestTaskDuration(t *testing.T) { start := time.Now().Add(-90 * time.Second) tk := Task{Status: "success", RunStartedAt: start, UpdatedAt: start.Add(75 * time.Second)} if d := tk.Duration(); d != 75*time.Second { t.Errorf("duration = %s", d) } if (Task{Status: "running", RunStartedAt: start}).Duration() != 0 { t.Error("active task should have zero duration") } }