Files
mesh-sdk/go/delivery/forge_inputs_test.go
T
jschoubben af4e4e60a3 Carry mesh package versions in build asks and fingerprints
ADR 0573 §2 has every module build against the latest published version of
each mesh package it uses, chosen by the planner and written by the build,
never pinned by hand (issue 1585, S4). The ask, the fetch request and the
merge preview now carry the packages and their versions, and the new-form
fingerprint takes each version, so a package's trunk merge changes every
dependent's fingerprint. A module using no package keeps its fingerprint.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 01:57:26 +01:00

199 lines
9.0 KiB
Go

package delivery
import (
"reflect"
"strings"
"testing"
)
const oddManifest = "{\n\t\"module\": \"a\",\n \"z\": 1, \"a\": [ \"x\",\"y\" ],\n \"why\": \"é — <b> & \\n\"\n}\n\n"
func mergedWithInputs() Forge {
return Forge{Kind: ForgeMerged, Repository: "novox/mesh-sdk", Number: 36, Head: "aaaa", Merge: "cccc", Root: "t0",
Trees: map[string]string{"a": "t1", "b": "t2"},
Manifests: map[string]string{"a": oddManifest},
Locks: map[string]map[string]string{"a": {"go.sum": strings.Repeat("ab", 32)}}}
}
// 1442/AC1
func TestForgeMergedCarriesManifestsAndLocks(t *testing.T) {
f := mergedWithInputs()
data, err := EncodeForge(f)
if err != nil {
t.Fatal(err)
}
for _, frag := range []string{`"manifests":{"a":`, `"locks":{"a":{"go.sum":`} {
if !strings.Contains(string(data), frag) {
t.Errorf("the encoding lacks %s: %s", frag, data)
}
}
got, err := DecodeForge(data)
if err != nil {
t.Fatal(err)
}
if got.Manifests["a"] != oddManifest || !SameForge(got, withV(f)) {
t.Errorf("the message changed on the way: %q", got.Manifests["a"])
}
// ADR 0565 §2 (issue 1953 D1): a merged message carries the predefined list exact, or says the merge holds none; a
// pull message carries neither.
f.Predefined = []byte(`{"domain":{"type":"string"}}`)
if data, err := EncodeForge(f); err != nil {
t.Fatal(err)
} else if got, err := DecodeForge(data); err != nil || string(got.Predefined) != `{"domain":{"type":"string"}}` {
t.Errorf("the predefined list read back as %s, %v", got.Predefined, err)
}
f.Predefined, f.PredefinedRead = nil, true
if _, err := EncodeForge(f); err != nil {
t.Errorf("a merge holding no predefined list: %v", err)
}
pull := Forge{Kind: ForgePull, Repository: "novox/mesh-sdk", Number: 1, Head: "h", Action: PullOpened, PredefinedRead: true}
if _, err := EncodeForge(pull); err == nil || !strings.Contains(err.Error(), "only a merged message") {
t.Errorf("a pull message carrying the predefined list: %v", err)
}
}
func withV(f Forge) Forge { f.V = Version; return f }
// 1442/AC2
func TestForgeMergedRefusesBadInputs(t *testing.T) {
for name, tc := range map[string]struct {
mut func(*Forge)
want string
}{
"manifest outside trees": {func(f *Forge) { f.Manifests["zz"] = "{}" }, `"zz"`},
"locks outside trees": {func(f *Forge) { f.Locks["yy"] = map[string]string{"go.sum": "h"} }, `"yy"`},
"empty manifest": {func(f *Forge) { f.Manifests["b"] = "" }, `"b"`},
"empty lock hash": {func(f *Forge) { f.Locks["a"]["go.sum"] = "" }, `"a"`},
// No repository holds placement (ADR 0567 §11): a message carrying placement files is refused, on a merge or a pull.
"placement files": {func(f *Forge) { f.RetiredPlacements = []byte(`{"anchor":"{}"}`) }, "mesh-placement"},
"placements read": {func(f *Forge) { f.RetiredRead = true }, "mesh-placement"},
// ADR 0565 §2 (issue 1953 D1): the predefined list is one JSON object, said present or absent, never both.
"predefined not an object": {func(f *Forge) { f.Predefined = []byte(`["domain"]`) }, "one JSON object"},
"predefined both": {func(f *Forge) { f.Predefined = []byte(`{}`); f.PredefinedRead = true }, "says the merge holds none"},
} {
f := mergedWithInputs()
tc.mut(&f)
if _, err := EncodeForge(f); err == nil || !strings.Contains(err.Error(), tc.want) {
t.Errorf("%s: %v, want a refusal naming %s", name, err, tc.want)
}
}
}
// 1442/AC3
func TestForgePullCarriesManifestsAndLocks(t *testing.T) {
pull := func(action string) Forge {
return Forge{Kind: ForgePull, Repository: "novox/mesh-sdk", Number: 1, Head: "h", Action: action,
Dirs: []string{"a"}, DirsKnown: true, Manifests: map[string]string{"a": oddManifest},
Locks: map[string]map[string]string{"a": {"go.sum": strings.Repeat("0", 64)}}}
}
for _, action := range []string{PullOpened, PullUpdated} {
data, err := EncodeForge(pull(action))
if err != nil {
t.Fatalf("%s: %v", action, err)
}
if got, err := DecodeForge(data); err != nil || got.Manifests["a"] != oddManifest {
t.Errorf("%s: %v", action, err)
}
}
f := pull(PullOpened)
f.Manifests["b"] = "{}"
if _, err := EncodeForge(f); err == nil || !strings.Contains(err.Error(), `"b"`) {
t.Errorf("a manifest outside Dirs: %v", err)
}
f = pull(PullOpened)
f.Locks["c"] = map[string]string{"go.sum": "h"}
if _, err := EncodeForge(f); err == nil || !strings.Contains(err.Error(), `"c"`) {
t.Errorf("locks outside Dirs: %v", err)
}
for name, f := range map[string]Forge{
"refused manifests": {Kind: ForgeRefused, Repository: "a/b", Act: "x", Words: "no", Manifests: map[string]string{"a": "{}"}},
"refused locks": {Kind: ForgeRefused, Repository: "a/b", Act: "x", Words: "no", Locks: map[string]map[string]string{"a": {"go.sum": "h"}}},
"open manifests": {Kind: ForgeOpen, Manifests: map[string]string{"a": "{}"}},
"open locks": {Kind: ForgeOpen, Locks: map[string]map[string]string{"a": {"go.sum": "h"}}},
} {
if _, err := EncodeForge(f); err == nil {
t.Errorf("%s was encoded", name)
}
}
}
// 1442/AC4
func TestForgeFromAnOlderBridge(t *testing.T) {
old := `{"v":1,"kind":"merged","repository":"novox/mesh-sdk","number":36,"head":"aaaa","merge":"cccc","trees":{"a":"t1"},"root":"t0"}`
f, err := DecodeForge([]byte(old))
if err != nil || f.Manifests != nil || f.Locks != nil {
t.Fatalf("%+v, %v", f, err)
}
data, err := EncodeForge(f)
// the older message is written with the struct's key order, so the bytes compare as they are
if err != nil || string(data) != old {
t.Errorf("%s, %v, want exactly %s", data, err, old)
}
}
// 1442/AC5
func TestModuleKeepsInputs2(t *testing.T) {
kept := KeptInputs2{Tree: "t1", Bases: []string{"src2:" + strings.Repeat("1", 64), "src2:" + strings.Repeat("2", 64)},
Builder: "src2:" + strings.Repeat("3", 64), Contexts: map[string]string{"web": "t9"},
Locks: map[string]string{"web": strings.Repeat("cd", 32)}}
m := Module{FP: "src1:x", Manifest: strings.Repeat("ef", 32), Inputs2: &kept, Repository: "novox/m", Commit: "c"}
m.FP = SourcePrefix + "x"
data, err := EncodeModule(m)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(data), `"inputs2":{"tree":"t1"`) {
t.Errorf("the encoding lacks inputs2: %s", data)
}
got, err := DecodeModule(data)
if err != nil {
t.Fatal(err)
}
m.V = Version
if !SameModule(got, m) {
t.Errorf("Inputs2 did not come back equal: %+v", got.Inputs2)
}
tc := map[string]Toolchain2{"web": {Language: "typescript", Version: "5.9", Recipe: "2"}}
want, none := SourceFingerprint2(SourceInputs2{Module: "m", Tree: kept.Tree, Bases: kept.Bases, Builder: kept.Builder,
Contexts: kept.Contexts, Locks: kept.Locks, Toolchains: tc})
have, none2 := SourceFingerprint2(got.Inputs2.With("m", tc))
if want == "" || none != "" || none2 != "" || have != want {
t.Errorf("fingerprints differ: %q %q (%q %q)", have, want, none, none2)
}
m.Manifest = ""
if _, err := EncodeModule(m); err == nil {
t.Error("Inputs2 without a manifest digest was encoded")
}
// Issue 1585 (ADR 0573 §2): the mesh packages a go.sum names, those of other repositories, and the latest published
// version of each: the trunk commit of the deepest module of its repository whose directory holds it.
sum := "git.novox.be/novox/mesh-sdk/go v0.1.7 h1:a=\ngit.novox.be/novox/mesh-sdk/go v0.1.7/go.mod h1:b=\n" +
"git.novox.be/novox/mesh-controller/modules/route-proxy/contract v0.1.0 h1:c=\n" +
"github.com/nats-io/nats.go v1.0.0 h1:d=\ngit.novox.be/novox/mesh-host v0.0.0-1 h1:e=\n"
pkgs := MeshPackagesOf([]byte(sum))
if want := []string{"git.novox.be/novox/mesh-controller/modules/route-proxy/contract", "git.novox.be/novox/mesh-host", "git.novox.be/novox/mesh-sdk/go"}; !reflect.DeepEqual(pkgs, want) {
t.Errorf("the packages of a go.sum: %v, want %v", pkgs, want)
}
if got := OtherPackages(pkgs, "novox/mesh-controller"); len(got) != 2 || got[0] != "git.novox.be/novox/mesh-host" {
t.Errorf("the packages of other repositories: %v", got)
}
held := map[string]Module{
"mesh-sdk": {Repository: "novox/mesh-sdk", Path: RootPath, Commit: "c-sdk"},
"route-proxy": {Repository: "novox/mesh-controller", Path: "modules/route-proxy", Commit: "c-rp"},
"controller": {Repository: "novox/mesh-controller", Path: RootPath, Commit: "c-ctl"},
"route-proxy2": {Repository: "novox/mesh-controller", Path: "modules/route", Commit: "c-wrong"},
"no-path": {Repository: "novox/mesh-host", Commit: "c-host"},
}
versions, missing := PackageVersionsOf(pkgs, held)
if want := (PackageVersions{"git.novox.be/novox/mesh-sdk/go": "c-sdk", "git.novox.be/novox/mesh-controller/modules/route-proxy/contract": "c-rp"}); !reflect.DeepEqual(versions, want) {
t.Errorf("versions %v, want %v", versions, want)
}
if len(missing) != 1 || missing[0] != "git.novox.be/novox/mesh-host" {
t.Errorf("missing %v: a module that names no directory holds no package", missing)
}
kept.Packages = []string{"git.novox.be/novox/mesh-sdk/go", "git.novox.be/novox/mesh-host"}
if fp, none := SourceFingerprint2(kept.WithPackages("m", tc, versions)); fp != "" || !strings.HasPrefix(none, NoPackageVersion) {
t.Errorf("a package with no version: %q %q", fp, none)
}
}