Files
youle_cocos/cocoscreator_projects/framework-tests/protocol/room-handlers.test.ts
T
joywayerandClaude Opus 5 02dc5d51ca chore(spec): 综合清理 + legacy-layer 迁移 spec/plan/data
主要改动:
- 切到 funplay-cocos-mcp v0.5.1 (用户级配置, 项目级 .mcp.json 删除)
- 仓库文档/CLAUDE.md/.gitignore 等清理过时 cocos-mcp-server 引用
- memory 文件同步: cocos-mcp-setup/path/blocker/spriteframe-uuid/prefab-persist 等加 funplay 实测警告
- memory 新建 funplay-cocos-mcp-pending-verification.md (后已被实测覆盖)
- spec/plan/data:
  - docs/superpowers/specs/2026-09-02-legacy-layer-migration-design.md
  - docs/superpowers/plans/2026-09-02-legacy-layer-migration.md
  - docs/superpowers/data/layer-spirit-summary.json
- YouleNexus: profiles.ts / defaults.ts / PlayerInfoView.prefab / scene 改动

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 07:36:54 +08:00

137 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { RoomStore } from '../../YouleNexus/assets/framework/platform/stores/room-store.ts';
import { PlayerStore } from '../../YouleNexus/assets/framework/platform/stores/player-store.ts';
import { roomHandlers } from '../../YouleNexus/assets/framework/protocol/room-handlers.ts';
import type { RoomRecoveryData } from '../../YouleNexus/assets/framework/platform/stores/room-store.ts';
function makeRoom() {
const room = new RoomStore();
room.applyRecovery({
roomcode: 'R1', isbattle: 1, roommode: 1,
seat: 1, isowner: 0,
players: [
null,
{ playerid: 100, nickname: '甲', bean: 10, roomcard: 0 } as any,
{ playerid: 200, nickname: '乙', bean: 20, roomcard: 0 } as any,
null,
],
} as RoomRecoveryData);
return room;
}
function makePlayer() {
const p = new PlayerStore();
p.applyLogin({ playerid: 100, nickname: '甲', bean: 10, roomcard: 0 } as any);
return p;
}
test('self_join_room: 已在房应被覆盖(再次入房罕见但支持)', () => {
const room = makeRoom();
// 假设自己是 seat=2 加入
roomHandlers.self_join_room({ room, player: makePlayer() }, { seat: 2, playerid: 200 } as any);
assert.equal(room.state.value.seat, 2);
});
test('other_join_room: 第三方加入,players 数组对应座位填入', () => {
const room = new RoomStore();
room.applyRecovery({
roomcode: 'R1', seat: 1, isowner: 0,
players: [null, { playerid: 100 } as any, null, null],
} as RoomRecoveryData);
roomHandlers.other_join_room({ room, player: makePlayer() }, { seat: 3, playerid: 300, nickname: '丙' } as any);
const v = room.state.value;
assert.equal(v.players[2]?.playerid, 300, 'seat=3 应填到 players[2]');
assert.equal(v.players[2]?.nickname, '丙');
});
test('self_exit_room: 自己座位清空', () => {
const room = makeRoom();
// 假设自己 seat=1
roomHandlers.self_exit_room({ room, player: makePlayer() }, { seat: 1 } as any);
assert.equal(room.state.value.players[0], null, 'seat=1(0基)应清空');
// 自己的 PlayerStore 不变(退出房间不抹掉登录态)
});
test('other_exit_room: 第三方座位清空', () => {
const room = makeRoom();
roomHandlers.other_exit_room({ room, player: makePlayer() }, { seat: 2 } as any);
assert.equal(room.state.value.players[1], null);
});
test('other_exit_room: 未知座位 → 显式抛错(第二准则)', () => {
const room = makeRoom();
assert.throws(
() => roomHandlers.other_exit_room({ room, player: makePlayer() }, { seat: 99 } as any),
/seat=99/,
);
});
test('self_apply_free_room: state 进入解散投票中', () => {
const room = makeRoom();
roomHandlers.self_apply_free_room({ room, player: makePlayer() }, { applyer: 1 } as any);
assert.equal(room.state.value.state, 1);
});
test('other_apply_free_room: state 进入解散投票中', () => {
const room = makeRoom();
roomHandlers.other_apply_free_room({ room, player: makePlayer() }, { applyer: 2 } as any);
assert.equal(room.state.value.state, 1);
});
test('self/other_agree_free_room: 不改变 state,仅标记同意', () => {
const room = makeRoom();
roomHandlers.self_apply_free_room({ room, player: makePlayer() }, {} as any);
roomHandlers.self_agree_free_room({ room, player: makePlayer() }, {} as any);
// 协议无 agreeList 字段(state 保持 1=投票中),由其他机制累计
assert.equal(room.state.value.state, 1);
});
test('self/other_refuse_free_room: 不改变 state', () => {
const room = makeRoom();
roomHandlers.self_apply_free_room({ room, player: makePlayer() }, {} as any);
roomHandlers.self_refuse_free_room({ room, player: makePlayer() }, {} as any);
assert.equal(room.state.value.state, 1);
});
test('other_offline: 触发但不破坏 RoomStore(offline 状态由子游戏持有)', () => {
const room = makeRoom();
// RoomStore 不持有 offline 字段(handler 应是 no-op 或仅校验入参)
roomHandlers.other_offline({ room, player: makePlayer() }, { seat: 1 } as any);
// state 不变
assert.equal(room.state.value.state, 0);
});
test('other_online: 触发但不破坏 RoomStore', () => {
const room = makeRoom();
roomHandlers.other_online({ room, player: makePlayer() }, { seat: 1 } as any);
assert.equal(room.state.value.state, 0);
});
test('change_seat: 交换两个座位(seat=1 ↔ seat=3)', () => {
const room = makeRoom();
// 初始: players = [null, 100, 200, null]
// change_seat(from=1, to=3) → i_from=0, i_to=2
// swap: [null, 100, 200, null] ↔ [200, 100, null, null]
roomHandlers.change_seat({ room, player: makePlayer() }, { from: 1, to: 3 } as any);
const v = room.state.value;
// swap 结果:players[0]=200, players[1]=100, players[2]=null, players[3]=null
assert.equal(v.players[0]?.playerid, 200, '原 seat=3 的 200 现在在 seat=1(0基=0)');
assert.equal(v.players[1]?.playerid, 100, '原 seat=2 的 100 仍在原位');
assert.equal(v.players[2], null, '原 seat=1 的 null 现在在 seat=3(0基=2)');
});
test('free_room: 解散投票结束 → state=2(已解散,业务侧清场)', () => {
const room = makeRoom();
roomHandlers.self_apply_free_room({ room, player: makePlayer() }, {} as any);
roomHandlers.free_room({ room, player: makePlayer() }, { result: 1 } as any);
// state 字段语义: 0=正常 1=投票中 2=已解散
assert.equal(room.state.value.state, 2);
});
test('unknown rpc 不在 handler 表里 → 显式抛错(第二准则)', () => {
const room = makeRoom();
const h = roomHandlers as unknown as Record<string, (ctx: any, d: any) => void>;
// Object.freeze 后,h['unknown_rpc'] 是 undefined,调用会抛
assert.throws(() => h['unknown_rpc']({ room, player: makePlayer() }, {}), /unknown_rpc/);
});