From 3eee45aa495c7c13e2f3cf59b95b80409d4f5f13 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 22:13:07 +0100 Subject: [PATCH] GATE: the gate's whole-module test opens a record and a source key from a take-in.taken, and its test bus declares the template states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test bus made buckets from the old state section alone, so a template state (deliveries, groups, owed, conditions) was 'not declared'; it now makes a bucket for each requires entry of the template state under its local name (A-20). Builds: design 47 G §613, A-20, A-33, A-34 (G-17, G-20) --- .../cmd/mesh-delivery-gate/bus_test.go | 4 +- .../cmd/mesh-delivery-gate/manifest_test.go | 18 +++++++ .../mesh-delivery-gate/takenin_bus_test.go | 54 +++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 modules/mesh-delivery-gate/cmd/mesh-delivery-gate/takenin_bus_test.go diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/bus_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/bus_test.go index 1e6dcdde5..7d6726788 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/bus_test.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/bus_test.go @@ -82,7 +82,7 @@ func newTestBus(t *testing.T) *testBus { b := &testBus{nc: nc, js: js, kvs: map[string]jetstream.KeyValue{}, answers: map[string][]byte{}} // The controller's job: a bucket per state the manifest declares. m, _ := readManifest(t) - for _, name := range m.buckets() { + for _, name := range m.withTemplateStates() { kv, err := js.CreateKeyValue(context.Background(), jetstream.KeyValueConfig{Bucket: m.Module + "_" + name, History: 10}) if err != nil { t.Fatal(err) @@ -101,7 +101,7 @@ func newTestBus(t *testing.T) *testBus { // The streams the events are on (mesh.mod..event., mesh.seat..event.). if _, err := js.CreateStream(context.Background(), jetstream.StreamConfig{Name: "EVENTS", Subjects: []string{"mesh.seat.git.event.>", "mesh.seat." + oc.Seat + ".event.>", - "mesh.mod.mesh-delivery-forge-bridge.event.>", "mesh.mod.mesh-delivery-teller.event.>"}}); err != nil { + "mesh.mod.mesh-delivery-forge-bridge.event.>", "mesh.mod.mesh-delivery-indexer.event.>"}}); err != nil { t.Fatal(err) } return b diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/manifest_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/manifest_test.go index 8812f29a6..71837c6b1 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/manifest_test.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/manifest_test.go @@ -34,10 +34,28 @@ type manifest struct { State []any `json:"state"` Reads []string `json:"reads"` Tools []string `json:"tools"` + // Requires are the bus state the module asks for: the template `state` entries are keyed buckets under their local names. + Requires []struct { + Name string `json:"name"` + Storage string `json:"storage"` + Template string `json:"template"` + } `json:"requires"` // Capabilities are what the gate's machine's tool runner must state (buscalls.Capabilities). Capabilities []string `json:"capabilities"` } +// withTemplateStates are the buckets a test bus makes: the old `state` section's, and the `requires` entries of the template +// `state` under their local names (design 47 G §613, A-20). +func (m manifest) withTemplateStates() []string { + out := m.buckets() + for _, r := range m.Requires { + if r.Storage == "key-value" && r.Template == "state" { + out = append(out, r.Name) + } + } + return out +} + // buckets are the names of the state the manifest declares, each a bare name or an object with a name and a history. func (m manifest) buckets() []string { var out []string diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/takenin_bus_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/takenin_bus_test.go new file mode 100644 index 000000000..528e42ea9 --- /dev/null +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/takenin_bus_test.go @@ -0,0 +1,54 @@ +//go:build integration + +package main + +import ( + "context" + "strings" + "testing" + "time" + + mdc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" +) + +// The gate whole, over a real bus (design 47 G A-33, A-34): a take-in.taken the indexer says opens a delivery record in the +// gate's ledger with the take-in as its outcome, writes the branch's source key and says source-moved; the gate asks the +// controller nothing, and no state it no longer declares is needed (the template states are buckets under their local names). +const eventWithin = 20 * time.Second + +func TestAGateWholeTakesAnIndexersTakeInIntoARecordAndASourceKeyOverARealBus(t *testing.T) { + b := newTestBus(t) + ctx := context.Background() + rt := b.start(t) + evctx, cancel := context.WithCancel(ctx) + defer cancel() + rt.deliverEvents(evctx) + rt.untilListening() + merge := strings.Repeat("c", 40) + b.publish(t, "mesh.mod.mesh-delivery-indexer.event.take-in.taken", map[string]any{"v": 1, "kind": "taken", "key": "m.00000000000000000001", + "what": "revision", "branch": "main", "source": 3, "since": "2026-11-11T19:00:00Z", + "merges": []any{map[string]any{"repository": "novox/app", "number": 5, "merge": merge}}, + "entries": []any{map[string]any{"module": "app", "commit": merge, "fingerprint": "f", "repository": "novox/app", "pull": 5}}}) + rt.until("the source key of main", eventWithin, func() bool { + e, err := b.kvs["source"].Get(ctx, mdc.SourceKey("main")) + if err != nil { + return false + } + s, err := mdc.DecodeSource(e.Value()) + return err == nil && s.Heads["novox/app"] == merge + }) + rt.until("the record of the take-in", eventWithin, func() bool { + b.mu.Lock() + defer b.mu.Unlock() + if b.ledger == nil { + return false + } + for instance := range b.ledger.states { + if strings.Contains(instance, "main") { + return true + } + } + return false + }) + rt.noControllerAsk() +}