From 7b3f213ae8d2ab66dd32a9586506df8bf5609dc1 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 03:48:21 +0100 Subject: [PATCH] =?UTF-8?q?D6i:=20a=20merge=20taken=20in=20before=20the=20?= =?UTF-8?q?verdicts=20state=20binds=20has=20each=20manifest=20judged=20as?= =?UTF-8?q?=20new=20and=20said=20at=20the=20bind,=20in=20the=20indexer's?= =?UTF-8?q?=20whole-module=20test=20(issue=201966=20note=20154,=20ADR=2006?= =?UTF-8?q?68=20=C2=A74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without the unjudged state the bind's pass accepted the refused manifest with a warning and the waiting one, and said manifest-accepted for both; the row settles Q3: the indexer says what grants follows, so the grants part is unchanged (no D8). Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../mesh-delivery-indexer/integration_test.go | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go index 09d21fa0..ce7132fd 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go @@ -662,8 +662,9 @@ func TestIndexerKeepsManifestsAtAMerge(t *testing.T) { // 1444/AC4, 1449/AC4, 15 js, ctx, _, modules := startIndexer(t, func(ctx context.Context, builds buildLog) { builds.end(t, "novox/mesh-catalog", "avahi", "modules/avahi") }, func(ctx context.Context, js jetstream.JetStream) { - // The verdicts state and the stream of the indexer's events, as the controller makes them from its manifest. - if _, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: index.VerdictsBucket, History: index.VerdictsHistory}); err != nil { + // The indexer's state unjudged, as the controller makes it from its manifest; the verdicts state is made only once + // the merge is taken in (issue 1966 note 154: a take-in at the start, before the verdicts state's bind). + if _, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: index.UnjudgedBucket, History: 1}); err != nil { t.Fatal(err) } }) @@ -707,7 +708,20 @@ func TestIndexerKeepsManifestsAtAMerge(t *testing.T) { // 1444/AC4, 1449/AC4, 15 t.Errorf("manifest = %+v err %v, want avahi and the file's bytes", got, err) } - verdicts, err := js.KeyValue(ctx, index.VerdictsBucket) + // 1966 (ADR 0668 §4, note 154): the take-in wrote its four manifests with no verdicts state on the bus, so it keeps + // them in its state unjudged; when the verdicts state binds, each is judged as new (bad refused in full, never as a + // warning), every accepted one is said as manifest-accepted, and the state is left empty. + unjudged, err := js.KeyValue(ctx, index.UnjudgedBucket) + if err != nil { + t.Fatal(err) + } + kept, _ := unjudged.Keys(ctx) + for _, raw := range []string{string(manifest), judged["good"], judged["bad"], judged["later"]} { + if !slices.Contains(kept, meshcatalogclient.ManifestDigest([]byte(raw))) { + t.Errorf("the take-in's new manifest %s is not kept unjudged: the state holds %v", meshcatalogclient.ManifestDigest([]byte(raw)), kept) + } + } + verdicts, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: index.VerdictsBucket, History: index.VerdictsHistory}) if err != nil { t.Fatal(err) } @@ -732,12 +746,20 @@ func TestIndexerKeepsManifestsAtAMerge(t *testing.T) { // 1444/AC4, 1449/AC4, 15 if !strings.Contains(strings.Join(got["bad"].Problems, " "), `provides "amqp"`) || strings.Join(got["later"].Unknown, " ") != "a-field-from-later" { t.Errorf("the refusal names no problem or the wait no field: %+v", got) } + // The pass ends the state's keys after it wrote and said every verdict. + left, _ := unjudged.Keys(ctx) + for deadline := time.Now().Add(10 * time.Second); len(left) != 0 && time.Now().Before(deadline); time.Sleep(50 * time.Millisecond) { + left, _ = unjudged.Keys(ctx) + } + if len(left) != 0 { + t.Errorf("the state unjudged still holds %v within 10 s, once every manifest has its verdict", left) + } // manifest-refused, said once for each refusal (a wait is no refusal). events, err := js.Stream(ctx, "EVENTS") if err != nil { t.Fatal(err) } - said := map[string]bool{} + said, acceptedSaid := map[string]bool{}, map[string]bool{} for seq := uint64(1); ; seq++ { msg, err := events.GetMsg(ctx, seq) if err != nil { @@ -747,10 +769,17 @@ func TestIndexerKeepsManifestsAtAMerge(t *testing.T) { // 1444/AC4, 1449/AC4, 15 if msg.Subject == "mesh.seat.mesh-catalog.event."+meshcatalog.EventManifestRefused && json.Unmarshal(msg.Data, &r) == nil { said[r.Module] = true } + var a meshcatalog.ManifestAcceptedEvent + if msg.Subject == "mesh.seat.mesh-catalog.event."+meshcatalog.EventManifestAccepted && json.Unmarshal(msg.Data, &a) == nil { + acceptedSaid[a.Module] = true + } } if !said["bad"] || said["later"] || said["good"] { t.Errorf("manifest-refused said for %v, want bad only", said) } + if !acceptedSaid["good"] || acceptedSaid["bad"] || acceptedSaid["later"] { + t.Errorf("manifest-accepted said for %v, want good and no refused or waiting one", acceptedSaid) + } // How a composer holds a refused module by these verdicts is the node-engine's to test (ADR 0593 §7: the composer // and its bus reader are its own). }