package main import ( "context" "encoding/json" "fmt" "os" "slices" "strings" "sync" "testing" "time" ) // Issue 1482 (novox/hq ADR 0466 ยง3): the provider watches the build modules' state `keys` beside ssh-client's, and // registers each builder entry read-only on nox-builder. func keyJSON(node, account, kind, public string) []byte { b, _ := json.Marshal(map[string]string{"node": node, "account": account, "kind": kind, "public": public, "fingerprint": "SHA256:x", "at": "2026-10-20T10:00:00Z"}) return b } // watched is a team rig whose keys come through watches of the modules' states, as in the running provider. type watched struct { *teamRig sources map[string]*fakeKeys passes *int logs *[]string mu *sync.Mutex } func newWatched(t *testing.T) *watched { r := newTeamRig(t) r.svc.loaded, r.svc.loadedFrom = false, map[string]bool{} r.svc.Pause = func(time.Duration) {} n, logs, mu := 0, []string{}, &sync.Mutex{} r.svc.OnKeys = func(string) { n++ } r.svc.cfg.Log = func(f string, a ...any) { mu.Lock(); logs = append(logs, fmt.Sprintf(f, a...)); mu.Unlock() } w := &watched{teamRig: r, sources: map[string]*fakeKeys{}, passes: &n, logs: &logs, mu: mu} for _, module := range []string{"ssh-client", "mesh-build-go", "mesh-build-node"} { w.sources[module] = &fakeKeys{} if err := r.svc.WatchSource(context.Background(), module, w.sources[module]); err != nil { t.Fatal(err) } } return w } func (w *watched) said() string { w.mu.Lock() defer w.mu.Unlock() return strings.Join(*w.logs, "\n") } // 1482/AC1: a mesh-build-go.keys entry ace.mesh-build-go of kind builder: its public half is registered read-only on // nox-builder titled mesh:ace.mesh-build-go. func TestABuilderEntryOfABuildModulesStateIsRegisteredReadOnlyOnNoxBuilder(t *testing.T) { w := newWatched(t) w.bind("ace", "mesh-build-go") w.sources["mesh-build-go"].send("ace.mesh-build-go", "put", keyJSON("ace", "mesh-build-go", "builder", pubGo+" mesh-build-go@ace (mesh)"), true) w.pass() keys := w.forge.keys["nox-builder"] if len(keys) != 1 || keys[0].Title != "mesh:ace.mesh-build-go" || !keys[0].ReadOnly || keys[0].Key != pubGo+" mesh-build-go@ace (mesh)" { t.Errorf("nox-builder's keys = %+v", keys) } if !w.svc.loadedFrom["mesh-build-go"] { t.Errorf("the state was watched and its keys are not counted as read") } } // 1482/AC2: that entry changed: the old key is removed and the new one registered; deleted: the key is removed and no key // titled otherwise is touched. func TestAChangedBuilderEntryReplacesItsKeyAndADeletedOneRemovesIt(t *testing.T) { w := newWatched(t) w.bind("ace", "mesh-build-go") src := w.sources["mesh-build-go"] src.send("ace.mesh-build-go", "put", keyJSON("ace", "mesh-build-go", "builder", pubGo), true) w.pass() w.forge.mu.Lock() w.forge.keys["nox-builder"] = append(w.forge.keys["nox-builder"], forgeKey{ID: 99, Title: "my own", Key: pubOps}) w.forge.mu.Unlock() before := *w.passes src.send("ace.mesh-build-go", "put", keyJSON("ace", "mesh-build-go", "builder", pubNode), false) if *w.passes != before+1 { t.Errorf("a change asked for %d passes, want 1", *w.passes-before) } w.pass() got := w.forge.keys["nox-builder"] if len(got) != 2 || got[0].Title != "my own" || got[1].Title != "mesh:ace.mesh-build-go" || got[1].Key != pubNode || !got[1].ReadOnly { t.Errorf("after the change: %+v", got) } src.send("ace.mesh-build-go", "delete", nil, false) w.pass() if got := strings.Join(w.forge.titles("nox-builder"), ","); got != "my own" { t.Errorf("after the delete: %s, want only the operator's key", got) } } // 1482/AC3: an entry of kind machine in a build module's state, or of kind builder in ssh-client's, is ignored and // said in the provider's log. func TestAKeyOfTheWrongKindForItsStateIsIgnoredAndSaid(t *testing.T) { w := newWatched(t) w.bind("ace", "mesh-build-go") w.bind("g14", "ssh-client") w.sources["mesh-build-go"].send("ace.mesh-build-go", "put", keyJSON("ace", "mesh-build-go", "machine", pubGo), true) w.sources["ssh-client"].send("g14.nox", "put", keyJSON("g14", "nox", "builder", pubA), true) w.pass() if len(w.forge.users) != 0 || w.bus.has("ace.mesh-build-go.forge-account") || w.bus.has("g14.ssh-client.forge-account") { t.Errorf("a key of the wrong kind was served: users %v", w.forge.users) } said := w.said() for _, want := range []string{`ace.mesh-build-go in the state keys of mesh-build-go is of kind "machine", not "builder"`, `g14.nox in the state keys of ssh-client is of kind "builder", not "machine"`} { if !strings.Contains(said, want) { t.Errorf("the log lacks %q:\n%s", want, said) } } } // A build module's state that never answers holds back its own entries, never a machine's. func TestABuildModulesStateThatNeverAnswersHoldsBackOnlyItsOwnEntries(t *testing.T) { w := newWatched(t) w.svc.loadedFrom = map[string]bool{} // mesh-build-go's watch has not answered w.bind("ace", "mesh-build-go") w.bind("g14", "ssh-client") w.sources["ssh-client"].send("g14.nox", "put", keyJSON("g14", "nox", "machine", pubA+" nox@g14 (mesh)"), true) out := w.pass() if !w.bus.has("g14.ssh-client.forge-account") { t.Errorf("a machine's entry waited for a build module's state") } waits := 0 for _, o := range out { if strings.Contains(o.Key, "mesh-build-go") && strings.Contains(o.Why, "keys of mesh-build-go are not read yet") { waits++ } } if waits != 1 { t.Errorf("outcomes = %+v", out) } } // 1482/AC4 (the provider's half): the manifest reads the states of both build modules beside ssh-client's and the // controller's assignments; the provider watches exactly the modules the code names as builders. func TestTheManifestReadsTheBuildModulesKeys(t *testing.T) { raw, err := os.ReadFile("../../module.json") if err != nil { t.Fatal(err) } var m struct { Reads []string `json:"reads"` } if err := json.Unmarshal(raw, &m); err != nil { t.Fatal(err) } for _, state := range []string{"mesh-controller.assignments", "ssh-client.keys", "mesh-build-go.keys", "mesh-build-node.keys"} { if !slices.Contains(m.Reads, state) { t.Errorf("gitea's reads %v lack %q", m.Reads, state) } } }