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 48eee1a9..a995b7a3 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 @@ -120,11 +120,21 @@ func TestIndexerWhole(t *testing.T) { // 1238/AC2, AC10; 1585/T14, T15 if _, err := tc.Put(ctx, key, value); err != nil { t.Fatal(err) } - // The trunk holds the indexer's entry, its Go artifact naming its package (§676): the validation's program is read from it (§712). - seedBuildModules(t, ctx, js, map[string]meshcatalog.CatalogEntry{index.Module: {V: index.CatalogEntryLayout, Module: index.Module, - Repository: "novox/mesh-delivery", Path: "modules/" + index.Module, Commit: "4444444444444444444444444444444444444444", None: sdkstep.ToolchainNoReport, - Artifacts: []meshcatalog.Artifact{{Kind: "bundle", Language: "go", Ref: index.Module + ":program", - Package: &meshcatalog.ArtifactPackage{Ecosystem: "go", Name: index.Module}}}}}) + // The trunk holds the indexer's entry, taken in at an earlier commit with its own manifest; the seat git answers its go.mod, so + // its Go artifacts carry the Go module the validation's program is read under (§712, §676, A-263). + seedBuildModules(t, ctx, js, map[string]meshcatalog.CatalogEntry{index.Module: trunkIndexerEntry(t, ctx, js)}) + if _, err := js.Conn().Subscribe("mesh.seat.git.tool.file", func(m *nats.Msg) { + var a git.FileInput + _ = json.Unmarshal(m.Data, &a) + answer := git.FileAnswer{Path: a.Path, Ref: a.Ref, Absent: true} + if a.Path == "modules/"+index.Module+"/go.mod" { + answer = git.FileAnswer{Path: a.Path, Ref: a.Ref, Content: "module " + index.Module + "\n"} + } + raw, _ := json.Marshal(map[string]any{"result": answer, "node": "n0"}) + _ = m.Respond(raw) + }); err != nil { + t.Fatal(err) + } validations = standInBuildSeat(t, ctx, js, &refusing) // The node-engine's composition on n1 (process.Conn.Call's subjects): the build of the indexer that runs there. answer := func(m *nats.Msg) { @@ -430,8 +440,8 @@ func TestIndexerWhole(t *testing.T) { // 1238/AC2, AC10; 1585/T14, T15 if refused, ok := said(takein.TakeInPending, m3Key); !ok || !strings.Contains(refused.Refused, "a: the stand-in's problem") || refused.Failed != "" { t.Fatalf("take-in.pending.%s = %+v (said %v) within 20 s, want the revision refused naming a's problem", m3Key, refused, ok) } - if w = waits(); len(w) != 1 || w[0].Key != m3Key || w[0].Validation == nil || w[0].Validation.Accepted != 0 || len(validations.asked()) != 2 { - t.Fatalf("indexer_pending = %+v, asks %d: want the refused revision pending with its validation", w, len(validations.asked())) + if w = waits(); len(w) != 1 || w[0].Key != m3Key || w[0].Validation == nil || w[0].Validation.Accepted != 0 || len(validations.asked()) != 3 { + t.Fatalf("indexer_pending = %+v, asks %d: want the refused revision pending with its validation, asked once after m1 and the group", w, len(validations.asked())) } if raw, ok := get(index.ModulesState, "a"); !ok || strings.Contains(string(raw), m3.Merge) { t.Errorf("modules.a = %s: the refused revision was taken in", raw) @@ -439,6 +449,37 @@ func TestIndexerWhole(t *testing.T) { // 1238/AC2, AC10; 1585/T14, T15 } +// trunkIndexerEntry keeps the indexer's own manifest by its digest, accepted, and answers its trunk entry at an earlier commit. +func trunkIndexerEntry(t *testing.T, ctx context.Context, js jetstream.JetStream) meshcatalog.CatalogEntry { + t.Helper() + raw, err := os.ReadFile("../../module.json") + if err != nil { + t.Fatal(err) + } + var m catalogue.Manifest + if err := json.Unmarshal(raw, &m); err != nil { + t.Fatal(err) + } + digest := meshcatalogclient.ManifestDigest(raw) + manifests, err := js.KeyValue(ctx, index.ManifestsBucket) + if err != nil { + t.Fatal(err) + } + mkey, _ := meshcatalogclient.ManifestsKey(digest) + mdata, _ := meshcatalogclient.EncodeManifest(meshcatalog.ManifestsByDigestValue{Module: index.Module, Manifest: string(raw)}) + if _, err := manifests.Put(ctx, mkey, mdata); err != nil { + t.Fatal(err) + } + held := meshcatalog.ModulesByModuleValue{Repository: "novox/mesh-delivery", Commit: "4444444444444444444444444444444444444444", Path: "modules/" + index.Module, + Manifest: digest, None2: sdkstep.ToolchainNoReport} + e := index.CatalogEntryOf(index.Module, held, m, string(raw), graph.Node{None: held.None2}) + for _, a := range m.Build.Artifacts { + e.Artifacts = append(e.Artifacts, meshcatalog.Artifact{Kind: a.Kind, Language: a.Language, Ref: index.ArtifactRefOf(index.Module, a.Name), + Package: &meshcatalog.ArtifactPackage{Ecosystem: index.EcosystemGo, Name: index.Module}}) + } + return e +} + // standIn is the build seat node-build-go as the indexer's validation meets it: the asks it heard, oldest first. type standIn struct { mu sync.Mutex @@ -783,7 +824,7 @@ func startIndexerRun(t *testing.T, put func(context.Context, buildLog), before . start := func() { runCtx, cancel := context.WithCancel(ctx) done := make(chan error, 1) - go func() { done <- run(runCtx, func(k string) string { return env[k] }, os.Stderr) }() + go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }() stop = func() { cancel(); <-done } } start() @@ -1536,7 +1577,8 @@ func serveAssignerAssignments(t *testing.T, ctx context.Context, js jetstream.Je t.Fatal(err) } if _, err := js.CreateOrUpdateConsumer(ctx, "EVENTS", jetstream.ConsumerConfig{Durable: "n1_mesh-delivery-indexer", AckPolicy: jetstream.AckExplicitPolicy, - FilterSubjects: []string{"mesh.mod." + assignerHolderModule + ".event.views.>", "mesh.seat.mesh-delivery.event.>"}}); err != nil { + // and the build seat's step-ended the manifest's consumes names (design 47 G §713: the end of a validation ask). + FilterSubjects: []string{"mesh.mod." + assignerHolderModule + ".event.views.>", "mesh.seat.mesh-delivery.event.>", nodebuildgo.SubjectStepEndedAll}}); err != nil { t.Fatal(err) } em := views.Emitter{Owner: assignerHolderModule, Subject: process.Account{Node: "n1", Module: assignerHolderModule}.EventSubject, @@ -1731,4 +1773,3 @@ func TestIndexerTakesInAWatchedBranchOtherThanTheFirstIntoItsOwnRevision(t *test t.Errorf("modules.avahi = %s, want the trunk's modules left", e.Value()) } } -var _ = io.Discard diff --git a/modules/mesh-delivery-indexer/internal/index/validation_wait.go b/modules/mesh-delivery-indexer/internal/index/validation_wait.go index f24e27a1..5e9c7f1e 100644 --- a/modules/mesh-delivery-indexer/internal/index/validation_wait.go +++ b/modules/mesh-delivery-indexer/internal/index/validation_wait.go @@ -35,9 +35,11 @@ func newestOfRevision(ready []PendingEntry) int { return n } +// branchOfRevision is the watched branch the revision's merges landed on, its newest merged message's base (design 47 G §714); +// a message that names none is the trunk's. A pending value's own branch is its pull request's, never the branch validated. func branchOfRevision(ready []PendingEntry) string { - if ref := ready[newestOfRevision(ready)].Value.Ref; ref != nil && ref.Branch != "" { - return ref.Branch + if base := ready[newestOfRevision(ready)].Forge.Base; base != "" { + return base } return TrunkBranch } diff --git a/modules/mesh-delivery-indexer/manifest_test.go b/modules/mesh-delivery-indexer/manifest_test.go index 5dfaa46b..defaa435 100644 --- a/modules/mesh-delivery-indexer/manifest_test.go +++ b/modules/mesh-delivery-indexer/manifest_test.go @@ -179,7 +179,9 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build // Three invokes: the file of a registered module's manifest and lock files (registered.go), the forge's trees (the named paths' objects, since the indexer is the one reader of a manifest's paths: // ADR 0499 clauses 2 and 3, ADR 0495 clause 1; and the module directories of the trunk, issue 1900) and whether a // commit is in another (issue 1900). - if want := []string{"seat:git.trees", "seat:git.file", "seat:git.contains", "seat:git.open-pulls", "seat:git.branch", "mesh-delivery-forge-bridge.bridge_merged_message", "seat:node-engine.composition", "seat:mesh-delivery.source", "seat:mesh-module-assigner.assignments", "seat:mesh-module-assigner.placement", "seat:mesh-hand-acts.hand-act", "seat:mesh-watchdogs.presence", "seat:node-build-go.steps", "seat:node-build-node.steps", "seat:node-build-image.steps", "mesh-delivery-builder.builder_versions"}; !reflect.DeepEqual(m.Invokes, want) { + if want := []string{"seat:git.trees", "seat:git.file", "seat:git.contains", "seat:git.open-pulls", "seat:git.branch", "mesh-delivery-forge-bridge.bridge_merged_message", "seat:node-engine.composition", "seat:mesh-delivery.source", "seat:mesh-module-assigner.assignments", "seat:mesh-module-assigner.placement", "seat:mesh-hand-acts.hand-act", "seat:mesh-watchdogs.presence", "seat:node-build-go.steps", + "seat:node-build-go.accept.check", // design 47 G A-231: the validation's run ask (§712) on the check queue + "seat:node-build-node.steps", "seat:node-build-image.steps", "mesh-delivery-builder.builder_versions"}; !reflect.DeepEqual(m.Invokes, want) { t.Errorf("invokes = %v, want %v", m.Invokes, want) } // 1945: indexer_take_in, served by its tools bundle through the takeins state; the process serves none. The same bundle diff --git a/modules/mesh-delivery-indexer/module.json b/modules/mesh-delivery-indexer/module.json index 1255de8a..26fdb649 100644 --- a/modules/mesh-delivery-indexer/module.json +++ b/modules/mesh-delivery-indexer/module.json @@ -175,6 +175,7 @@ "seat:mesh-hand-acts.hand-act", "seat:mesh-watchdogs.presence", "seat:node-build-go.steps", + "seat:node-build-go.accept.check", "seat:node-build-node.steps", "seat:node-build-image.steps", "mesh-delivery-builder.builder_versions"