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

99 lines
3.6 KiB
JavaScript

const TavernMatchBridge = require('../src/network/tavernMatchBridge');
describe('TavernMatchBridge', () => {
function makeManager() {
const rooms = new Map();
return {
rooms,
onTick: jest.fn(),
onFinish: jest.fn()
};
}
function makeBridge({ rooms = [], io = null } = {}) {
const manager = makeManager();
for (const r of rooms) manager.rooms.set(r.id, r);
const emitted = [];
const bridge = new TavernMatchBridge({
matchManager: manager,
io: io || { to: () => ({ emit: (e, p) => emitted.push({ e, p }) }) },
debugManager: null
});
bridge.enabled = true;
return { bridge, manager, emitted };
}
function makeRoom(id = 'm1') {
return {
id,
economy: 'free',
variant: 'race',
tickCount: 5,
status: 'active',
rows: 2,
cols: 3,
occupants: new Map([['a', { id: 'a', x: 1, y: 2, name: 'A', avatar: 'default', facing: 'down' }]]),
playerStates: new Map([['a', { alive: true, finished: false, escaped: false, hasTreasure: false, placement: null }]]),
monster: { x: 3, y: 4 },
treasure: null,
dungeon: { map: [["'1", "'1", '#'], ['#', "'1", '>']], entrance: [0, 0], exit: [2, 1] },
winnerId: null,
seedHash: 'deadbeef',
seed: 'never-publish-this'
};
}
test('forwards public match tick to tavern room', () => {
const room = makeRoom('m1');
const { bridge, manager, emitted } = makeBridge({ rooms: [room] });
bridge.initialize();
manager.onTick('m1', { tick: 5, events: [] });
expect(emitted.some(e => e.e === 'tavern_match_tick' && e.p.matchId === 'm1')).toBe(true);
});
test('forwards match end to tavern room', async () => {
const room = makeRoom('m2');
room.status = 'finished';
room.winnerId = 'a';
const { bridge, manager, emitted } = makeBridge({ rooms: [room] });
bridge.initialize();
await manager.onFinish(room);
expect(emitted.some(e => e.e === 'tavern_match_end' && e.p.matchId === 'm2')).toBe(true);
});
test('getActiveMatches returns public snapshots', () => {
const room = makeRoom('m3');
const { bridge } = makeBridge({ rooms: [room] });
const list = bridge.getActiveMatches();
expect(list.length).toBe(1);
expect(list[0].matchId).toBe('m3');
expect(list[0].players.length).toBe(1);
});
test('public snapshots include a renderer-safe map but never the unrevealed seed', () => {
const room = makeRoom('m-render');
room.toGameState = () => ({
visibleTiles: room.dungeon.map,
exploredTiles: room.dungeon.map,
lighting: {},
dungeonRows: 2,
dungeonCols: 3,
players: [{ id: 'a', x: 1, y: 1, alive: true }],
monster: { x: 0, y: 1 },
treasure: { x: 1, y: 0, carrierId: null },
entrance: [0, 0],
exit: [2, 1],
seed: room.seed,
privateEvents: [{ input: 'left' }]
});
const { bridge } = makeBridge({ rooms: [room] });
const snap = bridge.getActiveMatches()[0];
expect(snap.visibleTiles).toEqual(room.dungeon.map);
expect(snap.exploredTiles).toEqual(room.dungeon.map);
expect(snap).toMatchObject({ dungeonRows: 2, dungeonCols: 3, seedHash: 'deadbeef' });
expect(snap.seed).toBeUndefined();
expect(snap.privateEvents).toBeUndefined();
});
});