From 2d3d5ab616f5c06129772ded6f7e0231e4f0f0fe Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 18 Oct 2026 19:18:11 +0200 Subject: [PATCH] Test that declarationWith sets modules and builds for a node that reads them (issue 1145); the Approve of a head it did not cover refuses a new module, heads keyed by the repository (issue 1146) --- .../modules_wire_store_test.go | 91 +++++++++++++++++++ .../new_module_approved_test.go | 29 +++++- cmd/mesh-controller/new_module_bus_test.go | 4 +- 3 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 cmd/mesh-controller/modules_wire_store_test.go diff --git a/cmd/mesh-controller/modules_wire_store_test.go b/cmd/mesh-controller/modules_wire_store_test.go new file mode 100644 index 00000000..a708ddcd --- /dev/null +++ b/cmd/mesh-controller/modules_wire_store_test.go @@ -0,0 +1,91 @@ +//go:build integration + +package main + +import ( + "encoding/json" + "strings" + "testing" + "time" + + "github.com/novox/mesh-controller/internal/link" +) + +// 1145/AC1 on a store: declarationWith itself sets the module of each resource and the build of each module for a +// node recorded as reading them, and for no other. Skips without a store. +func TestAComposedDeclarationCarriesModulesAndBuildsOnlyForANodeThatReadsThem(t *testing.T) { + open := aMesh(t) + ctx := t.Context() + inv := open.inventory + start := time.Now().Add(-time.Hour) + manifest := resticFixture(false) + manifest["module"], manifest["version"] = "restic", "1" + raw, _ := json.Marshal(manifest) + b := link.BuildResult{ID: link.NewBuildID(start), Repository: "novox/mesh-catalog", Path: "modules/restic", + On: "anchor", Module: "restic", Commit: "c1", Manifest: raw, + Source: &link.SourceOnSeat{Seat: "git", Repository: "novox/mesh-catalog"}} + if _, _, err := takeIn(ctx, inv, asTheOperator(t, inv, b)); err != nil { + t.Fatal(err) + } + for _, n := range []string{"anchor", "laptop"} { + if _, err := inv.Assign(ctx, n, "restic"); err != nil { + t.Fatal(err) + } + } + anchor, err := inv.NodeByName(ctx, "anchor") + if err != nil { + t.Fatal(err) + } + if err := inv.RecordReadsModules(ctx, anchor.ID, true); err != nil { + t.Fatal(err) + } + wantBuild, err := inv.BuildOf(ctx, "restic", "c1") + if err != nil || wantBuild == "" { + t.Fatalf("the build of restic at c1 is %q (%v)", wantBuild, err) + } + + declare := func(node string) sendable { + t.Helper() + plan, settings, err := planFor(ctx, open, node) + if err != nil { + t.Fatal(err) + } + gens, err := generators(ctx, open) + if err != nil { + t.Fatal(err) + } + d, err := declarationWith(ctx, open, node, plan, settings, gens, Allocating) + if err != nil { + t.Fatal(err) + } + return d + } + reads := bodyKeys(t, declare("anchor")) + builds, _ := reads["builds"].(map[string]any) + if builds["restic"] != wantBuild { + t.Errorf("1145/AC1: anchor reads modules and was sent builds %v, want restic at %s", reads["builds"], wantBuild) + } + owned := 0 + for _, r := range reads["resources"].([]any) { + r := r.(map[string]any) + if strings.HasPrefix(r["id"].(string), "restic.") { + owned++ + if r["module"] != "restic" { + t.Errorf("1145/AC1: resource %v carries module %v, want restic", r["id"], r["module"]) + } + } + } + if owned == 0 { + t.Fatal("1145/AC1: no resource of restic was composed") + } + + plain := bodyKeys(t, declare("laptop")) + if _, there := plain["builds"]; there { + t.Errorf("1145/AC1: laptop never said it reads modules and was sent builds %v", plain["builds"]) + } + for _, r := range plain["resources"].([]any) { + if _, there := r.(map[string]any)["module"]; there { + t.Fatalf("1145/AC1: laptop was sent a module on %v", r) + } + } +} diff --git a/cmd/mesh-controller/new_module_approved_test.go b/cmd/mesh-controller/new_module_approved_test.go index 98ea4081..3c442a8d 100644 --- a/cmd/mesh-controller/new_module_approved_test.go +++ b/cmd/mesh-controller/new_module_approved_test.go @@ -37,6 +37,24 @@ func (m *anEmptyMesh) MergeOf(context.Context, string, string) (inventory.Batche return m.merge, true, nil } +// aDeliveryMergeOf is the Approved merge of a pull request of novox/mesh-delivery, its heads keyed by that repository, +// and the ask and warrant bound to exactly those heads (the digest the Approve left covers them). +func aDeliveryMergeOf(t *testing.T, head string, covered map[string]string) anApprovedMerge { + t.Helper() + a := approvedMerge(t) + a.merge.Owner, a.merge.Repo, a.merge.Number, a.merge.HeadSHA = "novox", "mesh-delivery", 7, head + a.merge.Heads = covered + for n := range a.ask.Options { + d, err := asks.ActDigest(mergeAct(a.merge.AskID, a.ask.Options[n].ID, covered)) + if err != nil { + t.Fatal(err) + } + a.ask.Options[n].Binds = d + } + a.warrant.AskDigest = a.ask.Digest() + return a +} + func TestANewModuleIsRegisteredOnTheOperatorsApproveOfItsMergeAndRefusedOtherwise(t *testing.T) { const commit, head = "c0ffee0123456789", "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" built := inventory.Source{Repository: "novox/mesh-delivery", Seat: "git"} @@ -53,8 +71,12 @@ func TestANewModuleIsRegisteredOnTheOperatorsApproveOfItsMergeAndRefusedOtherwis {what: "the Approve of the merged head, a protected trunk on the forge", request: "merge", accepted: true, ac: "1146/AC1"}, {what: "a build asked through a verb", request: "build", ac: "1146/AC2"}, {what: "a merge with no warrant", request: "merge", unkept: true, ac: "1146/AC2"}, - {what: "a warrant of another head", request: "merge", ac: "1146/AC2", change: func(a *anApprovedMerge) { - a.merge.Heads = map[string]string{"novox/mesh-host@aaaaaaaaaaaa": "e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5"} + {what: "a merged head the Approve did not cover", request: "merge", ac: "1146/AC2", change: func(a *anApprovedMerge) { + *a = aDeliveryMergeOf(t, head, map[string]string{ + "novox/mesh-delivery@eeeeeeeeeeee": "e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5"}) + }}, + {what: "heads other than the ones the Approve was bound to", request: "merge", ac: "1146/AC2", change: func(a *anApprovedMerge) { + a.merge.Heads = map[string]string{"novox/mesh-delivery@eeeeeeeeeeee": "e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5"} }}, {what: "a repository not on the mesh's forge", request: "merge", ac: "1146/AC3", built: &inventory.Source{Repository: "http://elsewhere.example/x/y.git"}}, @@ -62,8 +84,7 @@ func TestANewModuleIsRegisteredOnTheOperatorsApproveOfItsMergeAndRefusedOtherwis {what: "an unprotected trunk", request: "merge", ac: "1146/AC3", forge: forgeFacts{ID: 9, Guarded: false, Why: "direct push"}}, } { t.Run(c.ac+" "+c.what, func(t *testing.T) { - a := approvedMerge(t) - a.merge.Owner, a.merge.Repo, a.merge.Number, a.merge.HeadSHA = "novox", "mesh-delivery", 7, head + a := aDeliveryMergeOf(t, head, map[string]string{"novox/mesh-delivery@a1a1a1a1a1a1": head}) if c.change != nil { c.change(&a) } diff --git a/cmd/mesh-controller/new_module_bus_test.go b/cmd/mesh-controller/new_module_bus_test.go index f23dbe3f..c2e88074 100644 --- a/cmd/mesh-controller/new_module_bus_test.go +++ b/cmd/mesh-controller/new_module_bus_test.go @@ -65,8 +65,8 @@ func TestANewModuleFromANewRepositoryIsRegisteredOnlyOnTheOperatorsApproveOnTheB t.Fatal(err) } - a := approvedMerge(t) - a.merge.Owner, a.merge.Repo, a.merge.Number = "novox", "mesh-delivery", 7 + a := aDeliveryMergeOf(t, "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + map[string]string{"novox/mesh-delivery@a1a1a1a1a1a1": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1"}) if c.answer { record, _ := json.Marshal(map[string]any{"state": "closed", "ask": a.ask, "warrant": a.warrant}) if _, err := records.Put(ctx, theDelivery+"."+a.merge.AskID, record); err != nil {