generated from mathias/template-go-web
The atlas no longer hand-maintains its content. Authored data lives in one place (internal/atlas/atlas.json); the Go layer overlays sourced facts and serves the result at /api/atlas.json; the frontend fetches + renders (inline arrays kept only as an offline fallback). First generated source: the CI/CD stage's nodes are parsed from the repo's own .gitea/workflows/cd.yml — so the viz shows the pipeline that actually runs (guard/check/build/deploy), dropping the aspirational var-go/oath-gate node that isn't wired yet. That's the point: it can't drift from the real pipeline. New internal/atlas package (JobsFromWorkflow, Build) built test-first. Adds gopkg.in/yaml.v3 (justified: parsing the workflow YAML; stdlib has no YAML). Verified: go build/vet/lint(0)/test green; /api/atlas.json → 9 stages, CI = real jobs; frontend renders from the fetch (screenshot). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
84 lines
1.9 KiB
Go
84 lines
1.9 KiB
Go
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")
|
|
}
|
|
}
|
|
|
|
func TestAtlasJSON_ServesAtlasWithGeneratedCIStage(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")
|
|
}
|
|
|
|
// The generate:ci-jobs stage must be populated from the real cd.yml jobs.
|
|
got := map[string]bool{}
|
|
for _, s := range a.Stages {
|
|
if s.No == "STAGE 06" {
|
|
for _, n := range s.Nodes {
|
|
got[n.Title] = true
|
|
}
|
|
}
|
|
}
|
|
for _, want := range []string{"guard", "check", "build", "deploy"} {
|
|
if !got[want] {
|
|
t.Fatalf("CI stage missing generated job %q (got %v)", want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|