A membership carries its module's bus-state bindings, by local name (issue 1966 S3)
ADR 0595 §1, §7: the binding travels on the bus module's created and the grants part writes it into the membership; omitted when empty, so a membership without bindings encodes byte for byte as before. The two address tests are one table.
This commit is contained in:
@@ -57,6 +57,33 @@ type Membership struct {
|
||||
Hears []HeardIssued `json:"hears,omitempty"`
|
||||
// Waits are the event families a session may wait on through the mesh MCP server (novox/hq ADR 0367 §2).
|
||||
Waits []WaitIssued `json:"waits,omitempty"`
|
||||
// Bindings are the module's bus state as the bus module created it, one per entry its manifest asks for in
|
||||
// `requires` (novox/hq ADR 0595 §1, §7): written by the grants part when it hears the bus module's `created`.
|
||||
Bindings []BusBinding `json:"bindings,omitempty"`
|
||||
}
|
||||
|
||||
// BusBinding is one bus-state entry's names on the bus, by the local name its module asks for it by (novox/hq ADR 0595
|
||||
// §1, design 25 § 12.2): the module names only locally, and reaches its own entries, and only those, through these.
|
||||
// Which names are set follows the storage type: a stream's Stream and Subject, a key-value bucket's Bucket, an object
|
||||
// store's ObjectStore, and a ledger's History, State, Indexes and Snapshot.
|
||||
type BusBinding struct {
|
||||
Local string `json:"local"`
|
||||
// Storage and Template are the entry's storage type and template (catalogue.Storage…, catalogue.Template…).
|
||||
Storage string `json:"storage"`
|
||||
Template string `json:"template,omitempty"`
|
||||
// Stream and Subject are a stream's name and the subject its messages are on; Subject is also a ledger's history's.
|
||||
Stream string `json:"stream,omitempty"`
|
||||
Subject string `json:"subject,omitempty"`
|
||||
// Bucket is a key-value bucket's name.
|
||||
Bucket string `json:"bucket,omitempty"`
|
||||
// ObjectStore is an object store's name.
|
||||
ObjectStore string `json:"object-store,omitempty"`
|
||||
// Ledger, for the storage type ledger: its history stream (one subject per instance), its state bucket, each
|
||||
// declared index's bucket by index name, and the subject of the instances' snapshots (ADR 0597 §1, §4, §8).
|
||||
History string `json:"history,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
Indexes map[string]string `json:"indexes,omitempty"`
|
||||
Snapshot string `json:"snapshot,omitempty"`
|
||||
}
|
||||
|
||||
// Served is one address a tool is answered on.
|
||||
|
||||
@@ -6,31 +6,22 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// A credential is read out of a bus address, and the bare address carries no credential and a scheme.
|
||||
func TestACredentialIsReadOutOfABusAddress(t *testing.T) {
|
||||
for _, c := range []struct{ in, user, password, bare string }{
|
||||
{"nats://controller:secret@127.0.0.1:4222", "controller", "secret", "nats://127.0.0.1:4222"},
|
||||
{"controller:secret@127.0.0.1:4222", "controller", "secret", "127.0.0.1:4222"},
|
||||
{"nats://127.0.0.1:4222", "", "", "nats://127.0.0.1:4222"},
|
||||
{"127.0.0.1:4222", "", "", "127.0.0.1:4222"},
|
||||
for _, c := range []struct{ in, user, password, bare, schemed string }{
|
||||
{"nats://controller:secret@127.0.0.1:4222", "controller", "secret", "nats://127.0.0.1:4222", "nats://127.0.0.1:4222"},
|
||||
{"controller:secret@127.0.0.1:4222", "controller", "secret", "127.0.0.1:4222", "nats://127.0.0.1:4222"},
|
||||
{"nats://127.0.0.1:4222", "", "", "nats://127.0.0.1:4222", "nats://127.0.0.1:4222"},
|
||||
{"127.0.0.1:4222", "", "", "127.0.0.1:4222", "nats://127.0.0.1:4222"},
|
||||
// A password containing an at-sign: split on the last one.
|
||||
{"nats://controller:a@b@127.0.0.1:4222", "controller", "a@b", "nats://127.0.0.1:4222"},
|
||||
{"nats://controller:a@b@127.0.0.1:4222", "controller", "a@b", "nats://127.0.0.1:4222", "nats://127.0.0.1:4222"},
|
||||
} {
|
||||
user, password, bare := CredentialIn(c.in)
|
||||
if user != c.user || password != c.password || bare != c.bare {
|
||||
t.Errorf("%q read as %q/%q at %q; wanted %q/%q at %q", c.in, user, password, bare, c.user, c.password, c.bare)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestABareAddressCarriesNoCredentialAndAScheme(t *testing.T) {
|
||||
for in, want := range map[string]string{
|
||||
"nats://controller:secret@10.0.0.1:4222": "nats://10.0.0.1:4222",
|
||||
"controller:secret@10.0.0.1:4222": "nats://10.0.0.1:4222",
|
||||
"nats://10.0.0.1:4222": "nats://10.0.0.1:4222",
|
||||
"10.0.0.1:4222": "nats://10.0.0.1:4222",
|
||||
} {
|
||||
if got := BareAddress(in); got != want {
|
||||
t.Errorf("BareAddress(%q) = %q, want %q", in, got, want)
|
||||
if got := BareAddress(c.in); got != c.schemed {
|
||||
t.Errorf("BareAddress(%q) = %q, want %q", c.in, got, c.schemed)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +57,24 @@ func TestAMembershipRoundTripsAsIssued(t *testing.T) {
|
||||
if !reflect.DeepEqual(in, out) {
|
||||
t.Fatalf("round trip changed it:\n%+v\n%+v", in, out)
|
||||
}
|
||||
// 1966/AC4: a membership with a ledger binding round-trips; one without bindings encodes as before, byte for byte.
|
||||
bound := in
|
||||
bound.Bindings = []BusBinding{{Local: "records", Storage: "ledger", Template: "ledger", History: "forge_records",
|
||||
Subject: "forge_records.>", State: "forge_records_state", Indexes: map[string]string{"search": "forge_records_search"},
|
||||
Snapshot: "forge_records_snapshot.>"}}
|
||||
boundRaw, err := json.Marshal(bound)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var boundBack Membership
|
||||
if err := json.Unmarshal(boundRaw, &boundBack); err != nil || !reflect.DeepEqual(bound, boundBack) {
|
||||
t.Fatalf("a membership with a ledger binding read back as %+v (%v)", boundBack, err)
|
||||
}
|
||||
before, _ := json.Marshal(Membership{Node: "ace", Module: "forge", Tools: "t", Emits: "e", Serves: []Served{{Subject: "s"}},
|
||||
State: []StateIssued{{Name: "repos", Bucket: "forge_repos", Writes: true}}})
|
||||
if string(before) != `{"node":"ace","module":"forge","serves":[{"subject":"s"}],"emits":"e","tools":"t","state":[{"name":"repos","bucket":"forge_repos","writes":true}]}` {
|
||||
t.Fatalf("a membership without bindings encodes otherwise than before: %s", before)
|
||||
}
|
||||
var wire map[string]json.RawMessage
|
||||
_ = json.Unmarshal(raw, &wire)
|
||||
for _, k := range []string{"receives", "mesh", "seat-traffic"} {
|
||||
|
||||
Reference in New Issue
Block a user