The compose integration test builds the node-engine's real composer, not a stand-in (ADR 0593 §7)
The test copies the node-engine's repository from beside the check (MESH_CHECK_BESIDE, mesh-host, with mesh-sdk's Go module beside it as one workspace when a group places it), has the module build github.com/novox/mesh-host/cmd/mesh-compose there, and runs it; it fails loudly when the repository is not beside. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -5,6 +5,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -23,25 +24,59 @@ import (
|
||||
"git.novox.be/novox/mesh-sdk/go/testbus"
|
||||
)
|
||||
|
||||
// composerSource is a stand-in for the node-engine's composer command: it reads -request <file> and answers a verdict
|
||||
// accepting the one manifest, but only when the request's manifest was filled (no digest left in it).
|
||||
const composerSource = `package main
|
||||
// nodeEngineTree is the node-engine's repository as the checker places it beside this check (MESH_CHECK_BESIDE, the
|
||||
// repository mesh-host), and sdkBeside mesh-sdk's Go module there when it is placed too (a group that changes it). The test
|
||||
// builds the node-engine's real composer command from that tree; it does not stand one in. Without the repository beside it
|
||||
// fails: a composer test that could not build its composer proves nothing.
|
||||
func nodeEngineTree(t *testing.T) (tree, sdk string) {
|
||||
t.Helper()
|
||||
beside := os.Getenv("MESH_CHECK_BESIDE")
|
||||
if beside == "" {
|
||||
t.Fatal("NOT CHECKED: MESH_CHECK_BESIDE is not set, so the node-engine's repository (mesh-host) is not beside this check")
|
||||
}
|
||||
tree = filepath.Join(beside, "mesh-host")
|
||||
if _, err := os.Stat(filepath.Join(tree, "cmd", "mesh-compose")); err != nil {
|
||||
t.Fatalf("NOT CHECKED: the node-engine's composer command is not beside this check in MESH_CHECK_BESIDE=%s: %v", beside, err)
|
||||
}
|
||||
if dir := filepath.Join(beside, "mesh-sdk", "go"); dirExists(dir) {
|
||||
sdk = dir
|
||||
}
|
||||
return tree, sdk
|
||||
}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
func dirExists(p string) bool { st, err := os.Stat(p); return err == nil && st.IsDir() }
|
||||
|
||||
func main() {
|
||||
raw, err := os.ReadFile(os.Args[2])
|
||||
if err != nil || strings.Contains(string(raw), "\"digest\"") {
|
||||
fmt.Println(` + "`" + `{"v":1,"sdk":"stand-in","cases":[{"name":"unfilled","manifests":{}}]}` + "`" + `)
|
||||
return
|
||||
// copyTree copies the files of a source tree into dst, leaving out its .git.
|
||||
func copyHostTree(t *testing.T, src, dst string) {
|
||||
t.Helper()
|
||||
src, err := filepath.EvalSymlinks(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = filepath.WalkDir(src, func(p string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rel, _ := filepath.Rel(src, p)
|
||||
if d.IsDir() {
|
||||
if d.Name() == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return os.MkdirAll(filepath.Join(dst, rel), 0o700)
|
||||
}
|
||||
if !d.Type().IsRegular() {
|
||||
return nil
|
||||
}
|
||||
raw, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join(dst, rel), raw, 0o600)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(` + "`" + `{"v":1,"sdk":"stand-in","cases":[{"name":"change","manifests":{"demo":{"accepted":true}}}]}` + "`" + `)
|
||||
}
|
||||
`
|
||||
|
||||
// goEnvOf is one value of this machine's go env.
|
||||
func goEnvOf(t *testing.T, name string) string {
|
||||
@@ -54,8 +89,7 @@ func goEnvOf(t *testing.T, name string) string {
|
||||
}
|
||||
|
||||
// 1622/AC1, AC3 against the real bus (ADR 0593 §7, C9): a compose ask put on the Go build seat's check stream is pulled by
|
||||
// the module started whole on that bus, which fetches the commit (here a tree whose Go module is at the repository's root,
|
||||
// as the node-engine's repository is), builds the composer command the ask names there with nothing fetched, fills the
|
||||
// the module started whole on that bus, which fetches the commit (the node-engine's repository beside the check, its Go module at the root), builds the composer command the ask names there with nothing fetched, fills the
|
||||
// request's manifests from the indexer's state and runs the composer; the step key <node>.<ask id> passes with the
|
||||
// composer's verdict. A second ask of the same commit runs the kept composer: fetched and built once.
|
||||
func TestAComposeAskIsBuiltOnceAndAnsweredOnTheBus(t *testing.T) {
|
||||
@@ -82,8 +116,14 @@ func TestAComposeAskIsBuiltOnceAndAnsweredOnTheBus(t *testing.T) {
|
||||
m.Run = StepRunner(t.TempDir())
|
||||
m.GoModCache = goEnvOf(t, "GOMODCACHE") // the module cache this module's own build filled, read and not fetched
|
||||
run, gocache := m.Run, goEnvOf(t, "GOCACHE") // this machine's build cache, so the composer's packages compile once
|
||||
hostTree, sdkDir := nodeEngineTree(t)
|
||||
workFile := ""
|
||||
m.Run = func(ctx context.Context, env []string, name string, args ...string) forgekey.Ran {
|
||||
return run(ctx, append(env, "GOCACHE="+gocache), name, args...)
|
||||
env = append(env, "GOCACHE="+gocache)
|
||||
if workFile != "" {
|
||||
env = append(env, "GOWORK="+workFile, "GOFLAGS=-mod=readonly") // a workspace takes no -mod=mod
|
||||
}
|
||||
return run(ctx, env, name, args...)
|
||||
}
|
||||
read, digests := indexerHolds(t, `{"module":"demo","version":"1"}`)
|
||||
m.ReadManifest = read
|
||||
@@ -99,13 +139,15 @@ func TestAComposeAskIsBuiltOnceAndAnsweredOnTheBus(t *testing.T) {
|
||||
if err := os.MkdirAll(tree, 0o700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(tree, "cmd", "mesh-compose"), 0o700); err != nil {
|
||||
return nil, err
|
||||
copyHostTree(t, hostTree, tree)
|
||||
if sdkDir != "" { // the group's mesh-sdk head, laid beside the node-engine's tree as one workspace
|
||||
work := "go 1.26.0\n\nuse (\n\t" + tree + "\n\t" + sdkDir + "\n)\n"
|
||||
if err := os.WriteFile(filepath.Join(m.Work, ws, "go.work"), []byte(work), 0o600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workFile = filepath.Join(m.Work, ws, "go.work")
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(tree, "go.mod"), []byte("module reader\n\ngo 1.26.0\n"), 0o600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]any{"workspace": ws}, os.WriteFile(filepath.Join(tree, "cmd", "mesh-compose", "main.go"), []byte(composerSource), 0o600)
|
||||
return map[string]any{"workspace": ws}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +157,7 @@ func TestAComposeAskIsBuiltOnceAndAnsweredOnTheBus(t *testing.T) {
|
||||
|
||||
ask := func() delivery.Step {
|
||||
a := composeAskOf(t, digests[0])
|
||||
a.Compose.Fetch["repository"], a.Compose.Dir, a.Compose.Package = "novox/mesh-host", delivery.RootPath, "reader/cmd/mesh-compose"
|
||||
a.Compose.Fetch["repository"], a.Compose.Dir, a.Compose.Package = "novox/mesh-host", delivery.RootPath, "github.com/novox/mesh-host/cmd/mesh-compose"
|
||||
raw, err := delivery.EncodeStepAsk(a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -149,8 +191,8 @@ func TestAComposeAskIsBuiltOnceAndAnsweredOnTheBus(t *testing.T) {
|
||||
if err := json.Unmarshal(ans.Verdict, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ans.Built != wantBuilt || !strings.HasPrefix(ans.Key, "composer-") || len(v.Cases) != 1 || !v.Cases[0].Manifests["demo"].Accepted || v.SDK != "stand-in" {
|
||||
t.Fatalf("ask %d: answer built %v key %s, verdict %+v; want built %v and demo accepted by the stand-in composer", n+1, ans.Built, ans.Key, v, wantBuilt)
|
||||
if ans.Built != wantBuilt || !strings.HasPrefix(ans.Key, "composer-") || v.V != composer.Version || len(v.Cases) != 1 || v.Cases[0].Name != "change" {
|
||||
t.Fatalf("ask %d: answer built %v key %s, verdict %+v; want built %v and the verdict of the case \"change\"", n+1, ans.Built, ans.Key, v, wantBuilt)
|
||||
}
|
||||
}
|
||||
if fetches != 1 {
|
||||
|
||||
Reference in New Issue
Block a user