generated from mathias/template-go-web
feat(atlas): Phase B — data-driven /api/atlas.json, CI stage generated from cd.yml
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>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package atlas_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"git.d-ma.be/mathias/cad-atlas/internal/atlas"
|
||||
)
|
||||
|
||||
func TestBuild_OverlaysCIStageNodesFromWorkflow(t *testing.T) {
|
||||
atlasJSON := []byte(`{
|
||||
"substrate": [{"n":"koala","k":"gpu"}],
|
||||
"stages": [
|
||||
{"no":"STAGE 06","title":"PR → CI","generate":"ci-jobs","nodes":[]},
|
||||
{"no":"STAGE 01","title":"TELOS","nodes":[{"t":"Intention substrate"}]}
|
||||
]}`)
|
||||
workflow := []byte("jobs:\n check:\n a: 1\n build:\n b: 2\n deploy:\n c: 3\n")
|
||||
|
||||
a, err := atlas.Build(atlasJSON, workflow)
|
||||
if err != nil {
|
||||
t.Fatalf("Build: %v", err)
|
||||
}
|
||||
|
||||
// CI stage nodes generated from the workflow's jobs, in order.
|
||||
var titles []string
|
||||
for _, n := range a.Stages[0].Nodes {
|
||||
titles = append(titles, n.Title)
|
||||
}
|
||||
if want := []string{"check", "build", "deploy"}; !reflect.DeepEqual(titles, want) {
|
||||
t.Fatalf("CI stage nodes = %v, want %v", titles, want)
|
||||
}
|
||||
|
||||
// Non-generated stage is untouched.
|
||||
if a.Stages[1].Nodes[0].Title != "Intention substrate" {
|
||||
t.Fatalf("authored stage was altered: %+v", a.Stages[1])
|
||||
}
|
||||
|
||||
// Substrate preserved.
|
||||
if len(a.Substrate) != 1 || a.Substrate[0].Name != "koala" {
|
||||
t.Fatalf("substrate not preserved: %+v", a.Substrate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuild_ErrorsOnBadAtlasJSON(t *testing.T) {
|
||||
if _, err := atlas.Build([]byte("{not json"), []byte("jobs:\n x:\n a: 1\n")); err == nil {
|
||||
t.Fatal("expected error on bad atlas JSON, got nil")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user