Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ import IRedisService from '../../../../../common/service/redis/IRedisService';
*/
@Injectable()
export class RedisServiceInMemory implements IRedisService, OnModuleDestroy {
private static instance: RedisServiceInMemory;
private readonly store = new Map<
string,
{ value: string; expiresAt?: number }
>();

constructor() {
if (RedisServiceInMemory.instance) return RedisServiceInMemory.instance;
RedisServiceInMemory.instance = this;
}

private isDestroyed = false;

async set(key: string, value: string, ttlS?: number): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { CacheKeys } from '../../../common/service/redis/cacheKeys.enum';
import { OnlinePlayerStatus } from '../../../onlinePlayers/enum/OnlinePlayerStatus';
import OnlinePlayersCommonModule from '../modules/onlinePlayersCommon.module';
import { RedisService } from '../../../common/service/redis/redis.service';
import OnlinePlayersBuilderFactory from '../data/onlinePlayersBuilderFactory';
import { OnlinePlayerBuilder } from '../data/onlinePlayers/OnlinePlayerBuilder';
import { BattleWaitStatus } from '../../../onlinePlayers/payload/additionalTypes/BattleWaitStatus';

describe('OnlinePlayersService.addPlayerOnline() test suite', () => {
let service: OnlinePlayersService;
Expand All @@ -18,6 +21,12 @@ describe('OnlinePlayersService.addPlayerOnline() test suite', () => {
.setName('player1')
.build();

const addPlayerBuilder =
OnlinePlayersBuilderFactory.getBuilder('AddOnlinePlayer');
const onlinePlayerBuilder = OnlinePlayersBuilderFactory.getBuilder(
'OnlinePlayer',
) as OnlinePlayerBuilder<BattleWaitStatus>;

const playerModel = PlayerModule.getPlayerModel();

beforeEach(async () => {
Expand All @@ -33,21 +42,50 @@ describe('OnlinePlayersService.addPlayerOnline() test suite', () => {
});

it('Should be able to add one player to cache', async () => {
const playerToAdd = addPlayerBuilder
.setPlayerId(player1._id)
.setStatus(OnlinePlayerStatus.BATTLE)
.build();

const expectedKey = `${CacheKeys.ONLINE_PLAYERS}:${player1._id}`;
const expectedPayload = JSON.stringify({
_id: player1._id,
name: player1.name,
status: OnlinePlayerStatus.BATTLE,
});
const expectedPayload = JSON.stringify(
onlinePlayerBuilder
.setId(playerToAdd.player_id)
.setName(player1.name)
.setStatus(playerToAdd.status)
.build(),
);

const redisSet = jest.spyOn(redisService, 'set');

await service.addPlayerOnline(playerToAdd);

expect(redisSet).toHaveBeenCalledTimes(1);
expect(redisSet).toHaveBeenCalledWith(expectedKey, expectedPayload, 90);
});

it(`Should set queue number if player has status ${OnlinePlayerStatus.BATTLE_WAIT}`, async () => {
const playerToAdd = addPlayerBuilder
.setPlayerId(player1._id)
.setStatus(OnlinePlayerStatus.BATTLE_WAIT)
.build();
const expectedKey = `${CacheKeys.ONLINE_PLAYERS}:${player1._id}`;
const expectedPayload = JSON.stringify(
onlinePlayerBuilder
.setId(playerToAdd.player_id)
.setName(player1.name)
.setStatus(playerToAdd.status)
.setAdditional({ queueNumber: 0 })
.build(),
);

const redisSet = jest.spyOn(redisService, 'set');

await service.addPlayerOnline({
player_id: player1._id,
status: OnlinePlayerStatus.BATTLE,
status: OnlinePlayerStatus.BATTLE_WAIT,
});

expect(redisSet).toHaveBeenCalledTimes(1);
expect(redisSet).toHaveBeenCalledWith(expectedKey, expectedPayload, 90);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { OnlinePlayersService } from '../../../onlinePlayers/onlinePlayers.service';
import { RedisService } from '../../../common/service/redis/redis.service';
import PlayerBuilderFactory from '../../player/data/playerBuilderFactory';
import PlayerModule from '../../player/modules/player.module';
import OnlinePlayersModule from '../modules/onlinePlayers.module';
import OnlinePlayersCommonModule from '../modules/onlinePlayersCommon.module';
import OnlinePlayer from '../../../onlinePlayers/payload/OnlinePlayer';
import { OnlinePlayerStatus } from '../../../onlinePlayers/enum/OnlinePlayerStatus';

describe('OnlinePlayersService.getOnlinePlayerById() test suite', () => {
let service: OnlinePlayersService;

let redisService: RedisService;

const playerBuilder = PlayerBuilderFactory.getBuilder('Player');
const player1 = playerBuilder
.setUniqueIdentifier('player1')
.setName('player1')
.build();

const playerModel = PlayerModule.getPlayerModel();

beforeEach(async () => {
jest.clearAllMocks();
service = await OnlinePlayersModule.getOnlinePlayersService();

const player1Resp = await playerModel.create(player1);
player1._id = player1Resp._id.toString();

redisService = (await OnlinePlayersCommonModule.getModule()).get(
RedisService,
);
});

it('Should return player if it exists', async () => {
const existingPlayer: OnlinePlayer = {
_id: player1._id,
name: player1.name,
status: OnlinePlayerStatus.UI,
};
jest
.spyOn(redisService, 'get')
.mockResolvedValue(JSON.stringify(existingPlayer));

const [player, errors] = await service.getOnlinePlayerById(player1._id);

expect(errors).toBeNull();
expect(player).toEqual(existingPlayer);
});

it('Should return ServiceError NOT_FOUND if player does not exists', async () => {
jest.spyOn(redisService, 'get').mockResolvedValue(null);

const [player, errors] = await service.getOnlinePlayerById(player1._id);

expect(player).toBeNull();
expect(errors).toContainSE_NOT_FOUND();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { OnlinePlayerStatus } from '../../../onlinePlayers/enum/OnlinePlayerStat
import { RedisService } from '../../../common/service/redis/redis.service';
import OnlinePlayersCommonModule from '../modules/onlinePlayersCommon.module';

describe('OnlinePlayersService.getAllOnlinePlayers() test suite', () => {
describe('OnlinePlayersService.getOnlinePlayers() test suite', () => {
let service: OnlinePlayersService;

let redisService: RedisService;
Expand Down Expand Up @@ -57,7 +57,7 @@ describe('OnlinePlayersService.getAllOnlinePlayers() test suite', () => {
[_id2]: JSON.stringify(payload2),
});

const player_ids = await service.getAllOnlinePlayers();
const player_ids = await service.getOnlinePlayers();

expect(player_ids).toContainEqual(payload1);
expect(player_ids).toContainEqual(payload2);
Expand All @@ -82,7 +82,7 @@ describe('OnlinePlayersService.getAllOnlinePlayers() test suite', () => {
[_id2]: JSON.stringify(payload2),
});

const player_ids = await service.getAllOnlinePlayers({
const player_ids = await service.getOnlinePlayers({
filter: { status: [OnlinePlayerStatus.UI] },
});

Expand All @@ -93,7 +93,7 @@ describe('OnlinePlayersService.getAllOnlinePlayers() test suite', () => {
it('Should not return players if there are no players', async () => {
jest.spyOn(redisService, 'getValuesByKeyPattern').mockResolvedValue({});

const player_ids = await service.getAllOnlinePlayers();
const player_ids = await service.getOnlinePlayers();

expect(player_ids).not.toContain(_id1);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import BattleQueueModule from '../../modules/battleQueue.module';
import PlayerModule from '../../../player/modules/player.module';
import PlayerBuilderFactory from '../../../player/data/playerBuilderFactory';
import { BattleQueueService } from '../../../../onlinePlayers/battleQueue/battleQueue.service';
import { OnlinePlayerStatus } from '../../../../onlinePlayers/enum/OnlinePlayerStatus';
import OnlinePlayersBuilderFactory from '../../data/onlinePlayersBuilderFactory';
import { OnlinePlayerBuilder } from '../../data/onlinePlayers/OnlinePlayerBuilder';
import { BattleWaitStatus } from '../../../../onlinePlayers/payload/additionalTypes/BattleWaitStatus';

describe('BattleQueueService.getPlayerQueueNumber() test suite', () => {
let service: BattleQueueService;

const playerBuilder = PlayerBuilderFactory.getBuilder('Player');
const player1 = playerBuilder
.setName('player1')
.setUniqueIdentifier('player1')
.build();
const player2 = playerBuilder
.setName('player2')
.setUniqueIdentifier('player2')
.build();
const player3 = playerBuilder
.setName('player3')
.setUniqueIdentifier('player3')
.build();
const playerModel = PlayerModule.getPlayerModel();

const onlinePlayerBuilder = OnlinePlayersBuilderFactory.getBuilder(
'OnlinePlayer',
) as OnlinePlayerBuilder<BattleWaitStatus>;

beforeEach(async () => {
service = await BattleQueueModule.getBattleQueueService();

const createdPlayer1 = await playerModel.create(player1);
player1._id = createdPlayer1._id.toString();
const createdPlayer2 = await playerModel.create(player2);
player2._id = createdPlayer2._id.toString();
const createdPlayer3 = await playerModel.create(player3);
player3._id = createdPlayer3._id.toString();
});

it('Should return increased by 1 order number for each player', async () => {
const onlinePlayer1 = onlinePlayerBuilder
.setId(player1._id)
.setName(player1.name)
.build();
const onlinePlayer2 = onlinePlayerBuilder
.setId(player2._id)
.setName(player2.name)
.build();
const onlinePlayer3 = onlinePlayerBuilder
.setId(player3._id)
.setName(player3.name)
.build();

const [number1, errors1] =
await service.getPlayerQueueNumber(onlinePlayer1);
const [number2, errors2] =
await service.getPlayerQueueNumber(onlinePlayer2);
const [number3, errors3] =
await service.getPlayerQueueNumber(onlinePlayer3);

expect(errors1).toBeNull();
expect(number1).toBe(0);

expect(errors2).toBeNull();
expect(number2).toBe(1);

expect(errors3).toBeNull();
expect(number3).toBe(2);
});

it('Should return the same number the player has if the player is already in queue', async () => {
const queueNumber = 0;
const onlinePlayer1 = onlinePlayerBuilder
.setId(player1._id)
.setName(player1.name)
.setStatus(OnlinePlayerStatus.BATTLE_WAIT)
.setAdditional({ queueNumber })
.build();

const [number1, errors1] =
await service.getPlayerQueueNumber(onlinePlayer1);
const [number2, errors2] =
await service.getPlayerQueueNumber(onlinePlayer1);
const [number3, errors3] =
await service.getPlayerQueueNumber(onlinePlayer1);

expect(errors1).toBeNull();
expect(number1).toBe(queueNumber);

expect(errors2).toBeNull();
expect(number2).toBe(queueNumber);

expect(errors3).toBeNull();
expect(number3).toBe(queueNumber);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { BattleQueueService } from 'src/onlinePlayers/battleQueue/battleQueue.service';
import BattleQueueModule from '../../modules/battleQueue.module';
import OnlinePlayersBuilderFactory from '../../data/onlinePlayersBuilderFactory';
import { OnlinePlayerBuilder } from '../../data/onlinePlayers/OnlinePlayerBuilder';
import { BattleWaitStatus } from '../../../../onlinePlayers/payload/additionalTypes/BattleWaitStatus';

describe('BattleQueueService.sortPlayersByQueueNumber', () => {
let service: BattleQueueService;
let playerBuilder: OnlinePlayerBuilder<BattleWaitStatus>;

beforeEach(async () => {
service = await BattleQueueModule.getBattleQueueService();
playerBuilder = OnlinePlayersBuilderFactory.getBuilder('OnlinePlayer');
});

it('should sort players in ascending queueNumber order', () => {
const players = [
playerBuilder.setAdditional({ queueNumber: 10 }).build(),
playerBuilder.setAdditional({ queueNumber: 5 }).build(),
playerBuilder.setAdditional({ queueNumber: 7 }).build(),
];

const [sorted, errors] = service.sortPlayersByQueueNumber(players);
const sortedNumbers = sorted.map((p) => p.additional.queueNumber);

expect(errors).toBeNull();
expect(sortedNumbers).toEqual([5, 7, 10]);
});

it('should sort players considering wrap-around queue numbers', () => {
const players = [
playerBuilder.setAdditional({ queueNumber: 1 }).build(),
playerBuilder.setAdditional({ queueNumber: 9999 }).build(),
playerBuilder.setAdditional({ queueNumber: 5 }).build(),
playerBuilder.setAdditional({ queueNumber: 9998 }).build(),
playerBuilder.setAdditional({ queueNumber: 0 }).build(),
];

const [sorted, errors] = service.sortPlayersByQueueNumber(players);
const sortedNumbers = sorted.map((p) => p.additional.queueNumber);

expect(errors).toBeNull();
expect(sortedNumbers).toEqual([9998, 9999, 0, 1, 5]);
});

it('should still sort correctly with gaps and wrap', () => {
const players = [
playerBuilder.setAdditional({ queueNumber: 9998 }).build(),
playerBuilder.setAdditional({ queueNumber: 3 }).build(),
playerBuilder.setAdditional({ queueNumber: 9995 }).build(),
playerBuilder.setAdditional({ queueNumber: 9 }).build(),
playerBuilder.setAdditional({ queueNumber: 1000 }).build(),
];

const [sorted, errors] = service.sortPlayersByQueueNumber(players);
const sortedNumbers = sorted.map((p) => p.additional.queueNumber);

expect(errors).toBeNull();
expect(sortedNumbers).toEqual([9995, 9998, 3, 9, 1000]);
});

it('Should return ServiceError NOT_FOUND if empty array is provided', () => {
const [sorted, errors] = service.sortPlayersByQueueNumber([]);
expect(sorted).toBeNull();
expect(errors).toContainSE_NOT_FOUND();
});

it('Should return ServiceError NOT_FOUND if no players have the order number', () => {
const players1 = [playerBuilder.build(), playerBuilder.build()];
const [sorted, errors] = service.sortPlayersByQueueNumber(players1);
expect(sorted).toBeNull();
expect(errors).toContainSE_NOT_FOUND();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import AddOnlinePlayer from '../../../../onlinePlayers/payload/AddOnlinePlayer';
import { OnlinePlayerStatus } from '../../../../onlinePlayers/enum/OnlinePlayerStatus';

export class AddOnlinePlayerBuilder {
private readonly base: Partial<AddOnlinePlayer> = {
player_id: undefined,
status: OnlinePlayerStatus.UI,
};

build(): AddOnlinePlayer {
return { ...this.base } as AddOnlinePlayer;
}

setPlayerId(player_id: string): this {
this.base.player_id = player_id;
return this;
}

setStatus(status: OnlinePlayerStatus): this {
this.base.status = status;
return this;
}
}
Loading