feat: migrate platform login scenes and native login flows
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。
|
||||
const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const});
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { resolveBootstrap } from '../../YouleNexus/assets/framework/config/bootstrap.ts';
|
||||
@@ -9,7 +11,7 @@ test('resolveBootstrap:显式 hostKind 透传严格 RuntimeConfig,并保留
|
||||
async fetch() { return { player_server_tcp: '5.5.5.5:5000' }; },
|
||||
};
|
||||
const result = await resolveBootstrap({
|
||||
hostKind: 'h5', win: {}, search: '', fetcher,
|
||||
startupConfig:STARTUP_CONFIG, hostKind: 'h5', win: {}, search: '', fetcher,
|
||||
});
|
||||
assert.equal(result.source, 'remote');
|
||||
assert.equal(result.profile.name, 'local');
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { runInNewContext } from 'node:vm';
|
||||
import ts from 'typescript';
|
||||
import { ConfigFetchError, ConfigParseError } from '../../YouleNexus/assets/framework/config/remote-config.ts';
|
||||
|
||||
// 在引擎资源边界验证错误、JSON 解析和缓存释放,不以 HTTP 模拟项目资源。
|
||||
function fixture(debug: boolean, text: string, error?: Error) {
|
||||
const calls: string[] = [];
|
||||
const exports: any = {};
|
||||
class TextAsset {}
|
||||
const resources = {
|
||||
load(path: string, type: unknown, done: Function) {
|
||||
assert.equal(type, TextAsset); calls.push(`load:${path}`); done(error, { text });
|
||||
},
|
||||
release(path: string, type: unknown) { assert.equal(type, TextAsset); calls.push(`release:${path}`); },
|
||||
};
|
||||
const source = readFileSync(new URL('../../YouleNexus/assets/scripts/platform-login/local-config-fetcher.ts', import.meta.url), 'utf8');
|
||||
runInNewContext(ts.transpileModule(source, {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
||||
}).outputText, { exports, require(name: string) {
|
||||
if (name === 'cc') return { resources, TextAsset };
|
||||
if (name === 'cc/env') return { DEBUG: debug };
|
||||
if (name.endsWith('/remote-config.ts')) return { ConfigFetchError, ConfigParseError };
|
||||
throw Error(`Unexpected import: ${name}`);
|
||||
} });
|
||||
return { fetcher: new exports.CocosLocalConfigFetcher(), calls };
|
||||
}
|
||||
|
||||
test('project txt parses JSON and releases the asset after every load', async () => {
|
||||
const f = fixture(true, '{"showVisitor":1}');
|
||||
assert.equal((await f.fetcher.fetch('dev-config/test')).showVisitor, 1);
|
||||
await f.fetcher.fetch('dev-config/test');
|
||||
assert.deepEqual(f.calls, ['load:dev-config/test', 'release:dev-config/test', 'load:dev-config/test', 'release:dev-config/test']);
|
||||
});
|
||||
|
||||
test('release build never loads local txt; missing or malformed assets expose source errors', async () => {
|
||||
const release = fixture(false, '{}');
|
||||
await assert.rejects(release.fetcher.fetch('dev-config/test'), ConfigFetchError);
|
||||
assert.deepEqual(release.calls, []);
|
||||
const missing = fixture(true, '', Error('not found'));
|
||||
await assert.rejects(missing.fetcher.fetch('dev-config/test'), /test.txt.*not found/);
|
||||
const malformed = fixture(true, 'not json');
|
||||
await assert.rejects(malformed.fetcher.fetch('dev-config/test'), ConfigParseError);
|
||||
assert.deepEqual(malformed.calls, ['load:dev-config/test', 'release:dev-config/test']);
|
||||
});
|
||||
@@ -7,7 +7,9 @@ import {
|
||||
resolveLocalStartup,
|
||||
type LocalStartupContext,
|
||||
} from '../../YouleNexus/assets/framework/config/local-startup.ts';
|
||||
import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。
|
||||
const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const});
|
||||
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';
|
||||
@@ -22,7 +24,7 @@ function options(search = ''): ResolveRuntimeConfigOptions & {
|
||||
return { player_server_tcp: 'remote.example:3088' };
|
||||
},
|
||||
};
|
||||
return { hostKind: 'h5', win: {}, search, fetcher };
|
||||
return { startupConfig:STARTUP_CONFIG, hostKind: 'h5', win: {}, search, fetcher };
|
||||
}
|
||||
|
||||
test('local debug fetches remote config before choosing authoritative local servers', async () => {
|
||||
@@ -174,3 +176,12 @@ test('local consumers validate the injected source snapshot and its exact target
|
||||
assert.throws(()=>assertLocalStartupContext({...context,config:{...context.config,startupConfig:{...context.config.startupConfig,...patch}}} as any),/mode|useLocalServer|localServers|gameserver/);
|
||||
}
|
||||
});
|
||||
|
||||
test('local startup validates the selected project txt source', async () => {
|
||||
const localFetcher={async fetch(){return {showVisitor:1};}};
|
||||
const context=await resolveLocalStartup({startupConfig:{...STARTUP_CONFIG,configSource:'local'},hostKind:'h5',win:{},search:'',
|
||||
fetcher:{async fetch(){throw Error('remote forbidden');}},localFetcher});
|
||||
assert.equal(context.config.source,'local');
|
||||
assert.equal(context.config.gameserver,STARTUP_CONFIG.localConfigResource);
|
||||
assert.doesNotThrow(()=>assertLocalStartupContext(context));
|
||||
});
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
STARTUP_CONFIG,
|
||||
STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG,
|
||||
validateStartupConfig,
|
||||
decodeGameConfig,
|
||||
resolveGameServer,
|
||||
} from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。
|
||||
const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const});
|
||||
|
||||
test('decodeGameConfig:按旧协议把 -/# 解码为远程 txt URL', () => {
|
||||
assert.equal(decodeGameConfig('host-path#8080-config'), 'http://host/path:8080/config.txt');
|
||||
@@ -23,7 +25,7 @@ test('resolveGameServer:空 gameconfig 使用配置源,非空值使用解码
|
||||
});
|
||||
|
||||
test('one frozen source defines the complete default startup configuration', () => {
|
||||
assert.deepEqual(STARTUP_CONFIG, { mode: 'debug', gameserver: 'https://tsgames.daoqi88.cn/config/update_jsonv2.txt', useLocalServer: true, localServers: ['ws://127.0.0.1:3088'] });
|
||||
assert.deepEqual(STARTUP_CONFIG, { mode: 'debug', configSource: 'remote', localConfigResource: 'dev-config/update_jsonv2', gameserver: 'https://tsgames.daoqi88.cn/config_test/update_jsonv2.txt', useLocalServer: true, localServers: ['ws://127.0.0.1:3088'] });
|
||||
assert.ok(Object.isFrozen(STARTUP_CONFIG)); assert.ok(Object.isFrozen(STARTUP_CONFIG.localServers));
|
||||
});
|
||||
test('source validation rejects malformed configuration without defaults', () => {
|
||||
@@ -32,3 +34,11 @@ test('source validation rejects malformed configuration without defaults', () =>
|
||||
}
|
||||
for (const url of ['ws://127.4.3.2:3088','wss://localhost:3088','ws://[::1]:3088']) assert.deepEqual(validateStartupConfig({...STARTUP_CONFIG,localServers:[url]}).localServers,[url]);
|
||||
});
|
||||
|
||||
test('config source and local resource paths are explicit and local is debug only', () => {
|
||||
for (const patch of [{configSource:undefined},{configSource:'other'},{configSource:'local',mode:'release'},
|
||||
{localConfigResource:''},{localConfigResource:'../outside'},{localConfigResource:'dev-config/a.txt'},{localConfigResource:'/absolute'}]) {
|
||||
assert.throws(()=>validateStartupConfig({...STARTUP_CONFIG,...patch} as any), /startup config/);
|
||||
}
|
||||
assert.equal(validateStartupConfig({...STARTUP_CONFIG,configSource:'local'}).configSource,'local');
|
||||
});
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。
|
||||
const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const});
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { HttpConfigFetcher } from '../../YouleNexus/assets/framework/config/remote-config-fetcher.ts';
|
||||
@@ -36,7 +38,7 @@ test('resolveRuntimeConfig:把 cacheBust 注入唯一的 HTTP 请求构造点'
|
||||
});
|
||||
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(calls[0]?.input, 'https://tsgames.daoqi88.cn/config/update_jsonv2.txt?123');
|
||||
assert.equal(calls[0]?.input, `${STARTUP_CONFIG.gameserver}?123`);
|
||||
assert.deepEqual(calls[0]?.init, { method: 'POST', body: '' });
|
||||
});
|
||||
|
||||
|
||||
@@ -17,6 +17,19 @@ function legacy(raw: unknown, name: string, selected = identity) {
|
||||
GameData: { AgentId: selected.agentid, GameId: selected.gameid, ChannelId: selected.channelid, marketID: selected.marketid },
|
||||
});
|
||||
}
|
||||
|
||||
test('get_paravalue is the shared legacy-compatible reader, including an absent data wrapper', () => {
|
||||
assert.equal(typeof api.get_paravalue, 'function');
|
||||
const raw = {showVisitor:1,agentlist:[
|
||||
{agentid:11,showVisitor:0,gamelist:[{gameid:22,deviceLogin:true}]},
|
||||
{agentid:11,showVisitor:2},
|
||||
]};
|
||||
for(const name of ['showVisitor','deviceLogin','missing']) {
|
||||
assert.deepEqual(api.get_paravalue({data:raw},name,identity),legacy(raw,name));
|
||||
assert.deepEqual(view(raw).getValue(name),api.get_paravalue({data:raw},name,identity));
|
||||
}
|
||||
assert.equal(api.get_paravalue({},'showVisitor',identity),legacy(undefined,'showVisitor'));
|
||||
});
|
||||
test('memory view agrees with actual legacy function across all five scopes and preserves raw objects', () => {
|
||||
const scopes: Record<string, unknown>[] = [
|
||||
{ hall_config: { root: true }, rootOnly: 'root', falsy: 'inherited' },
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts';
|
||||
// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。
|
||||
const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const});
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { BUILD_IDENTITY } from '../../YouleNexus/assets/framework/config/sources/defaults.ts';
|
||||
@@ -30,9 +32,9 @@ test('h5:只读 query 身份,不访问原生宿主,并生成严格 remote
|
||||
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.gameserver, STARTUP_CONFIG.gameserver);
|
||||
assert.equal(result.rawConfig, REMOTE);
|
||||
assert.deepEqual(fetcher.calls, ['https://tsgames.daoqi88.cn/config/update_jsonv2.txt']);
|
||||
assert.deepEqual(fetcher.calls, [STARTUP_CONFIG.gameserver]);
|
||||
assert.equal(Object.isFrozen(result), true);
|
||||
assert.equal(Object.isFrozen(result.identity), true);
|
||||
assert.equal(Object.isFrozen(result.servers), true);
|
||||
@@ -201,7 +203,7 @@ for (const mode of ['debug','release'] as const) for (const useLocalServer of [t
|
||||
source.localServers=['ws://localhost:9999']; assert.deepEqual(result.startupConfig.localServers,STARTUP_CONFIG.localServers);
|
||||
});
|
||||
test('default runtime starts without query configuration', async () => {
|
||||
const result=await resolveRuntimeConfig({hostKind:'h5',win:{},search:'',fetcher:returning({notice:'kept'})});
|
||||
const result=await resolveRuntimeConfig({startupConfig:STARTUP_CONFIG,hostKind:'h5',win:{},search:'',fetcher:returning({notice:'kept'})});
|
||||
assert.deepEqual(result.startupConfig,STARTUP_CONFIG); assert.deepEqual(result.servers,STARTUP_CONFIG.localServers);
|
||||
});
|
||||
|
||||
@@ -216,3 +218,38 @@ for (const mode of ['debug','release'] as const) for (const useLocalServer of [t
|
||||
const fetcher={async fetch(){throw new Error('remote unavailable');}};
|
||||
await assert.rejects(resolveRuntimeConfig({startupConfig:{...STARTUP_CONFIG,mode,useLocalServer},hostKind:'h5',win:{},search:'',fetcher}),/远程配置抓取失败/);
|
||||
});
|
||||
|
||||
// 本地来源必须在运行时边界隔离网络读取,并复用正式配置的层级取值规则。
|
||||
test('local txt selects one source, ignores gameconfig and preserves scoped values', async () => {
|
||||
const remote = returning(REMOTE);
|
||||
const local = returning({showVisitor: 1, agentlist: [{agentid: BUILD_IDENTITY.agentid, deviceLogin: true}]});
|
||||
const result = await resolveRuntimeConfig({startupConfig:{...STARTUP_CONFIG, configSource:'local'},
|
||||
hostKind:'h5',win:{},search:'?gameconfig=unwanted-host',fetcher:remote,localFetcher:local});
|
||||
assert.equal(result.source,'local');
|
||||
assert.deepEqual(remote.calls,[]);
|
||||
assert.deepEqual(local.calls,[STARTUP_CONFIG.localConfigResource]);
|
||||
assert.equal(result.remoteConfig.getValue('showVisitor'),1);
|
||||
assert.equal(result.remoteConfig.getValue('deviceLogin'),true);
|
||||
assert.deepEqual(result.servers,STARTUP_CONFIG.localServers);
|
||||
});
|
||||
|
||||
test('local txt errors never fall back to remote, release rejects before I/O', async () => {
|
||||
const remote=returning(REMOTE); let localCalls=0;
|
||||
const localFetcher={async fetch(){localCalls++;throw Error('missing txt');}};
|
||||
const options={startupConfig:{...STARTUP_CONFIG,configSource:'local' as const},hostKind:'h5' as const,win:{},search:'',fetcher:remote,localFetcher};
|
||||
await assert.rejects(resolveRuntimeConfig(options),/本地配置读取失败/);
|
||||
assert.equal(localCalls,1);assert.equal(remote.calls.length,0);
|
||||
await assert.rejects(resolveRuntimeConfig({...options,startupConfig:{...options.startupConfig,mode:'release'}}),/configSource/);
|
||||
assert.equal(localCalls,1);
|
||||
await assert.rejects(resolveRuntimeConfig({...options,localFetcher:undefined}),/localFetcher/);
|
||||
});
|
||||
|
||||
test('local txt and socket target are independent; remote mode never touches local loader', async () => {
|
||||
const remote=returning(REMOTE),local=returning(REMOTE);
|
||||
const options={hostKind:'h5' as const,win:{},search:'',fetcher:remote,localFetcher:local};
|
||||
const result=await resolveRuntimeConfig({...options,startupConfig:{...STARTUP_CONFIG,configSource:'local',useLocalServer:false}});
|
||||
assert.deepEqual(result.servers,['ws://5.5.5.5:5000','ws://5.5.5.5:5000']);
|
||||
assert.equal(remote.calls.length,0);assert.equal(local.calls.length,1);
|
||||
await resolveRuntimeConfig({...options,startupConfig:{...STARTUP_CONFIG,configSource:'remote'}});
|
||||
assert.equal(remote.calls.length,1);assert.equal(local.calls.length,1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user