Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,18 +42,48 @@ 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);
Expand Down
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
@@ -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,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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import OnlinePlayer from '../../../../onlinePlayers/payload/OnlinePlayer';
import { OnlinePlayerStatus } from '../../../../onlinePlayers/enum/OnlinePlayerStatus';

export class OnlinePlayerBuilder<Additional = any> {
private readonly base: Partial<OnlinePlayer<Additional>> = {
_id: undefined,
name: 'player1',
status: OnlinePlayerStatus.UI,
additional: undefined,
};

build(): OnlinePlayer<Additional> {
return { ...this.base } as OnlinePlayer<Additional>;
}

setId(id: string): this {
this.base._id = id;
return this;
}

setName(name: string): this {
this.base.name = name;
return this;
}

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

setAdditional(additional: Additional): this {
this.base.additional = additional;
return this;
}
}
22 changes: 22 additions & 0 deletions src/__tests__/onlinePlayers/data/onlinePlayersBuilderFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { OnlinePlayerBuilder } from './onlinePlayers/OnlinePlayerBuilder';
import { AddOnlinePlayerBuilder } from './onlinePlayers/AddOnlinePlayerBuilder';

type BuilderName = 'OnlinePlayer' | 'AddOnlinePlayer';

type BuilderMap = {
OnlinePlayer: OnlinePlayerBuilder;
AddOnlinePlayer: AddOnlinePlayerBuilder;
};

export default class OnlinePlayersBuilderFactory {
static getBuilder<T extends BuilderName>(builderName: T): BuilderMap[T] {
switch (builderName) {
case 'OnlinePlayer':
return new OnlinePlayerBuilder() as BuilderMap[T];
case 'AddOnlinePlayer':
return new AddOnlinePlayerBuilder() as BuilderMap[T];
default:
throw new Error(`Unknown builder name: ${builderName}`);
}
}
}
11 changes: 11 additions & 0 deletions src/__tests__/onlinePlayers/modules/battleQueue.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { BattleQueueService } from '../../../onlinePlayers/battleQueue/battleQueue.service';
import OnlinePlayersCommonModule from './onlinePlayersCommon.module';

export default class BattleQueueModule {
private constructor() {}

static async getBattleQueueService() {
const module = await OnlinePlayersCommonModule.getModule();
return module.resolve(BattleQueueService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PlayerSchema } from '../../../player/schemas/player.schema';
import { RedisModule } from '../../../common/service/redis/redis.module';
import { RedisServiceInMemory } from '../../common/service/redis/mocks/RedisServiceInMemory';
import { RedisService } from '../../../common/service/redis/redis.service';
import { BattleQueueService } from '../../../onlinePlayers/battleQueue/battleQueue.service';

export default class OnlinePlayersCommonModule {
private static module: TestingModule;
Expand All @@ -25,7 +26,7 @@ export default class OnlinePlayersCommonModule {
RequestHelperModule,
RedisModule,
],
providers: [OnlinePlayersService],
providers: [OnlinePlayersService, BattleQueueService],
})
.overrideProvider(RedisService)
.useClass(RedisServiceInMemory)
Expand Down
Loading