Serve a seat's verbs from the membership once one is issued (hq issue 218) #45
@@ -342,13 +342,16 @@ async function serveClaimedSeats(
|
||||
for (const module of served) {
|
||||
const m = typeof broker.membership === "function" ? broker.membership(module) : undefined;
|
||||
for (const s of m?.seats ?? []) add({ seat: s.seat, verb: s.verb, subject: s.subject, holder: module });
|
||||
// The credential's claims, for the module's own runtime: where the mesh issued the verb when
|
||||
// it has; the derived shape until then.
|
||||
if (module !== self) continue;
|
||||
// The credential's claims, for the module's own runtime, ONLY until the mesh issues a
|
||||
// membership (novox/hq issue 218). The credential names what the module claims; the membership
|
||||
// names what it holds on this machine. A seat held once for the mesh is claimed by every
|
||||
// machine running the module and held by one, so once a membership exists it decides: a claim
|
||||
// it leaves out is not held here, and serving it anyway announced the seat from a machine the
|
||||
// bus then refused it on.
|
||||
if (module !== self || m) continue;
|
||||
for (const claim of credential?.claims ?? []) {
|
||||
for (const verb of claim.serves ?? []) {
|
||||
const subject = m?.seats?.find((s) => s.seat === claim.seat && s.verb === verb)?.subject
|
||||
?? seatToolSubject(claim.seat, verb, claim.scope, credential?.node);
|
||||
const subject = seatToolSubject(claim.seat, verb, claim.scope, credential?.node);
|
||||
add({ seat: claim.seat, verb, subject, holder: module });
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// A copy for the issue 218 test: a fixture registers once per process, at import.
|
||||
// A module that is both software and a role: postgres's own tools under its name, and its
|
||||
// implementation of the mesh-store seat's verbs under the seat's (novox/hq ADR 0159, 0160).
|
||||
import { registerModuleTools } from "@novox/mesh-sdk/tools";
|
||||
|
||||
registerModuleTools("postgres", () => [
|
||||
{ name: "postgres_create_database", description: "make one", input: {}, run: async () => ({ made: true }) },
|
||||
{ name: "databases", description: "postgres's own listing", input: {}, run: async () => ({ software: "postgres" }) },
|
||||
]);
|
||||
|
||||
registerModuleTools("mesh-store", () => [
|
||||
{ name: "databases", description: "what the store holds", input: {}, run: async () => ({ seat: "mesh-store" }) },
|
||||
]);
|
||||
@@ -140,6 +140,36 @@ test("a seat's verbs are implemented under the seat's name, served where issued,
|
||||
}
|
||||
});
|
||||
|
||||
// novox/hq issue 218: the store seat is claimed by every machine running postgres and held by one.
|
||||
// Where the mesh issued a membership without the seat, the claim in the credential serves nothing:
|
||||
// the module's own tools answer, the seat's verbs do not, and the runtime does not announce them.
|
||||
test("a claimant the membership does not make the holder serves none of the seat's verbs", async (t) => {
|
||||
if (!url) return t.skip("MESH_TEST_NATS unset");
|
||||
resetTools();
|
||||
const stream = await anAssignmentsStream();
|
||||
await stream.issue({
|
||||
node: "elsewhere",
|
||||
module: "postgres",
|
||||
serves: [{ subject: "mesh.mod.postgres.tool.{tool}.elsewhere" }],
|
||||
emits: "mesh.mod.postgres.event.{event}",
|
||||
tools: "mesh.mod.postgres.tool.tools",
|
||||
});
|
||||
const credential = { url, node: "elsewhere", module: "postgres", claims: [{ seat: "mesh-store", scope: "mesh", serves: ["databases", "query"] }] };
|
||||
const pg = await connectNats(credential);
|
||||
const asker = await connectNats({ url, module: "console", node: "workstation" });
|
||||
let stop = () => {};
|
||||
try {
|
||||
stop = await runTools({ broker: pg, credential, moduleEntrypoints: [fixture("store-claimant.mjs")] });
|
||||
assert.deepEqual((await callTool(asker, "postgres.databases@elsewhere", {})).result, { software: "postgres" });
|
||||
await assert.rejects(callTool(asker, "seat:mesh-store.databases", {}), /no responders|503/i, "the store is not held here");
|
||||
} finally {
|
||||
stop();
|
||||
await asker.close();
|
||||
await pg.close();
|
||||
await stream.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("a module named like its seat registers once, and answers as the module and as the seat", async (t) => {
|
||||
if (!url) return t.skip("MESH_TEST_NATS unset");
|
||||
resetTools();
|
||||
|
||||
Reference in New Issue
Block a user