mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
f72aba0c91
The last 20 wire methods move onto the decorators: db.* and the three unconditional methods (DbRpc), meta.* (MetaRpc) and the host-mediated surface of users/broadcasts/notify/ai/oauth/scheduler (HostSurfaceRpc). That closes the ledger at 113/113, so the legacy has() blocks in rpc-host.ts are gone and the router is down to what was never domain-specific: build the granted subset of the dispatch map, dispatch, audit, map errors. PluginHostDepsFactory was the 26-argument wiring sheet behind those blocks. With the handlers in the domains it has two dependencies left, so it is now PluginRpcHostFactory: the plugin's own sqlite handle, the inter-plugin router bound with this plugin as the caller, and the audit sink. The 18 host-to-plugin hook calls get the same treatment. They used to spell out the fn name and the timeout at each controller, which meant nothing checked that a hook:* grant on the consent screen had a consumer behind it, that the fn matched what the SDK documents, or that two call sites agreed on the budget. PluginHooks declares all 18 with @PluginHook and performs them, and the controllers call it by name. With both halves complete, requireTotalCoverage goes on: a KNOWN_METHOD without a handler, or a hook without a consumer, now fails app boot instead of surfacing as a PERMISSION_DENIED nobody can explain.
78 lines
3.7 KiB
TypeScript
78 lines
3.7 KiB
TypeScript
/**
|
|
* The coverage ledger: the anchor for the whole decorator rollout.
|
|
*
|
|
* Every wire method is owned by exactly one decorated *.rpc.ts class in the registry.
|
|
* Both sets are derived by construction, so no list is maintained by hand and the test
|
|
* cannot drift.
|
|
*
|
|
* The rollout is finished, so the legacy set is empty and the ledger's job flips from
|
|
* "how far along is the move" to "nothing fell out of it": a method deleted without a
|
|
* replacement, or a new *.rpc.ts class that no one listed in allRpcControllers().
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import { PluginRpcHost } from '../../../src/nest/plugins/host/rpc-host';
|
|
import { createTestPluginRegistry } from '../../../src/nest/plugins/host/rpc-kit/testing';
|
|
import { PluginRpcRegistryService } from '../../../src/nest/plugins/host/rpc-kit/registry.service';
|
|
import {
|
|
KNOWN_METHODS,
|
|
KNOWN_PERMISSIONS,
|
|
UNCONDITIONAL_METHODS,
|
|
} from '../../../src/nest/plugins/protocol/envelope';
|
|
import { allRpcControllers, makeDeps } from '../../helpers/rpc-host-deps';
|
|
|
|
/** Reads the router's private dispatch map. That map IS the thing under test. */
|
|
const boundMethods = (host: PluginRpcHost): Set<string> =>
|
|
new Set((host as unknown as { methods: Map<string, unknown> }).methods.keys());
|
|
|
|
describe('plugin RPC coverage ledger', () => {
|
|
const registry = createTestPluginRegistry(allRpcControllers());
|
|
// Every permission granted, so the router registers everything it can.
|
|
const host = new PluginRpcHost('probe', new Set<string>(KNOWN_PERMISSIONS), makeDeps(), registry);
|
|
const all = boundMethods(host);
|
|
const fromRegistry = registry.methodNames();
|
|
const vocabulary = new Set<string>([...KNOWN_METHODS, ...UNCONDITIONAL_METHODS]);
|
|
|
|
it('RPCLEDGER-001 every bound method comes from the registry, none from anywhere else', () => {
|
|
expect([...all].filter((m) => !fromRegistry.has(m))).toEqual([]);
|
|
});
|
|
|
|
it('RPCLEDGER-002 every registered method is one the wire protocol knows', () => {
|
|
expect([...all].filter((m) => !vocabulary.has(m))).toEqual([]);
|
|
});
|
|
|
|
it('RPCLEDGER-003 no method in the vocabulary is orphaned', () => {
|
|
expect([...vocabulary].filter((m) => !all.has(m))).toEqual([]);
|
|
});
|
|
|
|
it('RPCLEDGER-004 the registry covers the vocabulary exactly', () => {
|
|
expect(all).toEqual(vocabulary);
|
|
expect(fromRegistry.size).toBe(KNOWN_METHODS.length + UNCONDITIONAL_METHODS.length);
|
|
});
|
|
|
|
it('RPCLEDGER-005 the surface is the 113 methods the protocol declares', () => {
|
|
// Pinned rather than derived: a method DELETED from KNOWN_METHODS together with
|
|
// its handler would keep every test above green, and this is the line that makes
|
|
// that show up in the diff.
|
|
expect(KNOWN_METHODS.length).toBe(110);
|
|
expect(UNCONDITIONAL_METHODS.length).toBe(3);
|
|
expect(fromRegistry.size).toBe(113);
|
|
});
|
|
|
|
it('RPCLEDGER-006 boot-time total coverage is armed in production', () => {
|
|
// The rollout's closing move: the registry service now FAILS APP BOOT when a
|
|
// KNOWN_METHOD has no decorated owner, instead of letting it surface as a runtime
|
|
// PERMISSION_DENIED nobody can explain.
|
|
const options = (new PluginRpcRegistryService({} as never, {} as never) as unknown as {
|
|
options: { requireTotalCoverage?: boolean };
|
|
}).options;
|
|
expect(options.requireTotalCoverage).toBe(true);
|
|
});
|
|
|
|
it('RPCLEDGER-007 a registry missing a method fails validate() rather than booting', () => {
|
|
// Drops TagsRpc, a method owner. Dropping the last entry would drop PluginHooks
|
|
// instead, and the failure would be about hooks rather than methods.
|
|
const short = allRpcControllers().slice(1);
|
|
expect(() => createTestPluginRegistry(short, { requireTotalCoverage: true })).toThrow(/has no handler/);
|
|
});
|
|
});
|