From e69f55468c99a5ca29f34ce7a3833d1cca7e648b Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 12 Nov 2026 00:59:22 +0100 Subject: [PATCH] fix9-1: the teller, view and watchman manifest tests' reader takes requires, and hold the findings as requires MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds: design 47 G §383, §387; A-30 --- modules/mesh-delivery-teller/manifest_test.go | 31 ++++++++++++++++--- modules/mesh-delivery-view/manifest_test.go | 29 +++++++++++++++-- .../mesh-delivery-watchman/manifest_test.go | 30 +++++++++++++++--- 3 files changed, 79 insertions(+), 11 deletions(-) diff --git a/modules/mesh-delivery-teller/manifest_test.go b/modules/mesh-delivery-teller/manifest_test.go index bbc56f58..aca44653 100644 --- a/modules/mesh-delivery-teller/manifest_test.go +++ b/modules/mesh-delivery-teller/manifest_test.go @@ -55,6 +55,7 @@ type manifest struct { OwnSecrets map[string]string `json:"own-secrets"` SecretsOwner string `json:"secrets-owner"` Resources []map[string]any `json:"resources"` + Requires []require `json:"requires"` Build struct { Artifacts []artifact `json:"artifacts"` } `json:"build"` @@ -93,7 +94,7 @@ func TestManifestDeclarations(t *testing.T) { // 1240/AC1 // answers is the teller's answer to each ask; asked is what it asked of the router, which condition it raised and which // gate key and revision each ask was made at (issue 1343); ends is the ends asked by hand with teller_end_ask (issue 774). // first-views is where its first view of the assigner's asks stands and findings its condition past the bound (ADR 0615). - if want := []state{{Name: "answers", History: 5}, {Name: "asked", History: 1}, {Name: "ends", History: 1}, {Name: "first-views", History: 1}, {Name: "findings", History: 1}}; !reflect.DeepEqual(m.State, want) { + if want := []state{{Name: "answers", History: 5}, {Name: "asked", History: 1}, {Name: "ends", History: 1}, {Name: "first-views", History: 1}}; !reflect.DeepEqual(m.State, want) { t.Errorf("state = %+v, want %+v", m.State, want) } // The view's answers to the assigner's asks are read so the view's answer fills an ask that needs no proof (1297/B10); the @@ -133,14 +134,17 @@ func TestManifestDeclarations(t *testing.T) { // 1240/AC1 t.Errorf("emits = %v, want %v", m.Emits, want) } // design 47 G §383, §387: the stream -findings, 7 days and 16 MiB; §368: version is MAJOR.MINOR. - if want := []stream{{Name: "mesh-delivery-teller-findings", Events: []string{"finding-written.*", "finding-cleared.*", "findings-stated.*"}, MaxAgeDays: 7, MaxMiB: 16}}; !reflect.DeepEqual(m.Streams, want) { - t.Errorf("streams = %+v, want %+v", m.Streams, want) + if len(m.Streams) != 0 { + t.Errorf("streams = %+v: the finding stream is a require now", m.Streams) + } + if want := findingRequires(1); !reflect.DeepEqual(m.Requires, want) { + t.Errorf("requires = %+v, want %+v", m.Requires, want) } if m.Version != "0.1" { t.Errorf("version = %q, want 0.1", m.Version) } // No bus or seat (ADR 0437 §4). - for _, k := range []string{"logs", "claims", "provides", "requires", "defines-seats", "seat", "seats", "bus"} { + for _, k := range []string{"logs", "claims", "provides", "defines-seats", "seat", "seats", "bus"} { if _, ok := top[k]; ok { t.Errorf("the manifest declares %q: the teller has none", k) } @@ -243,3 +247,22 @@ func TestNoPartHandWritesItsBusConnection(t *testing.T) { } } } + +// require is one entry of the manifest's requires: a provision the module asks for by a template (ADR 0595), which the +// controller's reader takes. +type require struct { + Name string `json:"name"` + Storage string `json:"storage"` + Template string `json:"template"` + Options map[string]any `json:"options"` +} + +// findingRequires is the findings state and the finding stream a module asks for by their templates (design 47 G +// §383, §387; A-30): 7 days and 16 MiB, the findings state keeping the given history. +func findingRequires(history float64) []require { + return []require{ + {Name: "findings", Storage: "key-value", Template: "state", Options: map[string]any{"history": history}}, + {Name: "finding-events", Storage: "stream", Template: "events", Options: map[string]any{ + "events": []any{"finding-written.*", "finding-cleared.*", "findings-stated.*"}, "max-age-days": float64(7), "max-mib": float64(16)}}, + } +} diff --git a/modules/mesh-delivery-view/manifest_test.go b/modules/mesh-delivery-view/manifest_test.go index 846c0b58..bf6b6067 100644 --- a/modules/mesh-delivery-view/manifest_test.go +++ b/modules/mesh-delivery-view/manifest_test.go @@ -75,6 +75,7 @@ type manifest struct { OwnSecrets map[string]string `json:"own-secrets"` SecretsOwner string `json:"secrets-owner"` Resources []map[string]any `json:"resources"` + Requires []require `json:"requires"` Items []item `json:"items"` Build struct { Artifacts []artifact `json:"artifacts"` @@ -128,8 +129,11 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7 t.Errorf("invokes = %v, want %v", m.Invokes, want) } // design 47 G §387: the keeper's ask for the findings is heard, the finding stream declared (§383), and §368: version is MAJOR.MINOR. - if want := []stream{{Name: "mesh-delivery-view-findings", Events: []string{"finding-written.*", "finding-cleared.*", "findings-stated.*"}, MaxAgeDays: 7, MaxMiB: 16}}; !reflect.DeepEqual(m.Streams, want) { - t.Errorf("streams = %+v, want %+v", m.Streams, want) + if len(m.Streams) != 0 { + t.Errorf("streams = %+v: the finding stream is a require now", m.Streams) + } + if want := findingRequires(1); !reflect.DeepEqual(m.Requires, want) { + t.Errorf("requires = %+v, want %+v", m.Requires, want) } if m.Version != "0.1" { t.Errorf("version = %q, want 0.1", m.Version) @@ -149,7 +153,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7 // The states the view writes: the operator's answers to the asks that need no proof (ADR 0528 clause 6, 1297/B9), which the // teller reads, and how far each repository reached (ADR 0556 clause 2, issue 1280), which the gate reads. // Its first views of the owners' states and the finding of one unanswered past its bound (ADR 0615 §6). - if want := []state{{Name: "answers", History: 5}, {Name: "reach", History: 1}, {Name: "first-views", History: 1}, {Name: "findings", History: 1}}; !reflect.DeepEqual(m.State, want) { + if want := []state{{Name: "answers", History: 5}, {Name: "reach", History: 1}, {Name: "first-views", History: 1}}; !reflect.DeepEqual(m.State, want) { t.Errorf("state = %+v, want %+v", m.State, want) } if _, ok := m.OwnSecrets["broker"]; !ok || len(m.OwnSecrets) != 1 { @@ -430,3 +434,22 @@ func TestEveryStateTheCommandsReadIsIssued(t *testing.T) { t.Fatal("no stdio.State literal found under cmd/: the scan reads nothing") } } + +// require is one entry of the manifest's requires: a provision the module asks for by a template (ADR 0595), which the +// controller's reader takes. +type require struct { + Name string `json:"name"` + Storage string `json:"storage"` + Template string `json:"template"` + Options map[string]any `json:"options"` +} + +// findingRequires is the findings state and the finding stream a module asks for by their templates (design 47 G +// §383, §387; A-30): 7 days and 16 MiB, the findings state keeping the given history. +func findingRequires(history float64) []require { + return []require{ + {Name: "findings", Storage: "key-value", Template: "state", Options: map[string]any{"history": history}}, + {Name: "finding-events", Storage: "stream", Template: "events", Options: map[string]any{ + "events": []any{"finding-written.*", "finding-cleared.*", "findings-stated.*"}, "max-age-days": float64(7), "max-mib": float64(16)}}, + } +} diff --git a/modules/mesh-delivery-watchman/manifest_test.go b/modules/mesh-delivery-watchman/manifest_test.go index 5f6a53dc..16598284 100644 --- a/modules/mesh-delivery-watchman/manifest_test.go +++ b/modules/mesh-delivery-watchman/manifest_test.go @@ -66,6 +66,7 @@ type manifest struct { OwnSecrets map[string]string `json:"own-secrets"` SecretsOwner string `json:"secrets-owner"` Resources []map[string]any `json:"resources"` + Requires []require `json:"requires"` Build struct { Artifacts []artifact `json:"artifacts"` } `json:"build"` @@ -95,7 +96,7 @@ func TestManifestStateAndReads(t *testing.T) { // 1135/AC6 if m.Module != "mesh-delivery-watchman" || m.Moved || m.Slug != "watchman" { t.Errorf("module %q moved %v (it has no template, so it is not moved) slug %q", m.Module, m.Moved, m.Slug) } - want := []state{{Name: "presence", History: 20}, {Name: "pulse", KeyTTL: true}, {Name: "stuck", History: 20}, {Name: "findings", History: 5}, {Name: "first-views", History: 1}} + want := []state{{Name: "presence", History: 20}, {Name: "pulse", KeyTTL: true}, {Name: "stuck", History: 20}, {Name: "first-views", History: 1}} if !reflect.DeepEqual(m.State, want) { t.Errorf("state = %+v, want %+v", m.State, want) } @@ -110,9 +111,11 @@ func TestManifestStateAndReads(t *testing.T) { // 1135/AC6 t.Errorf("version = %q, want MAJOR.MINOR", m.Version) } // Design 47 G §387: the finding stream, 7 days and 16 MiB, and the keeper's ask. - if len(m.Streams) != 1 || m.Streams[0].Name != "mesh-delivery-watchman-findings" || m.Streams[0].MaxAgeDays != 7 || m.Streams[0].MaxMiB != 16 || - !reflect.DeepEqual(m.Streams[0].Events, []string{"finding-written.*", "finding-cleared.*", "findings-stated.*"}) { - t.Errorf("streams = %+v, want the finding stream", m.Streams) + if len(m.Streams) != 0 { + t.Errorf("streams = %+v: the finding stream is a require now", m.Streams) + } + if want := findingRequires(5); !reflect.DeepEqual(m.Requires, want) { + t.Errorf("requires = %+v, want %+v", m.Requires, want) } // DLV-M5 (ADR 0615 §1): the assigner's wants and the indexer's modules are their owners' states, asked by their read // tools and followed by their views families, never read. @@ -266,3 +269,22 @@ func TestNoTickerTimerOrSleep(t *testing.T) { // 1135/AC7 t.Fatalf("read %d source files, want the program's", files) } } + +// require is one entry of the manifest's requires: a provision the module asks for by a template (ADR 0595), which the +// controller's reader takes. +type require struct { + Name string `json:"name"` + Storage string `json:"storage"` + Template string `json:"template"` + Options map[string]any `json:"options"` +} + +// findingRequires is the findings state and the finding stream a module asks for by their templates (design 47 G +// §383, §387; A-30): 7 days and 16 MiB, the findings state keeping the given history. +func findingRequires(history float64) []require { + return []require{ + {Name: "findings", Storage: "key-value", Template: "state", Options: map[string]any{"history": history}}, + {Name: "finding-events", Storage: "stream", Template: "events", Options: map[string]any{ + "events": []any{"finding-written.*", "finding-cleared.*", "findings-stated.*"}, "max-age-days": float64(7), "max-mib": float64(16)}}, + } +}