fix(config): restore legacy gameserver and urlserver contract
This commit is contained in:
@@ -1,116 +1,16 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { resolveBootstrap } from '../../YouleNexus/assets/framework/config/bootstrap.ts';
|
||||
import { BUILD_IDENTITY } from '../../YouleNexus/assets/framework/config/sources/defaults.ts';
|
||||
import type { ConfigFetcher, RemoteConfig } from '../../YouleNexus/assets/framework/config/remote-config.ts';
|
||||
import { PROFILES } from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
import type { ConfigFetcher } from '../../YouleNexus/assets/framework/config/remote-config.ts';
|
||||
|
||||
function fetcherReturning(config: RemoteConfig): ConfigFetcher & { calls: string[] } {
|
||||
const calls: string[] = [];
|
||||
return { calls, fetch: async (url: string) => { calls.push(url); return config; } };
|
||||
}
|
||||
const throwingFetcher: ConfigFetcher = {
|
||||
fetch: async () => { throw new Error('net down'); },
|
||||
};
|
||||
|
||||
const REMOTE: RemoteConfig = { data: { player_server_tcp: '5.5.5.5:5000' } };
|
||||
|
||||
test('debug + profile=local:用 profile.server 直连,不抓远程', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const r = await resolveBootstrap({ mode: 'debug', win: {}, search: '?profile=local', fetcher, isNative: false });
|
||||
assert.deepEqual(r.servers, ['ws://127.0.0.1:3088']);
|
||||
assert.equal(r.rawConfig, null);
|
||||
assert.equal(fetcher.calls.length, 0);
|
||||
});
|
||||
|
||||
test('debug + prod + H5:抓远程配置产出 servers,identity 来自 defaults', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const r = await resolveBootstrap({ mode: 'debug', win: {}, search: '', fetcher, isNative: false });
|
||||
assert.deepEqual(r.servers, ['ws://5.5.5.5:5000']);
|
||||
assert.equal(r.identity.agentid, BUILD_IDENTITY.agentid);
|
||||
assert.equal(r.identity.gameid, BUILD_IDENTITY.gameid);
|
||||
assert.equal(fetcher.calls.length, 1);
|
||||
});
|
||||
|
||||
test('debug + H5 显式 URL query 覆盖身份(最高优先级)', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const r = await resolveBootstrap({ mode: 'debug', win: {}, search: '?agentid=999&channelid=C9', fetcher, isNative: false });
|
||||
assert.equal(r.identity.agentid, '999');
|
||||
assert.equal(r.identity.channelid, 'C9');
|
||||
});
|
||||
|
||||
test('debug + 原生模式:identity 来自 window.settings', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const win = { settings: { getothername: () => 'NA', getchannelName: () => 'NC', getmarketname: () => 3 } };
|
||||
const r = await resolveBootstrap({ mode: 'debug', win, search: '', fetcher, isNative: true });
|
||||
assert.equal(r.identity.agentid, 'NA');
|
||||
assert.equal(r.identity.channelid, 'NC');
|
||||
assert.equal(r.identity.marketid, 3);
|
||||
});
|
||||
|
||||
test('远程抓取失败 → 抛 ConfigFetchError(不兜底)', async () => {
|
||||
await assert.rejects(
|
||||
resolveBootstrap({ mode: 'debug', win: {}, search: '', fetcher: throwingFetcher, isNative: false }),
|
||||
/远程配置抓取失败/,
|
||||
);
|
||||
});
|
||||
|
||||
test('远程配置未给出可用服务器地址 → 抛错(不兜底)', async () => {
|
||||
const emptyFetcher: ConfigFetcher = { fetch: async () => ({ data: {} }) };
|
||||
await assert.rejects(
|
||||
resolveBootstrap({ mode: 'debug', win: {}, search: '', fetcher: emptyFetcher, isNative: false }),
|
||||
/未给出可用服务器/,
|
||||
);
|
||||
});
|
||||
|
||||
test('debug 结果含 mode/isDebugger', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const r = await resolveBootstrap({ mode: 'debug', win: {}, search: '', fetcher, isNative: false });
|
||||
assert.equal(r.mode, 'debug');
|
||||
assert.equal(r.isDebugger, true);
|
||||
});
|
||||
|
||||
test('release:忽略所有 URL 覆盖,强制 prod,identity 不被 URL 改', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const r = await resolveBootstrap({ mode: 'release', win: {}, search: '?profile=local&agentid=999', fetcher, isNative: false });
|
||||
assert.equal(r.profile.name, 'prod');
|
||||
assert.deepEqual(r.servers, ['ws://5.5.5.5:5000']); // 走远程,不是 local 直连
|
||||
assert.equal(r.identity.agentid, BUILD_IDENTITY.agentid); // 999 无效
|
||||
assert.equal(r.isDebugger, false);
|
||||
assert.equal(r.mode, 'release');
|
||||
assert.equal(fetcher.calls.length, 1);
|
||||
});
|
||||
|
||||
test('release:原生身份仍生效(URL 的 999 无效)', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const win = { settings: { getothername: () => 'NA', getchannelName: () => 'NC', getmarketname: () => 3 } };
|
||||
const r = await resolveBootstrap({ mode: 'release', win, search: '?agentid=999', fetcher, isNative: true });
|
||||
assert.equal(r.identity.agentid, 'NA');
|
||||
assert.equal(r.identity.channelid, 'NC');
|
||||
});
|
||||
|
||||
test('debug + serverOverride:走远程全链路但连接地址覆盖为本地', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE); // REMOTE.player_server_tcp = 5.5.5.5:5000
|
||||
const r = await resolveBootstrap({ mode: 'debug', win: {}, search: '?profile=staging', fetcher, isNative: false });
|
||||
assert.deepEqual(r.servers, [PROFILES.staging.serverOverride]); // 覆盖为 profile.serverOverride
|
||||
assert.ok(r.rawConfig); // 远程配置仍抓取(全链路)
|
||||
assert.equal(fetcher.calls.length, 1);
|
||||
});
|
||||
|
||||
test('release:serverOverride 不生效(强制 prod,用远程解析地址)', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const r = await resolveBootstrap({ mode: 'release', win: {}, search: '?profile=staging', fetcher, isNative: true });
|
||||
assert.deepEqual(r.servers, ['ws://5.5.5.5:5000']); // prod 无 override,用远程解析
|
||||
});
|
||||
|
||||
test('debug:account 来自当前 profile', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const r = await resolveBootstrap({ mode: 'debug', win: {}, search: '?profile=staging', fetcher, isNative: false });
|
||||
assert.deepEqual(r.account, PROFILES.staging.account);
|
||||
});
|
||||
|
||||
test('release:account 恒为 undefined(不从调试配置泄漏账号)', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const r = await resolveBootstrap({ mode: 'release', win: {}, search: '?profile=staging', fetcher, isNative: true });
|
||||
assert.equal(r.account, undefined);
|
||||
test('resolveBootstrap:显式 hostKind 透传严格 RuntimeConfig,并保留 profile 元数据', async () => {
|
||||
const fetcher: ConfigFetcher = {
|
||||
async fetch() { return { data: { urlserver: '5.5.5.5:5000' } }; },
|
||||
};
|
||||
const result = await resolveBootstrap({
|
||||
mode: 'debug', hostKind: 'h5', win: {}, search: '?profile=local', fetcher,
|
||||
});
|
||||
assert.equal(result.source, 'direct');
|
||||
assert.equal(result.profile.name, 'local');
|
||||
assert.deepEqual(result.servers, ['ws://127.0.0.1:3088']);
|
||||
});
|
||||
|
||||
@@ -36,3 +36,22 @@ test('resolveIdentity:marketid=0 是有效值,应覆盖', () => {
|
||||
const id = resolveIdentity([() => BUILD_IDENTITY, () => ({ marketid: 0 })]);
|
||||
assert.equal(id.marketid, 0);
|
||||
});
|
||||
|
||||
test('resolveIdentity:不完整身份显式拒绝,完整结果被冻结', () => {
|
||||
assert.throws(
|
||||
() => resolveIdentity([() => ({ gameid: 'G', version: 1 })]),
|
||||
/identity.*agentid.*channelid.*marketid/i,
|
||||
);
|
||||
assert.equal(Object.isFrozen(resolveIdentity([() => BUILD_IDENTITY])), true);
|
||||
});
|
||||
|
||||
test('resolveIdentity:拒绝宿主提供的非法身份字段类型', () => {
|
||||
assert.throws(
|
||||
() => resolveIdentity([() => ({ ...BUILD_IDENTITY, agentid: true as any })]),
|
||||
/identity.*agentid.*type/i,
|
||||
);
|
||||
assert.throws(
|
||||
() => resolveIdentity([() => ({ ...BUILD_IDENTITY, version: Number.NaN })]),
|
||||
/identity.*version.*type/i,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,27 +1,40 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { resolveActiveProfile, PROFILES, ACTIVE_PROFILE } from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
import {
|
||||
ACTIVE_PROFILE,
|
||||
PROFILES,
|
||||
decodeGameConfig,
|
||||
resolveActiveProfile,
|
||||
resolveGameServer,
|
||||
} from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
|
||||
test('默认返回 ACTIVE_PROFILE', () => {
|
||||
const p = resolveActiveProfile('');
|
||||
assert.equal(p.name, ACTIVE_PROFILE);
|
||||
test('decodeGameConfig:按旧协议把 -/# 解码为远程 txt URL', () => {
|
||||
assert.equal(decodeGameConfig('host-path#8080-config'), 'http://host/path:8080/config.txt');
|
||||
});
|
||||
|
||||
test('prod profile 无调试覆盖、isDebugger=false', () => {
|
||||
const prod = PROFILES['prod'];
|
||||
assert.equal(prod.isDebugger, false);
|
||||
assert.equal(prod.server, undefined);
|
||||
assert.equal(prod.identity, undefined);
|
||||
assert.ok(prod.gameserver && prod.gameserver.length > 0);
|
||||
test('resolveGameServer:空 gameconfig 使用配置源,非空值使用解码后的注入源', () => {
|
||||
assert.equal(
|
||||
resolveGameServer({ configured: 'https://default/config.txt', injectedGameConfig: '' }),
|
||||
'https://default/config.txt',
|
||||
);
|
||||
assert.equal(
|
||||
resolveGameServer({ configured: 'https://default/config.txt', injectedGameConfig: 'host-path#80-a' }),
|
||||
'http://host/path:80/a.txt',
|
||||
);
|
||||
});
|
||||
|
||||
test('?profile=local 切到 local,含调试服地址', () => {
|
||||
const p = resolveActiveProfile('?profile=local');
|
||||
assert.equal(p.name, 'local');
|
||||
assert.ok(p.server && p.server.startsWith('ws://'));
|
||||
test('默认 profile 是 ACTIVE_PROFILE,local 是显式 direct 源', () => {
|
||||
assert.equal(resolveActiveProfile('').name, ACTIVE_PROFILE);
|
||||
const local = resolveActiveProfile('?profile=local');
|
||||
assert.equal(local.source, 'direct');
|
||||
assert.deepEqual(local.servers, ['ws://127.0.0.1:3088']);
|
||||
});
|
||||
|
||||
test('未知 profile 回退到 ACTIVE_PROFILE', () => {
|
||||
const p = resolveActiveProfile('?profile=nope');
|
||||
assert.equal(p.name, ACTIVE_PROFILE);
|
||||
test('未知 profile 显式报错,不回退到 ACTIVE_PROFILE', () => {
|
||||
assert.throws(() => resolveActiveProfile('?profile=nope'), /未知 profile/);
|
||||
});
|
||||
|
||||
test('正常 build 可达 profile 不含占位调试账号', () => {
|
||||
assert.equal(JSON.stringify(PROFILES).includes('REPLACE_ME'), false);
|
||||
assert.equal(PROFILES.staging.account, undefined);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { HttpConfigFetcher } from '../../YouleNexus/assets/framework/config/remote-config-fetcher.ts';
|
||||
import { resolveRuntimeConfig } from '../../YouleNexus/assets/framework/config/runtime-config.ts';
|
||||
|
||||
test('HttpConfigFetcher:POST 空 body,并无条件以 ? 拼接 cache bust', async () => {
|
||||
const calls: Array<{ input: string; init?: RequestInit }> = [];
|
||||
const fetcher = new HttpConfigFetcher(async (input, init) => {
|
||||
calls.push({ input: String(input), init });
|
||||
return new Response('{"data":{"urlserver":"10.0.0.1:3088"}}');
|
||||
});
|
||||
|
||||
await fetcher.fetch('https://config.example/file.txt?existing=1', () => '123');
|
||||
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(calls[0]?.input, 'https://config.example/file.txt?existing=1?123');
|
||||
assert.deepEqual(calls[0]?.init, { method: 'POST', body: '' });
|
||||
});
|
||||
|
||||
test('resolveRuntimeConfig:把 cacheBust 注入唯一的 HTTP 请求构造点', async () => {
|
||||
const calls: Array<{ input: string; init?: RequestInit }> = [];
|
||||
const fetcher = new HttpConfigFetcher(async (input, init) => {
|
||||
calls.push({ input: String(input), init });
|
||||
return new Response('{"data":{"urlserver":"10.0.0.1:3088"}}');
|
||||
});
|
||||
|
||||
await resolveRuntimeConfig({
|
||||
mode: 'release',
|
||||
hostKind: 'h5',
|
||||
win: {},
|
||||
search: '?agentid=A&channelid=C',
|
||||
fetcher,
|
||||
cacheBust: () => '123',
|
||||
});
|
||||
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(calls[0]?.input, 'https://tsgames.daoqi88.cn/config/update_jsonv2.txt?123');
|
||||
assert.deepEqual(calls[0]?.init, { method: 'POST', body: '' });
|
||||
});
|
||||
|
||||
test('HttpConfigFetcher:非法 JSON 显式抛 ConfigParseError', async () => {
|
||||
const fetcher = new HttpConfigFetcher(async () => new Response('not-json'));
|
||||
await assert.rejects(
|
||||
fetcher.fetch('https://config.example/file.txt', () => '123'),
|
||||
/远程配置 JSON 解析失败/,
|
||||
);
|
||||
});
|
||||
@@ -1,57 +1,41 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { getParam, resolveServers, type RemoteConfig } from '../../YouleNexus/assets/framework/config/remote-config.ts';
|
||||
import type { ChannelIdentity } from '../../YouleNexus/assets/framework/config/identity.ts';
|
||||
import { parseUrlServers } from '../../YouleNexus/assets/framework/config/remote-config.ts';
|
||||
|
||||
const ID: ChannelIdentity = { agentid: 'A', channelid: 'C', gameid: 'G', marketid: 2, version: 1 };
|
||||
const single = JSON.parse(readFileSync(
|
||||
new URL('../fixtures/contracts/remote-config-single.json', import.meta.url),
|
||||
'utf8',
|
||||
)) as unknown;
|
||||
const array = JSON.parse(readFileSync(
|
||||
new URL('../fixtures/contracts/remote-config-array.json', import.meta.url),
|
||||
'utf8',
|
||||
)) as unknown;
|
||||
|
||||
const CONFIG: RemoteConfig = {
|
||||
data: {
|
||||
player_server_tcp: '1.1.1.1:1000', // 顶层默认
|
||||
agentlist: [{
|
||||
agentid: 'A',
|
||||
player_server_tcp: '2.2.2.2:2000', // agent 级覆盖
|
||||
gamelist: [{
|
||||
gameid: 'G',
|
||||
channellist: [{
|
||||
channelid: 'C',
|
||||
player_server_tcp: '3.3.3.3:3000', // channel 级覆盖(最具体)
|
||||
visitor_server_tcp: '4.4.4.4:4000',
|
||||
marketlist: [{ marketid: 2 }],
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
test('getParam:最具体层级胜出', () => {
|
||||
assert.equal(getParam(CONFIG, 'player_server_tcp', ID), '3.3.3.3:3000');
|
||||
test('parseUrlServers:读取 data.urlserver 字符串并补 ws://', () => {
|
||||
assert.deepEqual(parseUrlServers(single), ['ws://10.0.0.1:3088']);
|
||||
});
|
||||
|
||||
test('getParam:channel 未匹配时回退到 agent 级值', () => {
|
||||
const id2 = { ...ID, channelid: 'OTHER' };
|
||||
assert.equal(getParam(CONFIG, 'player_server_tcp', id2), '2.2.2.2:2000');
|
||||
test('parseUrlServers:读取 data.urlserver 数组并保留已有 wss://', () => {
|
||||
assert.deepEqual(parseUrlServers(array), ['ws://10.0.0.1:3088', 'wss://backup.example/ws']);
|
||||
});
|
||||
|
||||
test('getParam:agent 未匹配时回退到顶层默认', () => {
|
||||
const id3 = { ...ID, agentid: 'NONE' };
|
||||
assert.equal(getParam(CONFIG, 'player_server_tcp', id3), '1.1.1.1:1000');
|
||||
test('parseUrlServers:拒绝旧的 *_server_tcp 字段', () => {
|
||||
assert.throws(
|
||||
() => parseUrlServers({ data: { player_server_tcp: 'wrong:1' } }),
|
||||
/data\.urlserver/,
|
||||
);
|
||||
});
|
||||
|
||||
test('getParam:键不存在 → null', () => {
|
||||
assert.equal(getParam(CONFIG, 'not_a_key', ID), null);
|
||||
test('parseUrlServers:保留原顺序和重复项,并冻结结果', () => {
|
||||
const servers = parseUrlServers({ data: { urlserver: ['same:1', 'same:1', 'ws://last:2'] } });
|
||||
assert.deepEqual(servers, ['ws://same:1', 'ws://same:1', 'ws://last:2']);
|
||||
assert.equal(Object.isFrozen(servers), true);
|
||||
});
|
||||
|
||||
test('getParam:无 data → null', () => {
|
||||
assert.equal(getParam({}, 'player_server_tcp', ID), null);
|
||||
});
|
||||
|
||||
test('resolveServers:player 优先、visitor 回退,前缀 ws://', () => {
|
||||
assert.deepEqual(resolveServers(CONFIG, ID), ['ws://3.3.3.3:3000', 'ws://4.4.4.4:4000']);
|
||||
});
|
||||
|
||||
test('resolveServers:已带 ws:// 不重复前缀;无地址 → 空数组', () => {
|
||||
const cfg: RemoteConfig = { data: { player_server_tcp: 'ws://9.9.9.9:9' } };
|
||||
assert.deepEqual(resolveServers(cfg, ID), ['ws://9.9.9.9:9']);
|
||||
assert.deepEqual(resolveServers({ data: {} }, ID), []);
|
||||
test('parseUrlServers:拒绝空字符串、空数组和非字符串数组项', () => {
|
||||
assert.throws(() => parseUrlServers({ data: { urlserver: '' } }), /data\.urlserver/);
|
||||
assert.throws(() => parseUrlServers({ data: { urlserver: [] } }), /data\.urlserver/);
|
||||
assert.throws(() => parseUrlServers({ data: { urlserver: ['ok:1', ''] } }), /data\.urlserver/);
|
||||
assert.throws(() => parseUrlServers({ data: { urlserver: ['ok:1', 2] } }), /data\.urlserver/);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { BUILD_IDENTITY } from '../../YouleNexus/assets/framework/config/sources/defaults.ts';
|
||||
import { resolveRuntimeConfig } from '../../YouleNexus/assets/framework/config/runtime-config.ts';
|
||||
import type { ConfigFetcher } from '../../YouleNexus/assets/framework/config/remote-config.ts';
|
||||
|
||||
function returning(value: unknown): ConfigFetcher & { calls: string[] } {
|
||||
const calls: string[] = [];
|
||||
return {
|
||||
calls,
|
||||
async fetch(gameserver: string) { calls.push(gameserver); return value; },
|
||||
};
|
||||
}
|
||||
|
||||
const REMOTE = { data: { urlserver: ['5.5.5.5:5000', '5.5.5.5:5000'] } };
|
||||
|
||||
test('h5:只读 query 身份,不访问原生宿主,并生成严格 remote 配置', async () => {
|
||||
const fetcher = returning(REMOTE);
|
||||
const throwingSettings = new Proxy({}, { get() { throw new Error('native access forbidden'); } });
|
||||
const result = await resolveRuntimeConfig({
|
||||
mode: 'release',
|
||||
hostKind: 'h5',
|
||||
win: { settings: throwingSettings },
|
||||
search: '?agentid=HA&channelid=HC',
|
||||
fetcher,
|
||||
cacheBust: () => '123',
|
||||
});
|
||||
|
||||
assert.deepEqual(result.identity, { ...BUILD_IDENTITY, agentid: 'HA', channelid: 'HC' });
|
||||
assert.deepEqual(result.servers, ['ws://5.5.5.5:5000', 'ws://5.5.5.5:5000']);
|
||||
assert.equal(result.source, 'remote');
|
||||
assert.equal(result.gameserver, 'https://tsgames.daoqi88.cn/config/update_jsonv2.txt');
|
||||
assert.equal(result.rawConfig, REMOTE);
|
||||
assert.deepEqual(fetcher.calls, ['https://tsgames.daoqi88.cn/config/update_jsonv2.txt']);
|
||||
assert.equal(Object.isFrozen(result), true);
|
||||
assert.equal(Object.isFrozen(result.identity), true);
|
||||
assert.equal(Object.isFrozen(result.servers), true);
|
||||
});
|
||||
|
||||
test('native-settings:使用四个原生方法,gameconfig 是唯一 gameserver 注入名', async () => {
|
||||
const calls: string[] = [];
|
||||
const fetcher = returning(REMOTE);
|
||||
const result = await resolveRuntimeConfig({
|
||||
mode: 'release',
|
||||
hostKind: 'native-settings',
|
||||
win: {
|
||||
settings: {
|
||||
getothername(name: string) { calls.push(`getothername:${name}`); return name === 'agent' ? 'NA' : 'host-path#80-a'; },
|
||||
getchannelName() { calls.push('getchannelName'); return 'NC'; },
|
||||
getmarketname() { calls.push('getmarketname'); return 7; },
|
||||
},
|
||||
},
|
||||
search: '',
|
||||
fetcher,
|
||||
});
|
||||
|
||||
assert.deepEqual(calls, ['getothername:agent', 'getchannelName', 'getmarketname', 'getothername:gameconfig']);
|
||||
assert.deepEqual(result.identity, {
|
||||
agentid: 'NA', channelid: 'NC', gameid: BUILD_IDENTITY.gameid, marketid: 7, version: BUILD_IDENTITY.version,
|
||||
});
|
||||
assert.equal(result.gameserver, 'http://host/path:80/a.txt');
|
||||
assert.deepEqual(fetcher.calls, ['http://host/path:80/a.txt']);
|
||||
});
|
||||
|
||||
test('uAgent_3:只读 app_<name>,settings 不参与身份或 gameserver', async () => {
|
||||
const fetcher = returning(REMOTE);
|
||||
const result = await resolveRuntimeConfig({
|
||||
mode: 'release',
|
||||
hostKind: 'uAgent_3',
|
||||
win: {
|
||||
settings: new Proxy({}, { get() { throw new Error('settings forbidden'); } }),
|
||||
app_agent: 'UA', app_channel: 'UC', app_market: 9, app_gameconfig: 'u-host#80-cfg',
|
||||
},
|
||||
search: '',
|
||||
fetcher,
|
||||
});
|
||||
|
||||
assert.deepEqual(result.identity, {
|
||||
agentid: 'UA', channelid: 'UC', gameid: BUILD_IDENTITY.gameid, marketid: 9, version: BUILD_IDENTITY.version,
|
||||
});
|
||||
assert.equal(result.gameserver, 'http://u/host:80/cfg.txt');
|
||||
});
|
||||
|
||||
test('native-settings:不完整最终身份显式拒绝,不拿 build identity 兜底', async () => {
|
||||
await assert.rejects(
|
||||
resolveRuntimeConfig({
|
||||
mode: 'release', hostKind: 'native-settings', win: {}, search: '', fetcher: returning(REMOTE),
|
||||
}),
|
||||
/identity.*agentid.*channelid/i,
|
||||
);
|
||||
});
|
||||
|
||||
test('显式 local debug:direct 字段不靠 null 推断,完整服务器来自 profile', async () => {
|
||||
const fetcher = returning(REMOTE);
|
||||
const result = await resolveRuntimeConfig({
|
||||
mode: 'debug', hostKind: 'h5', win: {}, search: '?profile=local', fetcher,
|
||||
});
|
||||
assert.equal(result.source, 'direct');
|
||||
assert.equal(result.gameserver, null);
|
||||
assert.equal(result.rawConfig, null);
|
||||
assert.deepEqual(result.servers, ['ws://127.0.0.1:3088']);
|
||||
assert.deepEqual(fetcher.calls, []);
|
||||
});
|
||||
|
||||
test('远程抓取失败显式报错,不降级 direct', async () => {
|
||||
const fetcher: ConfigFetcher = { async fetch() { throw new Error('net down'); } };
|
||||
await assert.rejects(
|
||||
resolveRuntimeConfig({ mode: 'release', hostKind: 'h5', win: {}, search: '', fetcher }),
|
||||
/远程配置抓取失败/,
|
||||
);
|
||||
});
|
||||
@@ -1,29 +1,59 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { nativeSettingsSource } from '../../../YouleNexus/assets/framework/config/sources/native-settings.ts';
|
||||
import {
|
||||
getchannelName,
|
||||
getmarketname,
|
||||
getothername,
|
||||
nativeIdentitySource,
|
||||
} from '../../../YouleNexus/assets/framework/config/sources/native-settings.ts';
|
||||
|
||||
test('优先从 window.settings 读取 agentid/channelid/marketid', () => {
|
||||
test('非 uAgent_3 getothername 调 settings,异常只在源边界转换为空串', () => {
|
||||
const names: string[] = [];
|
||||
const win = { settings: { getothername(name: string) { names.push(name); throw new Error('bridge'); } } };
|
||||
assert.equal(getothername(win, 'native-settings', 'gameconfig'), '');
|
||||
assert.deepEqual(names, ['gameconfig']);
|
||||
});
|
||||
|
||||
test('getchannelName 返回 truthy settings 结果,否则按宿主执行旧缺省语义', () => {
|
||||
assert.equal(getchannelName({ settings: { getchannelName: () => 'C1' } }, 'native-settings'), 'C1');
|
||||
assert.equal(getchannelName({ settings: { getchannelName: () => 0 } }, 'native-settings'), '');
|
||||
assert.equal(getchannelName({ settings: { getchannelName: () => { throw new Error('bridge'); } } }, 'native-settings'), '');
|
||||
assert.equal(getchannelName({
|
||||
settings: { getchannelName: () => { throw new Error('bridge'); } },
|
||||
app_channel: 'APP-C',
|
||||
}, 'uAgent_3'), 'APP-C');
|
||||
});
|
||||
|
||||
test('getmarketname 返回 settings 结果;异常按宿主执行旧缺省语义', () => {
|
||||
assert.equal(getmarketname({ settings: { getmarketname: () => 0 } }, 'native-settings'), 0);
|
||||
assert.equal(getmarketname({ settings: { getmarketname: () => { throw new Error('bridge'); } } }, 'native-settings'), '4');
|
||||
assert.equal(getmarketname({
|
||||
settings: { getmarketname: () => { throw new Error('bridge'); } },
|
||||
app_market: 8,
|
||||
}, 'uAgent_3'), 8);
|
||||
});
|
||||
|
||||
test('uAgent_3 getothername 只读 app_<name>,不调用 settings', () => {
|
||||
let settingsCalls = 0;
|
||||
const win = {
|
||||
settings: { getothername: () => { settingsCalls += 1; return 'WRONG'; } },
|
||||
app_agent: 'APP-A',
|
||||
app_gameconfig: 'host-path#80-a',
|
||||
};
|
||||
assert.equal(getothername(win, 'uAgent_3', 'agent'), 'APP-A');
|
||||
assert.equal(getothername(win, 'uAgent_3', 'gameconfig'), 'host-path#80-a');
|
||||
assert.equal(settingsCalls, 0);
|
||||
});
|
||||
|
||||
test('nativeIdentitySource 仅返回所选宿主提供的原生身份字段', () => {
|
||||
const win = {
|
||||
settings: {
|
||||
getothername: (name: string) => (name === 'agent' ? 'NA' : ''),
|
||||
getothername: (name: string) => name === 'agent' ? 'NA' : '',
|
||||
getchannelName: () => 'NC',
|
||||
getmarketname: () => 7,
|
||||
},
|
||||
};
|
||||
assert.deepEqual(nativeSettingsSource(win)(), { agentid: 'NA', channelid: 'NC', marketid: 7 });
|
||||
});
|
||||
|
||||
test('settings 抛错时回退到全局 app_agent / app_market', () => {
|
||||
const win: any = {
|
||||
settings: { getothername: () => { throw new Error('no bridge'); },
|
||||
getchannelName: () => { throw new Error('no bridge'); },
|
||||
getmarketname: () => { throw new Error('no bridge'); } },
|
||||
app_agent: 'GA',
|
||||
app_market: 5,
|
||||
};
|
||||
assert.deepEqual(nativeSettingsSource(win)(), { agentid: 'GA', marketid: 5 });
|
||||
});
|
||||
|
||||
test('完全无原生注入 → 空对象', () => {
|
||||
assert.deepEqual(nativeSettingsSource({})(), {});
|
||||
assert.deepEqual(nativeIdentitySource(win, 'native-settings')(), {
|
||||
agentid: 'NA', channelid: 'NC', marketid: 7,
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user