diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 82bc05403237ee17dff39e1e1d77aa625b7400dc..f096dc0ec77bb3b03467ff0e4b8b67d4f5519406 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,7 +32,8 @@ npm_test: - nodejs script: - npm install - - node_modules/mocha/bin/mocha --opts src/tests/mocha.opts + - export NODE_ENV='test' + - node_modules/nyc/bin/nyc.js --reporter=text node_modules/mocha/bin/mocha --opts src/tests/mocha.opts build: stage: build diff --git a/package.json b/package.json index 94dcf6668bb1572d61a3d2ee7da4dcdd2f8dafa9..23623307e5a45c2ce74b680dad836dcf7bbbbbe1 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "description": "Version 2 of arsnova.click (Backend)", "scripts": { "clean": "rm -rf ./dist/*", - "test": "mocha --opts src/tests/mocha.opts", + "test": "node_modules/nyc/bin/nyc.js --reporter=text mocha --opts src/tests/mocha.opts", "prebuild:DEV": "npm run clean", "prebuild:PROD": "npm run clean", "build:DEV": "tsc && cp -r assets dist/", @@ -76,12 +76,30 @@ "chai": "^4.2.0", "chai-http": "^4.3.0", "mocha": "^6.2.2", + "mocha-prepare": "^0.1.0", "mocha-typescript": "^1.1.17", + "mongo-unit": "^1.4.5", "nyc": "^14.1.1", "sinon": "latest", "ts-loader": "^6.2.1", "ts-node": "^8.4.1", "tslint": "^5.20.1", "typescript": "^3.7.2" + }, + "nyc": { + "extension": [ + ".ts", + ".tsx" + ], + "exclude": [ + "**/*.d.ts", + "jobs/**", + "src/tests/**", + "src/interfaces/**" + ], + "reporter": [ + "text" + ], + "all": true } } diff --git a/src/db/quiz/QuizDAO.ts b/src/db/quiz/QuizDAO.ts index fa24580d09db69baf3bd697149cc44fe681f093d..f87c06f337c43d48bb2dd82e3e786e5f18bda666 100644 --- a/src/db/quiz/QuizDAO.ts +++ b/src/db/quiz/QuizDAO.ts @@ -170,7 +170,7 @@ class QuizDAO extends AbstractDAO { public isActiveQuiz(quizName: string): Promise { return QuizModel.exists({ - name: this.buildQuiznameQuery(name), + name: this.buildQuiznameQuery(quizName), state: { $in: [QuizState.Active, QuizState.Running, QuizState.Finished] }, }); } diff --git a/src/export/ExcelWorksheet.ts b/src/export/ExcelWorksheet.ts index d3fef43d7f7e061daf92f8b0932283c0dfd0c618..b3dfe29c0138b458d53d23601f30f234cf0d41c5 100644 --- a/src/export/ExcelWorksheet.ts +++ b/src/export/ExcelWorksheet.ts @@ -1,3 +1,4 @@ +import { EventEmitter } from 'events'; import * as xlsx from 'excel4node'; import * as MessageFormat from 'messageformat'; import { Document } from 'mongoose'; @@ -7,7 +8,6 @@ import { IQuizBase } from '../interfaces/quizzes/IQuizEntity'; import { Leaderboard } from '../lib/leaderboard/leaderboard'; import { MemberModelItem } from '../models/member/MemberModel'; import { excelDefaultWorksheetOptions } from './lib/excel_default_options'; - import { ExcelTheme } from './lib/excel_default_styles'; export abstract class ExcelWorksheet { @@ -27,12 +27,6 @@ export abstract class ExcelWorksheet { return this._mf; } - protected _leaderBoardData: Array; - - get leaderBoardData(): Array { - return this._leaderBoardData; - } - protected _ws: xlsx.Worksheet; get ws(): xlsx.Worksheet { @@ -52,6 +46,7 @@ export abstract class ExcelWorksheet { private readonly _createdAt: string; private readonly _quiz: IQuizBase; private _columnsToFormat: number; + protected readonly loaded = new EventEmitter(); protected constructor({ theme, translation, quiz, mf, questionIndex }) { this._theme = theme; @@ -88,13 +83,13 @@ export abstract class ExcelWorksheet { if (this._responsesWithConfidenceValue.length > 0) { this._columnsToFormat++; } + + this.loaded.emit('load'); }); if (this._quiz.sessionConfig.nicks.restrictToCasLogin) { this._columnsToFormat += 2; } - - this.getLeaderboardData(questionIndex).then(data => this._leaderBoardData = data); } protected generateCreatedAtString(): string { @@ -104,7 +99,7 @@ export abstract class ExcelWorksheet { return `${dateYMD} ${this._mf('export.exported_at')} ${dateHM} ${this._mf('export.exported_at_time')}`; } - protected async getLeaderboardData(questionIndex: number): Promise> { + protected async getLeaderboardData(): Promise> { const leaderBoard = new Leaderboard(); const { correctResponses } = await leaderBoard.buildLeaderboard(this.quiz); return leaderBoard.sortBy(correctResponses, 'score'); diff --git a/src/export/FreeTextExcelWorksheet.ts b/src/export/FreeTextExcelWorksheet.ts index 6681126d3eaaee193b486dbf7a4f3a3620c13054..03d9314de14d8e270b92ec6cb296b63959bd612c 100644 --- a/src/export/FreeTextExcelWorksheet.ts +++ b/src/export/FreeTextExcelWorksheet.ts @@ -1,6 +1,7 @@ import MemberDAO from '../db/MemberDAO'; import { IExcelWorksheet } from '../interfaces/iExcel'; import { IQuestionFreetext } from '../interfaces/questions/IQuestionFreetext'; +import { asyncForEach } from '../lib/async-for-each'; import { MemberModelItem } from '../models/member/MemberModel'; import { ExcelWorksheet } from './ExcelWorksheet'; @@ -28,8 +29,10 @@ export class FreeTextExcelWorksheet extends ExcelWorksheet implements IExcelWork }); })); - this.formatSheet(); - this.addSheetData(); + this.loaded.on('load', () => { + this.formatSheet(); + this.addSheetData(); + }); } public async formatSheet(): Promise { @@ -104,8 +107,8 @@ export class FreeTextExcelWorksheet extends ExcelWorksheet implements IExcelWork }); this.ws.cell(11, 1, attendeeEntryRows + 10, columnsToFormat, !hasEntries).style(attendeeEntryRowStyle); - this.allResponses.forEach((responseItem, indexInList) => { - const leaderboardItem = this.leaderBoardData.filter(lbItem => lbItem.name === responseItem.name)[0]; + await asyncForEach(this.allResponses, async (responseItem, indexInList) => { + const leaderboardItem = (await this.getLeaderboardData()).filter(lbItem => lbItem.name === responseItem.name)[0]; let nextColumnIndex = 2; const targetRow = indexInList + 11; if (this._isCasRequired) { @@ -158,7 +161,7 @@ export class FreeTextExcelWorksheet extends ExcelWorksheet implements IExcelWork ${this.mf(answerOption.configTrimWhitespaces ? 'global.yes' : 'global.no')}`); this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':'); - const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions) + const correctResponsesPercentage: number = (await this.getLeaderboardData()).map(leaderboard => leaderboard.correctQuestions) .filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length / (await MemberDAO.getMembersOfQuiz(this.quiz.name)).length * 100; this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage))); diff --git a/src/export/MultipleChoiceExcelWorksheet.ts b/src/export/MultipleChoiceExcelWorksheet.ts index cfec622766565cb849987fdeb87da69cbdabe806..88571a7e3f53b6a88036ef4640e7e04ec47bba25 100644 --- a/src/export/MultipleChoiceExcelWorksheet.ts +++ b/src/export/MultipleChoiceExcelWorksheet.ts @@ -24,8 +24,11 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc this._ws = wb.addWorksheet(`${mf('export.question')} ${questionIndex + 1}`, this._options); this._questionIndex = questionIndex; this._question = this.quiz.questionList[questionIndex] as IQuestionChoice; - this.formatSheet(); - this.addSheetData(); + + this.loaded.on('load', () => { + this.formatSheet(); + this.addSheetData(); + }); } public async formatSheet(): Promise { @@ -152,7 +155,7 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc this.ws.cell(6, 1).string(this.mf('export.number_of_answers') + ':'); this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':'); - const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions) + const correctResponsesPercentage: number = (await this.getLeaderboardData()).map(leaderboard => leaderboard.correctQuestions) .filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length / (await MemberDAO.getMembersOfQuiz(this.quiz.name)).length * 100; this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage))); @@ -171,7 +174,7 @@ export class MultipleChoiceExcelWorksheet extends ExcelWorksheet implements IExc for (let j = 0; j < answerList.length; j++) { this.ws.cell(2, (j + 2)).string(this.mf('export.answer') + ' ' + (j + 1)); this.ws.cell(4, (j + 2)).string(answerList[j].answerText); - this.ws.cell(6, (j + 2)).number(calculateNumberOfAnswers(this.quiz, this._questionIndex, j)); + this.ws.cell(6, (j + 2)).number(await calculateNumberOfAnswers(this.quiz, this._questionIndex, j)); } let nextColumnIndex = 1; diff --git a/src/export/RangedExcelWorksheet.ts b/src/export/RangedExcelWorksheet.ts index 59aa173d5999868dce02176e21dd8c39ea2a7f14..d0d752125e6afb7bbfa954ad1995d845186fd99a 100644 --- a/src/export/RangedExcelWorksheet.ts +++ b/src/export/RangedExcelWorksheet.ts @@ -22,8 +22,11 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh this._ws = wb.addWorksheet(`${mf('export.question')} ${questionIndex + 1}`, this._options); this._questionIndex = questionIndex; this._question = this.quiz.questionList[questionIndex] as IQuestionRanged; - this.formatSheet(); - this.addSheetData(); + + this.loaded.on('load', () => { + this.formatSheet(); + this.addSheetData(); + }); } public async formatSheet(): Promise { @@ -138,8 +141,10 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh lastColumn: minColums, }); - const hasEntries = this.leaderBoardData.length > 0; - const attendeeEntryRows = hasEntries ? (this.leaderBoardData.length) : 1; + const leaderBoardData = await this.getLeaderboardData(); + + const hasEntries = leaderBoardData.length > 0; + const attendeeEntryRows = hasEntries ? (leaderBoardData.length) : 1; const attendeeEntryRowStyle = hasEntries ? defaultStyles.attendeeEntryRowStyle : Object.assign({}, defaultStyles.attendeeEntryRowStyle, { alignment: { horizontal: 'center', @@ -147,7 +152,7 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh }); this.ws.cell(11, 1, attendeeEntryRows + 10, columnsToFormat, !hasEntries).style(attendeeEntryRowStyle); - await asyncForEach(this.leaderBoardData, async (leaderboardItem, indexInList) => { + await asyncForEach(leaderBoardData, async (leaderboardItem, indexInList) => { let nextColumnIndex = 2; const targetRow = indexInList + 11; if (this._isCasRequired) { @@ -190,6 +195,7 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh } public async addSheetData(): Promise { + const leaderBoardData = await this.getLeaderboardData(); const castedQuestion = this._question as IQuestionRanged; const numberOfInputValuesPerGroup = await calculateNumberOfRangedAnswers(this.quiz, this._questionIndex, castedQuestion.rangeMin, castedQuestion.correctValue, castedQuestion.rangeMax); @@ -212,7 +218,7 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh this.ws.cell(6, 4).number(numberOfInputValuesPerGroup.maxRange); this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':'); - const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions) + const correctResponsesPercentage: number = leaderBoardData.map(leaderboard => leaderboard.correctQuestions) .filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length / (await MemberDAO.getMembersOfQuiz(this.quiz.name)).length * 100; this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage))); @@ -239,7 +245,7 @@ export class RangedExcelWorksheet extends ExcelWorksheet implements IExcelWorksh this.ws.cell(10, nextColumnIndex++).string(this.mf('export.time')); let nextStartRow = 10; - await asyncForEach(this.leaderBoardData, async leaderboardItem => { + await asyncForEach(leaderBoardData, async leaderboardItem => { const responseItem = (await MemberDAO.getMembersOfQuiz(this.quiz.name)).filter(nickitem => { return nickitem.name === leaderboardItem.name; })[0].responses[this._questionIndex]; diff --git a/src/export/SingleChoiceExcelWorksheet.ts b/src/export/SingleChoiceExcelWorksheet.ts index 831e8e7823b206835c6729ee785697c58a8acf6d..475eb798c91925025e3151db52c32b6db498118c 100644 --- a/src/export/SingleChoiceExcelWorksheet.ts +++ b/src/export/SingleChoiceExcelWorksheet.ts @@ -24,8 +24,11 @@ export class SingleChoiceExcelWorksheet extends ExcelWorksheet implements IExcel this._ws = wb.addWorksheet(`${mf('export.question')} ${questionIndex + 1}`, this._options); this._questionIndex = questionIndex; this._question = this.quiz.questionList[questionIndex] as IQuestionChoice; - this.formatSheet(); - this.addSheetData(); + + this.loaded.on('load', () => { + this.formatSheet(); + this.addSheetData(); + }); } public async formatSheet(): Promise { @@ -164,12 +167,12 @@ export class SingleChoiceExcelWorksheet extends ExcelWorksheet implements IExcel for (let j = 0; j < answerList.length; j++) { this.ws.cell(2, (j + 2)).string(this.mf('export.answer') + ' ' + (j + 1)); this.ws.cell(4, (j + 2)).string(answerList[j].answerText); - this.ws.cell(6, (j + 2)).number(calculateNumberOfAnswers(this.quiz, this._questionIndex, j)); + this.ws.cell(6, (j + 2)).number(await calculateNumberOfAnswers(this.quiz, this._questionIndex, j)); } this.ws.cell(6, 1).string(this.mf('export.number_of_answers') + ':'); this.ws.cell(7, 1).string(this.mf('export.percent_correct') + ':'); - const correctResponsesPercentage: number = this.leaderBoardData.map(leaderboard => leaderboard.correctQuestions) + const correctResponsesPercentage: number = (await this.getLeaderboardData()).map(leaderboard => leaderboard.correctQuestions) .filter(correctQuestions => correctQuestions.includes(this._questionIndex)).length / (await MemberDAO.getMembersOfQuiz(this.quiz.name)).length * 100; this.ws.cell(7, 2).number((isNaN(correctResponsesPercentage) ? 0 : Math.round(correctResponsesPercentage))); diff --git a/src/export/SummaryExcelWorksheet.ts b/src/export/SummaryExcelWorksheet.ts index c57a59f3a3c099d21cd51f99737d23bd4ce40b14..9a1a6fff4ff05abcecff9aa16830f47dcbc53413 100644 --- a/src/export/SummaryExcelWorksheet.ts +++ b/src/export/SummaryExcelWorksheet.ts @@ -16,17 +16,6 @@ declare global { setWidth(width: number): void; } - - interface IWorksheet { - cell: Function; - addImage: Function; - - row(index: number): IRow; - } - - interface IWorkbook { - addWorksheet: Function; - } } } @@ -42,12 +31,14 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks questionIndex: null, }); this._ws = wb.addWorksheet(mf('export.summary'), this._options); - this.formatSheet(); - this.addSheetData(); + Promise.all([ + this.formatSheet(), this.addSheetData(), + ]); } - public formatSheet(): void { + public async formatSheet(): Promise { const defaultStyles = this._theme.getStyles(); + const leaderBoardData = await this.getLeaderboardData(); this.ws.row(1).setHeight(20); this.ws.column(1).setWidth(30); @@ -112,7 +103,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks }); let dataWithoutCompleteCorrectQuestions = 0; - this.leaderBoardData.forEach((leaderboardItem, indexInList) => { + await asyncForEach(leaderBoardData, (leaderboardItem, indexInList) => { let hasNotAllQuestionsCorrect = false; this.quiz.questionList.forEach((item, index) => { if (![ @@ -148,15 +139,14 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks }); }); - if (dataWithoutCompleteCorrectQuestions === this.leaderBoardData.length) { + if (dataWithoutCompleteCorrectQuestions === leaderBoardData.length) { this.ws.cell(++currentRowIndex, 1, currentRowIndex, this.columnsToFormat, true).style(Object.assign({}, defaultStyles.attendeeEntryRowStyle, { alignment: { horizontal: 'center', }, })); } else { - this.ws.cell(currentRowIndex, 1, (this.leaderBoardData.length + currentRowIndex - 1 - dataWithoutCompleteCorrectQuestions), - this.columnsToFormat) + this.ws.cell(currentRowIndex, 1, (leaderBoardData.length + currentRowIndex - 1 - dataWithoutCompleteCorrectQuestions), this.columnsToFormat) .style(defaultStyles.attendeeEntryRowStyle); } currentRowIndex += 6; @@ -172,10 +162,10 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks }); currentRowIndex++; - this.ws.cell(currentRowIndex, 1, (this.leaderBoardData.length + (currentRowIndex - 1)), this.columnsToFormat) + this.ws.cell(currentRowIndex, 1, (leaderBoardData.length + (currentRowIndex - 1)), this.columnsToFormat) .style(defaultStyles.attendeeEntryRowStyle); - this.leaderBoardData.forEach((leaderboardItem, indexInList) => { + leaderBoardData.forEach((leaderboardItem, indexInList) => { let nextColumnIndex = 3; const targetRow = indexInList + currentRowIndex; if (this.quiz.sessionConfig.confidenceSliderEnabled) { @@ -201,6 +191,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks } public async addSheetData(): Promise { + const leaderBoardData = await this.getLeaderboardData(); let currentRowIndex = 1; const numberOfResponses = (await MemberDAO.getMembersOfQuiz(this.quiz.name)).filter(nickname => { return nickname.responses.filter(response => { @@ -232,7 +223,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks currentRowIndex++; this.ws.cell(currentRowIndex, 1).string(`${this.mf('export.average_correct_answered_questions')}:`); - this.ws.cell(currentRowIndex, 3).number((this.leaderBoardData.map((x) => { + this.ws.cell(currentRowIndex, 3).number((leaderBoardData.map((x) => { return x.correctQuestions.length; }).reduce((a, b) => { return a + b; @@ -241,7 +232,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks if (this.quiz.sessionConfig.confidenceSliderEnabled) { this.ws.cell(currentRowIndex, 1).string(`${this.mf('export.average_confidence')}:`); - const averageConfidencePercentage = (this.leaderBoardData.filter((x) => { + const averageConfidencePercentage = (leaderBoardData.filter((x) => { return x.confidenceValue > -1; }).map((x) => { return x.confidenceValue; @@ -253,7 +244,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks } this.ws.cell(currentRowIndex, 1).string(`${this.mf('export.average_response_time')}:`); - this.ws.cell(currentRowIndex, 3).number((Math.round((this.leaderBoardData.map((x) => { + this.ws.cell(currentRowIndex, 3).number((Math.round((leaderBoardData.map((x) => { return x.responseTime; }).reduce((a, b) => { return a + b; @@ -280,7 +271,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks currentRowIndex++; let nextStartRow = currentRowIndex + 5; - await asyncForEach(this.leaderBoardData, async (leaderboardItem, indexInList) => { + await asyncForEach(leaderBoardData, async (leaderboardItem, indexInList) => { if (this.quiz.questionList.some((item, index) => ![QuestionType.SurveyQuestion, QuestionType.ABCDSingleChoiceQuestion].includes(item.TYPE) && leaderboardItem.correctQuestions.indexOf((index)) === -1)) { return; @@ -302,7 +293,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks } this.ws.cell(targetRow, nextColumnIndex++).number(Math.round(leaderboardItem.responseTime)); - this.ws.cell(targetRow, nextColumnIndex++).number(Math.round((leaderboardItem.responseTime / this.leaderBoardData.length))); + this.ws.cell(targetRow, nextColumnIndex++).number(Math.round((leaderboardItem.responseTime / leaderBoardData.length))); }); if (nextStartRow === currentRowIndex + 5) { @@ -341,7 +332,7 @@ export class SummaryExcelWorksheet extends ExcelWorksheet implements IExcelWorks this.ws.cell(targetRow, nextColumnIndex++).string(profile.username[0]); this.ws.cell(targetRow, nextColumnIndex++).string(profile.mail[0]); } - const leaderboardItem = this._leaderBoardData.find((item) => item.name === responseItem.name); + const leaderboardItem = leaderBoardData.find((item) => item.name === responseItem.name); if (leaderboardItem) { if (leaderboardItem.correctQuestions.length > 0) { const correctQuestionNumbers = leaderboardItem.correctQuestions.map((item) => item + 1); diff --git a/src/export/SurveyExcelWorksheet.ts b/src/export/SurveyExcelWorksheet.ts index 05d004969996d3344c76e56431a045eb331ec697..abf2a81a4350c66673538cf03117b6b59c36de2a 100644 --- a/src/export/SurveyExcelWorksheet.ts +++ b/src/export/SurveyExcelWorksheet.ts @@ -20,8 +20,11 @@ export class SurveyExcelWorksheet extends ExcelWorksheet implements IExcelWorksh this._ws = wb.addWorksheet(`${this.mf('export.question')} ${questionIndex + 1}`, this._options); this._questionIndex = questionIndex; this._question = this.quiz.questionList[questionIndex] as IQuestionSurvey; - this.formatSheet(); - this.addSheetData(); + + this.loaded.on('load', () => { + this.formatSheet(); + this.addSheetData(); + }); } public async formatSheet(): Promise { @@ -133,7 +136,7 @@ export class SurveyExcelWorksheet extends ExcelWorksheet implements IExcelWorksh for (let j = 0; j < answerList.length; j++) { this.ws.cell(2, (j + 2)).string(this.mf('export.answer') + ' ' + (j + 1)); this.ws.cell(4, (j + 2)).string(answerList[j].answerText); - this.ws.cell(6, (j + 2)).number(calculateNumberOfAnswers(this.quiz, this._questionIndex, j)); + this.ws.cell(6, (j + 2)).number(await calculateNumberOfAnswers(this.quiz, this._questionIndex, j)); } this.ws.cell(6, 1).string(this.mf('export.number_of_answers') + ':'); diff --git a/src/models/AssetModel.ts b/src/models/AssetModel.ts index 3b37ffab3c2682705f7db4c7ab491e029259c641..5f7d07635a91d59ec35ee21d05a38d2e24d4020e 100644 --- a/src/models/AssetModel.ts +++ b/src/models/AssetModel.ts @@ -1,8 +1,7 @@ -import { getModelForClass, index, prop } from '@typegoose/typegoose'; +import { getModelForClass, index, prop, Severity } from '@typegoose/typegoose'; import DbDAO from '../db/DbDAO'; import { DbCollection } from '../enums/DbOperation'; import { IAssetSerialized } from '../interfaces/IAsset'; -import LoggerService from '../services/LoggerService'; @index({ digest: 1 }, { unique: true }) export class AssetModelItem implements IAssetSerialized { @@ -21,12 +20,8 @@ export const AssetModel = getModelForClass(AssetModelItem, { timestamps: true, }, existingConnection: DbDAO.dbCon, + options: { + runSyncIndexes: true, + allowMixed: Severity.ALLOW, + }, }); - -AssetModel.collection.dropIndexes().then(() => AssetModel.createIndexes(err => { - if (!err) { - return; - } - - LoggerService.error('Unique index for AssetModel created with error', err); -})); diff --git a/src/models/UserModelItem/UserModel.ts b/src/models/UserModelItem/UserModel.ts index 26701f6e766e5e0e1ea0e1ffe9aa552abe450851..02074c4a8bb8c9300e717bbaf56ef9d9467d7e24 100644 --- a/src/models/UserModelItem/UserModel.ts +++ b/src/models/UserModelItem/UserModel.ts @@ -1,8 +1,7 @@ -import { arrayProp, getModelForClass, index, prop } from '@typegoose/typegoose'; +import { arrayProp, getModelForClass, index, prop, Severity } from '@typegoose/typegoose'; import DbDAO from '../../db/DbDAO'; import { DbCollection } from '../../enums/DbOperation'; import { IUserSerialized } from '../../interfaces/users/IUserSerialized'; -import LoggerService from '../../services/LoggerService'; @index({ name: 1 }, { unique: true }) export class UserModelItem implements IUserSerialized { @@ -24,12 +23,8 @@ export const UserModel = getModelForClass(UserModelItem, { timestamps: true, }, existingConnection: DbDAO.dbCon, + options: { + runSyncIndexes: true, + allowMixed: Severity.ALLOW, + }, }); - -UserModel.collection.dropIndexes().then(() => UserModel.createIndexes(err => { - if (!err) { - return; - } - - LoggerService.error('Unique index for UserModel created with error', err); -})); diff --git a/src/models/member/MemberModel.ts b/src/models/member/MemberModel.ts index b12137bd02c7e0f14a391f111139bdb4f4115193..fdd9c9abebb54145bbec5259e0c9e077829cde06 100644 --- a/src/models/member/MemberModel.ts +++ b/src/models/member/MemberModel.ts @@ -1,10 +1,9 @@ -import { arrayProp, getModelForClass, index, pre, prop } from '@typegoose/typegoose'; +import { arrayProp, getModelForClass, index, pre, prop, Severity } from '@typegoose/typegoose'; import DbDAO from '../../db/DbDAO'; import { DbCollection } from '../../enums/DbOperation'; import { IMemberSerialized } from '../../interfaces/entities/Member/IMemberSerialized'; import { IQuizResponse } from '../../interfaces/quizzes/IQuizResponse'; import { ICasData } from '../../interfaces/users/ICasData'; -import LoggerService from '../../services/LoggerService'; @index({ name: 1, @@ -58,12 +57,9 @@ export const MemberModel = getModelForClass(MemberModelItem, { timestamps: true, }, existingConnection: DbDAO.dbCon, + options: { + runSyncIndexes: true, + allowMixed: Severity.ALLOW, + }, }); -MemberModel.collection.dropIndexes().then(() => MemberModel.createIndexes(err => { - if (!err) { - return; - } - - LoggerService.error('Unique index for MemberModel created with error', err); -})); diff --git a/src/models/quiz/QuizModelItem.ts b/src/models/quiz/QuizModelItem.ts index ccdf5ae13d082d4173c1c04c2836823ca5f0bc19..98f24781afc39483f19bc3145c94638d5853f3ec 100644 --- a/src/models/quiz/QuizModelItem.ts +++ b/src/models/quiz/QuizModelItem.ts @@ -1,10 +1,9 @@ -import { arrayProp, getModelForClass, index, prop } from '@typegoose/typegoose'; +import { arrayProp, getModelForClass, index, prop, Severity } from '@typegoose/typegoose'; import DbDAO from '../../db/DbDAO'; import { DbCollection } from '../../enums/DbOperation'; import { QuizState } from '../../enums/QuizState'; import { QuizVisibility } from '../../enums/QuizVisibility'; import { IQuestion } from '../../interfaces/questions/IQuestion'; -import LoggerService from '../../services/LoggerService'; import { SessionConfigurationModelItem } from '../session-config/SessionConfigurationModelItem'; @index({ name: 1 }, { @@ -44,12 +43,8 @@ export const QuizModel = getModelForClass(QuizModelItem, { timestamps: true, }, existingConnection: DbDAO.dbCon, + options: { + runSyncIndexes: true, + allowMixed: Severity.ALLOW, + }, }); - -QuizModel.collection.dropIndexes().then(() => QuizModel.createIndexes(err => { - if (!err) { - return; - } - - LoggerService.error('Unique index for QuizModel created with error', err); -})); diff --git a/src/routers/rest/LibRouter.ts b/src/routers/rest/LibRouter.ts index 4e7bc2fb938edc0c1643e54d84ed66639b556008..bffc76c08fcfb2cc2775636a9ef4019b3f58b102 100644 --- a/src/routers/rest/LibRouter.ts +++ b/src/routers/rest/LibRouter.ts @@ -17,7 +17,7 @@ import { MessageProtocol, StatusProtocol } from '../../enums/Message'; import { IMessage } from '../../interfaces/communication/IMessage'; import { IQuiz } from '../../interfaces/quizzes/IQuizEntity'; import { ICasData } from '../../interfaces/users/ICasData'; -import { MatchAssetCachedQuiz, MatchTextToAssetsDb } from '../../lib/cache/assets'; +import { MatchAssetCachedQuiz } from '../../lib/cache/assets'; import { UserModelItem } from '../../models/UserModelItem/UserModel'; import { AuthService } from '../../services/AuthService'; import LoggerService from '../../services/LoggerService'; @@ -230,33 +230,6 @@ export class LibRouter extends AbstractRouter { }); } - @Post('/cache/quiz/assets') - public async cacheQuizAssets(@BodyParam('quiz') quiz: IQuiz): Promise { - - if (!quiz) { - throw new BadRequestError(`Malformed request received -> ${quiz}`); - } - - const promises: Array> = []; - - quiz.questionList.forEach(question => { - promises.push(MatchTextToAssetsDb(question.questionText).then(val => question.questionText = val)); - question.answerOptionList.forEach(answerOption => { - promises.push(MatchTextToAssetsDb(answerOption.answerText).then(val => answerOption.answerText = val)); - }); - }); - - await Promise.all(promises); - - return { - status: StatusProtocol.Success, - step: MessageProtocol.QuizAssets, - payload: { - quiz, - }, - }; - } - @Get('/cache/quiz/assets/:digest') public async getCache(@Param('digest') digest: string, @Res() response: Response): Promise { const doc = await AssetDAO.getAssetByDigestAsLean(digest); diff --git a/src/routers/rest/MemberRouter.ts b/src/routers/rest/MemberRouter.ts index 8b795b6acf4e09f76e7747a057fba45a46840c8b..ecd7d7713795f4cc9e631ad87981a1f9991e6d2e 100644 --- a/src/routers/rest/MemberRouter.ts +++ b/src/routers/rest/MemberRouter.ts @@ -134,7 +134,7 @@ export class MemberRouter extends AbstractRouter { @Put('/response') public async addResponse( @HeaderParam('authorization') token: string, // - @BodyParam('response', { required: false }) value: string, // + @BodyParam('response', { required: false }) value: Array | string | number, // ): Promise { if (!Array.isArray(value) && !['string', 'number'].includes(typeof value)) { diff --git a/src/services/LoggerService.ts b/src/services/LoggerService.ts index 083a879514097adbe32ea4011b21ec4638ef49b4..a9e04cd9db4787e98b0378b77ca7d2c4bd3dfa4c 100644 --- a/src/services/LoggerService.ts +++ b/src/services/LoggerService.ts @@ -6,6 +6,7 @@ import { staticStatistics } from '../statistics'; class LoggerService { private static instance: LoggerService; + public useLog = true; private _logger: bunyan; diff --git a/src/tests/app-bootstrap/app-bootstrap.test.ts b/src/tests/app-bootstrap/app-bootstrap.test.ts index 1e2345b2cef279e3468126d388b00755beb3fa62..9489f26dcbe0ad84dba5473f1baa6092d4af2171 100644 --- a/src/tests/app-bootstrap/app-bootstrap.test.ts +++ b/src/tests/app-bootstrap/app-bootstrap.test.ts @@ -2,6 +2,7 @@ import { setGlobalOptions } from '@typegoose/typegoose'; import { suite, test } from 'mocha-typescript'; +import * as mongoUnit from 'mongo-unit'; setGlobalOptions({ globalOptions: { @@ -12,6 +13,14 @@ setGlobalOptions({ @suite class AppBootstrapRouterTestSuite { + public async before(): Promise { + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, []); + } + + public async after(): Promise { + return mongoUnit.drop(); + } + @test public async ensureDefaultPathsExist(): Promise { } diff --git a/src/tests/export/export.test.ts b/src/tests/export/export.test.ts index 059721ea38a4f0ec4f3a6f94a13eb9c2a180f237..c754b104051062f152f3dd77f1a299f76f1c0017 100644 --- a/src/tests/export/export.test.ts +++ b/src/tests/export/export.test.ts @@ -4,16 +4,15 @@ import * as assert from 'assert'; import * as fs from 'fs'; import * as i18n from 'i18n'; import * as MessageFormat from 'messageformat'; -import { skip, slow, suite, test } from 'mocha-typescript'; +import { slow, suite, test } from 'mocha-typescript'; +import * as mongoUnit from 'mongo-unit'; import { Document } from 'mongoose'; import * as path from 'path'; import * as sinon from 'sinon'; import AMQPConnector from '../../db/AMQPConnector'; import MemberDAO from '../../db/MemberDAO'; -import MongoDBConnector from '../../db/MongoDBConnector'; import QuizDAO from '../../db/quiz/QuizDAO'; import { QuestionType } from '../../enums/QuestionType'; -import { QuizState } from '../../enums/QuizState'; import { ExcelWorkbook } from '../../export/ExcelWorkbook'; import { IQuestionRanged } from '../../interfaces/questions/IQuestionRanged'; import { IQuestionSurvey } from '../../interfaces/questions/IQuestionSurvey'; @@ -22,7 +21,7 @@ import LoggerService from '../../services/LoggerService'; import { staticStatistics } from '../../statistics'; import { generateQuiz } from '../fixtures'; -@suite @skip +@suite class ExcelExportTestSuite { private readonly _hashtag = 'mocha-export-test'; private _memberCount = 20; @@ -33,7 +32,7 @@ class ExcelExportTestSuite { private _dateFormatted = `${this._dateDay}-${this._date.getHours()}_${this._date.getMinutes()}`; private _exportLocation = path.join(__dirname, '..', '..', '..', 'test-generated', `Export-${this._hashtag}-${this._dateFormatted}.xlsx`); - public static before(): void { + public static async before(): Promise { i18n.configure({ locales: ['en'], defaultLocale: 'en', @@ -61,7 +60,7 @@ class ExcelExportTestSuite { } public async after(): Promise { - await QuizDAO.removeQuiz((await QuizDAO.getQuizByName(this._hashtag)).id); + return mongoUnit.drop(); } public async before(): Promise { @@ -70,31 +69,37 @@ class ExcelExportTestSuite { assertExchange: () => {}, publish: () => {}, }); - sandbox.stub(MongoDBConnector, 'connect').value({ assertExchange: () => {} }); + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, []); + } + + public randomIntFromInterval(min: number, max: number): number { + return Math.floor(Math.random() * (max - min + 1) + min); + } + + @test + public async initQuiz(): Promise { const doc = await QuizDAO.addQuiz(generateQuiz(this._hashtag)); - assert.equal(QuizDAO.isActiveQuiz(this._hashtag), false, 'Expected to find an inactive quiz item'); + assert.equal((await QuizDAO.isActiveQuiz(this._hashtag)), false, 'Expected to find an inactive quiz item'); const quiz: Document & QuizModelItem = JSON.parse( fs.readFileSync(path.join(staticStatistics.pathToAssets, 'predefined_quizzes', 'demo_quiz', 'en.demo_quiz.json')).toString('UTF-8')); quiz.name = this._hashtag; quiz._id = doc._id; - quiz.state = QuizState.Active; await QuizDAO.initQuiz(quiz); - } - public randomIntFromInterval(min: number, max: number): number { - return Math.floor(Math.random() * (max - min + 1) + min); - } - - @test - public initQuiz(): void { - assert.equal(QuizDAO.isActiveQuiz(this._hashtag), true, 'Expected to find an active quiz item'); + assert.equal((await QuizDAO.isActiveQuiz(this._hashtag)), true, 'Expected to find an active quiz item'); } @test public async addMembers(): Promise { - const quiz = await QuizDAO.getActiveQuizByName(this._hashtag); + const doc = await QuizDAO.addQuiz(generateQuiz(this._hashtag)); + const quiz: Document & QuizModelItem = JSON.parse( + fs.readFileSync(path.join(staticStatistics.pathToAssets, 'predefined_quizzes', 'demo_quiz', 'en.demo_quiz.json')).toString('UTF-8')); + quiz.name = this._hashtag; + quiz._id = doc._id; + await QuizDAO.initQuiz(quiz); + for (let memberIndex = 0; memberIndex < this._memberCount; memberIndex++) { await MemberDAO.addMember({ name: `testnick${memberIndex + 1}`, @@ -109,7 +114,13 @@ class ExcelExportTestSuite { @test public async addResponses(): Promise { - const quiz = await QuizDAO.getActiveQuizByName(this._hashtag); + const doc = await QuizDAO.addQuiz(generateQuiz(this._hashtag)); + const quiz: Document & QuizModelItem = JSON.parse( + fs.readFileSync(path.join(staticStatistics.pathToAssets, 'predefined_quizzes', 'demo_quiz', 'en.demo_quiz.json')).toString('UTF-8')); + quiz.name = this._hashtag; + quiz._id = doc._id; + await QuizDAO.initQuiz(quiz); + for (let questionIndex = 0; questionIndex < quiz.questionList.length; questionIndex++) { const question = quiz.questionList[questionIndex]; for (let memberIndex = 0; memberIndex < this._memberCount; memberIndex++) { @@ -185,7 +196,13 @@ class ExcelExportTestSuite { @test(slow(500)) public async generateExcelWorkbook(): Promise { - const quiz = await QuizDAO.getActiveQuizByName(this._hashtag); + const doc = await QuizDAO.addQuiz(generateQuiz(this._hashtag)); + const quiz: Document & QuizModelItem = JSON.parse( + fs.readFileSync(path.join(staticStatistics.pathToAssets, 'predefined_quizzes', 'demo_quiz', 'en.demo_quiz.json')).toString('UTF-8')); + quiz.name = this._hashtag; + quiz._id = doc._id; + await QuizDAO.initQuiz(quiz); + const wb = new ExcelWorkbook({ themeName: this._theme, translation: this._language, diff --git a/src/tests/mocha.opts b/src/tests/mocha.opts index 70dbcf915104e53b163c7f0d9720c7a3c3e29a8e..ae31e12b7a03599b6cb6d96057e261c9a28c9135 100644 --- a/src/tests/mocha.opts +++ b/src/tests/mocha.opts @@ -2,4 +2,6 @@ --exit --require source-map-support/register --require ts-node/register +--require src/tests/mongo-mock.js +--recursive src/tests/**/*.test.ts diff --git a/src/tests/mongo-mock.js b/src/tests/mongo-mock.js new file mode 100644 index 0000000000000000000000000000000000000000..dbb39a88782bb2c786bb27541c93a6413ddf46fe --- /dev/null +++ b/src/tests/mongo-mock.js @@ -0,0 +1,8 @@ +const prepare = require('mocha-prepare'); +const mongoUnit = require('mongo-unit'); + +prepare(done => mongoUnit.start({}) +.then(testMongoUrl => { + process.env.MONGODB_CONN_URL = testMongoUrl; + done() +})); diff --git a/src/tests/routes/api.test.ts b/src/tests/routes/api.test.ts index 35b18345f38ba6c428dbda0902d425a837a39cb4..b5ab5f7b3a2c3068530455581dc5c7355279425a 100644 --- a/src/tests/routes/api.test.ts +++ b/src/tests/routes/api.test.ts @@ -1,7 +1,8 @@ /// import * as chai from 'chai'; -import { suite, test } from 'mocha-typescript'; +import { slow, suite, test } from 'mocha-typescript'; +import * as mongoUnit from 'mongo-unit'; import app from '../../App'; import { staticStatistics } from '../../statistics'; @@ -13,7 +14,15 @@ const expect = chai.expect; class ApiRouterTestSuite { private _baseApiRoute = `${staticStatistics.routePrefix}/api/v1/`; - @test + public async before(): Promise { + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, []); + } + + public async after(): Promise { + return mongoUnit.drop(); + } + + @test @slow(5000) public async baseApiExists(): Promise { const res = await chai.request(app).get(`${this._baseApiRoute}`); expect(res.status).to.equal(200); diff --git a/src/tests/routes/app.test.ts b/src/tests/routes/app.test.ts index c4404deaf6d922d6b88fc1468c875b8b1c1da53f..04e714ed2925862763cd3b452717c7b1b48964d2 100644 --- a/src/tests/routes/app.test.ts +++ b/src/tests/routes/app.test.ts @@ -2,6 +2,7 @@ import * as chai from 'chai'; import { suite, test } from 'mocha-typescript'; +import * as mongoUnit from 'mongo-unit'; import app from '../../App'; import { staticStatistics } from '../../statistics'; @@ -15,6 +16,14 @@ const expect = chai.expect; class AppRouterTestSuite { private _baseApiRoute = `${staticStatistics.routePrefix}/`; + public async before(): Promise { + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, []); + } + + public async after(): Promise { + return mongoUnit.drop(); + } + @test public async baseStatisticsExists(): Promise { const res = await chai.request(app).get(`${this._baseApiRoute}`); diff --git a/src/tests/routes/expiry-quiz.test.ts b/src/tests/routes/expiry-quiz.test.ts index 9e80989ca969b288894704defcc8220cc168274b..2416cac0303c3f69ba90fc40b8eac828e47b70f5 100644 --- a/src/tests/routes/expiry-quiz.test.ts +++ b/src/tests/routes/expiry-quiz.test.ts @@ -2,6 +2,7 @@ import * as chai from 'chai'; import { suite, test } from 'mocha-typescript'; +import * as mongoUnit from 'mongo-unit'; import app from '../../App'; import LoginDAO from '../../db/UserDAO'; @@ -16,6 +17,14 @@ const expect = chai.expect; class ExpiryQuizTestSuite { private _baseApiRoute = `${staticStatistics.routePrefix}/api/v1/expiry-quiz`; + public async before(): Promise { + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, []); + } + + public async after(): Promise { + return mongoUnit.drop(); + } + @test public async baseApiExists(): Promise { const res = await chai.request(app).get(`${this._baseApiRoute}`); diff --git a/src/tests/routes/lib.test.ts b/src/tests/routes/lib.test.ts index 3bdf7946d5388d20e0dff6ada647edca44f1f919..da7efc9de14ea71ee1efa7a573fb626e8a5dc7b6 100644 --- a/src/tests/routes/lib.test.ts +++ b/src/tests/routes/lib.test.ts @@ -1,32 +1,32 @@ /// import * as chai from 'chai'; -import * as fs from 'fs'; import { slow, suite, test } from 'mocha-typescript'; -import * as path from 'path'; +import * as mongoUnit from 'mongo-unit'; import * as sinon from 'sinon'; - import router from '../../App'; import AMQPConnector from '../../db/AMQPConnector'; -import MongoDBConnector from '../../db/MongoDBConnector'; -import QuizDAO from '../../db/quiz/QuizDAO'; import UserDAO from '../../db/UserDAO'; -import { IQuiz } from '../../interfaces/quizzes/IQuizEntity'; -import { IUserSerialized } from '../../interfaces/users/IUserSerialized'; -import { QuizModelItem } from '../../models/quiz/QuizModelItem'; import { staticStatistics } from '../../statistics'; -require('../../lib/regExpEscape'); // Installing polyfill for RegExp.escape - chai.use(require('chai-http')); const expect = chai.expect; const hashtag = 'mocha-test-lib'; +const privateKey = Math.random().toString(10); @suite class LibRouterTestSuite { private _baseApiRoute = `${staticStatistics.routePrefix}/lib`; + public async before(): Promise { + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, []); + } + + public async after(): Promise { + return mongoUnit.drop(); + } + @test public async baseApiExists(): Promise { const res = await chai.request(router).get(`${this._baseApiRoute}`); @@ -70,45 +70,33 @@ class MathjaxLibRouterTestSuite { @suite class CacheQuizAssetsLibRouterTestSuite { private _baseApiRoute = `${staticStatistics.routePrefix}/lib/cache/quiz/assets`; - private _hashtag = hashtag; - private _quiz: IQuiz = JSON.parse( - fs.readFileSync(path.join(staticStatistics.pathToAssets, 'predefined_quizzes', 'demo_quiz', 'en.demo_quiz.json')).toString('UTF-8')); public async before(): Promise { const sandbox = sinon.createSandbox(); sandbox.stub(AMQPConnector, 'channel').value({ assertExchange: () => {} }); - sandbox.stub(MongoDBConnector, 'connect').value({ assertExchange: () => {} }); + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, { + assets: [ + { + url: 'https://upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Sixteen_faces_expressing_the_human_passions._Wellcome_L0068375.jpg/620px-Sixteen_faces_expressing_the_human_passions._Wellcome_L0068375.jpg', + digest: 'e5cf0e52c29860e10b80617d794e5aee0f6620701ab487591881e6635f142ef0', + mimeType: 'image/jpeg', + data: Buffer.from([]), + }, + ], + }); - this._quiz.name = this._hashtag; - const doc = await QuizDAO.addQuiz(this._quiz); - await QuizDAO.initQuiz(doc); sandbox.restore(); } public async after(): Promise { - await QuizDAO.removeQuiz((await QuizDAO.getQuizByName(hashtag)).id); - } - - @test @slow(5000) - public async postNewAssetExists(): Promise { - const res = await chai.request(router).post(`${this._baseApiRoute}/`).send({ quiz: this._quiz }); - expect(res.type).to.eql('application/json'); - } - - @test.skip - public async quizWithAssetUrlsExists(): Promise { - const parsedQuiz: QuizModelItem = await QuizDAO.getQuizByName(this._hashtag); - - expect(parsedQuiz.questionList.map(question => question.questionText) - .filter(questionText => questionText.indexOf(staticStatistics.rewriteAssetCacheUrl) > -1).length).to.be - .greaterThan(0, 'Expect to find the rewritten assets storage url'); + return mongoUnit.drop(); } @test @slow(5000) public async getByDigestExists(): Promise { - const res = await chai.request(router).get(`${this._baseApiRoute}/7b354ef246ea570c0cc360c1eb2bda4061aec31d1012b2011077de11b9b28898`); - expect(res.type).to.eql('text/html'); + const res = await chai.request(router).get(`${this._baseApiRoute}/e5cf0e52c29860e10b80617d794e5aee0f6620701ab487591881e6635f142ef0`); + expect(res.type).to.eql('image/jpeg'); } } @@ -116,20 +104,15 @@ class CacheQuizAssetsLibRouterTestSuite { class AuthorizeLibRouterTestSuite { private _baseApiRoute = `${staticStatistics.routePrefix}/lib/authorize`; - @test - public async authorizeExists(): Promise { - const res = await chai.request(router) - .get(`${this._baseApiRoute}`) - .set('referer', staticStatistics.rewriteAssetCacheUrl); - expect(res.type).to.eql('text/html'); - } - @test public async authorizeStaticExists(): Promise { await UserDAO.addUser({ name: 'testuser', passwordHash: 'testpasshash', - } as IUserSerialized); + userAuthorizations: [], + privateKey: 'privateKey', + tokenHash: 'tokenHash', + }); const res = await chai.request(router) .post(`${this._baseApiRoute}/static`).send({ username: 'testuser', diff --git a/src/tests/routes/lobby.test.ts b/src/tests/routes/lobby.test.ts index 6cd589901cf7daab8afcde9e94b9fc5b3ede27b8..24c7e5d3b9cdfddef1e775f89076e0496efa1141 100644 --- a/src/tests/routes/lobby.test.ts +++ b/src/tests/routes/lobby.test.ts @@ -3,6 +3,7 @@ import * as chai from 'chai'; import * as fs from 'fs'; import { suite, test } from 'mocha-typescript'; +import * as mongoUnit from 'mongo-unit'; import * as path from 'path'; import app from '../../App'; import QuizDAO from '../../db/quiz/QuizDAO'; @@ -23,13 +24,14 @@ class LobbyApiRouterTestSuite { private _hashtag = hashtag; public async before(): Promise { + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, []); const quiz: IQuiz = generateQuiz(hashtag); const doc = await QuizDAO.addQuiz(quiz); await QuizDAO.initQuiz(doc); } public async after(): Promise { - await QuizDAO.removeQuiz((await QuizDAO.getQuizByName(hashtag)).id); + return mongoUnit.drop(); } @test @@ -44,10 +46,13 @@ class LobbyApiRouterTestSuite { const quiz: IQuiz = JSON.parse( fs.readFileSync(path.join(staticStatistics.pathToAssets, 'predefined_quizzes', 'demo_quiz', 'en.demo_quiz.json')).toString('UTF-8')); quiz.name = this._hashtag; - const res = await chai.request(app).put(`${this._baseApiRoute}/`).send({ quiz }); + const res = await chai.request(app).put(`${this._baseApiRoute}/`).send({ + quiz, + privateKey: 'privateKey', + }); expect(res.status).to.equal(200); expect(res.type).to.equal('application/json'); - await expect(QuizDAO.isActiveQuiz(this._hashtag)).to.be.true; + await expect((await QuizDAO.isActiveQuiz(this._hashtag))).to.be.true; } @test diff --git a/src/tests/routes/member.test.ts b/src/tests/routes/member.test.ts index b4a3e2fcd6863e297f473e4f8828e4d8f0ef80a5..70a3afccb9d424b0a61fddb325e72139744ecdea 100644 --- a/src/tests/routes/member.test.ts +++ b/src/tests/routes/member.test.ts @@ -1,17 +1,15 @@ /// import * as chai from 'chai'; -import * as fs from 'fs'; import { suite, test } from 'mocha-typescript'; -import * as path from 'path'; +import * as mongoUnit from 'mongo-unit'; import * as sinon from 'sinon'; -import { SuperAgentRequest } from 'superagent'; import app from '../../App'; import AMQPConnector from '../../db/AMQPConnector'; +import MemberDAO from '../../db/MemberDAO'; import QuizDAO from '../../db/quiz/QuizDAO'; -import { QuizState } from '../../enums/QuizState'; -import { IQuiz } from '../../interfaces/quizzes/IQuizEntity'; import { staticStatistics } from '../../statistics'; +import { generateQuiz } from '../fixtures'; const chaiHttp = require('chai-http'); @@ -30,16 +28,11 @@ class MemberApiRouterTestSuite { assertExchange: () => {}, publish: () => {}, }); - - const quiz: IQuiz = JSON.parse( - fs.readFileSync(path.join(staticStatistics.pathToAssets, 'predefined_quizzes', 'demo_quiz', 'en.demo_quiz.json')).toString('UTF-8')); - quiz.name = this._hashtag; - quiz.state = QuizState.Active; - await QuizDAO.addQuiz(quiz); + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, []); } public async after(): Promise { - await QuizDAO.removeQuiz((await QuizDAO.getQuizByName(this._hashtag)).id); + return mongoUnit.drop(); } @test @@ -90,18 +83,9 @@ class MemberApiRouterTestSuite { @test public async addResponse(): Promise { - await chai.request(app).put(`${this._baseApiRoute}/`).set('authorization', 'testtoken').send({ - member: { - name: this._nickname, - groupName: 'Default', - token: 'testtoken', - currentQuizName: this._hashtag, - }, - }); + await this.initUser(); const res = await chai.request(app).put(`${this._baseApiRoute}/response`).set('authorization', 'testtoken').send({ - quizName: this._hashtag, - nickname: this._nickname, - value: [0], + response: [0], }); expect(res.status).to.equal(200); expect(res.type).to.equal('application/json'); @@ -114,13 +98,19 @@ class MemberApiRouterTestSuite { expect(res.type).to.equal('application/json'); } - private initUser(): SuperAgentRequest { + private async initUser(): Promise { + const quiz = generateQuiz(this._hashtag); + quiz.currentQuestionIndex = 0; + const doc = await QuizDAO.addQuiz(quiz); + await QuizDAO.initQuiz(doc); + return chai.request(app).put(`${this._baseApiRoute}/`).set('authorization', 'testtoken').send({ member: { name: this._nickname, groupName: 'Default', token: 'testtoken', currentQuizName: this._hashtag, + responses: MemberDAO.generateResponseForQuiz(quiz.questionList.length), }, }); } diff --git a/src/tests/routes/nicks.test.ts b/src/tests/routes/nicks.test.ts index 2c0a25d808535bb423a39af6068704cb5714a261..927d6c6123006725788b587ccdd66037a4ee3cd8 100644 --- a/src/tests/routes/nicks.test.ts +++ b/src/tests/routes/nicks.test.ts @@ -2,6 +2,7 @@ import * as chai from 'chai'; import { suite, test } from 'mocha-typescript'; +import * as mongoUnit from 'mongo-unit'; import app from '../../App'; import { staticStatistics } from '../../statistics'; @@ -17,6 +18,14 @@ const hashtag = 'mocha-test-api-v1'; class NicksApiRouterTestSuite { private _baseApiRoute = `${staticStatistics.routePrefix}/api/v1/nicks`; + public async before(): Promise { + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, []); + } + + public async after(): Promise { + return mongoUnit.drop(); + } + @test public async getBlockedNicks(): Promise { const res = await chai.request(app).get(`${this._baseApiRoute}/blocked`); diff --git a/src/tests/routes/quiz.test.ts b/src/tests/routes/quiz.test.ts index 8da782f87f23ecc6ceff1aeffbef69c8df189d6d..c2d78851fce551ab70cba0c46df1a0119f63cfd4 100644 --- a/src/tests/routes/quiz.test.ts +++ b/src/tests/routes/quiz.test.ts @@ -3,6 +3,7 @@ import * as chai from 'chai'; import * as fs from 'fs'; import { suite, test } from 'mocha-typescript'; +import * as mongoUnit from 'mongo-unit'; import * as path from 'path'; import app from '../../App'; import QuizDAO from '../../db/quiz/QuizDAO'; @@ -24,8 +25,16 @@ class QuizApiRouterTestSuite { private _hashtag = hashtag; private _privateKey = privateKey; + public static before(): void { + staticStatistics.pathToAssets = path.join(__dirname, '..', '..', '..', 'assets'); + } + + public async before(): Promise { + await mongoUnit.initDb(process.env.MONGODB_CONN_URL, []); + } + public async after(): Promise { - await QuizDAO.removeQuiz((await QuizDAO.getQuizByName(hashtag)).id); + return mongoUnit.drop(); } @test @@ -37,35 +46,25 @@ class QuizApiRouterTestSuite { @test public async generateDemoQuiz(): Promise { - staticStatistics.pathToAssets = path.join('..', '..', '..', 'assets'); const res = await chai.request(app).get(`${this._baseApiRoute}/generate/demo/en`); expect(res.type).to.equal('application/json'); expect(res.status).to.equal(200); - expect(res.body.hashtag).to.equal('Demo Quiz ' + ((await QuizDAO.getLastPersistedDemoQuizNumber()) + 1)); + expect(res.body.name).to.equal('Demo Quiz ' + ((await QuizDAO.getLastPersistedDemoQuizNumber()) + 1)); } @test public async generateAbcdQuiz(): Promise { - staticStatistics.pathToAssets = path.join('..', '..', '..', 'assets'); const res = await chai.request(app).get(`${this._baseApiRoute}/generate/abcd/en/5`); expect(res.status).to.equal(200); expect(res.type).to.equal('application/json'); } @test - public async getStatusWhenUndefined(): Promise { + public async getStatusWhenUnavailable(): Promise { const res = await chai.request(app).get(`${this._baseApiRoute}/status/${this._hashtag}`); expect(res.status).to.equal(200); expect(res.type).to.equal('application/json'); - expect(res.body.step).to.equal(MessageProtocol.Undefined); - } - - @test - public async getStatusWhenExists(): Promise { - const res = await chai.request(app).get(`${this._baseApiRoute}/status/${this._hashtag}`); - expect(res.status).to.equal(200); - expect(res.type).to.equal('application/json'); - expect(res.body.step).to.equal(MessageProtocol.Exists); + expect(res.body.step).to.equal(MessageProtocol.Unavailable); } @test @@ -104,13 +103,6 @@ class QuizApiRouterTestSuite { expect(res.type).to.equal('application/json'); } - @test - public async upload(): Promise { - const res = await chai.request(app).post(`${this._baseApiRoute}/upload`); - expect(res.status).to.equal(200); - expect(res.type).to.equal('application/json'); - } - @test public async readingConfirmation(): Promise { const res = await chai.request(app).post(`${this._baseApiRoute}/reading-confirmation`).send({ @@ -122,10 +114,7 @@ class QuizApiRouterTestSuite { @test public async deleteActiveQuiz(): Promise { - const res = await chai.request(app).del(`${this._baseApiRoute}/active`).send({ - quizName: this._hashtag, - privateKey: this._privateKey, - }); + const res = await chai.request(app).del(`${this._baseApiRoute}/active/${this._hashtag}`).set('authorization', this._privateKey).send(); expect(res.status).to.equal(200); expect(res.type).to.equal('application/json'); }