package web import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "git.d-ma.be/mathias/cad-atlas/internal/atlas" ) func TestRootServesAtlas(t *testing.T) { srv := httptest.NewServer(NewHandler()) defer srv.Close() resp, err := http.Get(srv.URL + "/") if err != nil { t.Fatalf("GET /: %v", err) } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { t.Fatalf("GET / status = %d, want 200", resp.StatusCode) } buf := make([]byte, 4096) n, _ := resp.Body.Read(buf) if !strings.Contains(string(buf[:n]), "CAD") { t.Fatalf("GET / body missing atlas marker %q", "CAD") } } // This asserts the SHAPE the endpoint serves, not the exact CI job names. // // STAGE 06 has two sources: the embedded workflow (deterministic) and, when the // forge is reachable, the live latest run — which overwrites it with real job // names and a "▶ run #N" node. The old assertion demanded the workflow-derived // names, so it passed on a laptop with no route to Gitea and failed inside CI, // where the overlay works exactly as designed. It went red on 2026-08-19 for a // commit that only touched atlas.json. // // The deterministic assertion lives in the atlas package // (TestBuild_OverlaysCIStageNodesFromWorkflow), which feeds Build a fixed // workflow and no network. Here we check the endpoint serves a usable atlas // whichever source won. func TestAtlasJSON_ServesAtlasWithPopulatedCIStage(t *testing.T) { srv := httptest.NewServer(NewHandler()) defer srv.Close() resp, err := http.Get(srv.URL + "/api/atlas.json") if err != nil { t.Fatalf("GET /api/atlas.json: %v", err) } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { t.Fatalf("status = %d, want 200", resp.StatusCode) } var a atlas.Atlas if err := json.NewDecoder(resp.Body).Decode(&a); err != nil { t.Fatalf("decode atlas: %v", err) } if len(a.Stages) == 0 { t.Fatal("atlas has no stages") } var ci *atlas.Stage for i := range a.Stages { if a.Stages[i].No == "STAGE 06" { ci = &a.Stages[i] } } if ci == nil { t.Fatal("STAGE 06 missing from the served atlas") } if len(ci.Nodes) == 0 { t.Fatal("STAGE 06 has no nodes — neither the embedded workflow nor the live run populated it") } for _, n := range ci.Nodes { if n.Title == "" { t.Fatalf("STAGE 06 node with no title: %+v", n) } } } func TestUnknownPath404(t *testing.T) { srv := httptest.NewServer(NewHandler()) defer srv.Close() resp, err := http.Get(srv.URL + "/nope") if err != nil { t.Fatalf("GET /nope: %v", err) } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusNotFound { t.Fatalf("GET /nope status = %d, want 404", resp.StatusCode) } }