package web
import (
"net/http/httptest"
"testing"
"time"
"craftdesign.group/quarry/internal/api"
"craftdesign.group/quarry/internal/ci"
"craftdesign.group/quarry/internal/ops"
)
// THE CI PAGE LISTS EACH SCHEDULED WORKFLOW WITH ITS CRON AND NEXT FIRE,
// AND A SCHEDULED RUN SAYS SO IN THE LIST AND ON ITS PAGE.
func TestTheCIPageShowsSchedules(t *testing.T) {
next := time.Date(2026, 10, 4, 3, 17, 0, 0, time.UTC)
runs := []api.CIRun{
{RunID: "run-1", Checkin: "abc", Workflow: "nightly", Pipeline: "ci/nightly.yml", Status: "completed", Started: "2026-10-03T03:17:00Z", Event: "schedule"},
{RunID: "run-2", Checkin: "abc", Workflow: "test", Pipeline: "ci/test.yml", Status: "completed", Started: "2026-10-03T03:10:00Z", Event: "push"},
}
r := ops.NewRegistry()
deps := api.Deps{Version: "v",
Repos: api.NewRepos(map[string]*api.Repo{"alpha": {Index: landingIndex(t, false)}}),
CI: fakeCI{runs: runs},
CISchedules: func(string) ([]ci.Scheduled, error) {
return []ci.Scheduled{{Pipeline: "ci/nightly.yml", Crons: []string{"17 3 * * *"}, Next: next,
LastFailure: "engine unreachable", LastFailureAt: next.Add(-24 * time.Hour)},
{Pipeline: "ci/bad.yml", Error: "line 3, column 13: on.schedule: fires too often"}}, nil
}}
if err := api.RegisterAll(r, deps); err != nil {
t.Fatal(err)
}
srv := httptest.NewServer(HandlerWith(r, nil, nil, []Option{
WithSessions(fakeSessions{user: "dan", roles: map[string]ops.Auth{"alpha": ops.Admin}}),
WithCaps(stubCaps{letters: "i8"}), WithEngine(fakeEngine{run: ci.Run{RunID: "run-1", Workflow: "nightly", Status: "completed"}})},
Verify(func(string, string, string, ops.Auth) api.Decision { return api.Denied })))
t.Cleanup(srv.Close)
page := asDan(t, srv, "/r/alpha/ci")
got := hookAttrs(page, "ci-schedule")
if len(got) != 2 || got[0]["data-pipeline"] != "ci/nightly.yml" || got[0]["data-cron"] != "17 3 * * *" || got[0]["data-next"] != next.Format(time.RFC3339) || got[0]["data-state"] != "firing" {
t.Errorf("FAIL: the schedule listing %v; want ci/nightly.yml, its cron and next fire", got)
}
if len(got) == 2 && (got[1]["data-pipeline"] != "ci/bad.yml" || got[1]["data-state"] != "refused" || got[1]["data-next"] != "") {
t.Errorf("FAIL: a refused schedule is listed as %v; want refused, no next fire", got[1])
}
if f := hookAttrs(page, "ci-schedule-failed"); len(f) != 1 || f[0]["data-pipeline"] != "ci/nightly.yml" {
t.Errorf("FAIL: the last failure lines %v; want one, on ci/nightly.yml", f)
}
ev := hookAttrs(page, "ci-event")
if len(ev) != 1 || ev[0]["data-event"] != "schedule" {
t.Errorf("FAIL: run list event labels %v; want the one scheduled run labelled", ev)
}
if ev := hookAttrs(asDan(t, srv, "/r/alpha/ci/run/run-1"), "ci-event"); len(ev) != 1 || ev[0]["data-event"] != "schedule" {
t.Errorf("FAIL: the run page's event label %v; want schedule", ev)
}
t.Log("ok: scheduled workflows listed with cron and next fire; scheduled runs labelled")
}