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

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)
This commit is contained in:
2026-11-11 22:13:07 +01:00
parent f0c7d468c3
commit 3eee45aa49
3 changed files with 74 additions and 2 deletions
@@ -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.<module>.event.<name>, mesh.seat.<seat>.event.<name>).
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
@@ -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
@@ -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()
}