From 13cf1a9c2fb72a8f155b0cc9271bf939f0698091 Mon Sep 17 00:00:00 2001 From: Kirill Ivlev Date: Sun, 24 Nov 2024 22:45:59 +0400 Subject: [PATCH] TGD-58 --- src/Consts/FeatureFlags.consts.ts | 1 + src/quiz/quiz.service.spec.ts | 130 +++++++++++++++++++++++++----- src/quiz/quiz.service.ts | 19 +++++ 3 files changed, 130 insertions(+), 20 deletions(-) diff --git a/src/Consts/FeatureFlags.consts.ts b/src/Consts/FeatureFlags.consts.ts index 31b1dda..848ffbe 100644 --- a/src/Consts/FeatureFlags.consts.ts +++ b/src/Consts/FeatureFlags.consts.ts @@ -2,4 +2,5 @@ export class FeatureFlagsConsts { static EnableEndgamePoints = 'EnableEndgamePoints'; static DontMarkQuestionsAsCompleted = 'DontMarkQuestionsAsCompleted'; static DisableVoice = 'DisableVoice'; + static StartVersusIfPlayersAnsweredInSameTime = 'StartVersusIfPlayersAnsweredInSameTime'; } \ No newline at end of file diff --git a/src/quiz/quiz.service.spec.ts b/src/quiz/quiz.service.spec.ts index bf168b7..f60b3b3 100644 --- a/src/quiz/quiz.service.spec.ts +++ b/src/quiz/quiz.service.spec.ts @@ -8,16 +8,19 @@ import {GuestsService} from "../guests/guests.service"; import {GuestsServiceMock} from "../mocks/guests-service.mock"; import {SharedService} from "../shared/shared.service"; import {SharedServiceMock} from "../mocks/shared-service.mock"; -import {CommandBus, EventBus} from "@nestjs/cqrs"; +import {CommandBus, EventBus, ICommand} from "@nestjs/cqrs"; import {EventbusMock} from "../mocks/eventbus.mock"; import {CommandbusMock} from "../mocks/commandbus.mock"; -import {FeatureflagService} from "../featureflag/featureflag.service"; +import {FeatureflagService, IFeatureFlagStatus} from "../featureflag/featureflag.service"; import {FeatureflagServiceMock} from "../mocks/featureflag-service.mock"; import {IncreasePlayerWinningRateCommand} from "../game/commands/increase-player-winning-rate.command"; import {IncreasePlayerScoreCommand} from "../guests/command/increase-player-score.command"; import {getRandomInt} from "../helpers/rand-number"; import {CreateNewQueueItemCommand} from "../game/commands/create-new-queue-item.command"; import {GameQueueTypes} from "../schemas/game-queue.schema"; +import {BeginVersusCommand} from "../game/commands/begin-versus.command" +import spyOn = jest.spyOn; +import clearAllMocks = jest.clearAllMocks; jest.mock('../../src/helpers/rand-number'); @@ -25,24 +28,26 @@ describe('QuizService', () => { let service: QuizService; let cmdBus: CommandBus; let guestService: GuestsService; + let featureFlagService: FeatureflagService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ QuizService, - { provide: getModelToken(Question.name), useValue: Model }, - { provide: getModelToken(QuestionStorage.name), useValue: Model}, - { provide: GuestsService, useValue: GuestsServiceMock }, - { provide: SharedService, useValue: SharedServiceMock }, - { provide: EventBus, useValue: EventbusMock }, - { provide: CommandBus, useValue: CommandbusMock }, - { provide: FeatureflagService, useValue: FeatureflagServiceMock } + {provide: getModelToken(Question.name), useValue: Model}, + {provide: getModelToken(QuestionStorage.name), useValue: Model}, + {provide: GuestsService, useValue: GuestsServiceMock}, + {provide: SharedService, useValue: SharedServiceMock}, + {provide: EventBus, useValue: EventbusMock}, + {provide: CommandBus, useValue: CommandbusMock}, + {provide: FeatureflagService, useValue: FeatureflagServiceMock} ], }).compile(); service = await module.resolve(QuizService); cmdBus = await module.resolve(CommandBus); guestService = await module.resolve(GuestsService); + featureFlagService = await module.resolve(FeatureflagService); }); it('should be defined', () => { @@ -50,6 +55,8 @@ describe('QuizService', () => { }); describe('calculateScore()', () => { + let cmdBusExecSpy: jest.SpyInstance, [command: ICommand], any>; + let getSpy; const questionDocumentMock = { text: 'test question', answered: false, @@ -70,16 +77,22 @@ describe('QuizService', () => { user: 3, time: new Date(), valid: true, + }, { + user: 4, + time: new Date(), + valid: false, }], scoreCalculated: false, save: jest.fn(), }; + beforeEach(() => { + cmdBusExecSpy = jest.spyOn(cmdBus, 'execute').mockResolvedValue(null); + getSpy = jest.spyOn(service,'get').mockResolvedValue(questionDocumentMock as any); + }); it('should not calculate score if it is already calculated', async () => { // setup questionDocumentMock.scoreCalculated = true; - const getSpy = jest.spyOn(service,'get').mockResolvedValue(questionDocumentMock as any); - const cmdBusExecSpy = jest.spyOn(cmdBus, 'execute').mockResolvedValue(null); // act await service.calculateScore(); @@ -92,8 +105,6 @@ describe('QuizService', () => { it('should assign points to winner', async () => { //setup questionDocumentMock.scoreCalculated = false; - const getSpy = jest.spyOn(service, 'get').mockResolvedValue(questionDocumentMock as any); - const cmdBusExecSpy = jest.spyOn(cmdBus, 'execute'); // act await service.calculateScore(); @@ -110,8 +121,6 @@ describe('QuizService', () => { // setup (getRandomInt as jest.Mock).mockReturnValue(65); questionDocumentMock.scoreCalculated = false; - const getSpy = jest.spyOn(service, 'get').mockResolvedValue(questionDocumentMock as any); - const cmdBusExecSpy = jest.spyOn(cmdBus, 'execute'); const whoShouldGetPenalty = questionDocumentMock.userAnswers.find(x => x.user == 2); //act @@ -127,8 +136,6 @@ describe('QuizService', () => { jest.clearAllMocks(); (getRandomInt as jest.Mock).mockReturnValue(10); questionDocumentMock.scoreCalculated = false; - jest.spyOn(service, 'get').mockResolvedValue(questionDocumentMock as any); - const cmdBusExecSpy = jest.spyOn(cmdBus, 'execute'); const whoShouldGetPenalty = questionDocumentMock.userAnswers.find(x => x.user == 2); //act @@ -141,16 +148,15 @@ describe('QuizService', () => { }) it('should set score calculated after calculation', async () => { + // setup questionDocumentMock.scoreCalculated = false; const saveSpy = jest.spyOn(questionDocumentMock,'save').mockResolvedValue(true); - jest.spyOn(service, 'get').mockResolvedValue(questionDocumentMock as any); - // act await service.calculateScore(); //validate expect(saveSpy).toHaveBeenCalled(); - }) + }); it('should add show results in queue', async () => { // setup @@ -165,6 +171,90 @@ describe('QuizService', () => { // validate expect(cmdBusExecSpy).toHaveBeenCalledWith(new CreateNewQueueItemCommand(expect.anything(), GameQueueTypes.showresults)); + }); + + + it('should start versus if user replied in less than 5 seconds if ff enabled', async () => { + // setup + questionDocumentMock.scoreCalculated = false; + const ffstate: IFeatureFlagStatus = { + name: '', + state: true, + } + spyOn(featureFlagService,'getFeatureFlag').mockResolvedValue(ffstate); + questionDocumentMock.userAnswers = [{ + user: 1, + time: new Date(new Date().setSeconds(new Date().getSeconds() - 5)), + valid: true, + }, + { + user: 2, + time: new Date(), + valid: true, + } + ] + getSpy = jest.spyOn(service,'get').mockResolvedValue(questionDocumentMock as any); + + // act + await service.calculateScore(); + + // validate + expect(cmdBusExecSpy).toHaveBeenCalledWith(new BeginVersusCommand(expect.anything(), expect.anything())); + }); + + it('should not start versus if FF is off and gap less than 5', async () => { + // setup + jest.clearAllMocks(); + questionDocumentMock.scoreCalculated = false; + const ffstate: IFeatureFlagStatus = { + name: '', + state: false, + } + spyOn(featureFlagService,'getFeatureFlag').mockResolvedValue(ffstate); + questionDocumentMock.userAnswers = [{ + user: 1, + time: new Date(new Date().setSeconds(new Date().getSeconds() - 3)), + valid: true, + }, + { + user: 2, + time: new Date(), + valid: true, + } + ] + getSpy = jest.spyOn(service,'get').mockResolvedValue(questionDocumentMock as any); + + // act + await service.calculateScore(); + + // validate + expect(cmdBusExecSpy).not.toHaveBeenCalledWith(new BeginVersusCommand(expect.anything(), expect.anything())); + + }); + it('should not start versus if gap more than 5 seconds', async () => { + // setup + questionDocumentMock.scoreCalculated = false; + questionDocumentMock.userAnswers = [{ + user: 1, + time: new Date(new Date().setSeconds(new Date().getSeconds() - 7)), + valid: true, + }, + { + user: 2, + time: new Date(), + valid: true, + } + ] + getSpy = jest.spyOn(service,'get').mockResolvedValue(questionDocumentMock as any); + + // act + await service.calculateScore(); + // validate + expect(cmdBusExecSpy).not.toHaveBeenCalledWith(new BeginVersusCommand(expect.anything(), expect.anything())); + }); + + it('should not start versus if only one player answered correctly', () => { + }) }); }); diff --git a/src/quiz/quiz.service.ts b/src/quiz/quiz.service.ts index 3a06f55..0fca176 100644 --- a/src/quiz/quiz.service.ts +++ b/src/quiz/quiz.service.ts @@ -20,6 +20,7 @@ import {FeatureflagService} from "../featureflag/featureflag.service"; import {FeatureFlagsConsts} from "../Consts/FeatureFlags.consts"; import {QuizEndGameResults} from "./quiz.types"; import {ClientNotificationType} from "../socket/socket.gateway"; +import {BeginVersusCommand} from "../game/commands/begin-versus.command"; @Injectable({ scope: Scope.TRANSIENT }) export class QuizService { @@ -120,6 +121,14 @@ export class QuizService { return Promise.resolve(true); } + private checkIfWeShouldStartVersus(answers: { valid: boolean; time: number; user: number }[]) { + if(answers.length === 0 && answers.length <= 2) { + return false; + } + const diff = Math.abs(new Date(answers[0].time).getTime() - new Date(answers[1].time).getTime()) / 1000; + return diff <= 5; + } + async calculateScore() { const question = await this.get(); if(question.scoreCalculated) { @@ -148,6 +157,16 @@ export class QuizService { totalWinningScore / sortedAnswers.filter((answer) => answer.valid).length)); this.commandBus.execute(new IncreasePlayerScoreCommand(answer.user,1)); }); + const ffState = await this.featureFlagService.getFeatureFlag(FeatureFlagsConsts.StartVersusIfPlayersAnsweredInSameTime) + if(ffState.state) { + if(this.checkIfWeShouldStartVersus(sortedAnswers.filter(x => x.valid))) { + await this.commandBus.execute( + new BeginVersusCommand( + sortedAnswers.filter(x => x.valid)[0].user, + sortedAnswers.filter(x => x.valid)[1].user, + )); + } + } await this.commandBus.execute(new IncreasePlayerWinningRateCommand(sortedAnswers[0].user, 15)); this.logger.debug(`Giving 1 point to first`); await this.commandBus.execute(new IncreasePlayerScoreCommand(winner.user,1)); -- 2.45.2