mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-24 09:38:32 +02:00
Begin payment logic and tests
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
const MovementManager = require('../src/game/movementManager');
|
||||
|
||||
function makeChaseGame() {
|
||||
// Minimal game stub with player + monster and deterministic chase
|
||||
return {
|
||||
player: { x: 1, y: 1, getState() { return { x: this.x, y: this.y }; } },
|
||||
monster: { x: 5, y: 1, getState() { return { x: this.x, y: this.y }; } },
|
||||
movePlayer(dx, dy) {
|
||||
this.player.x += dx; this.player.y += dy; return { status: 'moved' };
|
||||
},
|
||||
moveMonster() { // simple horizontal chase
|
||||
if (this.monster.x > this.player.x) this.monster.x--; else if (this.monster.x < this.player.x) this.monster.x++;
|
||||
if (this.monster.y > this.player.y) this.monster.y--; else if (this.monster.y < this.player.y) this.monster.y++;
|
||||
},
|
||||
getState() { return { player: { x: this.player.x, y: this.player.y }, monster: { x: this.monster.x, y: this.monster.y } }; }
|
||||
};
|
||||
}
|
||||
|
||||
describe('Monster chase integration (MovementManager hook)', () => {
|
||||
test('monster advances toward player each move', () => {
|
||||
const activeGames = new Map();
|
||||
const socketId = 'sock-monster';
|
||||
const game = makeChaseGame();
|
||||
activeGames.set(socketId, game);
|
||||
const emitted = [];
|
||||
const io = { to: () => ({ emit: (evt, data) => emitted.push({ evt, data }) }) };
|
||||
const debugManager = { getCurrentBlockHeight: () => 999 };
|
||||
// Provide postMoveHook similar to server wiring
|
||||
const mm = new MovementManager({ activeGames, io, debugManager, moveCooldown: 0, postMoveHook: ({ game }) => game.moveMonster() });
|
||||
|
||||
// Player moves right 3 times; monster should close distance
|
||||
const startDistance = game.monster.x - game.player.x;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
mm.handleMove(socketId, { direction: 'right' });
|
||||
}
|
||||
const endDistance = game.monster.x - game.player.x;
|
||||
expect(endDistance).toBeLessThan(startDistance); // monster got closer
|
||||
// Ensure we emitted 3 updates
|
||||
expect(emitted.filter(e => e.evt === 'game_update').length).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
const MovementManager = require('../src/game/movementManager');
|
||||
|
||||
// Minimal fake game implementing movePlayer & getState
|
||||
function makeGame() {
|
||||
return {
|
||||
player: { x: 5, y: 5, getState() { return { x: this.x, y: this.y }; } },
|
||||
movePlayer(dx, dy) {
|
||||
this.player.x += dx;
|
||||
this.player.y += dy;
|
||||
return { status: 'moved' };
|
||||
},
|
||||
getState() { return { player: { x: this.player.x, y: this.player.y } }; }
|
||||
};
|
||||
}
|
||||
|
||||
describe('MovementManager', () => {
|
||||
test('applies movement and rate limits subsequent rapid moves', () => {
|
||||
const activeGames = new Map();
|
||||
const socketId = 'sock1';
|
||||
const game = makeGame();
|
||||
activeGames.set(socketId, game);
|
||||
const emitted = [];
|
||||
const io = { to: () => ({ emit: (evt, data) => emitted.push({ evt, data }) }) };
|
||||
const debugManager = { getCurrentBlockHeight: () => 123 };
|
||||
const mm = new MovementManager({ activeGames, io, debugManager, moveCooldown: 100 });
|
||||
|
||||
mm.handleMove(socketId, { direction: 'right' });
|
||||
mm.handleMove(socketId, { direction: 'right' }); // should be rate-limited
|
||||
|
||||
expect(game.player.x).toBe(6); // only first move applied
|
||||
expect(emitted.filter(e => e.evt === 'game_update').length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
const PaymentHandlers = require('../src/network/paymentHandlers');
|
||||
|
||||
function makeMocks() {
|
||||
return {
|
||||
io: { to: () => ({ emit: () => {} }) },
|
||||
gameModeManager: {
|
||||
gameMode: 'PAID_SINGLE',
|
||||
cryptoType: 'WOW',
|
||||
singleGamePrice: 1000,
|
||||
createPaymentRequest: async () => ({ id: 'pid123', address: 'addr', amount: 1000, expiresAt: new Date() })
|
||||
},
|
||||
walletService: {
|
||||
startPaymentMonitoring: (addr, cb) => { /* simulate nothing */ },
|
||||
stopPaymentMonitoring: () => {}
|
||||
},
|
||||
debugManager: { getCurrentBlockHeight: () => 123, CONSOLE_LOGGING: false },
|
||||
queueManager: {
|
||||
getUserBySocket: () => ({ clientId: 'client1' }),
|
||||
getPlayerIndex: () => -1,
|
||||
addPlayer: () => {},
|
||||
markConfirmed: () => {}
|
||||
},
|
||||
broadcastManager: { sendStatusUpdate: () => {} }
|
||||
};
|
||||
}
|
||||
|
||||
describe('PaymentHandlers basic', () => {
|
||||
test('createAndShowPaymentRequest emits payment_created', async () => {
|
||||
const mocks = makeMocks();
|
||||
const emitted = {};
|
||||
mocks.io.to = () => ({ emit: (event, data) => { emitted[event] = data; } });
|
||||
ph = new PaymentHandlers(mocks);
|
||||
await ph.createAndShowPaymentRequest({ id: 'sock1' });
|
||||
expect(emitted.payment_created).toBeDefined();
|
||||
expect(emitted.payment_created.paymentId).toBe('pid123');
|
||||
});
|
||||
afterAll(() => {
|
||||
if (ph && typeof ph.dispose === 'function') {
|
||||
ph.dispose();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
const QueueManager = require('../src/network/queueManager');
|
||||
const PaymentHandlers = require('../src/network/paymentHandlers');
|
||||
|
||||
/**
|
||||
* Integration-style test to ensure a payment that confirms AFTER a block event
|
||||
* (where the entry was still unconfirmed) results in an immediate game start
|
||||
* without waiting for the next block.
|
||||
*/
|
||||
|
||||
describe('Payment flow integration: confirm-after-block immediate start', () => {
|
||||
test('unconfirmed at block tick then confirmed triggers immediate game', async () => {
|
||||
const events = [];
|
||||
const activeGames = new Map();
|
||||
const socketId = 'sock-int';
|
||||
const blockHeight = 100;
|
||||
|
||||
// Mock IO emitter
|
||||
const io = {
|
||||
to: () => ({
|
||||
emit: (event, data) => {
|
||||
events.push({ event, data });
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// Minimal broadcast manager stub
|
||||
const broadcastManager = {
|
||||
sendStatusUpdate: () => {},
|
||||
broadcastChatMessage: () => {}
|
||||
};
|
||||
|
||||
// Debug manager mock
|
||||
const debugManager = {
|
||||
CONSOLE_LOGGING: false,
|
||||
getCurrentBlockHeight: () => blockHeight
|
||||
};
|
||||
|
||||
// User lookup
|
||||
const userObj = { id: socketId, clientId: 'client-int' };
|
||||
const getUserBySocket = (id) => id === socketId ? userObj : null;
|
||||
|
||||
// createGameForUser mock (records active game & returns state)
|
||||
const createGameForUser = (user) => {
|
||||
const game = {
|
||||
getState: () => ({ player: { x:0, y:0 }, lighting: {}, torches: [], started: true })
|
||||
};
|
||||
activeGames.set(user.id, game);
|
||||
return game;
|
||||
};
|
||||
|
||||
// Game mode manager mock (paid single game mode)
|
||||
const gameModeManager = {
|
||||
gameMode: 'PAID_SINGLE',
|
||||
cryptoType: 'WOW',
|
||||
singleGamePrice: 1000,
|
||||
createPaymentRequest: async () => ({ id: 'pay-1', address: 'addr-1', amount: 1000, expiresAt: new Date(Date.now()+60000) })
|
||||
};
|
||||
|
||||
// Wallet service mock with manual callback control
|
||||
let monitorCallback;
|
||||
const walletService = {
|
||||
startPaymentMonitoring: (address, cb) => { monitorCallback = cb; },
|
||||
stopPaymentMonitoring: () => {}
|
||||
};
|
||||
|
||||
// Instantiate real QueueManager & PaymentHandlers
|
||||
const queueManager = new QueueManager({
|
||||
debugManager,
|
||||
broadcastManager,
|
||||
io,
|
||||
createGameForUser,
|
||||
getUserBySocket,
|
||||
activeGames,
|
||||
gameModeManager,
|
||||
consoleLogging: false
|
||||
});
|
||||
|
||||
const paymentHandlers = new PaymentHandlers({
|
||||
io,
|
||||
gameModeManager,
|
||||
walletService,
|
||||
debugManager,
|
||||
queueManager,
|
||||
broadcastManager
|
||||
});
|
||||
|
||||
// Fake socket object with id
|
||||
const fakeSocket = { id: socketId, emit: (event, data) => events.push({ event, data }) };
|
||||
|
||||
// Create payment request (does NOT queue yet) and start monitoring
|
||||
await paymentHandlers.createAndShowPaymentRequest(fakeSocket);
|
||||
|
||||
expect(monitorCallback).toBeDefined();
|
||||
|
||||
// Simulate mempool detection first
|
||||
monitorCallback({ in_mempool: true, confirmed: false, amount: 1000, confirmations: 0 });
|
||||
// Should have queued the player but not started a game
|
||||
expect(queueManager.getQueueLength()).toBe(1);
|
||||
expect(events.filter(e => e.event === 'game_start').length).toBe(0);
|
||||
|
||||
// Simulate block tick BEFORE confirmation (should skip unconfirmed)
|
||||
queueManager.startGamesForWaiting(blockHeight);
|
||||
expect(events.filter(e => e.event === 'game_start').length).toBe(0);
|
||||
// Entry should still be in queue (re-queued)
|
||||
expect(queueManager.getQueueLength()).toBe(1);
|
||||
|
||||
// Now simulate confirmation after the block
|
||||
monitorCallback({ in_mempool: true, confirmed: true, amount: 1000, confirmations: 1 });
|
||||
|
||||
// Immediate game start expected
|
||||
const gameStarts = events.filter(e => e.event === 'game_start');
|
||||
expect(gameStarts.length).toBe(1);
|
||||
expect(gameStarts[0].data.blockHeight).toBe(blockHeight);
|
||||
// Queue should now be empty
|
||||
expect(queueManager.getQueueLength()).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
const WalletRPCService = require('../src/payments/walletRPCService');
|
||||
|
||||
// Minimal debugManager mock
|
||||
const debugManagerMock = { CONSOLE_LOGGING: false };
|
||||
|
||||
describe('WalletRPCService interface', () => {
|
||||
test('exposes expected methods', () => {
|
||||
const svc = new WalletRPCService(debugManagerMock);
|
||||
expect(typeof svc.initialize).toBe('function');
|
||||
expect(typeof svc.rpcCall).toBe('function');
|
||||
expect(typeof svc.createPaymentRequest).toBe('function');
|
||||
expect(typeof svc.checkPaymentStatus).toBe('function');
|
||||
expect(typeof svc.startPaymentMonitoring).toBe('function');
|
||||
expect(typeof svc.stopPaymentMonitoring).toBe('function');
|
||||
expect(typeof svc.processBatchPayouts).toBe('function'); // newly added stub
|
||||
});
|
||||
|
||||
test('processBatchPayouts stub returns processed:0', async () => {
|
||||
const svc = new WalletRPCService(debugManagerMock);
|
||||
const res = await svc.processBatchPayouts();
|
||||
expect(res).toEqual({ processed: 0 });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user