tests & endgame logic (start)

This commit is contained in:
Kirill Ivlev 2024-11-11 12:32:04 +04:00
parent 08fa0563e7
commit ced62ddfba
14 changed files with 132 additions and 30 deletions

18
package-lock.json generated
View file

@ -41,7 +41,7 @@
}, },
"devDependencies": { "devDependencies": {
"@nestjs/schematics": "^10.0.3", "@nestjs/schematics": "^10.0.3",
"@nestjs/testing": "^10.2.8", "@nestjs/testing": "^10.4.7",
"@types/cron": "^2.0.1", "@types/cron": "^2.0.1",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/jest": "^29.5.8", "@types/jest": "^29.5.8",
@ -1905,12 +1905,13 @@
} }
}, },
"node_modules/@nestjs/testing": { "node_modules/@nestjs/testing": {
"version": "10.2.8", "version": "10.4.7",
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.2.8.tgz", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.7.tgz",
"integrity": "sha512-9Kj5IQhM67/nj/MT6Wi2OmWr5YQnCMptwKVFrX1TDaikpY12196v7frk0jVjdT7wms7rV07GZle9I2z0aSjqtQ==", "integrity": "sha512-aS3sQ0v4g8cyHDzW3xJv1+8MiFAkxUNXmnau588IFFI/nBIo/kevLNHNPr85keYekkJ/lwNDW72h8UGg8BYd9w==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"tslib": "2.6.2" "tslib": "2.7.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -1931,6 +1932,13 @@
} }
} }
}, },
"node_modules/@nestjs/testing/node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"dev": true,
"license": "0BSD"
},
"node_modules/@nestjs/websockets": { "node_modules/@nestjs/websockets": {
"version": "10.2.8", "version": "10.2.8",
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.2.8.tgz", "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.2.8.tgz",

View file

@ -53,7 +53,7 @@
}, },
"devDependencies": { "devDependencies": {
"@nestjs/schematics": "^10.0.3", "@nestjs/schematics": "^10.0.3",
"@nestjs/testing": "^10.2.8", "@nestjs/testing": "^10.4.7",
"@types/cron": "^2.0.1", "@types/cron": "^2.0.1",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/jest": "^29.5.8", "@types/jest": "^29.5.8",

View file

@ -3,6 +3,7 @@ import { GiveOutAPrizeCommand } from '../commands/give-out-a-prize.command';
import { GameService } from '../game.service'; import { GameService } from '../game.service';
import { Logger } from '@nestjs/common'; import { Logger } from '@nestjs/common';
import { GameQueueTypes } from '../../schemas/game-queue.schema'; import { GameQueueTypes } from '../../schemas/game-queue.schema';
import {GuestsService} from "../../guests/guests.service";
@CommandHandler(GiveOutAPrizeCommand) @CommandHandler(GiveOutAPrizeCommand)
export class GameGiveOutAPrizeCommandHandler export class GameGiveOutAPrizeCommandHandler
@ -10,11 +11,12 @@ export class GameGiveOutAPrizeCommandHandler
private readonly logger = new Logger(GameGiveOutAPrizeCommandHandler.name); private readonly logger = new Logger(GameGiveOutAPrizeCommandHandler.name);
constructor(private gameService: GameService) { constructor(private gameService: GameService, private guestService: GuestsService) {
} }
async execute(command: GiveOutAPrizeCommand): Promise<any> { async execute(command: GiveOutAPrizeCommand): Promise<any> {
this.logger.verbose(`Player winning a prize ${command.telegramId}`); this.logger.verbose(`Player winning a prize ${command.telegramId}`);
await this.guestService.incrementPrizeCount(command.telegramId);
return this.gameService.addTaskToGameQueue( return this.gameService.addTaskToGameQueue(
command.telegramId, command.telegramId,
GameQueueTypes.giveOutAPrize, GameQueueTypes.giveOutAPrize,

View file

@ -24,8 +24,6 @@ import {MqtMessageModel} from "../messaging/models/mqt-message.model";
import {ConfigService} from "@nestjs/config"; import {ConfigService} from "@nestjs/config";
import {StringHelper} from "../helpers/stringhelper"; import {StringHelper} from "../helpers/stringhelper";
import {DebuffsConsts} from "../game/entities/debuffs.consts"; import {DebuffsConsts} from "../game/entities/debuffs.consts";
import {CreateNewQueueItemCommand} from "../game/commands/create-new-queue-item.command";
import {GameQueueTypes} from "../schemas/game-queue.schema";
import {VoiceService} from "../voice/voice.service"; import {VoiceService} from "../voice/voice.service";
import {screpaDictManyInvalidAnswersDict} from "../voice/dicts/screpa-dict-many-invalid-answers.dict"; import {screpaDictManyInvalidAnswersDict} from "../voice/dicts/screpa-dict-many-invalid-answers.dict";
import {GuestPropertiesConsts} from "../schemas/properties.consts"; import {GuestPropertiesConsts} from "../schemas/properties.consts";
@ -66,6 +64,10 @@ export class GuestsService {
return this.guestModel.find().exec(); return this.guestModel.find().exec();
} }
getModel() {
return this.guestModel;
}
async filter(properties: object) { async filter(properties: object) {
return this.guestModel.find(properties).exec(); return this.guestModel.find(properties).exec();
} }
@ -340,4 +342,16 @@ export class GuestsService {
guest.invalidAnswersInRow = 0; guest.invalidAnswersInRow = 0;
await guest.save(); await guest.save();
} }
async incrementPrizeCount(telegramId: number) {
const guest = await this.findById(telegramId);
guest.rewardsReceived += 1;
await guest.save();
}
async updatePenaltiesCount(user: number) {
const guest = await this.findById(user);
guest.penaltiesReceived += 1;
await guest.save();
}
} }

View file

@ -0,0 +1,3 @@
export const GiftServiceMock = {
getRemainingPrizeCount: () => jest.fn(),
}

View file

@ -0,0 +1,3 @@
export const QuizServiceMock = {
getRemainQuestionCount: () =>jest.fn(),
}

View file

@ -0,0 +1,3 @@
export const SharedServiceMock = {
sendSocketNotificationToAllClients: jest.fn(),
}

View file

@ -0,0 +1,3 @@
export const StateServiceMock = {
setState: jest.fn(),
}

View file

@ -50,4 +50,10 @@ export class QuizController {
async dealPrize() { async dealPrize() {
return this.quizService.dealPrize(); return this.quizService.dealPrize();
} }
@Get('endgame-extrapoints')
async endgameExtrapoints()
{
return await this.quizService.addEndgamePoints();
}
} }

View file

@ -10,12 +10,11 @@ import {ValidAnswerReceivedEvent} from '../game/events/valid-answer.recieved';
import {QuestionStorage, QuestionStorageDocument,} from '../schemas/question-storage.schema'; import {QuestionStorage, QuestionStorageDocument,} from '../schemas/question-storage.schema';
import {WrongAnswerReceivedEvent} from '../game/events/wrong-answer-received.event'; import {WrongAnswerReceivedEvent} from '../game/events/wrong-answer-received.event';
import {ProceedGameQueueCommand} from '../game/commands/proceed-game-queue.command'; import {ProceedGameQueueCommand} from '../game/commands/proceed-game-queue.command';
import {getRandomInt} from 'src/helpers/rand-number'; import {getRandomInt} from '../helpers/rand-number';
import {Messages} from "../messaging/tg.text"; import {Messages} from "../messaging/tg.text";
import {CreateNewQueueItemCommand} from "../game/commands/create-new-queue-item.command"; import {CreateNewQueueItemCommand} from "../game/commands/create-new-queue-item.command";
import {GameQueueTypes} from "../schemas/game-queue.schema"; import {GameQueueTypes} from "../schemas/game-queue.schema";
import {IncreasePlayerWinningRateCommand} from "../game/commands/increase-player-winning-rate.command"; import {IncreasePlayerWinningRateCommand} from "../game/commands/increase-player-winning-rate.command";
import {SocketEvents} from "../shared/events.consts";
import {IncreasePlayerScoreCommand} from "../guests/command/increase-player-score.command"; import {IncreasePlayerScoreCommand} from "../guests/command/increase-player-score.command";
@Injectable({ scope: Scope.TRANSIENT }) @Injectable({ scope: Scope.TRANSIENT })
@ -156,10 +155,36 @@ export class QuizService {
return; return;
} }
targetUser = lastInvalidAnswer.user; targetUser = lastInvalidAnswer.user;
await this.guestService.updatePenaltiesCount(lastInvalidAnswer.user);
await this.commandBus.execute(new CreateNewQueueItemCommand(lastInvalidAnswer.user, GameQueueTypes.penalty)); await this.commandBus.execute(new CreateNewQueueItemCommand(lastInvalidAnswer.user, GameQueueTypes.penalty));
} }
await this.commandBus.execute(new CreateNewQueueItemCommand(targetUser, GameQueueTypes.showresults)); await this.commandBus.execute(new CreateNewQueueItemCommand(targetUser, GameQueueTypes.showresults));
}
public async addEndgamePoints() {
const maxInvalidAnswersPromise = this.guestService.getModel().find({}).sort({ ['invalidAnswers']: 'desc'}).exec();
const maxRewardsPromise = this.guestService.getModel().find({}).sort({['rewardsReceived']: "desc"}).exec();
const maxPenaltiesPromise = this.guestService.getModel().find({}).sort({['penaltiesReceived']: 'desc'}).exec();
//const { maxRewards, maxInvalidAnswers } = Promise.all([maxRewardsPromise, maxInvalidAnswersPromise]);
const [maxRewards, maxInvalidAnswers, maxPenaltiesReceived] = await Promise.all([maxRewardsPromise, maxInvalidAnswersPromise, maxPenaltiesPromise]);
return {
maxInvalidAnswers: {
id: maxInvalidAnswers[0].id,
count: maxInvalidAnswers[0].invalidAnswers,
name: maxInvalidAnswers[0].name,
},
maxRewards: {
id: maxRewards[0].id,
count: maxRewards[0].rewardsReceived,
name: maxRewards[0].name,
},
maxPenalties: {
id: maxPenaltiesReceived[0].id,
count: maxPenaltiesReceived[0].penaltiesReceived,
name: maxPenaltiesReceived[0].name,
}
}
} }
private async getNextQuestion() { private async getNextQuestion() {

View file

@ -1,18 +1,55 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { SchedulerService } from './scheduler.service'; import { SchedulerService } from './scheduler.service';
import {GiftsService} from "../gifts/gifts.service";
import {GiftServiceMock} from "../mocks/gift-service.mock";
import {StateService} from "../state/state.service";
import {StateServiceMock} from "../mocks/state-service.mock";
import {QuizService} from "../quiz/quiz.service";
import {QuizServiceMock} from "../mocks/quiz-service.mock";
import {SharedService} from "../shared/shared.service";
import {SharedServiceMock} from "../mocks/shared-service.mock";
describe('SchedulerService', () => { describe('SchedulerService', () => {
let service: SchedulerService; let service: SchedulerService;
let giftService: GiftsService;
let sharedService: SharedService;
let stateService: StateService;
beforeEach(async () => { beforeEach(async () => {
jest.clearAllMocks();
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
providers: [SchedulerService], providers: [SchedulerService,
{ provide: GiftsService, useValue: GiftServiceMock },
{ provide: StateService, useValue: StateServiceMock },
{ provide: QuizService, useValue: QuizServiceMock },
{ provide: SharedService, useValue: SharedServiceMock },
],
}).compile(); }).compile();
service = module.get<SchedulerService>(SchedulerService); service = module.get<SchedulerService>(SchedulerService);
giftService = module.get<GiftsService>(GiftsService);
sharedService = module.get<SharedService>(SharedService);
stateService = module.get<StateService>(StateService);
}); });
it('should be defined', () => { it('should be defined', () => {
expect(service).toBeDefined(); expect(service).toBeDefined();
}); });
it('should finish game if prizes count is 0', async () => {
const getRemainingPrizeCountFn = jest.spyOn(giftService, 'getRemainingPrizeCount').mockImplementation(() => Promise.resolve(0));
const notificationFn = jest.spyOn(sharedService,'sendSocketNotificationToAllClients').mockImplementation();
const setStateFn = jest.spyOn(stateService,'setState').mockImplementation((name,newstate) => Promise.resolve({ state: name, value: newstate}));
await service.gameStatus();
expect(getRemainingPrizeCountFn).toHaveBeenCalled();
expect(notificationFn).toHaveBeenCalled();
expect(notificationFn).toHaveBeenCalledWith('state_changed', expect.objectContaining({ state: 'main', value: 'finish'}));
});
it('should not finish game if prizes count above 0', async () => {
const getRemainingPrizeCountFn = jest.spyOn(giftService, 'getRemainingPrizeCount').mockImplementation(() => Promise.resolve(5));
const notificationFn = jest.spyOn(sharedService,'sendSocketNotificationToAllClients').mockImplementation()
await service.gameStatus();
expect(notificationFn).not.toHaveBeenCalled();
});
}); });

View file

@ -1,14 +1,10 @@
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule'; import { Cron } from '@nestjs/schedule';
import { StateService } from '../state/state.service'; import { StateService } from '../state/state.service';
import {CommandBus, QueryBus} from '@nestjs/cqrs'; import { QuizService } from '../quiz/quiz.service';
import { GiftsService } from 'src/gifts/gifts.service'; import { GiftsService } from '../gifts/gifts.service';
import { QuizService } from 'src/quiz/quiz.service'; import { SharedService } from '../shared/shared.service';
import { SharedService } from 'src/shared/shared.service';
import {GetGuestPropertyQuery} from "../guests/command/get-guest-property.handler";
import {GuestPropertiesConsts} from "../schemas/properties.consts";
import {GetGuestQuery} from "../guests/queries/getguest.query";
import {StringHelper} from "../helpers/stringhelper";
@Injectable() @Injectable()
export class SchedulerService { export class SchedulerService {
private readonly logger = new Logger(SchedulerService.name); private readonly logger = new Logger(SchedulerService.name);
@ -17,8 +13,6 @@ export class SchedulerService {
constructor( constructor(
private stateService: StateService, private stateService: StateService,
private cmdBus: CommandBus,
private queryBus: QueryBus,
private giftsService: GiftsService, private giftsService: GiftsService,
private quizService: QuizService, private quizService: QuizService,
private sharedService: SharedService, private sharedService: SharedService,
@ -38,6 +32,7 @@ export class SchedulerService {
const giftsLeft = await this.giftsService.getRemainingPrizeCount(); const giftsLeft = await this.giftsService.getRemainingPrizeCount();
if (giftsLeft === 0) { if (giftsLeft === 0) {
const state = await this.stateService.setState('main', 'finish'); const state = await this.stateService.setState('main', 'finish');
console.log(this.state);
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.sendSocketNotificationToAllClients(
'state_changed', 'state_changed',
state, state,

View file

@ -25,8 +25,6 @@ export class Guest {
@Prop({ default: 10 }) @Prop({ default: 10 })
prizeChance: number; prizeChance: number;
@Prop({ default: 0 }) @Prop({ default: 0 })
prizesCount: number;
@Prop({ default: 0 })
validAnswers: number; validAnswers: number;
@Prop({ default: 0 }) @Prop({ default: 0 })
invalidAnswers: number; invalidAnswers: number;
@ -34,6 +32,8 @@ export class Guest {
invalidAnswersInRow: number; invalidAnswersInRow: number;
@Prop({ default:0 }) @Prop({ default:0 })
rewardsReceived: number; rewardsReceived: number;
@Prop({ default: 0})
penaltiesReceived: number;
@Prop({ type: Map }) @Prop({ type: Map })
properties: Record<string, string>; properties: Record<string, string>;
} }

View file

@ -37,6 +37,9 @@ export class StateService {
const stateEntity = await this.getState(name); const stateEntity = await this.getState(name);
stateEntity.value = newValue; stateEntity.value = newValue;
await stateEntity.save(); await stateEntity.save();
return stateEntity; return {
state: stateEntity.state,
value: stateEntity.value,
}
} }
} }