Files
youle_cocos/cocoscreator_projects/framework-tests/sdk/game-contract-harness.test.ts
T

161 lines
5.7 KiB
TypeScript

import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts';
import { remoteFixture } from '../fixtures/remote-config.ts';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { test } from 'node:test';
import { runInNewContext } from 'node:vm';
import ts from 'typescript';
import * as gameSessionHost from '../../YouleNexus/assets/framework/sdk/runtime/game-session-host.ts';
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';
const GAME_ROOMTYPE = JSON.parse(readFileSync(
new URL('../fixtures/contracts/player-login-room.json', import.meta.url), 'utf8',
)).data.roomtype as readonly unknown[];
test('browser-like loader evaluates the emitted harness without Node builtin modules', () => {
const source = readFileSync(new URL(
'../../YouleNexus/assets/framework/sdk/testing/game-contract-harness.ts', import.meta.url,
), 'utf8');
const { outputText } = ts.transpileModule(source, {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 },
});
const exports: Record<string, unknown> = {};
const loaded: string[] = [];
assert.doesNotThrow(() => runInNewContext(outputText, {
exports,
require(specifier: string) {
if (specifier !== '../runtime/game-session-host.ts') {
throw new Error(`Creator cannot load runtime module: ${specifier}`);
}
loaded.push(specifier);
return gameSessionHost;
},
}));
assert.equal(typeof exports.assertGameContract, 'function');
assert.deepEqual(loaded, ['../runtime/game-session-host.ts']);
});
test('assertGameContract accepts a fixed-four game with its real nested roomtype and host fixture', () => {
let invalidations = 0;
let nextModuleId = 0;
const restoredDeskinfo: unknown[] = [];
const entry: GameEntry = {
key: 'fixture',
gameId: 41,
route: 'fixture-route',
resolveSeatCount: () => 4,
createModule: () => makeModule(++nextModuleId, (deskinfo) => { restoredDeskinfo.push(deskinfo); }),
};
assert.doesNotThrow(() => assertGameContract(
entry,
[{
roomtype: GAME_ROOMTYPE,
expectedSeatCount: 4,
makeHost: makeConformanceHost(entry, () => { invalidations++; }, restoredDeskinfo),
}],
assert,
));
assert.equal(invalidations, 2);
assert.equal(restoredDeskinfo.length, 0);
assert.ok(nextModuleId >= 2);
});
const RUNTIME_CONFIG: RuntimeConfig = Object.freeze({
startupConfig: {...STARTUP_CONFIG,mode:'release' as const,useLocalServer:false}, 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',
...remoteFixture(),
});
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.deepEqual(event.roomtype, GAME_ROOMTYPE);
assert.deepEqual(host!.getSnapshot().room.roomtype, event.roomtype);
}
},
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<string, unknown> };
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);
},
};
};
}