Test how a manifest and lock hashes become a module's new-form inputs (issue 1450, design 47 R4c-2)

This commit is contained in:
2026-10-20 18:55:54 +00:00
parent 27bfd83fa0
commit f69a561a16
@@ -0,0 +1,146 @@
package index
import (
"context"
"reflect"
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// aBase is the new-form fingerprint of a base module, the one SourceFingerprint2 gives for the inputs of avahi at a tree.
func aBase(t *testing.T) string {
t.Helper()
fp, none := delivery.SourceFingerprint2(delivery.SourceInputs2{Module: "mesh-tools", Tree: "basetree"})
if none != "" {
t.Fatal(none)
}
return fp
}
func noBases(string) (string, string) { return "", "none are held in this test" }
func TestAGoModuleKeepsItsTreeAndTheHashOfItsLockFile(t *testing.T) { // 1450/AC1
in := inputsOf("avahi", catalogueManifest(t, "avahi"), "avahitree", map[string]string{"go.sum": avahiGoSum}, noBases)
want := &delivery.KeptInputs2{Tree: "avahitree", Locks: map[string]string{"tools": avahiGoSum}}
if !reflect.DeepEqual(in.Kept, want) {
t.Errorf("kept = %+v, want %+v", in.Kept, want)
}
if !reflect.DeepEqual(in.Languages, map[string]string{"tools": "go"}) {
t.Errorf("languages = %v, want tools: go", in.Languages)
}
// Through the indexer: the merge's tree of the module's directory and the lock hashes of that directory, kept in modules.avahi.
ix, mods, _ := manifestsIndexer(t, rec(t, "build-1", "avahi", catalogueRepo, "modules/avahi", "main", "", "2026-10-19T09:00:00Z", aInputs))
f := withManifests(mergedMsg(catalogueRepo, map[string]string{"modules/avahi": "avahitree", "modules/other": "x"}),
map[string]string{"modules/avahi": catalogueManifest(t, "avahi")}, map[string]map[string]string{"modules/avahi": {"go.sum": avahiGoSum}})
if err := ix.Apply(context.Background(), f); err != nil {
t.Fatal(err)
}
m, ok := moduleOf(t, mods, "avahi")
if !ok || !reflect.DeepEqual(m.Inputs2, want) || m.Manifest != delivery.ManifestDigest([]byte(catalogueManifest(t, "avahi"))) {
t.Errorf("module = %+v (%v), want inputs %+v and the manifest digest", m, ok, want)
}
}
func TestAnArtifactThatResolvesPackagesAndHasNoLockFileHasAnEmptyHash(t *testing.T) { // 1450/AC2
in := inputsOf("avahi", catalogueManifest(t, "avahi"), "avahitree", nil, noBases)
if in.Kept == nil || !reflect.DeepEqual(in.Kept.Locks, map[string]string{"tools": ""}) {
t.Fatalf("kept = %+v, want Locks {tools: \"\"}", in.Kept)
}
// The fingerprint of such inputs is none, "no lock" (mesh-sdk SourceFingerprint2).
if fp, none := delivery.SourceFingerprint2(in.Kept.With("avahi", nil)); fp != "" || none != "no lock" {
t.Errorf("fingerprint = %q none %q, want none 'no lock'", fp, none)
}
// A TypeScript bundle's lock file is package-lock.json, not go.sum.
ts := inputsOf("audit-logger", catalogueManifest(t, "audit-logger"), "t", map[string]string{"go.sum": "wrong", "package-lock.json": avahiGoSum}, noBases)
if ts.Kept == nil || !reflect.DeepEqual(ts.Kept.Locks, map[string]string{"code": avahiGoSum}) || !reflect.DeepEqual(ts.Languages, map[string]string{"code": "typescript"}) {
t.Errorf("typescript: kept %+v languages %v", ts.Kept, ts.Languages)
}
// netcheck has a Go and a TypeScript bundle: each takes its own language's lock file.
both := inputsOf("netcheck", catalogueManifest(t, "netcheck"), "t", map[string]string{"go.sum": "g", "package-lock.json": "p"}, noBases)
if both.Kept == nil || !reflect.DeepEqual(both.Kept.Locks, map[string]string{"tools-go": "g", "tools-typescript": "p"}) {
t.Errorf("netcheck: kept %+v", both.Kept)
}
}
// aModuleWithABase is avahi's manifest (mesh-catalog a67f0196) with the one build.on entry that names a module as the
// controller's own test has it (mesh-controller internal/builder/standing_on_test.go:22): no manifest of the sources names
// a module as a base, only vendors' images.
func aModuleWithABase(t *testing.T) string {
t.Helper()
raw := catalogueManifest(t, "avahi")
with := strings.Replace(raw, `"build": {`, `"build": {
"on": [{"arg": "RUNTIME_BASE", "module": "mesh-tools", "artifact": "runtime"}],`, 1)
if with == raw {
t.Fatal("avahi's manifest has no build")
}
return with
}
func TestABaseEntersByTheFingerprintOfItsModuleOrNamesItsAbsence(t *testing.T) { // 1450/AC3
fp := aBase(t)
has := func(module string) (string, string) {
if module != "mesh-tools" {
t.Errorf("asked for the base %q", module)
}
return fp, ""
}
in := inputsOf("avahi", aModuleWithABase(t), "t", map[string]string{"go.sum": avahiGoSum}, has)
if in.Kept == nil || !reflect.DeepEqual(in.Kept.Bases, []string{fp}) {
t.Errorf("bases = %+v, want [%s]", in.Kept, fp)
}
none := inputsOf("avahi", aModuleWithABase(t), "t", map[string]string{"go.sum": avahiGoSum}, noBases)
if none.Kept == nil || len(none.Kept.Bases) != 0 || !strings.Contains(none.None2, "mesh-tools") || !strings.Contains(none.None2, "base") {
t.Errorf("kept %+v none2 %q, want no bases and none2 naming the base mesh-tools", none.Kept, none.None2)
}
// Through the indexer: modules.mesh-tools holds a fingerprint of the new form (a record written by a later release).
ix, mods, _ := manifestsIndexer(t, rec(t, "build-1", "avahi", catalogueRepo, "modules/avahi", "main", "", "2026-10-19T09:00:00Z", aInputs))
mods.put("mesh-tools", mustEncode(t, delivery.Module{Repository: catalogueRepo, Commit: merge, None: "x", FP2: fp, Manifest: strings.Repeat("a", 64)}))
f := withManifests(mergedMsg(catalogueRepo, map[string]string{"modules/avahi": "t"}), map[string]string{"modules/avahi": aModuleWithABase(t)}, nil)
if err := ix.Apply(context.Background(), f); err != nil {
t.Fatal(err)
}
if m, _ := moduleOf(t, mods, "avahi"); m.Inputs2 == nil || !reflect.DeepEqual(m.Inputs2.Bases, []string{fp}) {
t.Errorf("module = %+v, want the base's fingerprint", m)
}
}
func mustEncode(t *testing.T, m delivery.Module) []byte {
t.Helper()
b, err := delivery.EncodeModule(m)
if err != nil {
t.Fatal(err)
}
return b
}
func TestAnArtifactOfNoToolchainHasNoLanguageAndNoLock(t *testing.T) { // 1450/AC4
in := inputsOf("ca-trust", catalogueManifest(t, "ca-trust"), "t", map[string]string{"go.sum": avahiGoSum}, noBases)
if in.Kept == nil || len(in.Kept.Locks) != 0 || len(in.Languages) != 0 {
t.Errorf("kept %+v languages %v, want neither locks nor languages for an archive", in.Kept, in.Languages)
}
}
func TestAManifestTheParserRefusesLeavesNoInputsAndSaysWhy(t *testing.T) { // 1450/AC5
for name, raw := range map[string]string{
"not json": "not json",
"unknown field": strings.Replace(catalogueManifest(t, "avahi"), `"version": "1",`, `"version": "1", "scope": "everything",`, 1),
} {
in := inputsOf("avahi", raw, "t", nil, noBases)
if in.Kept != nil || !strings.HasPrefix(in.None2, "the manifest cannot be read: ") || len(in.None2) <= len("the manifest cannot be read: ") {
t.Errorf("%s: kept %+v none2 %q, want nil and 'the manifest cannot be read: <error>'", name, in.Kept, in.None2)
}
}
if in := inputsOf("someone-else", catalogueManifest(t, "avahi"), "t", nil, noBases); in.Kept != nil || !strings.Contains(in.None2, "avahi") {
t.Errorf("a manifest of another module: kept %+v none2 %q", in.Kept, in.None2)
}
}
func TestAContextIsNotPinned(t *testing.T) { // 1450 (the rule 1444 keeps: context not pinned, as today's form)
// build-agent's manifest from mesh-controller at f2d7bc94 has an image artifact built from a context in another repository.
in := inputsOf("build-agent", fixture(t, "controller-f2d7bc94-build-agent"), "t", nil, noBases)
if in.Kept == nil || in.None2 != None2Context {
t.Errorf("kept %+v none2 %q, want none2 %q", in.Kept, in.None2, None2Context)
}
}