Files
wownerogue/test/paymentPolicy_ultra.test.js
jwinterm fad2a695bd Wownerogue
A browser-based roguelike synchronized with crypto block timing. A run is a race
against the next block: reach the exit before the block lands, or the house wins.
The same codebase runs the solo dungeon, the social Tavern, and operator-selected
PvP and race rulesets, with separate free and paid leaderboards.

Self-hostable under the MIT License. See README.md to run it, docs/ARCHITECTURE.md
for the design, and docs/DEPLOY.md to operate an instance.
2026-07-27 08:23:01 -04:00

150 lines
6.5 KiB
JavaScript

const PaymentConfigManager = require('../src/config/paymentConfig');
const GameModeManager = require('../src/game/gameModeManager');
const ENV_KEYS = [
'GAME_MODE',
'PAYMENTS_ENABLED',
'PAYMENT_MODES',
'DIRECT_PAYMENT_ENABLED',
'CREDITS_ENABLED',
'FREE_PLAY_ENABLED',
'PAYOUTS_ENABLED',
'DIRECT_PAYOUTS_ENABLED',
'CREDITS_PAYOUTS_ENABLED',
'PAYOUT_MAX_PER_GAME',
'CREDITS_PAYOUT_MAX'
];
describe('production payment policy wiring', () => {
let saved;
beforeEach(() => {
saved = Object.fromEntries(ENV_KEYS.map(key => [key, process.env[key]]));
for (const key of ENV_KEYS) delete process.env[key];
});
afterEach(() => {
for (const key of ENV_KEYS) {
if (saved[key] === undefined) delete process.env[key];
else process.env[key] = saved[key];
}
});
test('PAYOUTS_ENABLED=false remains authoritative with both per-mode flags true', () => {
process.env.PAYMENTS_ENABLED = 'true';
process.env.PAYMENT_MODES = 'direct,credits';
process.env.PAYOUTS_ENABLED = 'false';
process.env.DIRECT_PAYOUTS_ENABLED = 'true';
process.env.CREDITS_PAYOUTS_ENABLED = 'true';
process.env.FREE_PLAY_ENABLED = 'false';
const configManager = new PaymentConfigManager({ logger: { warn() {}, info() {} } });
const manager = new GameModeManager(
{ query: jest.fn(), withTransaction: jest.fn() },
{ validateAddress: jest.fn() },
{ CONSOLE_LOGGING: false },
configManager
);
expect(configManager.getConfig().payouts.enabled).toBe(false);
expect(manager.payoutsEnabled).toBe(false);
expect(manager.isPayoutEnabledForMode('PAID_SINGLE')).toBe(false);
expect(manager.isPayoutEnabledForMode('PAID_CREDITS')).toBe(false);
expect(manager.getGameModeInfo()).toEqual(expect.objectContaining({
payoutsEnabled: false,
directPayoutsEnabled: false,
creditsPayoutsEnabled: false,
freePlayEnabled: false
}));
});
test('PAYMENTS_ENABLED=false remains authoritative over every per-mode enable', () => {
process.env.PAYMENTS_ENABLED = 'false';
process.env.PAYMENT_MODES = 'direct,credits';
process.env.DIRECT_PAYMENT_ENABLED = 'true';
process.env.CREDITS_ENABLED = 'true';
const config = new PaymentConfigManager({ logger: { warn() {}, info() {} } }).getConfig();
expect(config.paymentsEnabled).toBe(false);
expect(config.modes.direct.enabled).toBe(false);
expect(config.modes.credits.enabled).toBe(false);
});
test('invoice creation refuses before database or wallet work when the master switch is off', async () => {
process.env.PAYMENTS_ENABLED = 'false';
process.env.PAYMENT_MODES = 'direct,credits';
const configManager = new PaymentConfigManager({ logger: { warn() {}, info() {} } });
const db = { query: jest.fn(), withTransaction: jest.fn() };
const wallet = { createPaymentRequest: jest.fn(), validateAddress: jest.fn() };
const manager = new GameModeManager(db, wallet, { CONSOLE_LOGGING: false }, configManager);
await expect(manager.createPaymentRequest('socket-disabled', 'single_game'))
.rejects.toMatchObject({ code: 'PAYMENT_INTAKE_DISABLED' });
await expect(manager.createPaymentRequest('socket-disabled', 'credits_package'))
.rejects.toMatchObject({ code: 'PAYMENT_INTAKE_DISABLED' });
expect(db.query).not.toHaveBeenCalled();
expect(wallet.createPaymentRequest).not.toHaveBeenCalled();
});
test('per-product switches cannot be bypassed through the generic invoice method', async () => {
process.env.PAYMENTS_ENABLED = 'true';
process.env.PAYMENT_MODES = 'direct';
process.env.DIRECT_PAYMENT_ENABLED = 'true';
process.env.CREDITS_ENABLED = 'false';
const configManager = new PaymentConfigManager({ logger: { warn() {}, info() {} } });
const db = { query: jest.fn(), withTransaction: jest.fn() };
const wallet = { createPaymentRequest: jest.fn(), validateAddress: jest.fn() };
const manager = new GameModeManager(db, wallet, { CONSOLE_LOGGING: false }, configManager);
expect(manager.isPaymentIntakeEnabled('single_game')).toBe(true);
expect(manager.isPaymentIntakeEnabled('credits_package')).toBe(false);
await expect(manager.createPaymentRequest('socket-disabled', 'credits_package'))
.rejects.toMatchObject({ code: 'PAYMENT_INTAKE_DISABLED' });
expect(db.query).not.toHaveBeenCalled();
expect(wallet.createPaymentRequest).not.toHaveBeenCalled();
});
test('a contradictory legacy GAME_MODE cannot misreport the enabled payment policy', () => {
process.env.PAYMENTS_ENABLED = 'true';
process.env.GAME_MODE = 'PAID_SINGLE';
process.env.PAYMENT_MODES = 'credits';
process.env.DIRECT_PAYMENT_ENABLED = 'false';
process.env.CREDITS_ENABLED = 'true';
process.env.PAYOUTS_ENABLED = 'false';
process.env.DIRECT_PAYOUTS_ENABLED = 'false';
process.env.CREDITS_PAYOUTS_ENABLED = 'false';
const manager = new PaymentConfigManager({ logger: { warn() {}, info() {} } });
const gameModes = new GameModeManager(
{ query: jest.fn(), withTransaction: jest.fn() },
{ validateAddress: jest.fn() },
{ CONSOLE_LOGGING: false },
manager
);
expect(manager.getConfig().modes.direct.enabled).toBe(false);
expect(manager.getConfig().modes.credits.enabled).toBe(true);
expect(manager.getLegacyGameMode()).toBe('PAID_CREDITS');
expect(gameModes.getGameModeInfo()).toEqual(expect.objectContaining({
mode: 'PAID_CREDITS',
directModeEnabled: false,
creditsModeEnabled: true,
payoutsEnabled: false
}));
expect(gameModes.getPublicPreferredPaymentMode({
preferred_payment_mode: 'direct'
})).toBe('credits');
});
test('global per-game payout cap is an outer bound for direct and credit wins', () => {
process.env.PAYMENT_MODES = 'direct,credits';
process.env.PAYOUT_MAX_PER_GAME = '123456';
const config = new PaymentConfigManager({ logger: { warn() {}, info() {} } }).getConfig();
expect(config.payouts.rules.direct.maxPayout).toBe(123456n);
expect(config.payouts.rules.credits.maxPayout).toBe(123456n);
});
});