C 规范 §4.1 的红线落地: - platform/agent/room 三个 route → 平台 handlers(未注册显式抛错) - 其他 route → activeGame.onReceive(rpc, data)(未激活或不匹配显式抛错) ActiveGame: 当前子游戏槽(set/clear/get),进房时 setGame、离房 clear。 Router: 纯分发,不持状态。错误一律抛(第二准则:不静默吞包)。 128/128 tests pass, typecheck exit 0。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
75 lines
2.9 KiB
TypeScript
75 lines
2.9 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { Router } from '../../YouleNexus/assets/framework/protocol/router.ts';
|
|
import { ActiveGame } from '../../YouleNexus/assets/framework/protocol/active-game.ts';
|
|
import type { InboundMessage } from '../../YouleNexus/assets/framework/core/types/envelope.ts';
|
|
import type { IGameModule, GameContext } from '../../YouleNexus/assets/framework/sdk/index.ts';
|
|
|
|
class MockGame implements IGameModule {
|
|
readonly route = 'mock';
|
|
events: string[] = [];
|
|
onEnter(_ctx: GameContext): void {}
|
|
onExit(): void {}
|
|
onReceive(rpc: string, _d: unknown): void { this.events.push(`onReceive:${rpc}`); }
|
|
onReconnect(_d: unknown): void {}
|
|
}
|
|
|
|
const msg = (route: string, rpc: string, data: unknown = {}): InboundMessage =>
|
|
({ route, rpc, data } as InboundMessage);
|
|
|
|
test('platform route → 平台 handler(已注册)', () => {
|
|
const slot = new ActiveGame();
|
|
const calls: Array<[string, unknown]> = [];
|
|
const router = new Router({
|
|
activeGame: slot,
|
|
platformHandlers: { player_login: (d) => calls.push(['player_login', d]) },
|
|
});
|
|
router.dispatch(msg('agent', 'player_login', { state: 0 }));
|
|
assert.equal(calls.length, 1);
|
|
assert.equal(calls[0][0], 'player_login');
|
|
});
|
|
|
|
test('platform route → 平台 handler 未注册 → 显式抛错(第二准则)', () => {
|
|
const slot = new ActiveGame();
|
|
const router = new Router({ activeGame: slot, platformHandlers: {} });
|
|
assert.throws(() => router.dispatch(msg('agent', 'unknown_rpc')), /unknown_rpc/);
|
|
});
|
|
|
|
test('非 platform route 但 activeGame 已注册 → onReceive', () => {
|
|
const slot = new ActiveGame();
|
|
const game = new MockGame();
|
|
slot.set(game);
|
|
const router = new Router({ activeGame: slot, platformHandlers: {} });
|
|
router.dispatch(msg('mock', 'deal_card', { n: 1 }));
|
|
assert.deepEqual(game.events, ['onReceive:deal_card']);
|
|
});
|
|
|
|
test('非 platform route 但 activeGame 未注册 → 显式抛错', () => {
|
|
const slot = new ActiveGame();
|
|
const router = new Router({ activeGame: slot, platformHandlers: {} });
|
|
assert.throws(() => router.dispatch(msg('mock', 'deal_card')), /mock.*deal_card/);
|
|
});
|
|
|
|
test('route 与 activeGame.route 不匹配 → 显式抛错', () => {
|
|
const slot = new ActiveGame();
|
|
slot.set(new MockGame()); // route = 'mock'
|
|
const router = new Router({ activeGame: slot, platformHandlers: {} });
|
|
assert.throws(() => router.dispatch(msg('other-game', 'rpc')), /other-game/);
|
|
});
|
|
|
|
test('platform/agent/room 三 route 全部走平台 handler(对称)', () => {
|
|
const slot = new ActiveGame();
|
|
const calls: string[] = [];
|
|
const router = new Router({
|
|
activeGame: slot,
|
|
platformHandlers: {
|
|
r1: () => calls.push('r1'),
|
|
r2: () => calls.push('r2'),
|
|
r3: () => calls.push('r3'),
|
|
},
|
|
});
|
|
router.dispatch(msg('platform', 'r1'));
|
|
router.dispatch(msg('agent', 'r2'));
|
|
router.dispatch(msg('room', 'r3'));
|
|
assert.deepEqual(calls, ['r1', 'r2', 'r3']);
|
|
}); |