fix(platform): recognize session faults across realms
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import assert from 'node:assert/strict';
|
||||
import { test } from 'node:test';
|
||||
import { runInNewContext } from 'node:vm';
|
||||
import type {
|
||||
GameEntry,
|
||||
GameHost,
|
||||
@@ -8,7 +9,10 @@ import type {
|
||||
PlatformGameSnapshot,
|
||||
PlatformToGameEvent,
|
||||
} from '../../YouleNexus/assets/framework/sdk/contracts/index.ts';
|
||||
import { GameSessionHost } from '../../YouleNexus/assets/framework/sdk/runtime/game-session-host.ts';
|
||||
import {
|
||||
GameSessionHost,
|
||||
GameSessionOpenFault,
|
||||
} from '../../YouleNexus/assets/framework/sdk/runtime/game-session-host.ts';
|
||||
import { RuntimeSession } from '../../YouleNexus/assets/framework/platform/runtime-session.ts';
|
||||
import { PlatformStore } from '../../YouleNexus/assets/framework/platform/stores/platform-store.ts';
|
||||
import { selectGameSnapshot } from '../../YouleNexus/assets/framework/platform/stores/selectors.ts';
|
||||
@@ -383,3 +387,84 @@ test('RuntimeSession flattens attach cleanup failures before appending lease inv
|
||||
[setup.errors.attach, setup.errors.dispose, setup.errors.invalidate],
|
||||
);
|
||||
});
|
||||
|
||||
test('RuntimeSession flattens a foreign-realm branded open fault before lease invalidation', () => {
|
||||
const setup = makeLeaseSession();
|
||||
const primary = new Error('foreign primary');
|
||||
const cleanup = new Error('foreign cleanup');
|
||||
const foreignFault = runInNewContext(`(() => {
|
||||
const fault = new Error('foreign open fault');
|
||||
Object.defineProperties(fault, {
|
||||
[Symbol.for('youle.framework.sdk.GameSessionOpenFault')]: { value: true },
|
||||
errors: { value: [primary, cleanup] },
|
||||
});
|
||||
return fault;
|
||||
})()`, { primary, cleanup });
|
||||
assert.equal(foreignFault instanceof GameSessionOpenFault, false);
|
||||
setup.setFailures('invalidate');
|
||||
setup.gameSession.open = () => { throw foreignFault; };
|
||||
|
||||
const error = caughtError(() => setup.session.handleLogin(fixture('player-login-room.json')));
|
||||
|
||||
assert.equal(setup.session.state, 'faulted');
|
||||
assert.equal(setup.session.fault, error);
|
||||
assert.equal(setup.gameSession.state, 'idle');
|
||||
assert.equal(setup.leases[0]?.invalidations, 1);
|
||||
assert.deepEqual(
|
||||
(error as Error & { errors?: readonly unknown[] }).errors,
|
||||
[primary, cleanup, setup.errors.invalidate],
|
||||
);
|
||||
});
|
||||
|
||||
test('RuntimeSession keeps ordinary aggregates and unbranded or accessor spoofs as single errors', () => {
|
||||
const primary = new Error('user primary');
|
||||
const cleanup = new Error('user cleanup');
|
||||
const ordinaryAggregate = runInNewContext(
|
||||
'new AggregateError([primary, cleanup], "user aggregate")',
|
||||
{ primary, cleanup },
|
||||
);
|
||||
const unbranded = { errors: [primary, cleanup] };
|
||||
let accessorReads = 0;
|
||||
const accessorSpoof = new Error('accessor spoof');
|
||||
Object.defineProperties(accessorSpoof, {
|
||||
[Symbol.for('youle.framework.sdk.GameSessionOpenFault')]: { value: true },
|
||||
errors: {
|
||||
get() {
|
||||
accessorReads += 1;
|
||||
throw new Error('errors getter must not run');
|
||||
},
|
||||
},
|
||||
});
|
||||
const brandAccessorSpoof = new Error('brand accessor spoof');
|
||||
Object.defineProperties(brandAccessorSpoof, {
|
||||
[Symbol.for('youle.framework.sdk.GameSessionOpenFault')]: {
|
||||
get() {
|
||||
accessorReads += 1;
|
||||
throw new Error('brand getter must not run');
|
||||
},
|
||||
},
|
||||
errors: { value: [primary, cleanup] },
|
||||
});
|
||||
|
||||
for (const candidate of [
|
||||
ordinaryAggregate,
|
||||
unbranded,
|
||||
accessorSpoof,
|
||||
brandAccessorSpoof,
|
||||
]) {
|
||||
const setup = makeLeaseSession();
|
||||
setup.setFailures('invalidate');
|
||||
setup.gameSession.open = () => { throw candidate; };
|
||||
|
||||
const error = caughtError(() => setup.session.handleLogin(fixture('player-login-room.json')));
|
||||
const errors = (error as Error & { errors?: readonly unknown[] }).errors;
|
||||
|
||||
assert.equal(errors?.length, 2);
|
||||
assert.equal(errors?.[0], candidate);
|
||||
assert.equal(errors?.[1], setup.errors.invalidate);
|
||||
assert.equal(setup.session.state, 'faulted');
|
||||
assert.equal(setup.gameSession.state, 'idle');
|
||||
assert.equal(setup.leases[0]?.invalidations, 1);
|
||||
}
|
||||
assert.equal(accessorReads, 0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user