Files
wownerogue/test/addressManager.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

83 lines
3.7 KiB
JavaScript

const AddressManager = require('../src/network/addressManager');
const { AppError } = require('../src/utils/errors');
describe('AddressManager', () => {
const sampleAddress = 'WW3UyJFX6GPKdM21APyVYjGhfX3ebyHvPPgtFC3wHxkYTCGg8k2UM9DLTCzcgoaLHSKnvJKRtCPgJ1sxvgYiFCoK19vLk83Jn';
function createManager({ existingAddress = null, cryptoType = null, network = null, walletValidation = null } = {}) {
const emitMock = jest.fn();
const toMock = jest.fn().mockReturnValue({ emit: emitMock });
const broadcastMock = { sendStatusUpdate: jest.fn() };
const gameModeManager = {
cryptoType,
network,
setUserPayoutAddress: jest.fn().mockResolvedValue(true),
getOrCreateUser: jest.fn().mockResolvedValue({ payout_address: existingAddress })
};
if (walletValidation) {
gameModeManager.validatePayoutAddress = jest.fn().mockResolvedValue(walletValidation);
}
const manager = new AddressManager({
gameModeManager,
broadcastManager: broadcastMock,
io: { to: toMock },
debugManager: { CONSOLE_LOGGING: false },
onConfirmed: jest.fn()
});
return { manager, emitMock, toMock, broadcastMock, gameModeManager };
}
test('saveAddress persists and emits confirmation', async () => {
const { manager, emitMock, gameModeManager } = createManager();
await manager.saveAddress('socket-1', sampleAddress);
expect(gameModeManager.setUserPayoutAddress).toHaveBeenCalledWith('socket-1', sampleAddress);
expect(emitMock).toHaveBeenCalledWith('address_confirmed', expect.objectContaining({ address: sampleAddress }));
});
test('saveAddress rejects invalid address input', async () => {
const { manager } = createManager();
await expect(manager.saveAddress('socket-2', 'not-an-address')).rejects.toBeInstanceOf(AppError);
});
test('handleDetection skips confirmation when address already set', async () => {
const { manager, emitMock, gameModeManager } = createManager({ existingAddress: sampleAddress });
await manager.handleDetection('socket-3', sampleAddress);
expect(gameModeManager.getOrCreateUser).toHaveBeenCalled();
expect(manager.pending.has('socket-3')).toBe(false);
expect(emitMock).toHaveBeenCalledWith('address_confirmed', expect.objectContaining({ message: expect.stringContaining('already set') }));
});
test('accepts stagenet XMR prefixes and rejects mainnet prefixes on a stagenet server', async () => {
const { manager, gameModeManager } = createManager({
cryptoType: 'XMR',
network: 'stagenet',
walletValidation: { valid: true, nettype: 'stagenet' }
});
const stagenetAddress = `5${'1'.repeat(94)}`;
const mainnetAddress = `4${'1'.repeat(94)}`;
expect(manager.isValidAddress(stagenetAddress)).toBe(true);
expect(manager.isValidAddress(mainnetAddress)).toBe(false);
expect(await manager.saveAddress('socket-stage', stagenetAddress)).toBe(stagenetAddress);
expect(gameModeManager.setUserPayoutAddress).toHaveBeenCalledWith('socket-stage', stagenetAddress);
});
test('wallet-RPC validation fails closed before an address is persisted', async () => {
const { manager, gameModeManager } = createManager({
cryptoType: 'XMR',
network: 'stagenet',
walletValidation: { valid: false, nettype: 'mainnet' }
});
const stagenetShapedAddress = `7${'1'.repeat(94)}`;
expect(await manager.saveAddress('socket-bad', stagenetShapedAddress)).toBe(false);
expect(gameModeManager.setUserPayoutAddress).not.toHaveBeenCalled();
});
});