fix(config): resolve remote hierarchy in memory for every profile

This commit is contained in:
2026-09-05 21:21:24 +08:00
parent 62328b6fd6
commit 053302c5df
27 changed files with 286 additions and 143 deletions
@@ -40,7 +40,7 @@ async function setup(overrides: { config?: Partial<ResolveRuntimeConfigOptions>;
let fetches = 0;
const host = api.createLocalLoginHost({
configOptions: { mode: 'debug', hostKind: 'h5', win: {}, search: '?profile=local&version=731',
fetcher: { async fetch(): Promise<never> { fetches++; throw new Error('unexpected fetch'); } }, ...overrides.config },
fetcher: { async fetch() { fetches++; return { player_server_tcp: 'remote.example:3088' }; } }, ...overrides.config },
storage: { getItem: key => values.get(key) ?? null, setItem: (key, value) => { values.set(key, value); } },
random: () => .5, now: () => 123456, sampleHost: () => ({ location: null }),
view: { render: model => { overrides.render?.(model); pages.push(model); },
@@ -70,7 +70,7 @@ for (const missing of ['resources', 'minimum', 'socket'] as const) test(`login w
assert.equal(h.values.size, 0); assert.deepEqual(h.sockets[0]!.sent, []);
h.resources.resolve(); h.minimum.resolve(); if (missing === 'socket') h.sockets[0]!.open();
await starting;
assert.equal(h.host.getSnapshot().ready, true); assert.equal(h.fetches(), 0);
assert.equal(h.host.getSnapshot().ready, true); assert.equal(h.fetches(), 1);
assert.equal(h.sockets.length, 1);
assert.equal(h.evidence.filter(event => event.kind === 'startup-config').length, 1);
await h.host.login();
@@ -210,3 +210,35 @@ test('fatal overlay observer failure retains the original Runtime error object',
assert.strictEqual(fault.errors[0], primary); assert.strictEqual(fault.errors[1], observer);
assert.equal(h.host.getSnapshot().ready, false); assert.equal(h.sockets[0]!.closes, 1);
});
test('remote fetch and parse failures leave local sockets and account storage untouched', async () => {
for (const fetch of [async () => { throw new Error('network down'); }, async () => ({ agentlist: {} }), async () => null]) {
const h = await setup({ config: { fetcher: { fetch } } });
const result = h.host.start().catch(error => error);
await flush();
assert.equal(h.sockets.length, 0);
assert.equal(h.values.size, 0);
assert.ok(await result instanceof Error);
assert.equal(h.failures.length, 1);
}
});
test('stop while remote fetch is pending prevents late socket creation', async () => {
const pending = deferred();
let fetched = false;
const h = await setup({ config: { fetcher: { async fetch() {
fetched = true; await pending.promise; return { player_server_tcp: 'remote.example:3088' };
} } } });
const result = h.host.start().catch(error => error);
await flush(); assert.equal(fetched, true); assert.equal(h.sockets.length, 0);
h.host.stop(); pending.resolve(); await result;
assert.equal(h.sockets.length, 0); assert.equal(h.values.size, 0);
});
test('startup diagnostics exclude raw remote fields', async t => {
const h = await setup({ config: { fetcher: { async fetch() {
return { player_server_tcp: 'remote.example:3088', privateConfig: 'DO_NOT_RECORD_REMOTE_SECRET' };
} } } });
t.after(() => h.host.stop()); await h.ready();
assert.equal(JSON.stringify(h.evidence).includes('DO_NOT_RECORD_REMOTE_SECRET'), false);
});