TGD-58 #4

Merged
webster merged 1 commit from features/TGD-58 into 2024edition 2024-11-24 22:46:29 +04:00
3 changed files with 130 additions and 20 deletions
Showing only changes of commit 13cf1a9c2f - Show all commits

View file

@ -2,4 +2,5 @@ export class FeatureFlagsConsts {
static EnableEndgamePoints = 'EnableEndgamePoints'; static EnableEndgamePoints = 'EnableEndgamePoints';
static DontMarkQuestionsAsCompleted = 'DontMarkQuestionsAsCompleted'; static DontMarkQuestionsAsCompleted = 'DontMarkQuestionsAsCompleted';
static DisableVoice = 'DisableVoice'; static DisableVoice = 'DisableVoice';
static StartVersusIfPlayersAnsweredInSameTime = 'StartVersusIfPlayersAnsweredInSameTime';
} }

View file

@ -8,16 +8,19 @@ import {GuestsService} from "../guests/guests.service";
import {GuestsServiceMock} from "../mocks/guests-service.mock"; import {GuestsServiceMock} from "../mocks/guests-service.mock";
import {SharedService} from "../shared/shared.service"; import {SharedService} from "../shared/shared.service";
import {SharedServiceMock} from "../mocks/shared-service.mock"; 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 {EventbusMock} from "../mocks/eventbus.mock";
import {CommandbusMock} from "../mocks/commandbus.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 {FeatureflagServiceMock} from "../mocks/featureflag-service.mock";
import {IncreasePlayerWinningRateCommand} from "../game/commands/increase-player-winning-rate.command"; import {IncreasePlayerWinningRateCommand} from "../game/commands/increase-player-winning-rate.command";
import {IncreasePlayerScoreCommand} from "../guests/command/increase-player-score.command"; import {IncreasePlayerScoreCommand} from "../guests/command/increase-player-score.command";
import {getRandomInt} from "../helpers/rand-number"; import {getRandomInt} from "../helpers/rand-number";
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 {BeginVersusCommand} from "../game/commands/begin-versus.command"
import spyOn = jest.spyOn;
import clearAllMocks = jest.clearAllMocks;
jest.mock('../../src/helpers/rand-number'); jest.mock('../../src/helpers/rand-number');
@ -25,24 +28,26 @@ describe('QuizService', () => {
let service: QuizService; let service: QuizService;
let cmdBus: CommandBus; let cmdBus: CommandBus;
let guestService: GuestsService; let guestService: GuestsService;
let featureFlagService: FeatureflagService;
beforeEach(async () => { beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
providers: [ providers: [
QuizService, QuizService,
{ provide: getModelToken(Question.name), useValue: Model }, {provide: getModelToken(Question.name), useValue: Model},
{ provide: getModelToken(QuestionStorage.name), useValue: Model}, {provide: getModelToken(QuestionStorage.name), useValue: Model},
{ provide: GuestsService, useValue: GuestsServiceMock }, {provide: GuestsService, useValue: GuestsServiceMock},
{ provide: SharedService, useValue: SharedServiceMock }, {provide: SharedService, useValue: SharedServiceMock},
{ provide: EventBus, useValue: EventbusMock }, {provide: EventBus, useValue: EventbusMock},
{ provide: CommandBus, useValue: CommandbusMock }, {provide: CommandBus, useValue: CommandbusMock},
{ provide: FeatureflagService, useValue: FeatureflagServiceMock } {provide: FeatureflagService, useValue: FeatureflagServiceMock}
], ],
}).compile(); }).compile();
service = await module.resolve<QuizService>(QuizService); service = await module.resolve<QuizService>(QuizService);
cmdBus = await module.resolve<CommandBus>(CommandBus); cmdBus = await module.resolve<CommandBus>(CommandBus);
guestService = await module.resolve<GuestsService>(GuestsService); guestService = await module.resolve<GuestsService>(GuestsService);
featureFlagService = await module.resolve<FeatureflagService>(FeatureflagService);
}); });
it('should be defined', () => { it('should be defined', () => {
@ -50,6 +55,8 @@ describe('QuizService', () => {
}); });
describe('calculateScore()', () => { describe('calculateScore()', () => {
let cmdBusExecSpy: jest.SpyInstance<Promise<unknown>, [command: ICommand], any>;
let getSpy;
const questionDocumentMock = { const questionDocumentMock = {
text: 'test question', text: 'test question',
answered: false, answered: false,
@ -70,16 +77,22 @@ describe('QuizService', () => {
user: 3, user: 3,
time: new Date(), time: new Date(),
valid: true, valid: true,
}, {
user: 4,
time: new Date(),
valid: false,
}], }],
scoreCalculated: false, scoreCalculated: false,
save: jest.fn(), 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 () => { it('should not calculate score if it is already calculated', async () => {
// setup // setup
questionDocumentMock.scoreCalculated = true; questionDocumentMock.scoreCalculated = true;
const getSpy = jest.spyOn(service,'get').mockResolvedValue(questionDocumentMock as any);
const cmdBusExecSpy = jest.spyOn(cmdBus, 'execute').mockResolvedValue(null);
// act // act
await service.calculateScore(); await service.calculateScore();
@ -92,8 +105,6 @@ describe('QuizService', () => {
it('should assign points to winner', async () => { it('should assign points to winner', async () => {
//setup //setup
questionDocumentMock.scoreCalculated = false; questionDocumentMock.scoreCalculated = false;
const getSpy = jest.spyOn(service, 'get').mockResolvedValue(questionDocumentMock as any);
const cmdBusExecSpy = jest.spyOn(cmdBus, 'execute');
// act // act
await service.calculateScore(); await service.calculateScore();
@ -110,8 +121,6 @@ describe('QuizService', () => {
// setup // setup
(getRandomInt as jest.Mock).mockReturnValue(65); (getRandomInt as jest.Mock).mockReturnValue(65);
questionDocumentMock.scoreCalculated = false; 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); const whoShouldGetPenalty = questionDocumentMock.userAnswers.find(x => x.user == 2);
//act //act
@ -127,8 +136,6 @@ describe('QuizService', () => {
jest.clearAllMocks(); jest.clearAllMocks();
(getRandomInt as jest.Mock).mockReturnValue(10); (getRandomInt as jest.Mock).mockReturnValue(10);
questionDocumentMock.scoreCalculated = false; 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); const whoShouldGetPenalty = questionDocumentMock.userAnswers.find(x => x.user == 2);
//act //act
@ -141,16 +148,15 @@ describe('QuizService', () => {
}) })
it('should set score calculated after calculation', async () => { it('should set score calculated after calculation', async () => {
// setup
questionDocumentMock.scoreCalculated = false; questionDocumentMock.scoreCalculated = false;
const saveSpy = jest.spyOn(questionDocumentMock,'save').mockResolvedValue(true); const saveSpy = jest.spyOn(questionDocumentMock,'save').mockResolvedValue(true);
jest.spyOn(service, 'get').mockResolvedValue(questionDocumentMock as any);
// act // act
await service.calculateScore(); await service.calculateScore();
//validate //validate
expect(saveSpy).toHaveBeenCalled(); expect(saveSpy).toHaveBeenCalled();
}) });
it('should add show results in queue', async () => { it('should add show results in queue', async () => {
// setup // setup
@ -165,6 +171,90 @@ describe('QuizService', () => {
// validate // validate
expect(cmdBusExecSpy).toHaveBeenCalledWith(new CreateNewQueueItemCommand(expect.anything(), GameQueueTypes.showresults)); 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', () => {
}) })
}); });
}); });

View file

@ -20,6 +20,7 @@ import {FeatureflagService} from "../featureflag/featureflag.service";
import {FeatureFlagsConsts} from "../Consts/FeatureFlags.consts"; import {FeatureFlagsConsts} from "../Consts/FeatureFlags.consts";
import {QuizEndGameResults} from "./quiz.types"; import {QuizEndGameResults} from "./quiz.types";
import {ClientNotificationType} from "../socket/socket.gateway"; import {ClientNotificationType} from "../socket/socket.gateway";
import {BeginVersusCommand} from "../game/commands/begin-versus.command";
@Injectable({ scope: Scope.TRANSIENT }) @Injectable({ scope: Scope.TRANSIENT })
export class QuizService { export class QuizService {
@ -120,6 +121,14 @@ export class QuizService {
return Promise.resolve(true); 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() { async calculateScore() {
const question = await this.get(); const question = await this.get();
if(question.scoreCalculated) { if(question.scoreCalculated) {
@ -148,6 +157,16 @@ export class QuizService {
totalWinningScore / sortedAnswers.filter((answer) => answer.valid).length)); totalWinningScore / sortedAnswers.filter((answer) => answer.valid).length));
this.commandBus.execute(new IncreasePlayerScoreCommand(answer.user,1)); 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)); await this.commandBus.execute(new IncreasePlayerWinningRateCommand(sortedAnswers[0].user, 15));
this.logger.debug(`Giving 1 point to first`); this.logger.debug(`Giving 1 point to first`);
await this.commandBus.execute(new IncreasePlayerScoreCommand(winner.user,1)); await this.commandBus.execute(new IncreasePlayerScoreCommand(winner.user,1));