The indexer's whole-module test proves a merge kept as one catalog graph revision, said on the seat, and the recompute and the drop answered by the running indexer (milestone 47/G W4.13)

Rows in TestIndexerWritesModulesAtAMerge; the pin rows come with W4.5.

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 11:48:46 +01:00
parent a217a44f05
commit 07832b1a2c
@@ -686,8 +686,14 @@ func (l buildLog) end(t *testing.T, repo, module, path string) {
// message on FORGE makes modules.<m> within 20 s, the fingerprint of the merge's tree and the recorded inputs.
func TestIndexerWritesModulesAtAMerge(t *testing.T) { // 1239/AC9; I1, I3: the modules are the merged message's, no build record names them (issue 1851)
// The bucket holds only build-agent's record, which no fingerprint reads since R4g (ADR 0530 §1).
var asks jetstream.KeyValue
js, ctx, _, modules := startIndexer(t, func(ctx context.Context, builds buildLog) {
builds.end(t, "novox/mesh-controller", "build-agent", "modules/build-agent")
}, func(ctx context.Context, js jetstream.JetStream) {
var err error
if asks, err = js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: index.TakeInsBucket, History: 1}); err != nil {
t.Fatal(err)
}
})
const merge = "89abcdef0123456789abcdef0123456789abcdef"
data, err := forgemsg.EncodeForge(forgemsg.Forge{Kind: forgemsg.ForgeMerged, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
@@ -710,6 +716,60 @@ func TestIndexerWritesModulesAtAMerge(t *testing.T) { // 1239/AC9; I1, I3: the m
if m.Repository != "novox/mesh-delivery" || m.Commit != merge || m.Path != "modules/a" || m.Manifest == "" || m.Fp != "" || m.None != "" { // 1606/AC2
t.Errorf("module = %+v within 20 s, want path modules/a, its manifest, and no value of today's form", m)
}
// Milestone 47/G W4.13 (design 47, the indexer whole): the source revision taken in is one catalog graph revision kept
// as objects, said as catalog-revision on the seat's subject, every entry it lists stored once by digest.
objects := newBusObjects(js, testAccount, index.CatalogRevisionsObjects)
list, err := index.ListRevisions(ctx, objects, false)
if err != nil || len(list.Revisions) != 1 || list.Revisions[0].Name != "trunk.1" || !list.Revisions[0].Current ||
list.Entries != list.Revisions[0].Entries || len(list.Unlisted) != 0 || list.Fill.Bytes == 0 {
t.Fatalf("revisions kept = %+v (%v): want trunk.1 alone, current, each entry it lists stored once", list, err)
}
events, err := js.Stream(ctx, "EVENTS")
if err != nil {
t.Fatal(err)
}
if msg, err := events.GetLastMsgForSubject(ctx, "mesh.seat.mesh-catalog.event."+meshcatalog.EventCatalogRevision); err != nil ||
!strings.Contains(string(msg.Data), `"revision":1`) {
t.Errorf("catalog-revision said: %v (%v): want revision 1", msg, err)
}
// W4.12 through the process: a recompute by hand moves nothing and answers equal; a candidate's half dropped by hand
// leaves the trunk's revision and its entries.
if err := index.KeepRevision(ctx, objects, meshcatalog.CatalogRevision{Kind: "candidate", Group: "g1", Heads: "h1", Revision: 1,
Entries: []meshcatalog.EntryRef{{Module: "x", Digest: "dx"}}}, map[string][]byte{"dx": []byte(`{"module":"x"}`)}); err != nil {
t.Fatal(err)
}
answer := func(key string, value []byte) index.TakenIn {
t.Helper()
w, err := asks.Watch(ctx, key, jetstream.UpdatesOnly())
if err != nil {
t.Fatal(err)
}
defer w.Stop()
if _, err := asks.Put(ctx, key, value); err != nil {
t.Fatal(err)
}
for e := range w.Updates() {
if e == nil {
continue
}
if a, err := index.DecodeTakeIn(e.Value()); err == nil && a.Done() {
return *a.Result
}
}
t.Fatalf("%s is not answered", key)
return index.TakenIn{}
}
key, value, _ := index.NewRecomputeAsk("a check", "ctl", time.Now())
if r := answer(key, value); !r.Stored || !strings.Contains(r.Answer, "equal to the kept revision 1") {
t.Errorf("indexer_recompute answered %+v: want equal to the kept revision 1", r)
}
key, value, _ = index.NewDropCandidateGraphAsk("g1", "h1", "its gate event never came", "ctl", time.Now())
if r := answer(key, value); !r.Stored || !slices.Equal(r.Dropped, []string{"entry.dx"}) {
t.Errorf("indexer_drop_candidate_graph answered %+v: want candidate.g1.h1 dropped with entry.dx", r)
}
if after, err := index.ListRevisions(ctx, objects, false); err != nil || len(after.Revisions) != 1 || after.Entries != list.Entries {
t.Errorf("revisions after the drop = %+v (%v): want trunk.1 and its entries alone", after, err)
}
}
// TestIndexerKeepsManifestsAtAMerge: a merged message of mesh-catalog carrying the real manifest of the module avahi and the