Files
mesh-catalog/modules/mesh-build-go/buildstep/generated_test.go
T
jschoubben 7b08f25d4a CAT-BG4/BN1: the seat generator moves into buildstep, one program for Go and TypeScript
Its version is the build module's own, so a new generator is a new build of
mesh-build-go and mesh-build-node and a new fingerprint of what they compare;
no repository of its own. Moved from seatgen c1fdd7c unchanged but for the
import path.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 15:12:46 +01:00

135 lines
5.3 KiB
Go

package buildstep
import (
"context"
"errors"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/seatgen"
)
// contracts writes a contracts module of one seat, its generated files as the generator writes them, and answers its
// directory and the generated paths.
func contracts(t *testing.T, manifest string) (string, []string) {
t.Helper()
dir := t.TempDir()
for p, text := range map[string]string{
"module.json": manifest,
"seats/index.json": `{"format":1,"seats":["one-store"]}`,
"seats/one-store/seat.json": `{"format":1,"seat":"one-store","scope":"mesh","grain":"seat-list","decision":"ADR 1","verbs":[{"name":"query","input":{"type":"object"}}]}`,
} {
writeContract(t, filepath.Join(dir, p), text)
}
m, err := seatgen.Load(dir, "example.org/c/go", "@example/c")
if err != nil {
t.Fatal(err)
}
files, err := seatgen.Generate(m)
if err != nil {
t.Fatal(err)
}
var paths []string
for p, b := range files {
writeContract(t, filepath.Join(dir, p), string(b))
paths = append(paths, p)
}
// The names the compare reads, where the contracts module states them; a generated go.mod or package.json is kept.
if _, ok := files["go/go.mod"]; !ok {
writeContract(t, filepath.Join(dir, "go/go.mod"), "module example.org/c/go\n")
}
if _, ok := files["ts/package.json"]; !ok {
writeContract(t, filepath.Join(dir, "ts/package.json"), `{"name":"@example/c"}`)
}
return dir, paths
}
func writeContract(t *testing.T, path, text string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(text), 0o644); err != nil {
t.Fatal(err)
}
}
// **CAT-BG5, CAT-BN2**: a contracts module's generated files are regenerated and compared, each build module its own
// root; a file that differs, one missing and one the generator did not write are each named; a clean tree and a module
// naming no generated-from name none; a build ask answers them in its out-of-step.
func TestAContractsModulesGeneratedFilesAreComparedWithTheGenerator(t *testing.T) {
marked := `{"module":"c","build":{"artifacts":[{"name":"go","kind":"package","language":"go","paths":["go"],"generated-from":"."}]}}`
dir, paths := contracts(t, marked)
var goFile, tsFile string
for _, p := range paths {
if strings.HasPrefix(p, "go/") && strings.HasSuffix(p, ".go") && goFile == "" {
goFile = p
}
if strings.HasPrefix(p, "ts/") && strings.HasSuffix(p, ".ts") && tsFile == "" {
tsFile = p
}
}
if goFile == "" || tsFile == "" {
t.Fatalf("the generator wrote no Go or no TypeScript file: %v", paths)
}
for _, root := range []string{GeneratedRootGo, GeneratedRootTS} {
if got, err := OutOfStep(dir, root); err != nil || len(got) != 0 {
t.Fatalf("a clean tree, root %s: %v %v", root, got, err)
}
}
writeContract(t, filepath.Join(dir, goFile), "package changed\n")
writeContract(t, filepath.Join(dir, "go/extra.go"), "package extra\n")
writeContract(t, filepath.Join(dir, tsFile), "// changed\n")
got, err := OutOfStep(dir, GeneratedRootGo)
if want := sorted(goFile, "go/extra.go"); err != nil || !reflect.DeepEqual(got, want) {
t.Errorf("Go root: %v %v, want %v", got, err, want)
}
if got, _ := OutOfStep(dir, GeneratedRootTS); !reflect.DeepEqual(got, []string{tsFile}) {
t.Errorf("TypeScript root: %v, want %s alone", got, tsFile)
}
_ = os.Remove(filepath.Join(dir, goFile))
if got, _ := OutOfStep(dir, GeneratedRootGo); !reflect.DeepEqual(got, sorted(goFile, "go/extra.go")) {
t.Errorf("a missing file is not named: %v", got)
}
plain, _ := contracts(t, `{"module":"p","build":{"artifacts":[{"name":"go","kind":"package","language":"go","paths":["go"]}]}}`)
writeContract(t, filepath.Join(plain, "go/extra.go"), "package extra\n")
if got, err := OutOfStep(plain, GeneratedRootGo); err != nil || len(got) != 0 {
t.Errorf("a module naming no generated-from compared: %v %v", got, err)
}
// The build ask answers them beside what it built, and with a failed build too.
built := 0
steps := AskSteps{Compare: CompareStep(GeneratedRootGo, func(ws string) string { return ws }),
Fetch: func(context.Context, map[string]any) (any, error) {
return map[string]any{"workspace": filepath.Dir(dir)}, nil
},
Build: func(context.Context, map[string]any) (any, error) {
if built++; built > 1 {
return nil, errors.New("the second artifact does not build")
}
return map[string]any{"digest": "sha256:aa"}, nil
}}
job := delivery.BuildJob{Module: "c", Path: filepath.Base(dir), Fetch: map[string]any{"repository": "novox/c"},
Artifacts: []delivery.AskArtifact{{Artifact: "go"}}}
out, err := RunAsk(context.Background(), steps, ask(job))
if a, ok := out.(delivery.BuildAnswer); err != nil || !ok || !reflect.DeepEqual(a.OutOfStep, sorted(goFile, "go/extra.go")) || len(a.Artifacts) != 1 {
t.Errorf("the build ask's out-of-step: %+v %v", out, err)
}
job.Artifacts = append(job.Artifacts, delivery.AskArtifact{Artifact: "go-other"})
built = 0
out, err = RunAsk(context.Background(), steps, ask(job))
if a, ok := out.(delivery.BuildAnswer); err == nil || !ok || len(a.OutOfStep) != 2 {
t.Errorf("a failed build answered no out-of-step: %+v %v", out, err)
}
}
func sorted(paths ...string) []string {
sort.Strings(paths)
return paths
}