package main import ( "os" "path/filepath" "strings" "testing" ) // The merge gate (novox/hq to-be 45 §9): a change judged against every machine of the snapshot, by the // incidents it is written from. Each case raises a mesh, takes its snapshot, and judges a pull request's // tree against it in throwaway stores of its own. // catalogueMesh is two machines and a catalogue repository: a resolver and an object store on the // anchor, and on the laptop a network manager requiring the resolver, an album requiring the object // store, and a login manager. Every module is registered from novox/mesh-catalog, as the mesh's are. // aTree is a checkout of the catalogue repository holding these manifests. func aTree(t *testing.T, manifests map[string]string) string { t.Helper() dir := t.TempDir() for name, raw := range manifests { at := filepath.Join(dir, "modules", name) if err := os.MkdirAll(at, 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(at, "module.json"), []byte(raw), 0o644); err != nil { t.Fatal(err) } } return dir } func withEdit(manifests map[string]string, name, raw string) map[string]string { out := map[string]string{} for k, v := range manifests { out[k] = v } if raw == "" { delete(out, name) } else { out[name] = raw } return out } // The mesh as it is passes: every machine composes in the gate's store as the controller composed it. // **Issue 263**: a change makes the network manager require the object store's provision; on a machine // whose name is six characters its identity is 26 against a bound of 20. Refused in the pull request, // naming the module, and not on the anchor after it merged. // **Issue 236**: a login manager's service that omits its state passed the catalogue check and was // refused whole by the node-engine on the first machine. A change to a module a machine runs, and a // module nobody runs yet, are both refused before merge, naming the machine and the module. // **Version skew**: a manifest the controller judging it cannot read — the one the mesh runs, for a // catalogue change — fails here, not at registration after the merge. // A module a machine runs, removed from its source, fails until it is unassigned (ADR 0236). // **Issues 278 and 280**: a file in no held module's directory read as shared code, and a merge rebuilt the // catalogue. A file is a module's only by being inside it — no build reads one outside — so it rebuilds // nothing, and the gate says why; a merge that does rebuild widely is warned of. // **A delivery group is judged as one future state** (novox/hq ADR 0239): a catalogue change that needs a // provision only another repository's change adds fails alone and passes composed with it; and a group // whose other head breaks what the first needs fails, naming it. // catalogueMeshWithAnApp is catalogueMesh with an application built from a repository of its own, at its // root, on the anchor. // busMesh is aMesh with a module on the bus on the laptop, its account issued as `module issue` issues // one: minted, and its credential held as the module's own secret named broker. // **Issue 285**: every machine running a module on the bus failed to compose in the gate's store, with // the change and without — the store held the module's credential and no account for it, which // composition refuses (issue 203) — and the gate passed every change, "0 of 4 compose". The account the // mesh issued is raised with its credential, so the machine composes in the gate as on the mesh. // **Issue 285**: a machine the mesh composes that the gate cannot raise as it is leaves the change judged // against a machine that is not the mesh's — broken against broken, which passes whatever the change does. // That is an error, never a pass. // A path the snapshot withheld is given a path of its own where an access or a place needs one: a bare // "withheld" is no absolute path, and a machine whose setting composes on the mesh would not in the gate. func TestAWithheldPathIsStoodInForByAPath(t *testing.T) { got := standInPaths(map[string]any{ "accesses": map[string]any{"races": "withheld", "films": "/media/films", "shows": "/withheld"}, "places": map[string]any{"config": map[string]any{"path": "withheld", "owner": "1000:1000"}}, "puid": 1000, }) accesses := got["accesses"].(map[string]any) if accesses["races"] == accesses["shows"] || !strings.HasPrefix(accesses["races"].(string), "/") || !strings.HasPrefix(accesses["shows"].(string), "/") || accesses["films"] != "/media/films" { t.Errorf("accesses: %v", accesses) } place := got["places"].(map[string]any)["config"].(map[string]any) if !strings.HasPrefix(place["path"].(string), "/") || place["owner"] != "1000:1000" || got["puid"] != 1000 { t.Errorf("places: %v", got) } } // **Issue 286**: a check run by hand clones beside a change what the seat clones — one rule, read by the // controller's ask and by the facts — and finds the repository it checks from its origin. func TestACheckByHandClonesWhatTheSeatClones(t *testing.T) { for dir, refs := range map[string]map[string]string{ "mesh-catalog": {"mesh-catalog": "main"}, "mesh-host": {"mesh-host": "c0ffee"}, "mesh-controller": {"mesh-controller": "c0ffee", "mesh-controller-main": "main", "mesh-lab": "main", "mesh-sdk": sdkPinnedByGoMod(t)}, } { got := besideRefs(dir, "c0ffee") for d, ref := range refs { if got[d] != ref { t.Errorf("beside %s, %s is cloned at %q, not %q", dir, d, got[d], ref) } } } for owner, want := range map[string][3]string{ "ssh://git@git.example:222/novox/mesh-host.git": {"novox", "mesh-host", "ssh://git@git.example:222/novox"}, "git@git.example:novox/mesh-tools.git": {"novox", "mesh-tools", "git@git.example:novox"}, "http://git.example/novox/hq": {"novox", "hq", "http://git.example/novox"}, } { o, r, p := ownerRepoOf(owner) if [3]string{o, r, p} != want { t.Errorf("%s reads as %s %s %s", owner, o, r, p) } } } // sdkPinnedByGoMod is the SDK commit this tree's go.mod pins, read from the file rather than the build, so // sdkPinned is held to what the repository says (novox/hq issue 449). func sdkPinnedByGoMod(t *testing.T) string { t.Helper() raw, err := os.ReadFile(filepath.Join("..", "..", "go.mod")) if err != nil { t.Fatal(err) } for _, line := range strings.Split(string(raw), "\n") { fields := strings.Fields(line) if len(fields) >= 2 && fields[0] == sdkModule { if m := pseudoCommit.FindStringSubmatch(fields[1]); m != nil { return m[1] } return "go/" + fields[1] } } t.Fatalf("go.mod requires no %s", sdkModule) return "" } // **A module one head of a group removes and another defines is a move** (novox/hq ADR 0420 §6, issue 1025): // neither a second definition of one module name (issue 239) nor the removal of a module machines run // (ADR 0236). Alone, each head is still refused. This lays the heads over the shelf as judgeChange does, // which needs no database. // A group that moves a module machines run passes the gate whole, in either order of its heads, and says the // module moved (novox/hq ADR 0420 §6, issue 1025); a head alone is still refused. Needs the test store. // **A module lives whole in one repository** (novox/hq ADR 0420 §1, issue 1019): an artifact whose build // context names another repository is refused by the gate, and one naming its own passes it. // **A member of a delivery group judged alone sees a move** through the core clones beside its check // (novox/hq ADR 0420 §6, issue 1043): the controller's own gate, with the catalogue beside having removed the // module, is not refused for a second definition (issue 239); the catalogue's own gate, with the controller // beside defining it, is not refused for a removal (ADR 0236). A clone beside at main finds no move.