diff --git a/modules/mesh-delivery-forge-bridge/cmd/mesh-delivery-forge-bridge/integration_test.go b/modules/mesh-delivery-forge-bridge/cmd/mesh-delivery-forge-bridge/integration_test.go index a33ce668..f0365727 100644 --- a/modules/mesh-delivery-forge-bridge/cmd/mesh-delivery-forge-bridge/integration_test.go +++ b/modules/mesh-delivery-forge-bridge/cmd/mesh-delivery-forge-bridge/integration_test.go @@ -86,9 +86,11 @@ accounts { "$JS.ACK.EVENTS.n1_mesh-delivery-forge-bridge.>" "$JS.API.CONSUMER.INFO.EVENTS.n1_mesh-delivery-forge-bridge" "$JS.API.CONSUMER.MSG.NEXT.EVENTS.n1_mesh-delivery-forge-bridge" + "$JS.API.STREAM.INFO.SEAT_mesh-delivery_owed" "$JS.ACK.SEAT_mesh-delivery_owed.n1_mesh-delivery-forge-bridge.>" "$JS.API.CONSUMER.INFO.SEAT_mesh-delivery_owed.n1_mesh-delivery-forge-bridge" "$JS.API.CONSUMER.MSG.NEXT.SEAT_mesh-delivery_owed.n1_mesh-delivery-forge-bridge" + "$JS.API.STREAM.INFO.SEAT_mesh-delivery-checker_owed" "$JS.ACK.SEAT_mesh-delivery-checker_owed.n1_mesh-delivery-forge-bridge.>" "$JS.API.CONSUMER.INFO.SEAT_mesh-delivery-checker_owed.n1_mesh-delivery-forge-bridge" "$JS.API.CONSUMER.MSG.NEXT.SEAT_mesh-delivery-checker_owed.n1_mesh-delivery-forge-bridge" diff --git a/modules/mesh-grants/internal/buscompose/modstreams.go b/modules/mesh-grants/internal/buscompose/modstreams.go index af945c1b..0c724157 100644 --- a/modules/mesh-grants/internal/buscompose/modstreams.go +++ b/modules/mesh-grants/internal/buscompose/modstreams.go @@ -4,6 +4,8 @@ import ( "fmt" "sort" "strings" + + "git.novox.be/novox/mesh-sdk/go/seatdef" ) // A module's own stream on the bus (novox/hq ADR 0363). @@ -260,6 +262,11 @@ func heardGrants(p Principal, hears []Heard) []string { "$JS.API.CONSUMER.INFO."+h.Stream+"."+durable, "$JS.API.CONSUMER.MSG.NEXT."+h.Stream+"."+durable, "$JS.ACK."+h.Stream+"."+durable+".>") + if strings.HasPrefix(h.Stream, seatdef.KeptStreamPrefix) { + // A seat's kept event is heard through process.HearSeatEvents, which reads the stream's own info to see whether it is + // kept last per subject (design 47 G §641, A-19): the info of this stream alone. + out = append(out, "$JS.API.STREAM.INFO."+h.Stream) + } if h.LastPerSubject { // A stream kept last per subject is read whole at each start through an ordered consumer, made for that one read // and with no durable name: its creation, its next message, its info and the deletion of its own (design 47 G §556, A-4). diff --git a/modules/mesh-grants/internal/buscompose/orderedread_test.go b/modules/mesh-grants/internal/buscompose/orderedread_test.go index fc61c48f..8c7192f0 100644 --- a/modules/mesh-grants/internal/buscompose/orderedread_test.go +++ b/modules/mesh-grants/internal/buscompose/orderedread_test.go @@ -21,3 +21,17 @@ func TestAReaderOfAStreamKeptLastPerSubjectIsGrantedItsOrderedConsumer(t *testin t.Errorf("a reader of another stream is granted an ordered consumer: %v", plain) } } + +// A reader of a seat's kept event is granted the info of that stream, which process.HearSeatEvents reads to see whether the +// stream is kept last per subject, and no stream's info besides (design 47 G §641, A-19). +func TestAReaderOfASeatsKeptEventIsGrantedItsStreamInfo(t *testing.T) { + p := Principal{Module: "m", Node: "a"} + kept := heardGrants(p, []Heard{{Stream: "SEAT_mesh-delivery_owed", Filters: []string{"f"}}}) + if !slices.Contains(kept, "$JS.API.STREAM.INFO.SEAT_mesh-delivery_owed") { + t.Errorf("a reader of a seat's kept event is not granted its stream's info: %v", kept) + } + other := heardGrants(p, []Heard{{Stream: "mesh-x-events", Filters: []string{"f"}}}) + if slices.Contains(other, "$JS.API.STREAM.INFO.mesh-x-events") { + t.Errorf("a reader of a module's stream is granted its info: %v", other) + } +}