generated from mathias/template-go-web
feat(atlas): per-job CI status + build-injected version in the UI
Phase C: the CI stage now shows the latest run's per-JOB status (Lint/Test, Build, Deploy, …), each coloured by outcome, from the Gitea /actions/tasks per-job entries — replacing the static cd.yml job-id list when live. LatestRunJobs/RunNodes + RunSummary.State aggregate, all test-first. Version: injected at build via -ldflags from `git describe --tags` (CI checkout now fetch-depth:0 + --build-arg), served in /api/atlas.json, shown in the header. No more hand-maintained version label drifting from the git tag. Verified: build/vet/lint(0)/test green; version ldflag served correctly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+45
-43
@@ -6,65 +6,67 @@ import (
|
||||
"git.d-ma.be/mathias/cad-atlas/internal/atlas"
|
||||
)
|
||||
|
||||
func TestLatestRun_ReturnsNewestRun(t *testing.T) {
|
||||
tasks := []byte(`{"total_count":52,"workflow_runs":[
|
||||
{"run_number":27,"status":"success","conclusion":null,"head_sha":"633ba153f26","display_title":"Phase B tail","url":"https://git.d-ma.be/mathias/cad-atlas/actions/runs/27"},
|
||||
{"run_number":26,"status":"failure","head_sha":"aaa"}
|
||||
func TestLatestRunJobs_GroupsNewestRunReversedToPipelineOrder(t *testing.T) {
|
||||
// Gitea returns tasks newest-first (deploy finished last → appears first).
|
||||
tasks := []byte(`{"workflow_runs":[
|
||||
{"run_number":28,"name":"Deploy via GitOps","status":"success","head_sha":"633ba153f26abc","display_title":"feat: x"},
|
||||
{"run_number":28,"name":"Build & Import","status":"success"},
|
||||
{"run_number":28,"name":"Lint / Test / Vet","status":"success"},
|
||||
{"run_number":27,"name":"Deploy via GitOps","status":"failure"}
|
||||
]}`)
|
||||
|
||||
r, err := atlas.LatestRun(tasks)
|
||||
s, err := atlas.LatestRunJobs(tasks)
|
||||
if err != nil {
|
||||
t.Fatalf("LatestRun: %v", err)
|
||||
t.Fatalf("LatestRunJobs: %v", err)
|
||||
}
|
||||
if r.Number != 27 || r.SHA != "633ba153f26" || r.Title != "Phase B tail" {
|
||||
t.Fatalf("run = %+v", r)
|
||||
if s.Number != 28 || s.SHA != "633ba153f26abc" || s.Title != "feat: x" {
|
||||
t.Fatalf("summary = %+v", s)
|
||||
}
|
||||
if r.State() != "success" {
|
||||
t.Fatalf("State = %q, want success", r.State())
|
||||
// only run 28's jobs, reversed to pipeline order (Lint → Build → Deploy)
|
||||
got := []string{}
|
||||
for _, j := range s.Jobs {
|
||||
got = append(got, j.Name)
|
||||
}
|
||||
want := []string{"Lint / Test / Vet", "Build & Import", "Deploy via GitOps"}
|
||||
if len(got) != 3 || got[0] != want[0] || got[2] != want[2] {
|
||||
t.Fatalf("jobs = %v, want %v", got, want)
|
||||
}
|
||||
if s.State() != "success" {
|
||||
t.Fatalf("state = %q, want success", s.State())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLatestRun_ConclusionWinsOverStatus(t *testing.T) {
|
||||
tasks := []byte(`{"workflow_runs":[{"run_number":9,"status":"completed","conclusion":"failure"}]}`)
|
||||
r, err := atlas.LatestRun(tasks)
|
||||
if err != nil {
|
||||
t.Fatalf("LatestRun: %v", err)
|
||||
}
|
||||
if r.State() != "failure" {
|
||||
t.Fatalf("State = %q, want failure", r.State())
|
||||
func TestRunSummary_StateFailsIfAnyJobFailed(t *testing.T) {
|
||||
s := atlas.RunSummary{Jobs: []atlas.Job{
|
||||
{Status: "success"}, {Status: "completed", Conclusion: "failure"},
|
||||
}}
|
||||
if s.State() != "failure" {
|
||||
t.Fatalf("state = %q, want failure", s.State())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunNode_ColoursAndLabelsByOutcome(t *testing.T) {
|
||||
green := atlas.RunNode(atlas.Run{Number: 27, Status: "success", SHA: "633ba153f26abc", Title: "Phase B tail"})
|
||||
if green.Pill != "var(--green)" {
|
||||
t.Fatalf("success pill = %q, want var(--green)", green.Pill)
|
||||
func TestRunNodes_SummaryThenPerJobColoured(t *testing.T) {
|
||||
s := atlas.RunSummary{Number: 28, SHA: "633ba153f26", Title: "feat: x", Jobs: []atlas.Job{
|
||||
{Name: "Lint / Test / Vet", Status: "success"},
|
||||
{Name: "Deploy via GitOps", Status: "failure"},
|
||||
}}
|
||||
nodes := atlas.RunNodes(s)
|
||||
if len(nodes) != 3 {
|
||||
t.Fatalf("want 3 nodes (summary + 2 jobs), got %d", len(nodes))
|
||||
}
|
||||
if green.Title != "▶ run #27 · success" {
|
||||
t.Fatalf("title = %q", green.Title)
|
||||
if nodes[0].Title != "▶ run #28 · failure" || nodes[0].Pill != "var(--coral)" {
|
||||
t.Fatalf("summary node = %+v", nodes[0])
|
||||
}
|
||||
if green.Desc != "Phase B tail" {
|
||||
t.Fatalf("desc = %q", green.Desc)
|
||||
if nodes[1].Title != "Lint / Test / Vet" || nodes[1].Pill != "var(--green)" {
|
||||
t.Fatalf("job node 1 = %+v", nodes[1])
|
||||
}
|
||||
// short sha appears as a tag
|
||||
found := false
|
||||
for _, tag := range green.Tags {
|
||||
if tag == "633ba15" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("short sha tag missing: %v", green.Tags)
|
||||
}
|
||||
|
||||
red := atlas.RunNode(atlas.Run{Number: 5, Status: "completed", Conclusion: "failure"})
|
||||
if red.Pill != "var(--coral)" {
|
||||
t.Fatalf("failure pill = %q, want var(--coral)", red.Pill)
|
||||
if nodes[2].Pill != "var(--coral)" {
|
||||
t.Fatalf("failed job pill = %q", nodes[2].Pill)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLatestRun_ErrorsWhenNoRuns(t *testing.T) {
|
||||
if _, err := atlas.LatestRun([]byte(`{"workflow_runs":[]}`)); err == nil {
|
||||
t.Fatal("expected error on empty runs, got nil")
|
||||
func TestLatestRunJobs_ErrorsWhenEmpty(t *testing.T) {
|
||||
if _, err := atlas.LatestRunJobs([]byte(`{"workflow_runs":[]}`)); err == nil {
|
||||
t.Fatal("expected error on empty, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user