mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
2ec6d006b0
Twelve more methods, so the registry owns 50 of 113. rpc-host.ts is down from 1332 lines to 830 and the deps factory from 944 to 500. trips.update keeps its two field-level gates: archiving and changing the cover each need their own admin-configurable permission on top of trip_edit, so a plugin cannot do either on a trip it may otherwise edit. Member management keeps its own grant for the same kind of reason, since adding a member grants trip access, and the owner still cannot be removed through it because that would orphan the trip. canEditTrip stays in HostDeps for now: the db:meta gate still resolves an entity's edit permission through it and has not moved yet.
116 lines
4.2 KiB
TypeScript
116 lines
4.2 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(50);
|
|
expect(legacy.size).toBe(total - 50);
|
|
});
|
|
|
|
it('RPCLEDGER-006 the migrated methods are exactly the ones this PR claims', () => {
|
|
expect([...fromRegistry].sort()).toEqual([
|
|
'categories.list',
|
|
'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',
|
|
'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.listMine',
|
|
'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',
|
|
'weather.get',
|
|
]);
|
|
});
|
|
});
|