Files
mesh-controller/internal/inventory/stored_silent_file_test.go
T
jochen 18221c4079
mesh/merge-gate head 18221c4079d1 merged into trunk 7b2a9eae8020 — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 18221c4079d1 merged into trunk 7b2a9eae8020 — pass: its merge-check.sh passed
mesh/delivery delivered
Judge ADR 0326 §3 only where a definition is written (hq issue 574)
ParseManifest also reads stored builds again: a build's manifest at a
commit, the gate, a release, a module put back. betterbird's builds from
before its accounts file named its keys would have become uncomposable
there, so a send to g14 or shanks could fail and a rollback be refused.
The refusal now runs in module check, module add, a build taken in, the
builder and the merge gate's registration, and a file is no longer
reported by both §2 and §3.
2026-10-13 00:54:26 +02:00

68 lines
2.7 KiB
Go

package inventory
import (
"context"
"encoding/json"
"strings"
"testing"
"github.com/novox/mesh-controller/internal/catalogue"
)
// A build kept from before ADR 0326 §3 is still read and composed (novox/hq issue 574). betterbird's
// builds from before mesh-catalog 8f80cd4 declare a preference and own a mergeable file that names no
// keys: refused where a definition is written, but a machine still runs that build and a rollback may
// put it back, so reading it again — ManifestAt, which a walk, the gate and a release compose from —
// must not refuse it.
func TestAStoredBuildWithASilentMergeableFileIsStillReadAndComposed(t *testing.T) {
inv := fresh(t)
ctx := context.Background()
manifest, err := json.Marshal(map[string]any{
"module": "mail-client", "version": "1",
"settings": map[string]any{"prefs": map[string]any{
"kind": catalogue.KindPreference, "default": "// none", "why": "its own defaults until a machine names one"}},
"resources": []any{
map[string]any{"id": "user-js", "type": "file", "path": "/var/lib/mail-client/user.js", "mode": "0644",
"content": "${setting:prefs}\n"},
map[string]any{"id": "accounts", "type": "file", "path": "/var/lib/mail-client/accounts.json",
"mode": "0600", "merge": "json", "content": "{\"accounts\": {}}\n"},
},
})
if err != nil {
t.Fatal(err)
}
b := aBuild("before-574", "mail-client", "")
b.Manifest = manifest
if err := inv.RecordBuild(ctx, b); err != nil {
t.Fatal(err)
}
m, found, err := inv.ManifestAt(ctx, "mail-client", b.Commit)
if err != nil || !found {
t.Fatalf("the stored build is not read again: found %v, %v", found, err)
}
if written := catalogue.WrittenSettingsProblems(m); len(written) != 1 {
t.Fatalf("the rule this build predates should name its accounts file where it is written: %v", written)
}
// And composed, in today's form: the accounts file still takes its layer's keys.
with := catalogue.Rendering{Settings: catalogue.SettingsBy{"mail-client": {{From: "workstation",
Values: map[string]any{"accounts": map[string]any{"work": map[string]any{"email": "a@example.invalid"}}}}}}}
if err := catalogue.JudgeSettings(m, with.Settings["mail-client"], false); err != nil {
t.Fatalf("the stored build's settings do not compose: %v", err)
}
out, err := catalogue.Resolution{Node: "workstation", Modules: []catalogue.Manifest{m}}.Declaration(with)
if err != nil {
t.Fatalf("the stored build is not composed: %v", err)
}
for _, r := range out {
if r["id"] == "mail-client.accounts" {
if content, _ := r["content"].(string); !strings.Contains(content, "a@example.invalid") {
t.Fatalf("the accounts file did not take its layer: %v", content)
}
return
}
}
t.Fatalf("the accounts file was not composed: %v", out)
}