import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { test } from 'node:test'; import type { RuntimeConfig } from '../../YouleNexus/assets/framework/config/runtime-config.ts'; import type { OutboundEnvelope } from '../../YouleNexus/assets/framework/core/types/envelope.ts'; import { PlatformCommands } from '../../YouleNexus/assets/framework/platform/commands.ts'; import { PlatformStore } from '../../YouleNexus/assets/framework/platform/stores/platform-store.ts'; import { parseLoginResponse, } from '../../YouleNexus/assets/framework/protocol/contracts/index.ts'; const RUNTIME_CONFIG: RuntimeConfig = Object.freeze({ mode: 'release', isDebugger: false, identity: Object.freeze({ agentid: 'agent-from-config', channelid: 'channel-from-config', gameid: 'game-from-config', marketid: 'market-from-config', version: 41, }), servers: Object.freeze(['ws://fixture']), source: 'remote', gameserver: 'https://fixture/config', rawConfig: Object.freeze({ data: Object.freeze({ urlserver: 'fixture' }) }), }); function fixture(name: string): { data: Record } { return JSON.parse(readFileSync(new URL(`../fixtures/contracts/${name}`, import.meta.url), 'utf8')); } function makeStore(location: 'outside' | 'inside', stage = 0, infinite = 0): PlatformStore { const store = new PlatformStore(); const source = fixture(location === 'inside' ? 'player-login-room.json' : 'player-login-success.json'); if (location === 'inside') { source.data.isbattle = stage; source.data.infinite = infinite; } store.applyLoginSuccess(parseLoginResponse(source.data)); return store; } function makeCommands(store: PlatformStore): { readonly commands: PlatformCommands; readonly sent: OutboundEnvelope[]; } { const sent: OutboundEnvelope[] = []; return { commands: new PlatformCommands({ runtimeConfig: RUNTIME_CONFIG, store, send: { send: (envelope) => { sent.push(envelope); } }, }), sent, }; } test('prepare and exitRoom use the first-slice builders with authoritative config and room state', () => { const { commands, sent } = makeCommands(makeStore('inside', 0)); commands.prepare(); commands.exitRoom(); assert.deepEqual(sent, [ { app: 'youle', route: 'room', rpc: 'player_prepare', data: { agentid: 'agent-from-config', playerid: 430511, gameid: 'game-from-config', roomcode: '100001', }, }, { app: 'youle', route: 'room', rpc: 'self_exit_room', data: { agentid: 'agent-from-config', playerid: 430511, gameid: 'game-from-config', roomcode: '100001', }, }, ]); assert.equal(commands.prepare.length, 0); assert.equal(commands.exitRoom.length, 0); }); test('joinRoom uses authenticated Store identity and requires every non-optional environment field', () => { const { commands, sent } = makeCommands(makeStore('outside')); const location = { latitude: 28.7, longitude: 115.8 }; commands.joinRoom({ roomcode: '654321', location, ip: '203.0.113.7', vipMatch: 1, match_id: 'match-9', }); assert.deepEqual(sent, [{ app: 'youle', route: 'agent', rpc: 'self_join_room', data: { agentid: 'agent-from-config', playerid: 430511, gameid: 'game-from-config', roomcode: '654321', location, ip: '203.0.113.7', vipMatch: 1, match_id: 'match-9', }, }]); assert.equal((sent[0]!.data as { location: unknown }).location, location); assert.equal(commands.joinRoom.length, 1); assert.throws( () => commands.joinRoom({ roomcode: '654321', ip: '203.0.113.7' } as never), /location/i, ); assert.throws( () => commands.joinRoom({ roomcode: '654321', location } as never), /ip/i, ); assert.equal(sent.length, 1); }); test('exitRoom sends self_exit_room while an infinite room is in battle', () => { const { commands, sent } = makeCommands(makeStore('inside', 1, 1)); commands.exitRoom(); assert.deepEqual(sent, [{ app: 'youle', route: 'room', rpc: 'self_exit_room', data: { agentid: 'agent-from-config', playerid: 430511, gameid: 'game-from-config', roomcode: '100001', }, }]); }); test('command preconditions reject missing authentication, wrong room location, and battle exit without sending', () => { const unauthenticated = makeCommands(new PlatformStore()); assert.throws( () => unauthenticated.commands.joinRoom({ roomcode: '654321', location: null, ip: '' }), /authenticated|player/i, ); const outside = makeCommands(makeStore('outside')); assert.throws(() => outside.commands.prepare(), /inside.*room|room.*inside/i); assert.throws(() => outside.commands.exitRoom(), /inside.*room|room.*inside/i); const inside = makeCommands(makeStore('inside', 0)); assert.throws( () => inside.commands.joinRoom({ roomcode: '654321', location: null, ip: '' }), /outside.*room|already.*room/i, ); const battle = makeCommands(makeStore('inside', 1)); assert.throws(() => battle.commands.exitRoom(), /room\.exit.*stage=0/i); assert.equal(unauthenticated.sent.length, 0); assert.equal(outside.sent.length, 0); assert.equal(inside.sent.length, 0); assert.equal(battle.sent.length, 0); });