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 18f8e2d9..ff351125 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 @@ -721,6 +721,11 @@ func TestIndexerCountsOnlyThePlacedBuildNodesToolchains(t *testing.T) { t.Fatal(err) } } + // DLV-F5r (ADR 0621 ยง3): the assigner's runtime starts together with the indexer and answers its first view only after + // the indexer asked; within the bound the indexer follows the placement it answers (only n1's toolchain counts). + up := make(chan struct{}) + assignerLate = up + t.Cleanup(func() { assignerLate = nil }) js, ctx, _, modules := startIndexer(t, func(context.Context, buildLog) {}, func(ctx context.Context, js jetstream.JetStream) { kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: delivery.ToolchainsBucket("mesh-build-go"), History: 1}) if err != nil { @@ -744,6 +749,7 @@ func TestIndexerCountsOnlyThePlacedBuildNodesToolchains(t *testing.T) { place(ctx, t, "n1", "mesh-build-go") place(ctx, t, "n2") }) + close(up) data, err := delivery.EncodeForge(delivery.Forge{Kind: delivery.ForgeMerged, Repository: "novox/mesh-catalog", Branch: "feat/x", Number: 302, Head: "0123456789abcdef0123456789abcdef01234567", Merge: "89abcdef0123456789abcdef0123456789abcdef", Root: "r0", Trees: map[string]string{"modules/avahi": "newtree"}, @@ -809,6 +815,10 @@ func TestIndexerCountsOnlyThePlacedBuildNodesToolchains(t *testing.T) { } } +// assignerLate, when set before startIndexer, holds every answer of the assigner's first view until it is closed: the +// assigner's runtime started together with the indexer's, answering after the indexer asked (DLV-F5r). +var assignerLate chan struct{} + // serveAssignerPlacement is the assigner as the owner of its placement (ADR 0615, ADR 0621), with the real owner's side: // its first-view tool placement answered from the bucket at its mark (the bucket made empty where a test made none), // and its views family said by the views emitter onto ASSIGNER_VIEWS, which the indexer's durable consumer reads. @@ -835,7 +845,15 @@ func serveAssignerPlacement(t *testing.T, ctx context.Context, js jetstream.JetS if err := em.Start(ctx, delivery.PlacementState, placement, nil); err != nil { t.Fatal(err) } + late := assignerLate sub, err := nc.Subscribe("mesh.mod.mesh-delivery-assigner.call.placement.>", func(m *nats.Msg) { + if late != nil { // its runtime comes up after the indexer started (DLV-F5r) + select { + case <-late: + case <-ctx.Done(): + return + } + } a, err := views.FirstView(delivery.AssignerModule, delivery.PlacementState, jsSource{ctx, placement}, jsSource{ctx, marks}, "", time.Now()) out := map[string]any{"result": a, "node": "n0"} if err != nil {