import { remoteFixture } from '../fixtures/remote-config.ts'; import { test } from 'node:test'; import assert from 'node:assert/strict'; import { assertLocalConnectionTarget, assertLocalStartupContext, resolveLocalStartup, type LocalStartupContext, } from '../../YouleNexus/assets/framework/config/local-startup.ts'; import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; import type { ResolveRuntimeConfigOptions, RuntimeConfig } from '../../YouleNexus/assets/framework/config/runtime-config.ts'; import type { ConfigFetcher } from '../../YouleNexus/assets/framework/config/remote-config.ts'; import { BUILD_IDENTITY } from '../../YouleNexus/assets/framework/config/sources/defaults.ts'; function options(search = ''): ResolveRuntimeConfigOptions & { fetcher: ConfigFetcher & { calls: number }; } { const fetcher = { calls: 0, async fetch() { this.calls += 1; return { player_server_tcp: 'remote.example:3088' }; }, }; return { hostKind: 'h5', win: {}, search, fetcher }; } test('local debug fetches remote config before choosing authoritative local servers', async () => { const input = options(); const context = await resolveLocalStartup(input); assert.equal(context.hostKind, 'h5'); assert.equal(context.profile, 'local'); assert.equal(context.config.identity.gameid, BUILD_IDENTITY.gameid); assert.deepEqual(context.config.servers, STARTUP_CONFIG.localServers); assert.equal(input.fetcher.calls, 1); assert.equal(context.config.source, 'remote'); assert.equal(Object.isFrozen(context), true); }); test('rejects non-local startup modes and hosts before fetching', async () => { const cases: Array<[Partial, RegExp]> = [ [{ startupConfig: {...STARTUP_CONFIG, mode:'release'} }, /mode.*debug/i], [{ startupConfig: {...STARTUP_CONFIG, useLocalServer:false} }, /useLocalServer/i], [{ hostKind: 'native-settings' }, /hostKind.*h5/i], [{ hostKind: 'uAgent_3' }, /hostKind.*h5/i], ]; for (const [override, expected] of cases) { const base = options(); const input = { ...base, ...override }; await assert.rejects(resolveLocalStartup(input), expected); assert.equal(base.fetcher.calls, 0); } }); test('ignores conflicting and duplicate mode/profile queries', async () => { for (const search of ['', '?profile=prod', '?profile=local&profile=unknown&mode=release&mode=other']) { const input=options(search); const result=await resolveLocalStartup(input); assert.deepEqual(result.config.servers,STARTUP_CONFIG.localServers); assert.equal(input.fetcher.calls,1); } }); test('rejects gameconfig and ambiguous identity controls before fetching', async () => { const cases: Array<[string, RegExp]> = [ ['?profile=local&gameconfig=remote-host', /gameconfig/i], ['?profile=local&agentid=A&agentid=B', /agentid/i], ['?profile=local&version=1&version=2', /version/i], ['?profile=local&gameid=other', /gameid/i], ]; for (const [search, expected] of cases) { const input = options(search); await assert.rejects(resolveLocalStartup(input), expected); assert.equal(input.fetcher.calls, 0); } }); test('permits query identity overrides except gameid and validates numeric version', async () => { const context = await resolveLocalStartup(options( '?profile=local&agentid=A&channelid=C&marketid=9&version=12', )); assert.deepEqual(context.config.identity, { ...BUILD_IDENTITY, agentid: 'A', channelid: 'C', marketid: '9', version: 12, }); const invalid = options('?profile=local&version=not-a-number'); await assert.rejects(resolveLocalStartup(invalid), /identity.*version.*finite/i); assert.equal(invalid.fetcher.calls, 0); }); function validContext(): LocalStartupContext { return { hostKind: 'h5', profile: 'local', config: { startupConfig: STARTUP_CONFIG, mode: 'debug', isDebugger: true, identity: BUILD_IDENTITY, servers: STARTUP_CONFIG.localServers, source: 'remote', gameserver: STARTUP_CONFIG.gameserver, ...remoteFixture(BUILD_IDENTITY), }, }; } test('context validator rejects forged config fields and malformed identity', () => { const base = validContext(); const configCases: Array<[keyof RuntimeConfig, unknown]> = [ ['mode', 'release'], ['isDebugger', false], ['source', 'direct'], ['gameserver', 'https://example.com/config.txt'], ['rawConfig', {}], ['servers', ['ws://127.0.0.1:9999']], ['servers', ['https://127.0.0.1:3088']], ['servers', ['ws://user:pass@127.0.0.1:3088']], ]; for (const [field, value] of configCases) { assert.throws( () => assertLocalStartupContext({ ...base, config: { ...base.config, [field]: value } } as LocalStartupContext), new RegExp(field, 'i'), ); } assert.throws( () => assertLocalStartupContext({ ...base, config: { ...base.config, identity: { ...BUILD_IDENTITY, version: Number.NaN } }, }), /identity.*version.*finite/i, ); }); test('context validator names identity when a forged context omits the identity object', () => { const base = validContext(); for (const identity of [null, undefined]) { assert.throws( () => assertLocalStartupContext({ ...base, config: { ...base.config, identity }, } as unknown as LocalStartupContext), (error: unknown) => { assert.ok(error instanceof Error); assert.match(error.message, /identity/i); assert.ok((error as Error & { cause?: unknown }).cause instanceof Error); return true; }, ); } }); test('connection target must be a valid URL from the authoritative local profile', () => { const context = validContext(); assert.doesNotThrow(() => assertLocalConnectionTarget(context, 'ws://127.0.0.1:3088')); for (const [url, expected] of [ ['ws://127.0.0.1:9999', /url|target|servers/i], ['ws://example.com:3088', /url|target|servers|hostname/i], ['not-a-url', /url/i], ] as const) { assert.throws(() => assertLocalConnectionTarget(context, url), expected); } }); test('local consumers validate the injected source snapshot and its exact targets', async () => { const startupConfig={...STARTUP_CONFIG,gameserver:'https://fixture.example/config.txt',localServers:['ws://[::1]:4300']}; const context=await resolveLocalStartup({...options(),startupConfig}); assertLocalConnectionTarget(context,'ws://[::1]:4300'); assert.throws(()=>assertLocalConnectionTarget(context,STARTUP_CONFIG.localServers[0]),/target/); for (const patch of [{mode:'release'}, {useLocalServer:false}, {localServers:['ws://elsewhere:3088']}, {gameserver:'https://other.example/config.txt'}]) { assert.throws(()=>assertLocalStartupContext({...context,config:{...context.config,startupConfig:{...context.config.startupConfig,...patch}}} as any),/mode|useLocalServer|localServers|gameserver/); } });