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 { createGameHostAdapter } from '../../YouleNexus/assets/framework/platform/game-host-adapter.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'; import type { GameEntry, GameHost, GameModule, } from '../../YouleNexus/assets/framework/sdk/contracts/index.ts'; import { assertGameContract, type GameContractHost, } from '../../YouleNexus/assets/framework/sdk/testing/game-contract-harness.ts'; test('assertGameContract runs the reusable isolated session conformance suite', () => { let invalidations = 0; let nextModuleId = 0; const restoredDeskinfo: unknown[] = []; const entry: GameEntry = { key: 'fixture', gameId: 41, route: 'fixture-route', resolveSeatCount: (roomtype) => { const seats = roomtype[0]; if (seats === 2 || seats === 4 || seats === 10) return seats; return 4; }, createModule: () => makeModule(++nextModuleId, (deskinfo) => { restoredDeskinfo.push(deskinfo); }), }; assert.doesNotThrow(() => assertGameContract( entry, makeConformanceHost(entry, () => { invalidations++; }, restoredDeskinfo), )); assert.equal(invalidations, 2); assert.equal(restoredDeskinfo.length, 0); assert.ok(nextModuleId >= 2); }); const RUNTIME_CONFIG: RuntimeConfig = Object.freeze({ mode: 'release', isDebugger: false, identity: Object.freeze({ agentid: 'fixture-agent', channelid: 'fixture-channel', gameid: '41', marketid: 'fixture-market', version: 41, }), servers: Object.freeze(['ws://fixture']), source: 'remote', gameserver: 'https://fixture/config', rawConfig: Object.freeze({ data: Object.freeze({ urlserver: 'fixture' }) }), }); function makeModule(id: number, onRestore: (deskinfo: unknown) => void): GameModule { let host: GameHost | undefined; let restored: unknown; let eventCount = 0; let messageCycle: number | undefined; return { attach(value) { host = value; }, handlePlatformEvent(event) { eventCount++; if (event.type === 'room.entered') { assert.equal(event.roomtype[0], 4); } }, handleGameMessage(message) { assert.equal(message.rpc, 'conformance.message'); const data = message.data as { cycle: number }; assert.equal(typeof data.cycle, 'number'); messageCycle = data.cycle; }, restore(deskinfo) { restored = deskinfo; onRestore(deskinfo); }, dispose() { if (!host) { assert.equal(eventCount, 0); assert.equal(restored, undefined); return; } assert.ok(host); assert.equal(eventCount, 1); assert.deepEqual(restored, deskinfoFor(messageCycle!)); }, }; } function deskinfoFor(cycle: number): { readonly cycle: number } { return { cycle }; } function makeConformanceHost( entry: GameEntry, onInvalidate: () => void, restoredDeskinfo: unknown[], ): () => GameContractHost { return () => { const login = JSON.parse(readFileSync( new URL('../fixtures/contracts/player-login-room.json', import.meta.url), 'utf8', )) as { data: Record }; login.data.roomtype = [4]; login.data.isbattle = 0; const store = new PlatformStore(); store.applyLoginSuccess(parseLoginResponse(login.data)); const sent: OutboundEnvelope[] = []; const send = { send: (envelope: OutboundEnvelope) => { sent.push(envelope); } }; const commands = new PlatformCommands({ runtimeConfig: RUNTIME_CONFIG, store, send }); const lease = createGameHostAdapter({ gameEntry: entry, store, commands, send }); return { host: lease.host, invalidate() { lease.invalidate(); onInvalidate(); }, assertRestored(deskinfo) { assert.equal(restoredDeskinfo.shift(), deskinfo); }, }; }; }