mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
294cfcd8d7
collab, journal, atlas, vacay and collections: 32 more methods, so the registry owns 93 of 113. rpc-host.ts is at 565 lines, down from 1332, and the deps factory at 384, down from 944. These five are the risky group, because their addon gates lived inside the deps factory's closure rather than in the router. A handler that calls its service directly still works, just without the check, and neither the compiler nor the router tests would notice. So addon-gates.rpc.test.ts walks all 37 gated methods as one table and asserts two things for each: with the addon off it is refused with that addon's message, and the service is never reached. The table length is pinned, so a later method added without a row is a failure rather than a silent gap. One ordering detail worth knowing: on the atlas writes the router evaluated its params before the addon gate inside the closure, so a malformed country code still reports BAD_PARAMS rather than the addon. The handlers keep that order. The collections service reports refusals as status-tagged errors rather than typed ones, so mapCollectionError came across with the handlers: 403 and 404 become RESOURCE_FORBIDDEN, 400 and 409 become BAD_PARAMS, and anything untagged stays a HOST_ERROR instead of being dressed up as a permission problem.
159 lines
5.3 KiB
TypeScript
159 lines
5.3 KiB
TypeScript
/**
|
|
* The coverage ledger: the anchor for the whole decorator rollout.
|
|
*
|
|
* Every wire method is owned exactly once, either by the legacy router blocks in
|
|
* rpc-host.ts or by a 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.
|
|
*
|
|
* It fails the moment a migration PR moves a method and forgets to delete the legacy
|
|
* registration, deletes one without adding it, or forgets to list a new *.rpc.ts class
|
|
* 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 {
|
|
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 legacy = new Set([...all].filter((m) => !fromRegistry.has(m)));
|
|
|
|
it('RPCLEDGER-001 no method is owned by both the registry and the legacy router', () => {
|
|
expect([...fromRegistry].filter((m) => legacy.has(m))).toEqual([]);
|
|
});
|
|
|
|
it('RPCLEDGER-002 every registered method is one the wire protocol knows', () => {
|
|
const vocabulary = new Set<string>([...KNOWN_METHODS, ...UNCONDITIONAL_METHODS]);
|
|
expect([...all].filter((m) => !vocabulary.has(m))).toEqual([]);
|
|
});
|
|
|
|
it('RPCLEDGER-003 no method in the vocabulary is orphaned', () => {
|
|
const vocabulary = new Set<string>([...KNOWN_METHODS, ...UNCONDITIONAL_METHODS]);
|
|
expect([...vocabulary].filter((m) => !all.has(m))).toEqual([]);
|
|
});
|
|
|
|
it('RPCLEDGER-004 the two owner sets partition the vocabulary exactly', () => {
|
|
expect(all).toEqual(new Set<string>([...KNOWN_METHODS, ...UNCONDITIONAL_METHODS]));
|
|
expect(fromRegistry.size + legacy.size).toBe(all.size);
|
|
});
|
|
|
|
it('RPCLEDGER-005 the migration ledger reads as expected for this PR', () => {
|
|
// Update these two numbers in every rollout PR. They make the diff state the size
|
|
// of the move out loud, which is the number a reviewer wants to see.
|
|
const total = KNOWN_METHODS.length + UNCONDITIONAL_METHODS.length;
|
|
expect(fromRegistry.size).toBe(93);
|
|
expect(legacy.size).toBe(total - 93);
|
|
});
|
|
|
|
it('RPCLEDGER-006 the migrated methods are exactly the ones this PR claims', () => {
|
|
expect([...fromRegistry].sort()).toEqual([
|
|
'accommodations.create',
|
|
'accommodations.delete',
|
|
'accommodations.update',
|
|
'atlas.bucketList',
|
|
'atlas.createBucketItem',
|
|
'atlas.deleteBucketItem',
|
|
'atlas.markCountry',
|
|
'atlas.markRegion',
|
|
'atlas.unmarkCountry',
|
|
'atlas.unmarkRegion',
|
|
'atlas.visited',
|
|
'categories.list',
|
|
'collab.createMessage',
|
|
'collab.createNote',
|
|
'collab.createPoll',
|
|
'collab.listMessages',
|
|
'collab.listNotes',
|
|
'collab.listPolls',
|
|
'collab.votePoll',
|
|
'collections.copyToTrip',
|
|
'collections.create',
|
|
'collections.deletePlace',
|
|
'collections.get',
|
|
'collections.listMine',
|
|
'collections.savePlace',
|
|
'collections.update',
|
|
'costs.create',
|
|
'costs.delete',
|
|
'costs.getByTrip',
|
|
'costs.listMine',
|
|
'costs.update',
|
|
'daynotes.create',
|
|
'daynotes.delete',
|
|
'daynotes.list',
|
|
'daynotes.update',
|
|
'days.create',
|
|
'days.delete',
|
|
'days.update',
|
|
'files.create',
|
|
'files.createLink',
|
|
'files.getContent',
|
|
'files.list',
|
|
'files.softDelete',
|
|
'files.update',
|
|
'itinerary.assign',
|
|
'itinerary.unassign',
|
|
'journal.createEntry',
|
|
'journal.createJourney',
|
|
'journal.deleteEntry',
|
|
'journal.deleteJourney',
|
|
'journal.getEntries',
|
|
'journal.listMine',
|
|
'journal.updateEntry',
|
|
'packing.create',
|
|
'packing.createBag',
|
|
'packing.delete',
|
|
'packing.deleteBag',
|
|
'packing.list',
|
|
'packing.listBags',
|
|
'packing.setBagMembers',
|
|
'packing.update',
|
|
'packing.updateBag',
|
|
'places.create',
|
|
'places.delete',
|
|
'places.update',
|
|
'rates.get',
|
|
'reservations.create',
|
|
'reservations.delete',
|
|
'reservations.listMine',
|
|
'reservations.update',
|
|
'tags.create',
|
|
'tags.delete',
|
|
'tags.list',
|
|
'tags.update',
|
|
'todos.create',
|
|
'todos.delete',
|
|
'todos.list',
|
|
'todos.update',
|
|
'trips.addMember',
|
|
'trips.create',
|
|
'trips.getAccommodations',
|
|
'trips.getById',
|
|
'trips.getDays',
|
|
'trips.getPlaces',
|
|
'trips.getReservations',
|
|
'trips.listMine',
|
|
'trips.members',
|
|
'trips.removeMember',
|
|
'trips.update',
|
|
'vacay.mine',
|
|
'vacay.toggleCompanyHoliday',
|
|
'vacay.toggleEntry',
|
|
'weather.get',
|
|
]);
|
|
});
|
|
});
|