test: add closed public free-play acceptance probe

This commit is contained in:
2026-10-21 19:01:21 -04:00
parent 7189f2f123
commit 925a797ae9
2 changed files with 769 additions and 0 deletions
+262
View File
@@ -0,0 +1,262 @@
'use strict';
const { spawnSync } = require('node:child_process');
const path = require('node:path');
const {
armed, healthAllowed, browserObserver, allowOnlyPublicOrigin, BROWSER_LAUNCH,
leaveQueue, forfeitOwnedRound, cleanupClient
} = require('../src/scripts/public-free-acceptance');
const COMMIT = '6fca51695b941f3ef495912cc77b73790b490888';
const RELEASE = 'git-' + COMMIT.slice(0, 12);
function healthy() {
return {
ready: true,
release: { verified: true, id: RELEASE, commit: COMMIT },
games: { mode: 'FREE' },
money: { paymentsEnabled: false, payoutsEnabled: false },
checks: { database: 'up', chain: 'up', financialRecovery: 'reconciled',
wallet: 'not_required', daemonIdentity: 'verified' },
chain: { network: 'mainnet', source: 'daemon', height: 1 }
};
}
function freeMode() {
return {
release: { verified: true, id: RELEASE, commit: COMMIT },
operatedProductProfileId: 'such-play-wow-prestige', isTestNetwork: false,
mode: 'FREE', freePlayEnabled: true, cryptoType: 'WOW', network: 'mainnet',
paymentsEnabled: false, payoutsEnabled: false,
directModeEnabled: false, creditsModeEnabled: false,
directPayoutsEnabled: false, creditsPayoutsEnabled: false,
features: { paymentRequired: false, creditsSystem: false, payouts: false },
creditPackages: [], cosmeticProducts: [],
modes: { tavern: true, match: { enabled: true, activeRuleset: { id: 'race' },
economies: { free: true } } }
};
}
function socketFixture() {
const priorWindow = global.window;
const events = new Map();
const socket = { id: 'owned-a', on(name, listener) { events.set(name, listener); } };
global.window = {};
browserObserver({ commit: COMMIT, release: RELEASE });
global.window.io = () => socket;
global.window.io();
return {
facts: global.window.__publicFreeAcceptance,
emit(name, payload) { events.get(name)(payload); },
restore() { global.window = priorWindow; }
};
}
function pageFixture(facts, onClick = null) {
const clicks = [];
const page = {
evaluate(fn) { return Promise.resolve(fn()); },
waitForFunction(fn) { if (!fn()) throw new Error('unacknowledged'); return Promise.resolve(); },
locator(selector) {
return {
async click() {
clicks.push(selector);
if (onClick) onClick(selector, facts);
else {
if (selector === '#leaveMatchQueueBtn') facts.left = true;
if (selector === '#leaveSpectateBtn') facts.forfeitAck = true;
}
}
};
},
once() {}
};
return { page, clicks };
}
describe('fixed public free acceptance lane', () => {
test('entrypoint refuses by default before health or browser work', () => {
const script = path.resolve(__dirname, '../src/scripts/public-free-acceptance.js');
const refused = spawnSync(process.execPath, [script], { encoding: 'utf8', timeout: 2000 });
expect(refused.status).toBe(2);
expect(refused.stdout).toBe('');
expect(refused.stderr).toMatch(/closed/i);
expect(armed(['--run-public-free-acceptance'])).toBe(true);
expect(armed(['--run-public-free-acceptance', '--origin=https://other.invalid'])).toBe(false);
expect(armed(['--expected-commit=other'])).toBe(false);
expect(BROWSER_LAUNCH.handleSIGINT).toBe(false);
expect(BROWSER_LAUNCH.handleSIGTERM).toBe(false);
});
test('health requires exact verified release, real chain and closed money', () => {
expect(healthAllowed(200, healthy())).toBe(true);
const variants = [
value => { value.release.commit = 'a'.repeat(40); },
value => { value.release.verified = false; },
value => { value.money.paymentsEnabled = true; },
value => { value.money.payoutsEnabled = true; },
value => { value.chain.source = 'simulated'; },
value => { value.chain.network = 'stagenet'; },
value => { value.checks.financialRecovery = 'pending'; },
value => { value.checks.daemonIdentity = 'unverified'; }
];
for (const mutate of variants) {
const value = healthy();
mutate(value);
expect(healthAllowed(200, value)).toBe(false);
}
expect(healthAllowed(503, healthy())).toBe(false);
});
test('browser routes admit only the fixed public origin', async () => {
const handlers = {};
await allowOnlyPublicOrigin({
route(_pattern, handler) { handlers.http = handler; },
routeWebSocket(_pattern, handler) { handlers.socket = handler; }
});
const http = async address => {
let decision = null;
await handlers.http({ request: () => ({ url: () => address }),
continue: () => { decision = 'allowed'; },
abort: () => { decision = 'blocked'; } });
return decision;
};
const socket = address => {
let decision = null;
handlers.socket({ url: () => address,
connectToServer: () => { decision = 'allowed'; },
close: () => { decision = 'blocked'; } });
return decision;
};
expect(await http('https://play.wowne.ro/tavern')).toBe('allowed');
expect(await http('https://other.invalid/tavern')).toBe('blocked');
expect(await http('http://play.wowne.ro/tavern')).toBe('blocked');
expect(socket('wss://play.wowne.ro/socket.io/')).toBe('allowed');
expect(socket('wss://other.invalid/socket.io/')).toBe('blocked');
expect(socket('ws://play.wowne.ro/socket.io/')).toBe('blocked');
});
test('browser event observer retains derived facts and rejects paid mode or outside entrants', () => {
const fixture = socketFixture();
try {
fixture.emit('connect');
fixture.emit('game_mode_info', freeMode());
expect(fixture.facts.connected && fixture.facts.policy).toBe(true);
fixture.emit('game_mode_info', { ...freeMode(), payoutsEnabled: true });
expect(fixture.facts.policy).toBe(false);
expect(fixture.facts.unsafe).toBe(true);
fixture.emit('game_mode_info', freeMode());
expect(fixture.facts.policy).toBe(true);
expect(fixture.facts.unsafe).toBe(true);
fixture.emit('game_mode_info', { ...freeMode(), modes: {
tavern: true, match: { enabled: true, activeRuleset: { id: 'race' },
economies: { free: true, crypto_race: true } }
} });
expect(fixture.facts.policy).toBe(false);
fixture.facts.ownedIds = ['owned-a', 'owned-b'];
fixture.emit('match_queue_joined', { success: true, economy: 'free', position: 1,
token: 'private-field-must-not-be-stored' });
expect(fixture.facts.queue).toEqual({ success: true, free: true, position: 1 });
fixture.emit('match_joined', { matchId: 'private-match', economy: 'free',
ruleset: { id: 'race' }, countdownMs: 3000,
players: [{ id: 'owned-a' }, { id: 'outside' }] });
expect(fixture.facts.onlyOwned).toBe(false);
expect(fixture.facts.unsafe).toBe(true);
fixture.emit('match_joined', { matchId: 'private-match', economy: 'free',
ruleset: { id: 'race' }, countdownMs: 3000,
players: [{ id: 'owned-a' }, { id: 'owned-b' }] });
expect(fixture.facts.onlyOwned).toBe(false);
fixture.emit('match_tick', { matchId: 'private-match', state: { secret: 'not-stored' } });
expect(JSON.stringify(fixture.facts)).not.toContain('not-stored');
} finally { fixture.restore(); }
});
test('matched own entrants need countdown, start and durable result', () => {
const fixture = socketFixture();
try {
fixture.facts.ownedIds = ['owned-a', 'owned-b'];
fixture.emit('match_joined', { matchId: 'match', economy: 'free',
ruleset: { id: 'race' }, countdownMs: 3000,
players: [{ id: 'owned-a' }, { id: 'owned-b' }] });
expect(fixture.facts.onlyOwned && fixture.facts.countdown).toBe(true);
fixture.emit('match_joined', { matchId: 'different-match', economy: 'free',
ruleset: { id: 'race' }, countdownMs: 3000,
players: [{ id: 'owned-a' }, { id: 'owned-b' }] });
expect(fixture.facts.unsafe).toBe(true);
} finally { fixture.restore(); }
});
test('owned race derives movement and treats settlement pending as nonterminal', () => {
const fixture = socketFixture();
try {
fixture.facts.ownedIds = ['owned-a', 'owned-b'];
fixture.emit('match_joined', { matchId: 'match', economy: 'free',
ruleset: { id: 'race' }, countdownMs: 3000,
players: [{ id: 'owned-a' }, { id: 'owned-b' }] });
fixture.emit('match_start', { matchId: 'match', ruleset: { id: 'race' } });
fixture.emit('match_tick', { matchId: 'match', state: {
players: [{ id: 'owned-a', x: 4, y: 7 }] } });
fixture.emit('match_tick', { matchId: 'match', state: {
players: [{ id: 'owned-a', x: 5, y: 7 }] } });
fixture.emit('match_settlement_pending', { matchId: 'match' });
expect(fixture.facts.started && fixture.facts.ticks === 2 && fixture.facts.moved).toBe(true);
expect(fixture.facts.ended).toBe(false);
expect(fixture.facts.settlementPending).toBe(true);
fixture.emit('match_end', { matchId: 'match', players: [{ id: 'owned-a' }] });
expect(fixture.facts.ended && fixture.facts.ownResult).toBe(true);
expect(fixture.facts.settlementPending).toBe(false);
} finally { fixture.restore(); }
});
test('queue cleanup requires own leave acknowledgement; outsider match blocks forfeit', async () => {
const priorWindow = global.window;
const facts = { queue: { success: true }, left: false, joined: false, started: false,
ended: false, onlyOwned: false, settlementPending: false };
global.window = { __publicFreeAcceptance: facts };
try {
const { page, clicks } = pageFixture(facts);
expect(await leaveQueue(page)).toBe(true);
expect(facts.left).toBe(true);
expect(clicks).toEqual(['#leaveMatchQueueBtn']);
facts.joined = facts.started = true;
expect(await forfeitOwnedRound(page)).toBe(false);
expect(clicks).toEqual(['#leaveMatchQueueBtn']);
facts.onlyOwned = true;
facts.settlementPending = true;
expect(await forfeitOwnedRound(page)).toBe(false);
facts.settlementPending = false;
expect(await forfeitOwnedRound(page)).toBe(true);
expect(facts.forfeitAck).toBe(true);
} finally { global.window = priorWindow; }
});
test('missing queue acknowledgment is unknown, and a block-race transition reclassifies cleanup', async () => {
const priorWindow = global.window;
const facts = { joinIntent: true, queue: null, left: false, joined: false,
started: false, ended: false, onlyOwned: false, settlementPending: false };
global.window = { __publicFreeAcceptance: facts };
try {
const missing = pageFixture(facts);
expect(await cleanupClient(missing.page)).toEqual({ clean: false, forfeited: false });
expect(missing.clicks).toEqual([]);
facts.queue = { success: true, free: true, position: 1 };
const blockRace = pageFixture(facts, (selector, state) => {
if (selector === '#leaveMatchQueueBtn') {
state.joined = state.started = state.onlyOwned = true;
}
if (selector === '#leaveSpectateBtn') state.forfeitAck = true;
});
expect(await cleanupClient(blockRace.page)).toEqual({ clean: true, forfeited: true });
expect(blockRace.clicks).toEqual(['#leaveMatchQueueBtn', '#leaveSpectateBtn']);
facts.joined = facts.started = facts.forfeitAck = false;
facts.onlyOwned = false;
const outside = pageFixture(facts, (selector, state) => {
if (selector === '#leaveMatchQueueBtn') state.joined = state.started = true;
});
expect(await cleanupClient(outside.page)).toEqual({ clean: false, forfeited: false });
expect(outside.clicks).toEqual(['#leaveMatchQueueBtn']);
} finally { global.window = priorWindow; }
});
});