Issue 1585, S19: the strict parser, every manifest problem function and the judge's tables (192 declarations, about 5,200 lines) and their 244 tests leave; composition keeps its own refusals and the helpers it uses. The catalogue's tests decode the manifests they build. ParseManifest, ParseManifestWith, ParseManifestForInputs, CatalogueProblems and the rest are gone; mesh-delivery-indexer/judge holds them. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
155 lines
6.7 KiB
Go
155 lines
6.7 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"encoding/json"
|
|
"slices"
|
|
"testing"
|
|
)
|
|
|
|
// mailManifest is a module owning a restricted stream, as mesh-mail declares it (novox/hq ADR 0363), with
|
|
// the trusted mergeable file that takes its grants setting; extra is spliced in at the top.
|
|
func mailManifest(t *testing.T, streams, extra string) string {
|
|
t.Helper()
|
|
return `{"module":"mesh-mail","version":"1",` + extra + `"streams":` + streams + `,` +
|
|
`"resources":[{"id":"state","type":"directory","mode":"0700","place":"."},` +
|
|
`{"id":"grants","trusted":true,"type":"file","path":"${dir:state}/grants.json","mode":"0600",` +
|
|
`"merge":"json","takes-settings":["grants"],"content":"{\"grants\": {}}"}]}`
|
|
}
|
|
|
|
const mailStream = `[{"name":"mail","events":["mail.*.message.added","mail.*.message.changed",` +
|
|
`"mail.*.message.removed"],"max-age-days":7,"grant":{"config":"grants","events":"mail.{key}.**"}}]`
|
|
|
|
// A module declares a stream of its own (ADR 0363 §1): its events with a variable name (§3), its age and
|
|
// size with defaults, and a grant by a trusted setting of its own (§4).
|
|
func TestAModuleDeclaresAStreamOfItsOwn(t *testing.T) {
|
|
m, err := decodeForTest([]byte(mailManifest(t, mailStream, "")))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(m.Streams) != 1 {
|
|
t.Fatalf("streams not read: %+v", m.Streams)
|
|
}
|
|
s := m.Streams[0]
|
|
if s.BusName() != "MAIL" || s.Days() != 7 || s.Cap() != StreamDefaultMiB || s.Grant == nil ||
|
|
s.Grant.Config != "grants" || s.Grant.Granted("mesh") != "mail.mesh.**" {
|
|
t.Fatalf("stream read as %+v", s)
|
|
}
|
|
if !s.Carries("mail.work.message.added") || s.Carries("mail.work.body") || !s.Carries("mail.*.message.removed") {
|
|
t.Fatal("the stream's patterns do not say what it carries")
|
|
}
|
|
plain, err := decodeForTest([]byte(`{"module":"feed","version":"1","streams":[{"name":"feed-items","events":["item.**"]}]}`))
|
|
if err != nil {
|
|
t.Fatalf("a stream without a grant was refused: %v", err)
|
|
}
|
|
if got := plain.Streams[0]; got.BusName() != "FEED_ITEMS" || got.Days() != StreamDefaultDays || got.Grant != nil {
|
|
t.Fatalf("defaults read as %+v", got)
|
|
}
|
|
}
|
|
|
|
// The setting's value names who hears each key: a module wherever it runs, or on one machine (ADR 0363 §4).
|
|
func TestARestrictingSettingNamesAModuleOrAModuleOnAMachine(t *testing.T) {
|
|
var value any
|
|
if err := json.Unmarshal([]byte(`{"mesh":["reader","person.jochen"],"work":["ace.reader"],"home":["other"],`+
|
|
`"Bad.Key":["reader"]}`), &value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := GrantedKeys(value, "reader", "ace"); !slices.Equal(got, []string{"mesh", "work"}) {
|
|
t.Fatalf("on ace reader is granted %v", got)
|
|
}
|
|
if got := GrantedKeys(value, "reader", "shanks"); !slices.Equal(got, []string{"mesh"}) {
|
|
t.Fatalf("on shanks reader is granted %v", got)
|
|
}
|
|
if got := GrantedKeys(`{"mesh":["reader"]}`, "reader", "ace"); !slices.Equal(got, []string{"mesh"}) {
|
|
t.Fatalf("a value held as its JSON text grants %v", got)
|
|
}
|
|
if got := GrantedKeys([]any{"reader"}, "reader", "ace"); len(got) != 0 {
|
|
t.Fatalf("a value of another shape granted %v", got)
|
|
}
|
|
}
|
|
|
|
// A consumer hears a restricted stream only where its consumes asks and the setting grants it, and only
|
|
// by naming the emitter (ADR 0363 §5); what a stream carries whole leaves EVENTS.
|
|
func TestAConsumerHearsOnlyWhatItAsksAndIsGranted(t *testing.T) {
|
|
provider, err := decodeForTest([]byte(mailManifest(t, mailStream, `"emits":["account.added"],`)))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
shelf := map[string]Manifest{"mesh-mail": provider}
|
|
var value any
|
|
_ = json.Unmarshal([]byte(`{"mesh":["reader"],"work":["shanks.reader"]}`), &value)
|
|
values := func(module, setting string) []any {
|
|
if module == "mesh-mail" && setting == "grants" {
|
|
return []any{value}
|
|
}
|
|
return nil
|
|
}
|
|
reader := Manifest{Module: "reader", Consumes: []string{"mesh-mail.mail.**", "mesh-mail.account.added", "billing.paid"}}
|
|
|
|
parts, touches, onEvents := Hearing(reader, shelf, "ace", values)
|
|
if !touches || len(parts) != 1 || parts[0].Stream.BusName() != "MAIL" {
|
|
t.Fatalf("heard %+v", parts)
|
|
}
|
|
want := []string{"mail.mesh.message.added", "mail.mesh.message.changed", "mail.mesh.message.removed"}
|
|
if !slices.Equal(parts[0].Patterns, want) {
|
|
t.Fatalf("on ace reader hears %v, want %v", parts[0].Patterns, want)
|
|
}
|
|
if !slices.Equal(onEvents, []string{"mesh-mail.account.added", "billing.paid"}) {
|
|
t.Fatalf("left on EVENTS: %v", onEvents)
|
|
}
|
|
parts, _, _ = Hearing(reader, shelf, "shanks", values)
|
|
if len(parts) != 1 || len(parts[0].Patterns) != 6 {
|
|
t.Fatalf("on shanks reader hears %+v", parts)
|
|
}
|
|
|
|
narrow := Manifest{Module: "reader", Consumes: []string{"mesh-mail.mail.*.message.added"}}
|
|
parts, _, _ = Hearing(narrow, shelf, "ace", values)
|
|
if len(parts) != 1 || !slices.Equal(parts[0].Patterns, []string{"mail.mesh.message.added"}) {
|
|
t.Fatalf("a narrower consumes heard %+v", parts)
|
|
}
|
|
|
|
stranger := Manifest{Module: "stranger", Consumes: []string{"mesh-mail.mail.**"}}
|
|
parts, touches, onEvents = Hearing(stranger, shelf, "ace", values)
|
|
if len(parts) != 0 || !touches || len(onEvents) != 0 {
|
|
t.Fatalf("a module not granted heard %+v (touches %v, on EVENTS %v)", parts, touches, onEvents)
|
|
}
|
|
everything := Manifest{Module: "audit", Consumes: []string{"**", "*.mail.**"}}
|
|
parts, touches, _ = Hearing(everything, shelf, "ace", values)
|
|
if len(parts) != 0 || touches {
|
|
t.Fatalf("a wildcard emitter reached a module's stream: %+v", parts)
|
|
}
|
|
parts, _, _ = Hearing(reader, shelf, "ace", func(string, string) []any { return nil })
|
|
if len(parts) != 0 {
|
|
t.Fatalf("with no setting reader heard %+v", parts)
|
|
}
|
|
}
|
|
|
|
// A stream without a grant is heard by whoever asks, within its patterns.
|
|
func TestAnUnrestrictedStreamIsHeardByWhoeverAsks(t *testing.T) {
|
|
feed := Manifest{Module: "feed", Streams: []StreamDeclaration{{Name: "items", Events: []string{"item.*.added", "item.*.gone"}}}}
|
|
reader := Manifest{Module: "reader", Consumes: []string{"feed.item.**", "feed.item.x.added"}}
|
|
parts, touches, onEvents := Hearing(reader, map[string]Manifest{"feed": feed}, "ace", nil)
|
|
if !touches || len(onEvents) != 0 || len(parts) != 1 ||
|
|
!slices.Equal(parts[0].Patterns, []string{"item.*.added", "item.*.gone"}) {
|
|
t.Fatalf("heard %+v, on EVENTS %v", parts, onEvents)
|
|
}
|
|
}
|
|
|
|
func TestNarrowingTwoPatterns(t *testing.T) {
|
|
for _, c := range []struct{ a, b, want string }{
|
|
{"mail.**", "mail.*.message.added", "mail.*.message.added"},
|
|
{"mail.*.message.added", "mail.mesh.**", "mail.mesh.message.added"},
|
|
{"a.*", "*.b", "a.b"},
|
|
{"**", "a.**", "a.**"},
|
|
{"a.b", "a.c", ""},
|
|
{"a", "a.**", ""},
|
|
} {
|
|
got, ok := narrow(c.a, c.b)
|
|
if (c.want == "") == ok || got != c.want {
|
|
t.Errorf("narrow(%s, %s) = %q, %v; want %q", c.a, c.b, got, ok, c.want)
|
|
}
|
|
}
|
|
if got := uncovered([]string{"a.**", "a.b", "a.b", "c"}); !slices.Equal(got, []string{"a.**", "c"}) {
|
|
t.Fatalf("uncovered: %v", got)
|
|
}
|
|
}
|