mirror of
https://github.com/AlecM33/Werewolf.git
synced 2025-12-26 07:47:50 +01:00
redis part 6, fix test suites
This commit is contained in:
323
spec/unit/server/modules/Events_Spec.js
Normal file
323
spec/unit/server/modules/Events_Spec.js
Normal file
@@ -0,0 +1,323 @@
|
||||
// TODO: clean up these deep relative paths? jsconfig.json is not working...
|
||||
const Game = require('../../../../server/model/Game');
|
||||
const globals = require('../../../../server/config/globals');
|
||||
const EVENT_IDS = globals.EVENT_IDS;
|
||||
const USER_TYPES = globals.USER_TYPES;
|
||||
const STATUS = globals.STATUS;
|
||||
const GameManager = require('../../../../server/modules/singletons/GameManager.js');
|
||||
const TimerManager = require('../../../../server/modules/singletons/TimerManager.js');
|
||||
const Events = require('../../../../server/modules/Events.js');
|
||||
const GameStateCurator = require('../../../../server/modules/GameStateCurator.js');
|
||||
const logger = require('../../../../server/modules/Logger.js')(false);
|
||||
|
||||
describe('Events', () => {
|
||||
let gameManager, namespace, socket, game, timerManager;
|
||||
|
||||
beforeAll(() => {
|
||||
spyOn(logger, 'debug');
|
||||
spyOn(logger, 'error');
|
||||
|
||||
const inObj = { emit: () => {} };
|
||||
namespace = { in: () => { return inObj; }, to: () => { return inObj; }, sockets: new Map() };
|
||||
socket = { id: '123', emit: () => {}, to: () => { return { emit: () => {} }; } };
|
||||
gameManager = GameManager.instance ? GameManager.instance : new GameManager(logger, globals.ENVIRONMENT.PRODUCTION, 'test');
|
||||
timerManager = TimerManager.instance ? TimerManager.instance : new TimerManager(logger, 'test');
|
||||
gameManager.setGameSocketNamespace(namespace);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new Game(
|
||||
'ABCD',
|
||||
STATUS.LOBBY,
|
||||
[{ id: 'a', assigned: true, out: true, killed: false, userType: USER_TYPES.MODERATOR },
|
||||
{ id: 'b', gameRole: 'Villager', alignment: 'good', assigned: false, out: false, killed: false, userType: USER_TYPES.PLAYER }],
|
||||
[{ quantity: 2 }],
|
||||
false,
|
||||
'a',
|
||||
true,
|
||||
'a',
|
||||
new Date().toJSON(),
|
||||
null
|
||||
);
|
||||
spyOn(namespace, 'to').and.callThrough();
|
||||
spyOn(namespace, 'in').and.callThrough();
|
||||
spyOn(socket, 'to').and.callThrough();
|
||||
spyOn(namespace.in(), 'emit').and.callThrough();
|
||||
spyOn(gameManager, 'isGameFull').and.callThrough();
|
||||
spyOn(GameStateCurator, 'mapPerson').and.callThrough();
|
||||
namespace.sockets = new Map();
|
||||
timerManager.timerThreads = {};
|
||||
});
|
||||
|
||||
describe(EVENT_IDS.PLAYER_JOINED, () => {
|
||||
describe('stateChange', () => {
|
||||
it('should let a player join and mark the game as full', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.PLAYER_JOINED)
|
||||
.stateChange(game, { id: 'b', assigned: true }, { gameManager: gameManager });
|
||||
expect(gameManager.isGameFull).toHaveBeenCalled();
|
||||
expect(game.isFull).toEqual(true);
|
||||
expect(game.people.find(p => p.id === 'b').assigned).toEqual(true);
|
||||
});
|
||||
it('should let a player join and mark the game as NOT full', async () => {
|
||||
game.people.push({ id: 'c', assigned: false, userType: USER_TYPES.PLAYER });
|
||||
await Events.find((e) => e.id === EVENT_IDS.PLAYER_JOINED)
|
||||
.stateChange(game, { id: 'b', assigned: true }, { gameManager: gameManager });
|
||||
expect(gameManager.isGameFull).toHaveBeenCalled();
|
||||
expect(game.isFull).toEqual(false);
|
||||
expect(game.people.find(p => p.id === 'b').assigned).toEqual(true);
|
||||
});
|
||||
it('should not let the player join if their id does not match some unassigned person', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.PLAYER_JOINED)
|
||||
.stateChange(game, { id: 'd', assigned: true }, { gameManager: gameManager });
|
||||
expect(gameManager.isGameFull).not.toHaveBeenCalled();
|
||||
expect(game.isFull).toEqual(false);
|
||||
expect(game.people.find(p => p.id === 'd')).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
describe('communicate', () => {
|
||||
it('should communicate the join event to the rooms sockets, sending the new player', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.PLAYER_JOINED)
|
||||
.communicate(game, { id: 'b', assigned: true }, { gameManager: gameManager });
|
||||
expect(namespace.in).toHaveBeenCalledWith(game.accessCode);
|
||||
expect(namespace.in().emit).toHaveBeenCalledWith(
|
||||
globals.EVENTS.PLAYER_JOINED,
|
||||
GameStateCurator.mapPerson({ id: 'b', assigned: true }),
|
||||
game.isFull
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(EVENT_IDS.ADD_SPECTATOR, () => {
|
||||
describe('stateChange', () => {
|
||||
it('should add a spectator', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.ADD_SPECTATOR)
|
||||
.stateChange(game, { id: 'e', name: 'ghost', assigned: true }, { gameManager: gameManager });
|
||||
expect(gameManager.isGameFull).not.toHaveBeenCalled();
|
||||
expect(game.isFull).toEqual(false);
|
||||
expect(game.people.find(p => p.id === 'e').assigned).toEqual(true);
|
||||
expect(game.people.find(p => p.id === 'e').name).toEqual('ghost');
|
||||
});
|
||||
});
|
||||
describe('communicate', () => {
|
||||
it('should communicate the add spectator event to the rooms sockets, sending the new spectator', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.ADD_SPECTATOR)
|
||||
.communicate(game, { id: 'e', name: 'ghost', assigned: true }, { gameManager: gameManager });
|
||||
expect(namespace.in).toHaveBeenCalledWith(game.accessCode);
|
||||
expect(namespace.in().emit).toHaveBeenCalledWith(
|
||||
EVENT_IDS.ADD_SPECTATOR,
|
||||
GameStateCurator.mapPerson({ id: 'e', name: 'ghost', assigned: true })
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(EVENT_IDS.FETCH_GAME_STATE, () => {
|
||||
describe('stateChange', () => {
|
||||
it('should find the matching person and update their associated socket id if it is different', async () => {
|
||||
const mockSocket = { join: () => {} };
|
||||
spyOn(mockSocket, 'join').and.callThrough();
|
||||
namespace.sockets.set('123', mockSocket);
|
||||
game.people.push({ cookie: 'cookie', socketId: '456' });
|
||||
await Events.find((e) => e.id === EVENT_IDS.FETCH_GAME_STATE)
|
||||
.stateChange(game, { personId: 'cookie' }, { gameManager: gameManager, requestingSocketId: '123' });
|
||||
expect(mockSocket.join).toHaveBeenCalledWith(game.accessCode);
|
||||
expect(game.people.find(p => p.socketId === '123')).not.toBeNull();
|
||||
});
|
||||
it('should find the matching person and should NOT update their socketId if it is NOT different', async () => {
|
||||
const mockSocket = { join: () => {} };
|
||||
spyOn(mockSocket, 'join').and.callThrough();
|
||||
namespace.sockets.set('123', mockSocket);
|
||||
game.people.push({ cookie: 'cookie', socketId: '123' });
|
||||
await Events.find((e) => e.id === EVENT_IDS.FETCH_GAME_STATE)
|
||||
.stateChange(game, { personId: 'cookie' }, { gameManager: gameManager, requestingSocketId: '123' });
|
||||
expect(mockSocket.join).not.toHaveBeenCalled();
|
||||
expect(game.people.find(p => p.socketId === '123')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
describe('communicate', () => {
|
||||
it('should do nothing if the client is not expecting an acknowledgement', async () => {
|
||||
game.people.push({ cookie: 'cookie', socketId: '456' });
|
||||
const mockSocket = { join: () => {} };
|
||||
namespace.sockets.set('456', mockSocket);
|
||||
spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson').and.callThrough();
|
||||
await Events.find((e) => e.id === EVENT_IDS.FETCH_GAME_STATE)
|
||||
.communicate(game, { personId: 'cookie' }, { ackFn: null });
|
||||
expect(GameStateCurator.getGameStateFromPerspectiveOfPerson).not.toHaveBeenCalled();
|
||||
});
|
||||
it('should acknowledge the client with null if a matching person was not found', async () => {
|
||||
game.people.push({ cookie: 'differentCookie', socketId: '456' });
|
||||
const mockSocket = { join: () => {} };
|
||||
namespace.sockets.set('456', mockSocket);
|
||||
const vars = { ackFn: () => {}, gameManager: gameManager };
|
||||
spyOn(vars, 'ackFn').and.callThrough();
|
||||
spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson').and.callThrough();
|
||||
await Events.find((e) => e.id === EVENT_IDS.FETCH_GAME_STATE)
|
||||
.communicate(game, { personId: 'cookie' }, vars);
|
||||
expect(GameStateCurator.getGameStateFromPerspectiveOfPerson).not.toHaveBeenCalled();
|
||||
expect(vars.ackFn).toHaveBeenCalledWith(null);
|
||||
});
|
||||
it('should acknowledge the client with null if a matching person was found, but the socket is not connected' +
|
||||
' to the namespace', async () => {
|
||||
game.people.push({ cookie: 'cookie', socketId: '456' });
|
||||
const mockSocket = { join: () => {} };
|
||||
namespace.sockets.set('123', mockSocket);
|
||||
const vars = { ackFn: () => {}, gameManager: gameManager };
|
||||
spyOn(vars, 'ackFn').and.callThrough();
|
||||
spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson').and.callThrough();
|
||||
await Events.find((e) => e.id === EVENT_IDS.FETCH_GAME_STATE)
|
||||
.communicate(game, { personId: 'cookie' }, vars);
|
||||
expect(GameStateCurator.getGameStateFromPerspectiveOfPerson).not.toHaveBeenCalled();
|
||||
expect(vars.ackFn).toHaveBeenCalledWith(null);
|
||||
});
|
||||
it('should acknowledge the client with the game state if they are found and their socket is connected', async () => {
|
||||
game.people.push({ cookie: 'cookie', socketId: '456' });
|
||||
const mockSocket = { join: () => {} };
|
||||
namespace.sockets.set('456', mockSocket);
|
||||
const vars = { ackFn: () => {}, gameManager: gameManager };
|
||||
spyOn(vars, 'ackFn').and.callThrough();
|
||||
spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson').and.callThrough();
|
||||
await Events.find((e) => e.id === EVENT_IDS.FETCH_GAME_STATE)
|
||||
.communicate(game, { personId: 'cookie' }, vars);
|
||||
expect(GameStateCurator.getGameStateFromPerspectiveOfPerson).toHaveBeenCalled();
|
||||
expect(vars.ackFn).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(EVENT_IDS.START_GAME, () => {
|
||||
describe('stateChange', () => {
|
||||
it('should start the game', async () => {
|
||||
game.isFull = true;
|
||||
await Events.find((e) => e.id === EVENT_IDS.START_GAME)
|
||||
.stateChange(game, { id: 'b', assigned: true }, { gameManager: gameManager });
|
||||
expect(game.status).toEqual(STATUS.IN_PROGRESS);
|
||||
});
|
||||
it('should not start the game if it is not full', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.START_GAME)
|
||||
.stateChange(game, { id: 'b', assigned: true }, { gameManager: gameManager });
|
||||
expect(game.status).toEqual(STATUS.LOBBY);
|
||||
});
|
||||
it('should start the game and run the timer if the game has one', async () => {
|
||||
game.isFull = true;
|
||||
game.hasTimer = true;
|
||||
game.timerParams = {};
|
||||
spyOn(timerManager, 'runTimer').and.callFake((a, b) => {});
|
||||
await Events.find((e) => e.id === EVENT_IDS.START_GAME)
|
||||
.stateChange(game, { id: 'b', assigned: true }, { gameManager: gameManager, timerManager: timerManager });
|
||||
expect(game.status).toEqual(STATUS.IN_PROGRESS);
|
||||
expect(game.timerParams.paused).toEqual(true);
|
||||
expect(timerManager.runTimer).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('communicate', () => {
|
||||
it('should communicate the start event to the room', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.START_GAME)
|
||||
.communicate(game, {}, { gameManager: gameManager });
|
||||
expect(namespace.in).toHaveBeenCalledWith(game.accessCode);
|
||||
expect(namespace.in().emit).toHaveBeenCalledWith(EVENT_IDS.START_GAME);
|
||||
});
|
||||
it('should communicate the start event to the room and acknowledge the client', async () => {
|
||||
const vars = { ackFn: () => {}, gameManager: gameManager };
|
||||
spyOn(vars, 'ackFn').and.callThrough();
|
||||
await Events.find((e) => e.id === EVENT_IDS.START_GAME)
|
||||
.communicate(game, {}, vars);
|
||||
expect(namespace.in).toHaveBeenCalledWith(game.accessCode);
|
||||
expect(namespace.in().emit).toHaveBeenCalledWith(EVENT_IDS.START_GAME);
|
||||
expect(vars.ackFn).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe(EVENT_IDS.KILL_PLAYER, () => {
|
||||
describe('stateChange', () => {
|
||||
it('should kill the indicated player', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.KILL_PLAYER)
|
||||
.stateChange(game, { personId: 'b' }, { gameManager: gameManager });
|
||||
const person = game.people.find(p => p.id === 'b');
|
||||
expect(person.userType).toEqual(USER_TYPES.KILLED_PLAYER);
|
||||
expect(person.out).toBeTrue();
|
||||
expect(person.killed).toBeTrue();
|
||||
});
|
||||
it('should not kill the player if they are already out', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.KILL_PLAYER)
|
||||
.stateChange(game, { personId: 'a' }, { gameManager: gameManager });
|
||||
const person = game.people.find(p => p.id === 'a');
|
||||
expect(person.userType).toEqual(USER_TYPES.MODERATOR);
|
||||
expect(person.out).toBeTrue();
|
||||
expect(person.killed).toBeFalse();
|
||||
});
|
||||
});
|
||||
describe('communicate', () => {
|
||||
it('should communicate the killed player to the room', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.KILL_PLAYER)
|
||||
.communicate(game, { personId: 'b' }, { gameManager: gameManager });
|
||||
expect(namespace.in).toHaveBeenCalledWith(game.accessCode);
|
||||
expect(namespace.in().emit).toHaveBeenCalledWith(
|
||||
EVENT_IDS.KILL_PLAYER,
|
||||
'b'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe(EVENT_IDS.REVEAL_PLAYER, () => {
|
||||
describe('stateChange', () => {
|
||||
it('should reveal the indicated player', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.REVEAL_PLAYER)
|
||||
.stateChange(game, { personId: 'b' }, { gameManager: gameManager });
|
||||
const person = game.people.find(p => p.id === 'b');
|
||||
expect(person.userType).toEqual(USER_TYPES.PLAYER);
|
||||
expect(person.out).toBeFalse();
|
||||
expect(person.killed).toBeFalse();
|
||||
expect(person.revealed).toBeTrue();
|
||||
});
|
||||
});
|
||||
describe('communicate', () => {
|
||||
it('should communicate the killed player to the room', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.REVEAL_PLAYER)
|
||||
.communicate(game, { personId: 'b' }, { gameManager: gameManager });
|
||||
expect(namespace.in).toHaveBeenCalledWith(game.accessCode);
|
||||
expect(namespace.in().emit).toHaveBeenCalledWith(
|
||||
EVENT_IDS.REVEAL_PLAYER,
|
||||
{ id: 'b', gameRole: 'Villager', alignment: 'good' }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe(EVENT_IDS.END_GAME, () => {
|
||||
describe('stateChange', () => {
|
||||
it('should end the game and reveal all players', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.END_GAME)
|
||||
.stateChange(game, { id: 'b', assigned: true }, { gameManager: gameManager });
|
||||
expect(game.status).toEqual(STATUS.ENDED);
|
||||
expect(game.people.find(p => p.id === 'b').revealed).toBeTrue();
|
||||
});
|
||||
it('should end the game and kill the associated timer thread', async () => {
|
||||
game.hasTimer = true;
|
||||
timerManager.timerThreads = { ABCD: { kill: () => {} } };
|
||||
spyOn(timerManager.timerThreads.ABCD, 'kill').and.callThrough();
|
||||
await Events.find((e) => e.id === EVENT_IDS.END_GAME)
|
||||
.stateChange(game, { id: 'b', assigned: true }, { gameManager: gameManager, timerManager: timerManager, logger: { trace: () => {} } });
|
||||
expect(game.status).toEqual(STATUS.ENDED);
|
||||
expect(game.people.find(p => p.id === 'b').revealed).toBeTrue();
|
||||
expect(timerManager.timerThreads.ABCD.kill).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('communicate', () => {
|
||||
it('should communicate the end event to the room', async () => {
|
||||
await Events.find((e) => e.id === EVENT_IDS.END_GAME)
|
||||
.communicate(game, {}, { gameManager: gameManager });
|
||||
expect(namespace.in).toHaveBeenCalledWith(game.accessCode);
|
||||
expect(namespace.in().emit).toHaveBeenCalledWith(EVENT_IDS.END_GAME, GameStateCurator.mapPeopleForModerator(game.people));
|
||||
});
|
||||
it('should communicate the end event to the room and acknowledge the client', async () => {
|
||||
const vars = { ackFn: () => {}, gameManager: gameManager };
|
||||
spyOn(vars, 'ackFn').and.callThrough();
|
||||
await Events.find((e) => e.id === EVENT_IDS.END_GAME)
|
||||
.communicate(game, {}, vars);
|
||||
expect(namespace.in).toHaveBeenCalledWith(game.accessCode);
|
||||
expect(namespace.in().emit).toHaveBeenCalledWith(EVENT_IDS.END_GAME, GameStateCurator.mapPeopleForModerator(game.people));
|
||||
expect(vars.ackFn).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,12 +5,13 @@ const USER_TYPES = globals.USER_TYPES;
|
||||
const STATUS = globals.STATUS;
|
||||
const Person = require('../../../../server/model/Person');
|
||||
const GameManager = require('../../../../server/modules/singletons/GameManager.js');
|
||||
const TimerManager = require('../../../../server/modules/singletons/TimerManager.js');
|
||||
const EventManager = require('../../../../server/modules/singletons/EventManager.js');
|
||||
const GameStateCurator = require('../../../../server/modules/GameStateCurator.js');
|
||||
const ActiveGameRunner = require('../../../../server/modules/singletons/TimerManager.js');
|
||||
const logger = require('../../../../server/modules/Logger.js')(false);
|
||||
|
||||
describe('GameManager', () => {
|
||||
let gameManager, namespace, socket;
|
||||
let gameManager, timerManager, eventManager, namespace, socket, game;
|
||||
|
||||
beforeAll(() => {
|
||||
spyOn(logger, 'debug');
|
||||
@@ -19,346 +20,79 @@ describe('GameManager', () => {
|
||||
const inObj = { emit: () => {} };
|
||||
namespace = { in: () => { return inObj; }, to: () => { return inObj; } };
|
||||
socket = { id: '123', emit: () => {}, to: () => { return { emit: () => {} }; } };
|
||||
gameManager = new GameManager(logger, globals.ENVIRONMENT.PRODUCTION, new ActiveGameRunner(logger));
|
||||
gameManager = GameManager.instance ? GameManager.instance : new GameManager(logger, globals.ENVIRONMENT.PRODUCTION, 'test');
|
||||
timerManager = TimerManager.instance ? TimerManager.instance : new TimerManager(logger, 'test');
|
||||
eventManager = EventManager.instance ? EventManager.instance : new EventManager(logger, 'test');
|
||||
eventManager.publisher = { publish: async (...a) => {} }
|
||||
gameManager.eventManager = eventManager;
|
||||
gameManager.timerManager = timerManager;
|
||||
gameManager.setGameSocketNamespace(namespace);
|
||||
spyOn(gameManager, 'refreshGame').and.callFake(async () => {});
|
||||
spyOn(eventManager.publisher, 'publish').and.callFake(async () => {});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(namespace, 'to').and.callThrough();
|
||||
spyOn(socket, 'to').and.callThrough();
|
||||
});
|
||||
|
||||
describe('#transferModerator', () => {
|
||||
it('Should transfer successfully from a dedicated moderator to a killed player', () => {
|
||||
const personToTransferTo = new Person('1', '123', 'Joe', USER_TYPES.KILLED_PLAYER);
|
||||
personToTransferTo.socketId = 'socket1';
|
||||
personToTransferTo.out = true;
|
||||
const moderator = new Person('3', '789', 'Jack', USER_TYPES.MODERATOR);
|
||||
moderator.socketId = 'socket2';
|
||||
const game = new Game(
|
||||
'abc',
|
||||
globals.STATUS.IN_PROGRESS,
|
||||
[personToTransferTo, new Person('2', '456', 'Jane', USER_TYPES.PLAYER)],
|
||||
[],
|
||||
false,
|
||||
moderator,
|
||||
true,
|
||||
moderator.id,
|
||||
new Date().toJSON()
|
||||
);
|
||||
gameManager.transferModeratorPowers(socket, game, personToTransferTo, namespace, logger);
|
||||
|
||||
expect(game.moderator).toEqual(personToTransferTo);
|
||||
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
||||
expect(moderator.userType).toEqual(USER_TYPES.SPECTATOR);
|
||||
expect(namespace.to).toHaveBeenCalledWith(personToTransferTo.socketId);
|
||||
expect(namespace.to).toHaveBeenCalledWith(game.moderator.socketId);
|
||||
});
|
||||
|
||||
it('Should transfer successfully from a dedicated moderator to a spectator', () => {
|
||||
const personToTransferTo = new Person('1', '123', 'Joe', USER_TYPES.SPECTATOR);
|
||||
personToTransferTo.socketId = 'socket1';
|
||||
const moderator = new Person('3', '789', 'Jack', USER_TYPES.MODERATOR);
|
||||
moderator.socketId = 'socket2';
|
||||
const game = new Game(
|
||||
'abc',
|
||||
globals.STATUS.IN_PROGRESS,
|
||||
[new Person('2', '456', 'Jane', USER_TYPES.PLAYER)],
|
||||
[],
|
||||
false,
|
||||
moderator,
|
||||
true,
|
||||
moderator.id,
|
||||
new Date().toJSON()
|
||||
);
|
||||
game.spectators.push(personToTransferTo);
|
||||
gameManager.transferModeratorPowers(socket, game, personToTransferTo, namespace, logger);
|
||||
|
||||
expect(game.moderator).toEqual(personToTransferTo);
|
||||
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
||||
expect(moderator.userType).toEqual(USER_TYPES.SPECTATOR);
|
||||
expect(namespace.to).toHaveBeenCalledWith(personToTransferTo.socketId);
|
||||
expect(namespace.to).toHaveBeenCalledWith(game.moderator.socketId);
|
||||
});
|
||||
|
||||
it('Should transfer successfully from a temporary moderator to a killed player', () => {
|
||||
const personToTransferTo = new Person('1', '123', 'Joe', USER_TYPES.KILLED_PLAYER);
|
||||
personToTransferTo.out = true;
|
||||
personToTransferTo.socketId = 'socket1';
|
||||
const tempMod = new Person('3', '789', 'Jack', USER_TYPES.TEMPORARY_MODERATOR);
|
||||
tempMod.socketId = 'socket2';
|
||||
const game = new Game(
|
||||
'abc',
|
||||
globals.STATUS.IN_PROGRESS,
|
||||
[personToTransferTo, tempMod, new Person('2', '456', 'Jane', USER_TYPES.PLAYER)],
|
||||
[],
|
||||
false,
|
||||
tempMod,
|
||||
false,
|
||||
tempMod.id,
|
||||
new Date().toJSON()
|
||||
);
|
||||
gameManager.transferModeratorPowers(socket, game, personToTransferTo, namespace, logger);
|
||||
|
||||
expect(game.moderator).toEqual(personToTransferTo);
|
||||
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
||||
expect(tempMod.userType).toEqual(USER_TYPES.PLAYER);
|
||||
expect(namespace.to).toHaveBeenCalledWith(personToTransferTo.socketId);
|
||||
expect(namespace.to).toHaveBeenCalledWith(game.moderator.socketId);
|
||||
});
|
||||
|
||||
it('Should make the temporary moderator a dedicated moderator when they take themselves out of the game', () => {
|
||||
const tempMod = new Person('3', '789', 'Jack', USER_TYPES.TEMPORARY_MODERATOR);
|
||||
tempMod.socketId = 'socket1';
|
||||
const personToTransferTo = tempMod;
|
||||
tempMod.out = true;
|
||||
const game = new Game(
|
||||
'abc',
|
||||
globals.STATUS.IN_PROGRESS,
|
||||
[personToTransferTo, tempMod, new Person('2', '456', 'Jane', USER_TYPES.PLAYER)],
|
||||
[],
|
||||
false,
|
||||
tempMod,
|
||||
true,
|
||||
tempMod.id,
|
||||
new Date().toJSON()
|
||||
);
|
||||
gameManager.transferModeratorPowers(socket, game, personToTransferTo, namespace, logger);
|
||||
|
||||
expect(game.moderator).toEqual(personToTransferTo);
|
||||
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
||||
expect(tempMod.userType).toEqual(USER_TYPES.MODERATOR);
|
||||
expect(namespace.to).toHaveBeenCalledOnceWith(personToTransferTo.socketId);
|
||||
expect(socket.to).toHaveBeenCalledWith(game.accessCode);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#killPlayer', () => {
|
||||
it('Should mark a player as out and broadcast it, and should not transfer moderators if the moderator is a dedicated mod.', () => {
|
||||
spyOn(namespace.in(), 'emit');
|
||||
spyOn(gameManager, 'transferModeratorPowers');
|
||||
const player = new Person('1', '123', 'Joe', USER_TYPES.PLAYER);
|
||||
const mod = new Person('2', '456', 'Jane', USER_TYPES.MODERATOR);
|
||||
const game = new Game(
|
||||
'abc',
|
||||
globals.STATUS.IN_PROGRESS,
|
||||
[player],
|
||||
[],
|
||||
false,
|
||||
mod,
|
||||
true,
|
||||
mod.id,
|
||||
new Date().toJSON()
|
||||
);
|
||||
gameManager.killPlayer(socket, game, player, namespace, logger);
|
||||
|
||||
expect(player.out).toEqual(true);
|
||||
expect(player.userType).toEqual(USER_TYPES.KILLED_PLAYER);
|
||||
expect(namespace.in().emit).toHaveBeenCalled();
|
||||
expect(gameManager.transferModeratorPowers).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should mark a temporary moderator as out but preserve their user type, and call the transfer mod function', () => {
|
||||
spyOn(namespace.in(), 'emit');
|
||||
spyOn(gameManager, 'transferModeratorPowers');
|
||||
const tempMod = new Person('1', '123', 'Joe', USER_TYPES.TEMPORARY_MODERATOR);
|
||||
const game = new Game(
|
||||
'abc',
|
||||
globals.STATUS.IN_PROGRESS,
|
||||
[tempMod],
|
||||
[],
|
||||
false,
|
||||
tempMod,
|
||||
true,
|
||||
tempMod.id,
|
||||
new Date().toJSON()
|
||||
);
|
||||
gameManager.killPlayer(socket, game, tempMod, namespace, logger);
|
||||
|
||||
expect(tempMod.out).toEqual(true);
|
||||
expect(tempMod.userType).toEqual(USER_TYPES.TEMPORARY_MODERATOR);
|
||||
expect(namespace.in().emit).not.toHaveBeenCalled();
|
||||
expect(gameManager.transferModeratorPowers).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#handleRequestForGameState', () => {
|
||||
let gameRunner, mod, player;
|
||||
|
||||
beforeEach(() => {
|
||||
mod = new Person('2', '456', 'Jane', USER_TYPES.MODERATOR);
|
||||
player = new Person('1', '123', 'Joe', USER_TYPES.PLAYER);
|
||||
gameRunner = {
|
||||
activeGames: new Map([
|
||||
['abc', new Game(
|
||||
'abc',
|
||||
globals.STATUS.IN_PROGRESS,
|
||||
[player],
|
||||
[],
|
||||
false,
|
||||
mod,
|
||||
true,
|
||||
mod.id,
|
||||
new Date().toJSON())
|
||||
]
|
||||
])
|
||||
};
|
||||
});
|
||||
|
||||
it('should send the game state to a matching person with an active connection to the room', () => {
|
||||
const socket = { id: 'socket1' };
|
||||
spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson');
|
||||
player.socketId = 'socket1';
|
||||
spyOn(namespace.in(), 'emit');
|
||||
gameManager.handleRequestForGameState(
|
||||
gameRunner.activeGames.get('abc'),
|
||||
namespace,
|
||||
logger,
|
||||
gameRunner,
|
||||
'abc',
|
||||
'123',
|
||||
(arg) => {},
|
||||
socket
|
||||
);
|
||||
|
||||
expect(GameStateCurator.getGameStateFromPerspectiveOfPerson)
|
||||
.toHaveBeenCalledWith(gameRunner.activeGames.get('abc'), player);
|
||||
});
|
||||
|
||||
it('should send the game state to a matching person who reset their connection', () => {
|
||||
const socket = { id: 'socket_222222', join: () => {} };
|
||||
spyOn(socket, 'join');
|
||||
spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson');
|
||||
player.socketId = 'socket_111111';
|
||||
spyOn(namespace.in(), 'emit');
|
||||
gameManager.handleRequestForGameState(
|
||||
gameRunner.activeGames.get('abc'),
|
||||
namespace,
|
||||
logger,
|
||||
gameRunner,
|
||||
'abc',
|
||||
'123',
|
||||
(arg) => {},
|
||||
socket
|
||||
);
|
||||
|
||||
expect(GameStateCurator.getGameStateFromPerspectiveOfPerson)
|
||||
.toHaveBeenCalledWith(gameRunner.activeGames.get('abc'), player);
|
||||
expect(player.socketId).toEqual(socket.id);
|
||||
expect(socket.join).toHaveBeenCalled();
|
||||
});
|
||||
timerManager.timerThreads = {};
|
||||
game = new Game(
|
||||
'ABCD',
|
||||
STATUS.LOBBY,
|
||||
[{ id: 'a', name: 'person1', assigned: true, out: true, killed: false, userType: USER_TYPES.MODERATOR },
|
||||
{ id: 'b', name: 'person2', gameRole: 'Villager', alignment: 'good', assigned: false, out: false, killed: false, userType: USER_TYPES.PLAYER }],
|
||||
[{ quantity: 2 }],
|
||||
false,
|
||||
'a',
|
||||
true,
|
||||
'a',
|
||||
new Date().toJSON(),
|
||||
null
|
||||
);
|
||||
game.currentModeratorId = 'a';
|
||||
});
|
||||
|
||||
describe('#joinGame', () => {
|
||||
let game, person, moderator;
|
||||
|
||||
beforeEach(() => {
|
||||
person = new Person('1', '123', 'Placeholder', USER_TYPES.KILLED_PLAYER);
|
||||
moderator = new Person('3', '789', 'Jack', USER_TYPES.MODERATOR);
|
||||
game = new Game(
|
||||
'abc',
|
||||
globals.STATUS.IN_PROGRESS,
|
||||
[person],
|
||||
[],
|
||||
false,
|
||||
moderator,
|
||||
true,
|
||||
moderator.id,
|
||||
new Date().toJSON()
|
||||
);
|
||||
});
|
||||
|
||||
it('should mark the game as full when all players have been assigned', () => {
|
||||
moderator.assigned = true;
|
||||
|
||||
gameManager.joinGame(game, 'Jill', 'x');
|
||||
it('should mark the game as full when all players have been assigned', async () => {
|
||||
await gameManager.joinGame(game, 'Jill', 'x');
|
||||
|
||||
expect(game.isFull).toEqual(true);
|
||||
expect(game.people[0].name).toEqual('Jill');
|
||||
expect(game.people[0].assigned).toEqual(true);
|
||||
});
|
||||
|
||||
it('should create a spectator if the game is already full and broadcast it to the room', () => {
|
||||
moderator.assigned = true;
|
||||
person.assigned = true;
|
||||
game.people.find(p => p.id === 'b').assigned = true;
|
||||
game.isFull = true;
|
||||
spyOn(gameManager.namespace.in(), 'emit');
|
||||
|
||||
gameManager.joinGame(game, 'Jane', 'x');
|
||||
|
||||
expect(game.isFull).toEqual(true);
|
||||
expect(game.people[0].name).toEqual('Placeholder');
|
||||
expect(game.moderator.name).toEqual('Jack');
|
||||
expect(game.spectators.length).toEqual(1);
|
||||
expect(game.spectators[0].name).toEqual('Jane');
|
||||
expect(game.spectators[0].userType).toEqual(USER_TYPES.SPECTATOR);
|
||||
expect(gameManager.namespace.in().emit).toHaveBeenCalledWith(globals.EVENTS.UPDATE_SPECTATORS, jasmine.anything());
|
||||
});
|
||||
});
|
||||
|
||||
describe('#generateAccessCode', () => {
|
||||
it('should continue to generate access codes up to the max attempts when the generated code is already in use by another game', () => {
|
||||
gameManager.activeGameRunner.activeGames = new Map([['AAAA', {}]]);
|
||||
|
||||
const accessCode = gameManager.generateAccessCode(['A']);
|
||||
expect(accessCode).toEqual(null); // we might the max generation attempts of 50.
|
||||
});
|
||||
|
||||
it('should generate and return a unique access code', () => {
|
||||
gameManager.activeGameRunner.activeGames = new Map([['AAAA', {}]]);
|
||||
|
||||
const accessCode = gameManager.generateAccessCode(['B']);
|
||||
expect(accessCode).toEqual('BBBB');
|
||||
expect(game.people.filter(p => p.userType === USER_TYPES.SPECTATOR).length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#restartGame', () => {
|
||||
let person1,
|
||||
person2,
|
||||
person3,
|
||||
shuffleSpy,
|
||||
game,
|
||||
moderator;
|
||||
let shuffleSpy;
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
person1 = new Person('1', '123', 'Placeholder1', USER_TYPES.KILLED_PLAYER);
|
||||
person2 = new Person('2', '456', 'Placeholder2', USER_TYPES.PLAYER);
|
||||
person3 = new Person('3', '789', 'Placeholder3', USER_TYPES.PLAYER);
|
||||
moderator = new Person('4', '000', 'Jack', USER_TYPES.MODERATOR);
|
||||
person1.out = true;
|
||||
person2.revealed = true;
|
||||
moderator.assigned = true;
|
||||
shuffleSpy = spyOn(gameManager, 'shuffle').and.stub();
|
||||
game = new Game(
|
||||
'test',
|
||||
STATUS.ENDED,
|
||||
[person1, person2, person3],
|
||||
[
|
||||
{ role: 'Villager', description: 'test', team: 'good', quantity: 1 },
|
||||
{ role: 'Seer', description: 'test', team: 'good', quantity: 1 },
|
||||
{ role: 'Werewolf', description: 'test', team: 'evil', quantity: 1 }
|
||||
],
|
||||
false,
|
||||
moderator,
|
||||
true,
|
||||
'4',
|
||||
null
|
||||
);
|
||||
});
|
||||
|
||||
it('should reset all relevant game parameters', async () => {
|
||||
game.status = STATUS.ENDED;
|
||||
let player = game.people.find(p => p.id === 'b');
|
||||
player.userType = USER_TYPES.KILLED_PLAYER;
|
||||
player.killed = true;
|
||||
player.out = true;
|
||||
const emitSpy = spyOn(namespace.in(), 'emit');
|
||||
|
||||
await gameManager.restartGame(game, namespace);
|
||||
|
||||
expect(game.status).toEqual(STATUS.IN_PROGRESS);
|
||||
expect(game.moderator.id).toEqual('4');
|
||||
expect(game.moderator.userType).toEqual(USER_TYPES.MODERATOR);
|
||||
expect(person1.out).toEqual(false);
|
||||
expect(person2.revealed).toEqual(false);
|
||||
for (const person of game.people) {
|
||||
expect(person.gameRole).toBeDefined();
|
||||
}
|
||||
expect(player.userType).toEqual(USER_TYPES.PLAYER);
|
||||
expect(player.out).toBeFalse();
|
||||
expect(player.killed).toBeFalse();
|
||||
expect(shuffleSpy).toHaveBeenCalled();
|
||||
expect(emitSpy).toHaveBeenCalledWith(globals.EVENT_IDS.RESTART_GAME);
|
||||
});
|
||||
@@ -366,114 +100,38 @@ describe('GameManager', () => {
|
||||
it('should reset all relevant game parameters, including when the game has a timer', async () => {
|
||||
game.timerParams = { hours: 2, minutes: 2, paused: false };
|
||||
game.hasTimer = true;
|
||||
gameManager.activeGameRunner.timerThreads = { test: { kill: () => {} } };
|
||||
timerManager.timerThreads = { 'ABCD': { kill: () => {} } };
|
||||
game.status = STATUS.ENDED;
|
||||
|
||||
const threadKillSpy = spyOn(gameManager.activeGameRunner.timerThreads.test, 'kill');
|
||||
const runGameSpy = spyOn(gameManager.activeGameRunner, 'runGame').and.stub();
|
||||
const threadKillSpy = spyOn(timerManager.timerThreads['ABCD'], 'kill');
|
||||
const runTimerSpy = spyOn(timerManager, 'runTimer').and.stub();
|
||||
const emitSpy = spyOn(namespace.in(), 'emit');
|
||||
|
||||
await gameManager.restartGame(game, namespace);
|
||||
|
||||
expect(game.status).toEqual(STATUS.IN_PROGRESS);
|
||||
expect(game.timerParams.paused).toBeTrue();
|
||||
expect(game.moderator.id).toEqual('4');
|
||||
expect(game.moderator.userType).toEqual(USER_TYPES.MODERATOR);
|
||||
expect(person1.out).toEqual(false);
|
||||
expect(person2.revealed).toEqual(false);
|
||||
for (const person of game.people) {
|
||||
expect(person.gameRole).toBeDefined();
|
||||
}
|
||||
expect(threadKillSpy).toHaveBeenCalled();
|
||||
expect(runGameSpy).toHaveBeenCalled();
|
||||
expect(Object.keys(gameManager.activeGameRunner.timerThreads).length).toEqual(0);
|
||||
expect(runTimerSpy).toHaveBeenCalled();
|
||||
expect(shuffleSpy).toHaveBeenCalled();
|
||||
expect(emitSpy).toHaveBeenCalledWith(globals.EVENT_IDS.RESTART_GAME);
|
||||
});
|
||||
|
||||
it('should reset all relevant game parameters and preserve temporary moderator', async () => {
|
||||
it('should reset all relevant game parameters and create a temporary moderator', async () => {
|
||||
const emitSpy = spyOn(namespace.in(), 'emit');
|
||||
game.currentModeratorId = 'b';
|
||||
game.people.find(p => p.id === 'a').userType = USER_TYPES.SPECTATOR;
|
||||
game.moderator = game.people[0];
|
||||
game.moderator.userType = USER_TYPES.TEMPORARY_MODERATOR;
|
||||
game.people.find(p => p.id === 'b').userType = USER_TYPES.MODERATOR;
|
||||
game.hasDedicatedModerator = false;
|
||||
|
||||
await gameManager.restartGame(game, namespace);
|
||||
|
||||
expect(game.status).toEqual(STATUS.IN_PROGRESS);
|
||||
expect(game.moderator.id).toEqual('1');
|
||||
expect(game.moderator.userType).toEqual(USER_TYPES.TEMPORARY_MODERATOR);
|
||||
expect(game.moderator.gameRole).toBeDefined();
|
||||
expect(person1.out).toEqual(false);
|
||||
expect(person2.revealed).toEqual(false);
|
||||
for (const person of game.people) {
|
||||
expect(person.gameRole).toBeDefined();
|
||||
}
|
||||
expect(game.currentModeratorId).toEqual('b');
|
||||
expect(game.people.find(p => p.id === 'b').userType).toEqual(USER_TYPES.TEMPORARY_MODERATOR);
|
||||
expect(shuffleSpy).toHaveBeenCalled();
|
||||
expect(emitSpy).toHaveBeenCalledWith(globals.EVENT_IDS.RESTART_GAME);
|
||||
});
|
||||
|
||||
it('should reset all relevant game parameters and restore a temporary moderator from a dedicated moderator', async () => {
|
||||
const emitSpy = spyOn(namespace.in(), 'emit');
|
||||
game.moderator = game.people[0];
|
||||
game.moderator.userType = USER_TYPES.MODERATOR;
|
||||
game.hasDedicatedModerator = false;
|
||||
|
||||
await gameManager.restartGame(game, namespace);
|
||||
|
||||
expect(game.status).toEqual(STATUS.IN_PROGRESS);
|
||||
expect(game.moderator.id).toEqual('1');
|
||||
expect(game.moderator.userType).toEqual(USER_TYPES.TEMPORARY_MODERATOR);
|
||||
expect(game.moderator.gameRole).toBeDefined();
|
||||
expect(person1.out).toEqual(false);
|
||||
expect(person2.revealed).toEqual(false);
|
||||
for (const person of game.people) {
|
||||
expect(person.gameRole).toBeDefined();
|
||||
}
|
||||
expect(shuffleSpy).toHaveBeenCalled();
|
||||
expect(emitSpy).toHaveBeenCalledWith(globals.EVENT_IDS.RESTART_GAME);
|
||||
});
|
||||
|
||||
it('should reset all relevant game parameters and create a temporary mod if a dedicated mod transferred to a killed player', async () => {
|
||||
const emitSpy = spyOn(namespace.in(), 'emit');
|
||||
game.moderator = game.people[0];
|
||||
game.moderator.userType = USER_TYPES.MODERATOR;
|
||||
game.hasDedicatedModerator = true;
|
||||
|
||||
await gameManager.restartGame(game, namespace);
|
||||
|
||||
expect(game.status).toEqual(STATUS.IN_PROGRESS);
|
||||
expect(game.moderator.id).toEqual('1');
|
||||
expect(game.moderator.userType).toEqual(USER_TYPES.TEMPORARY_MODERATOR);
|
||||
expect(game.moderator.gameRole).toBeDefined();
|
||||
expect(person1.out).toEqual(false);
|
||||
expect(person2.revealed).toEqual(false);
|
||||
for (const person of game.people) {
|
||||
expect(person.gameRole).toBeDefined();
|
||||
}
|
||||
expect(shuffleSpy).toHaveBeenCalled();
|
||||
expect(emitSpy).toHaveBeenCalledWith(globals.EVENT_IDS.RESTART_GAME);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#pruneStaleGames', () => {
|
||||
it('delete a game if it was created more than 24 hours ago', () => {
|
||||
const moreThan24HoursAgo = new Date();
|
||||
moreThan24HoursAgo.setDate(moreThan24HoursAgo.getDate() - 1);
|
||||
moreThan24HoursAgo.setHours(moreThan24HoursAgo.getHours() - 1);
|
||||
gameManager.activeGameRunner.activeGames = new Map([['AAAA', { createTime: moreThan24HoursAgo.toJSON() }]]);
|
||||
|
||||
gameManager.pruneStaleGames();
|
||||
|
||||
expect(gameManager.activeGameRunner.activeGames.size).toEqual(0);
|
||||
});
|
||||
|
||||
it('should not delete a game if it was not created more than 24 hours ago', () => {
|
||||
const lessThan24HoursAgo = new Date();
|
||||
lessThan24HoursAgo.setHours(lessThan24HoursAgo.getHours() - 23);
|
||||
gameManager.activeGameRunner.activeGames = new Map([['AAAA', { createTime: lessThan24HoursAgo.toJSON() }]]);
|
||||
|
||||
gameManager.pruneStaleGames();
|
||||
|
||||
expect(gameManager.activeGameRunner.activeGames.size).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user