fix9-1: the teller, view and watchman manifest tests' reader takes requires, and hold the findings as requires

Builds: design 47 G §383, §387; A-30
This commit is contained in:
2026-11-12 01:23:28 +01:00
parent 1eb481ccc8
commit e69f55468c
3 changed files with 79 additions and 11 deletions
+27 -4
View File
@@ -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 <module>-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)}},
}
}
+26 -3
View File
@@ -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)}},
}
}
@@ -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)}},
}
}