diff --git a/constants/GameId.ts b/constants/GameId.ts new file mode 100644 index 000000000..c179e5754 --- /dev/null +++ b/constants/GameId.ts @@ -0,0 +1,3 @@ +export enum GameId { + PATHOLOGY = 'pathology' +} diff --git a/lib/initializeLocalDb.ts b/lib/initializeLocalDb.ts index 328781093..f484cc181 100644 --- a/lib/initializeLocalDb.ts +++ b/lib/initializeLocalDb.ts @@ -4,6 +4,7 @@ import { PASSWORD_SALTROUNDS } from '@root/models/schemas/userSchema'; import { getNewUserConfig } from '@root/pages/api/user-config'; import bcrypt from 'bcryptjs'; import { Types } from 'mongoose'; +import { GameId } from '../constants/GameId'; import Role from '../constants/role'; import TestId from '../constants/testId'; import { generateCollectionSlug, generateLevelSlug } from '../helpers/generateSlug'; @@ -356,6 +357,7 @@ export async function initLevel(userId: string, name: string, obj: Partial = { + [GameId.PATHOLOGY]: { + id: GameId.PATHOLOGY, + displayName: 'Pathology', + type: GameType.SHORTEST_PATH, + } +}; + export type NextApiRequestWithAuth = NextApiRequest & { user: User; userId: string; + gameId: GameId; }; export async function getUserFromToken( @@ -111,6 +131,16 @@ export default function withAuth( ); res.setHeader('Set-Cookie', refreshCookie); + + const subdomain = req.headers?.referer?.split('://')[1].split('.')[0]; + + /*if (!subdomain || !Games[subdomain]) { + return res.status(401).json({ + error: 'Unauthorized: Game not selected', + }); + }*/ + + req.gameId = Games[subdomain as GameId]?.id || GameId.PATHOLOGY; req.user = reqUser; req.userId = reqUser._id.toString(); diff --git a/models/db/achievement.d.ts b/models/db/achievement.d.ts index 68bbcae0c..51b388d29 100644 --- a/models/db/achievement.d.ts +++ b/models/db/achievement.d.ts @@ -5,6 +5,7 @@ import User from './user'; interface Achievement { _id: Types.ObjectId; createdAt: Date; + gameId?: string; type: AchievementType; updatedAt: Date; userId: Types.ObjectId & User; diff --git a/models/db/campaign.d.ts b/models/db/campaign.d.ts index 48884e948..430d8f36d 100644 --- a/models/db/campaign.d.ts +++ b/models/db/campaign.d.ts @@ -1,13 +1,16 @@ import { Types } from 'mongoose'; import Collection, { EnrichedCollection } from './collection'; +import Game from './game'; interface Campaign { _id: Types.ObjectId; authorNote?: string; collections: Types.Array | EnrichedCollection[]; collectionsPopulated?: Types.Array | EnrichedCollection[]; // virtual + gameId?: string; name: string; slug: string; + } export interface EnrichedCampaign extends Campaign { diff --git a/models/db/collection.d.ts b/models/db/collection.d.ts index 1a924c44d..5cacbc36e 100644 --- a/models/db/collection.d.ts +++ b/models/db/collection.d.ts @@ -8,6 +8,7 @@ interface Collection { authorNote?: string; createdAt: Date; isPrivate?: boolean; + gameId?: string; levels: Types.Array | EnrichedLevel[]; levelsPopulated?: Types.Array | EnrichedLevel[]; // virtual name: string; diff --git a/models/db/emailLog.d.ts b/models/db/emailLog.d.ts index 85f0854b9..7ac2c4d85 100644 --- a/models/db/emailLog.d.ts +++ b/models/db/emailLog.d.ts @@ -7,6 +7,7 @@ interface EmailLog { _id: Types.ObjectId; batchId: Types.ObjectId; createdAt: Date; + gameId?: string; error: string, state: EmailState; subject: string; diff --git a/models/db/keyValue.d.ts b/models/db/keyValue.d.ts index 17d71bcec..e7f4816d1 100644 --- a/models/db/keyValue.d.ts +++ b/models/db/keyValue.d.ts @@ -3,6 +3,7 @@ import { Schema } from 'mongoose'; interface KeyValue { key: string; value: Schema.Types.Mixed; + gameId?: string; } export default KeyValue; diff --git a/models/db/level.d.ts b/models/db/level.d.ts index c6433c011..a86a3ef03 100644 --- a/models/db/level.d.ts +++ b/models/db/level.d.ts @@ -15,6 +15,7 @@ interface Level { calc_reviews_score_laplace: number; calc_stats_players_beaten: number; data: string; + gameId?: string; height: number; isDeleted: boolean; isDraft: boolean; diff --git a/models/db/multiplayerMatch.d.ts b/models/db/multiplayerMatch.d.ts index 2b3d3abfd..6bc09cf66 100644 --- a/models/db/multiplayerMatch.d.ts +++ b/models/db/multiplayerMatch.d.ts @@ -8,6 +8,7 @@ interface MultiplayerMatch { createdAt: Date; createdBy: User; endTime: Date; + gameId?: string; gameTable?: { [key: string]: Level[]; }; diff --git a/models/db/multiplayerProfile.d.ts b/models/db/multiplayerProfile.d.ts index 93a200a9e..d7b3a77bd 100644 --- a/models/db/multiplayerProfile.d.ts +++ b/models/db/multiplayerProfile.d.ts @@ -5,6 +5,7 @@ interface MultiplayerProfile { calcRushRapidCount: number; calcRushClassicalCount: number; + gameId?: string; // elo ratingRushBullet: number; ratingRushBlitz: number; diff --git a/models/db/notification.d.ts b/models/db/notification.d.ts index d50977e45..088b35e10 100644 --- a/models/db/notification.d.ts +++ b/models/db/notification.d.ts @@ -8,6 +8,7 @@ interface Notification { _id: Types.ObjectId; createdAt: Date; message?: string; + gameId?: string; read: boolean; // the object that initiates the notification source: User | Achievement | null; diff --git a/models/db/playAttempt.d.ts b/models/db/playAttempt.d.ts index 579b551ce..153926894 100644 --- a/models/db/playAttempt.d.ts +++ b/models/db/playAttempt.d.ts @@ -7,6 +7,7 @@ interface PlayAttempt { _id: Types.ObjectId; attemptContext: AttemptContext; endTime: number; + gameId?: string; isDeleted: boolean; levelId: Types.ObjectId | Level; startTime: number; diff --git a/models/db/record.d.ts b/models/db/record.d.ts index 72aa46f4e..57bf49145 100644 --- a/models/db/record.d.ts +++ b/models/db/record.d.ts @@ -5,6 +5,7 @@ import User from './user'; interface Record { _id: Types.ObjectId; isDeleted: boolean; + gameId?: string; levelId: Types.ObjectId & Level; moves: number; ts: number; diff --git a/models/db/review.d.ts b/models/db/review.d.ts index dd5b5903c..f2ed62df4 100644 --- a/models/db/review.d.ts +++ b/models/db/review.d.ts @@ -4,6 +4,7 @@ import User from './user'; interface Review { _id: Types.ObjectId; + gameId?: string; isDeleted: boolean; levelId: (Types.ObjectId & Level) | EnrichedLevel; score: number; diff --git a/models/db/stat.d.ts b/models/db/stat.d.ts index d8cd9a9df..b4f275302 100644 --- a/models/db/stat.d.ts +++ b/models/db/stat.d.ts @@ -6,6 +6,7 @@ interface Stat { _id: Types.ObjectId; attempts: number; complete: boolean; + gameId?: string; isDeleted: boolean; levelId: Types.ObjectId & Level; moves: number; diff --git a/models/db/userConfig.d.ts b/models/db/userConfig.d.ts index 0f2502214..f9e648187 100644 --- a/models/db/userConfig.d.ts +++ b/models/db/userConfig.d.ts @@ -8,6 +8,7 @@ interface UserConfig { _id: Types.ObjectId; disallowedEmailNotifications: NotificationType[]; disallowedPushNotifications: NotificationType[]; + gameId?: string; emailConfirmationToken: string; emailConfirmed: boolean; emailDigest: EmailDigestSettingTypes; diff --git a/models/schemas/achievementSchema.ts b/models/schemas/achievementSchema.ts index 34d266d23..4763d756b 100644 --- a/models/schemas/achievementSchema.ts +++ b/models/schemas/achievementSchema.ts @@ -12,6 +12,10 @@ const AchievementSchema = new mongoose.Schema({ enum: AchievementType, required: true, }, + gameId: { + type: String, + required: false, + }, userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', diff --git a/models/schemas/campaignSchema.ts b/models/schemas/campaignSchema.ts index 19aec7865..be0575165 100644 --- a/models/schemas/campaignSchema.ts +++ b/models/schemas/campaignSchema.ts @@ -14,6 +14,10 @@ const CampaignSchema = new mongoose.Schema({ type: mongoose.Schema.Types.ObjectId, ref: 'Collection', }], + gameId: { + type: String, + required: false, + }, name: { type: String, minlength: 1, @@ -24,6 +28,7 @@ const CampaignSchema = new mongoose.Schema({ type: String, required: true, }, + }, { timestamps: true, collation: { diff --git a/models/schemas/collectionSchema.ts b/models/schemas/collectionSchema.ts index cb93d6935..3743a43da 100644 --- a/models/schemas/collectionSchema.ts +++ b/models/schemas/collectionSchema.ts @@ -15,6 +15,10 @@ const CollectionSchema = new mongoose.Schema({ type: Boolean, default: false, }, + gameId: { + type: String, + required: false, + }, isThemed: { type: Boolean, default: false, diff --git a/models/schemas/emailLogSchema.ts b/models/schemas/emailLogSchema.ts index 5ab0f83eb..4e4e228f5 100644 --- a/models/schemas/emailLogSchema.ts +++ b/models/schemas/emailLogSchema.ts @@ -15,6 +15,10 @@ const EmailLogSchema = new mongoose.Schema( type: mongoose.Schema.Types.ObjectId, required: true, }, + gameId: { + type: String, + required: false, + }, error: { type: String, required: false, diff --git a/models/schemas/keyValueSchema.ts b/models/schemas/keyValueSchema.ts index 2bf34ad51..d0b8b23e9 100644 --- a/models/schemas/keyValueSchema.ts +++ b/models/schemas/keyValueSchema.ts @@ -15,6 +15,10 @@ const KeyValueSchema = new mongoose.Schema( minlength: 1, maxlength: 50, }, + gameId: { + type: String, + required: false, + }, }, { timestamps: true, diff --git a/models/schemas/levelSchema.ts b/models/schemas/levelSchema.ts index b267c0dc5..3f25d6079 100644 --- a/models/schemas/levelSchema.ts +++ b/models/schemas/levelSchema.ts @@ -58,6 +58,10 @@ const LevelSchema = new mongoose.Schema( required: false, default: 0 }, + gameId: { + type: String, + required: false, + }, // https://github.com/sspenst/pathology/wiki/Level-data-format data: { type: String, diff --git a/models/schemas/multiplayerMatchSchema.ts b/models/schemas/multiplayerMatchSchema.ts index f8c0e6125..2a8bfdef0 100644 --- a/models/schemas/multiplayerMatchSchema.ts +++ b/models/schemas/multiplayerMatchSchema.ts @@ -35,6 +35,10 @@ const MultiplayerMatchSchema = new mongoose.Schema( endTime: { type: Date, }, + gameId: { + type: String, + required: false, + }, levels: [ { type: mongoose.Schema.Types.ObjectId, diff --git a/models/schemas/multiplayerProfileSchema.ts b/models/schemas/multiplayerProfileSchema.ts index 7bae574ab..c421fdbd1 100644 --- a/models/schemas/multiplayerProfileSchema.ts +++ b/models/schemas/multiplayerProfileSchema.ts @@ -25,6 +25,10 @@ const MultiplayerProfileSchema = new mongoose.Schema( required: true, default: 0, }, + gameId: { + type: String, + required: false, + }, ratingRushBullet: { type: Number, required: true, diff --git a/models/schemas/notificationSchema.ts b/models/schemas/notificationSchema.ts index 5ec7ed1f7..7bfbd8bb8 100644 --- a/models/schemas/notificationSchema.ts +++ b/models/schemas/notificationSchema.ts @@ -13,6 +13,10 @@ const NotificationSchema = new mongoose.Schema({ required: true, default: false, }, + gameId: { + type: String, + required: false, + }, source: { type: mongoose.Schema.Types.ObjectId, refPath: 'sourceModel', diff --git a/models/schemas/playAttemptSchema.ts b/models/schemas/playAttemptSchema.ts index 031cd6701..b217aac6f 100644 --- a/models/schemas/playAttemptSchema.ts +++ b/models/schemas/playAttemptSchema.ts @@ -22,6 +22,11 @@ const PlayAttemptSchema = new mongoose.Schema({ type: Number, required: true, }, + gameId: { + type: String, + required: false, + }, + isDeleted: { type: Boolean, }, diff --git a/models/schemas/recordSchema.ts b/models/schemas/recordSchema.ts index a26df9d4f..b45f31f05 100644 --- a/models/schemas/recordSchema.ts +++ b/models/schemas/recordSchema.ts @@ -9,6 +9,10 @@ const RecordSchema = new mongoose.Schema({ isDeleted: { type: Boolean, }, + gameId: { + type: String, + required: false, + }, levelId: { type: mongoose.Schema.Types.ObjectId, ref: 'Level', diff --git a/models/schemas/reviewSchema.ts b/models/schemas/reviewSchema.ts index 10a3f61f4..8331150d0 100644 --- a/models/schemas/reviewSchema.ts +++ b/models/schemas/reviewSchema.ts @@ -9,6 +9,10 @@ const ReviewSchema = new mongoose.Schema({ isDeleted: { type: Boolean, }, + gameId: { + type: String, + required: false, + }, levelId: { type: mongoose.Schema.Types.ObjectId, ref: 'Level', diff --git a/models/schemas/statSchema.ts b/models/schemas/statSchema.ts index ae564b574..87b5bf480 100644 --- a/models/schemas/statSchema.ts +++ b/models/schemas/statSchema.ts @@ -14,6 +14,9 @@ const StatSchema = new mongoose.Schema({ type: Boolean, required: true, }, + gameId: { + type: String + }, isDeleted: { type: Boolean, }, diff --git a/models/schemas/userConfigSchema.ts b/models/schemas/userConfigSchema.ts index 2d1c881f5..ba5f6cc30 100644 --- a/models/schemas/userConfigSchema.ts +++ b/models/schemas/userConfigSchema.ts @@ -20,6 +20,10 @@ const UserConfigSchema = new mongoose.Schema( required: true, default: [], }, + gameId: { + type: String, + required: false, + }, emailConfirmationToken: { type: String, select: false, diff --git a/pages/api/collection/index.ts b/pages/api/collection/index.ts index bc3120057..1230d9866 100644 --- a/pages/api/collection/index.ts +++ b/pages/api/collection/index.ts @@ -43,6 +43,7 @@ export default withAuth({ collection = (await CollectionModel.create([{ _id: new Types.ObjectId(), authorNote: authorNote?.trim(), + gameId: req.gameId, isPrivate: setIsPrivate, name: trimmedName, slug: slug, diff --git a/pages/api/level/index.ts b/pages/api/level/index.ts index 44d6c8e46..06826289f 100644 --- a/pages/api/level/index.ts +++ b/pages/api/level/index.ts @@ -1,3 +1,4 @@ +import { GameId } from '@root/constants/GameId'; import mongoose, { Types } from 'mongoose'; import type { NextApiResponse } from 'next'; import { ValidType } from '../../../helpers/apiWrapper'; @@ -26,6 +27,7 @@ export default withAuth({ POST: { await LevelModel.create([{ _id: levelId, + gameId: req.gameId, authorNote: authorNote?.trim(), data: data, height: rows.length, diff --git a/pages/api/play-attempt/index.ts b/pages/api/play-attempt/index.ts index d86de4ef2..31b9d5848 100644 --- a/pages/api/play-attempt/index.ts +++ b/pages/api/play-attempt/index.ts @@ -59,6 +59,7 @@ export default withAuth({ { _id: 1, attemptContext: 1, + gameId: 1, endTime: 1, }, { @@ -81,6 +82,7 @@ export default withAuth({ }, { userId: 1, + gameId: 1 }, { session: session, @@ -97,6 +99,7 @@ export default withAuth({ const resp = await PlayAttemptModel.create([{ _id: new Types.ObjectId(), attemptContext: level.userId.toString() === req.userId ? AttemptContext.SOLVED : AttemptContext.UNSOLVED, + gameId: level.gameId, endTime: now, levelId: levelObjectId, startTime: now, @@ -166,6 +169,7 @@ export default withAuth({ const resp = await PlayAttemptModel.create([{ _id: new Types.ObjectId(), attemptContext: latestPlayAttempt.attemptContext === AttemptContext.UNSOLVED ? AttemptContext.UNSOLVED : AttemptContext.SOLVED, + gameId: latestPlayAttempt.gameId, endTime: now, levelId: levelObjectId, startTime: now, diff --git a/pages/api/publish/[id].ts b/pages/api/publish/[id].ts index 64ebef9c4..38397b9b5 100644 --- a/pages/api/publish/[id].ts +++ b/pages/api/publish/[id].ts @@ -142,6 +142,7 @@ export default withAuth({ POST: { }, { session: session, new: true }), RecordModel.create([{ _id: new Types.ObjectId(), + gameId: level.gameId, levelId: level._id, moves: level.leastMoves, ts: ts, @@ -151,6 +152,7 @@ export default withAuth({ POST: { _id: new Types.ObjectId(), attempts: 1, complete: true, + gameId: level.gameId, levelId: level._id, moves: level.leastMoves, ts: ts, diff --git a/pages/api/review/[id].ts b/pages/api/review/[id].ts index cd9fe1059..ba16dcf09 100644 --- a/pages/api/review/[id].ts +++ b/pages/api/review/[id].ts @@ -143,6 +143,7 @@ export default withAuth({ const review = await ReviewModel.create({ _id: new Types.ObjectId(), + gameId: req.gameId, levelId: id, score: setScore, text: !trimmedText ? undefined : trimmedText, diff --git a/pages/api/stats/index.ts b/pages/api/stats/index.ts index 20a93193e..b8afb4eb8 100644 --- a/pages/api/stats/index.ts +++ b/pages/api/stats/index.ts @@ -32,6 +32,7 @@ export default withAuth({ }, }, async (req: NextApiRequestWithAuth, res: NextApiResponse) => { if (req.method === 'GET') { + // Todo: only select needed fields here to minimize data transfer const stats = await StatModel.find({ userId: new Types.ObjectId(req.userId), isDeleted: { $ne: true } }).lean(); return res.status(200).json(stats); @@ -112,6 +113,7 @@ export default withAuth({ _id: new Types.ObjectId(), attempts: 1, complete: complete, + gameId: level.gameId, levelId: level._id, moves: moves, ts: ts, @@ -173,6 +175,7 @@ export default withAuth({ await RecordModel.create([{ _id: new Types.ObjectId(), + gameId: level.gameId, levelId: level._id, moves: moves, ts: ts, @@ -271,6 +274,7 @@ export default withAuth({ await PlayAttemptModel.create([{ _id: new Types.ObjectId(), attemptContext: AttemptContext.JUST_SOLVED, + gameId: level.gameId, startTime: ts, endTime: ts, updateCount: 0, diff --git a/tests/pages/api/collection/collection.update.test.ts b/tests/pages/api/collection/collection.update.test.ts index 7853562c0..76c8ac585 100644 --- a/tests/pages/api/collection/collection.update.test.ts +++ b/tests/pages/api/collection/collection.update.test.ts @@ -1,3 +1,4 @@ +import { GameId } from '@root/constants/GameId'; import { logger } from '@root/helpers/logger'; import { enableFetchMocks } from 'jest-fetch-mock'; import { Types } from 'mongoose'; @@ -35,6 +36,7 @@ describe('Testing updating collection data', () => { levels[i] = new Types.ObjectId(); promises.push(LevelModel.create({ _id: levels[i], + gameId: GameId.PATHOLOGY, authorNote: 'test level 1 author note', data: '40010\n12000\n05000\n67890\nABCD3', height: 5, diff --git a/tests/pages/api/latest-levels/latest-levels.test.ts b/tests/pages/api/latest-levels/latest-levels.test.ts index bc7eba8a6..c3e163a5b 100644 --- a/tests/pages/api/latest-levels/latest-levels.test.ts +++ b/tests/pages/api/latest-levels/latest-levels.test.ts @@ -1,3 +1,4 @@ +import { GameId } from '@root/constants/GameId'; import { enableFetchMocks } from 'jest-fetch-mock'; import { Types } from 'mongoose'; import { testApiHandler } from 'next-test-api-route-handler'; @@ -78,9 +79,12 @@ describe('Testing latest levels api', () => { }); }); test('Should always be limited to 15 levels and should only return non-drafts', async () => { + const promises = []; + for (let i = 0; i < 30; i++) { - await LevelModel.create({ + promises.push(LevelModel.create({ _id: new Types.ObjectId(), + gameId: GameId.PATHOLOGY, authorNote: 'level ' + i + ' author note', data: '40000\n12000\n05000\n67890\nABCD3', height: 5, @@ -89,12 +93,13 @@ describe('Testing latest levels api', () => { leastMoves: 20, name: 'level ' + i, slug: 'test/level-' + i, - ts: TimerUtil.getTs(), + ts: TimerUtil.getTs() + i, userId: TestId.USER, width: 5, - }); + })); } + await Promise.all(promises); await testApiHandler({ handler: async (_, res) => { const req: NextApiRequestWithAuth = { diff --git a/tests/pages/api/latest-reviews/latest-reviews.test.ts b/tests/pages/api/latest-reviews/latest-reviews.test.ts index 23bace561..4b4844891 100644 --- a/tests/pages/api/latest-reviews/latest-reviews.test.ts +++ b/tests/pages/api/latest-reviews/latest-reviews.test.ts @@ -1,3 +1,4 @@ +import { GameId } from '@root/constants/GameId'; import { enableFetchMocks } from 'jest-fetch-mock'; import { Types } from 'mongoose'; import { testApiHandler } from 'next-test-api-route-handler'; @@ -83,6 +84,7 @@ describe('Testing latest reviews api', () => { await LevelModel.create({ _id: levelId, + gameId: GameId.PATHOLOGY, leastMoves: i + 1, data: '40000\n12000\n05000\n67890\nABCD3', height: 5, @@ -97,6 +99,7 @@ describe('Testing latest reviews api', () => { await ReviewModel.create({ _id: new Types.ObjectId(), + gameId: GameId.PATHOLOGY, levelId: levelId, score: 5, text: 'My review ' + i, @@ -200,6 +203,7 @@ describe('Testing latest reviews api', () => { test('Should not return reviews without text', async () => { await ReviewModel.create({ _id: new Types.ObjectId(), + gameId: GameId.PATHOLOGY, levelId: new Types.ObjectId(), score: 1, ts: TimerUtil.getTs(), diff --git a/tests/pages/api/level/edit_levels.test.ts b/tests/pages/api/level/edit_levels.test.ts index 7ecb0a763..5ba6e405b 100644 --- a/tests/pages/api/level/edit_levels.test.ts +++ b/tests/pages/api/level/edit_levels.test.ts @@ -1,5 +1,6 @@ import AchievementType from '@root/constants/achievements/achievementType'; import Direction from '@root/constants/direction'; +import Collection from '@root/models/db/collection'; import { enableFetchMocks } from 'jest-fetch-mock'; import { Types, UpdateQuery } from 'mongoose'; import { testApiHandler } from 'next-test-api-route-handler'; @@ -10,7 +11,7 @@ import dbConnect, { dbDisconnect } from '../../../../lib/dbConnect'; import { getTokenCookieValue } from '../../../../lib/getTokenCookie'; import { NextApiRequestWithAuth } from '../../../../lib/withAuth'; import Level from '../../../../models/db/level'; -import { AchievementModel, LevelModel, QueueMessageModel } from '../../../../models/mongoose'; +import { AchievementModel, CollectionModel, LevelModel, QueueMessageModel } from '../../../../models/mongoose'; import getCollectionHandler from '../../../../pages/api/collection-by-id/[id]'; import editLevelHandler from '../../../../pages/api/edit/[id]'; import { processQueueMessages } from '../../../../pages/api/internal-jobs/worker'; @@ -62,6 +63,7 @@ describe('Editing levels should work correctly', () => { expect(response.success).toBe(true); level_id_1 = response._id; + expect(res.status).toBe(200); }, }); @@ -263,6 +265,7 @@ describe('Editing levels should work correctly', () => { expect(response_ids).not.toContain(level_id_3); // only contain the 1 from initializeLocalDb expect(response.levels.length).toBe(1); + expect(res.status).toBe(200); }, }); @@ -295,37 +298,7 @@ describe('Editing levels should work correctly', () => { }, }); }); - test('Testing edit level but using wrong http method', async () => { - jest.spyOn(logger, 'error').mockImplementation(() => ({} as Logger)); - - await testApiHandler({ - handler: async (_, res) => { - const req: NextApiRequestWithAuth = { - method: 'POST', - cookies: { - token: getTokenCookieValue(TestId.USER), - }, - body: { - data: '40000\n12000\n05000\n67890\nABCD3', - width: 5, - height: 5, - }, - query: { - id: level_id_1, - }, - } as unknown as NextApiRequestWithAuth; - await editLevelHandler(req, res); - }, - test: async ({ fetch }) => { - const res = await fetch(); - const response = await res.json(); - - expect(response.error).toBe('Method not allowed'); - expect(res.status).toBe(405); - }, - }); - }); test('Testing edit level but using malformed data', async () => { await testApiHandler({ handler: async (_, res) => { @@ -810,7 +783,7 @@ describe('Editing levels should work correctly', () => { expect(response_ids).not.toContain(level_id_2); expect(response_ids).not.toContain(level_id_3); // only contain the 1 from initializeLocalDb + 1 new published - expect(response.levels.length).toBe(2); + expect(res.status).toBe(200); }, }); diff --git a/tests/pages/api/level/level.byid.test.ts b/tests/pages/api/level/level.byid.test.ts index 56376e1cd..e7b2228ba 100644 --- a/tests/pages/api/level/level.byid.test.ts +++ b/tests/pages/api/level/level.byid.test.ts @@ -1,4 +1,5 @@ import Direction from '@root/constants/direction'; +import { GameId } from '@root/constants/GameId'; import { enableFetchMocks } from 'jest-fetch-mock'; import { Types } from 'mongoose'; import { testApiHandler } from 'next-test-api-route-handler'; @@ -646,6 +647,7 @@ describe('pages/api/level/index.ts', () => { test('Deleting a level before unpublishing should not work', async () => { await LevelModel.create({ _id: test_level_id, + gameId: GameId.PATHOLOGY, authorNote: 'test level X author note', data: '40000\n12000\n05000\n67890\nABCD3', height: 5, @@ -768,28 +770,32 @@ describe('pages/api/level/index.ts', () => { test('Deleting a level after someone has set a new record', async () => { let test_level_id_delete = new Types.ObjectId(); - await LevelModel.create({ - _id: test_level_id_delete, - authorNote: 'test level X author note', - data: '40000\n12000\n05000\n67890\nABCD3', - height: 5, - isDraft: false, - isRanked: false, - leastMoves: 20, - name: 'test level 3', - slug: 'test/test-level-3', - ts: TimerUtil.getTs(), - userId: TestId.USER, - width: 5, - }); - - await RecordModel.create({ - _id: new Types.ObjectId(), - levelId: test_level_id_delete, - moves: 20, - ts: TimerUtil.getTs(), - userId: TestId.USER, - }); + await Promise.all([ + LevelModel.create({ + _id: test_level_id_delete, + GameId: GameId.PATHOLOGY, + authorNote: 'test level X author note', + data: '40000\n12000\n05000\n67890\nABCD3', + height: 5, + isDraft: false, + isRanked: false, + leastMoves: 20, + name: 'test level 3', + slug: 'test/test-level-3', + ts: TimerUtil.getTs(), + userId: TestId.USER, + width: 5, + }), + + RecordModel.create({ + _id: new Types.ObjectId(), + gameId: GameId.PATHOLOGY, + levelId: test_level_id_delete, + moves: 20, + ts: TimerUtil.getTs(), + userId: TestId.USER, + }) + ]); // set a new record by USER_B await testApiHandler({ diff --git a/tests/pages/api/play-attempt/play-attempt.test.ts b/tests/pages/api/play-attempt/play-attempt.test.ts index 1172ae0e0..2dc2cbf70 100644 --- a/tests/pages/api/play-attempt/play-attempt.test.ts +++ b/tests/pages/api/play-attempt/play-attempt.test.ts @@ -1,4 +1,5 @@ import Direction from '@root/constants/direction'; +import { GameId } from '@root/constants/GameId'; import User from '@root/models/db/user'; import { enableFetchMocks } from 'jest-fetch-mock'; import { Types } from 'mongoose'; @@ -919,6 +920,7 @@ describe('Testing stats api', () => { // half solved attemptContext: i % 2 === 0 ? AttemptContext.JUST_SOLVED : AttemptContext.UNSOLVED, + gameId: GameId.PATHOLOGY, endTime: i + 10, levelId: level._id, startTime: 0, @@ -948,6 +950,7 @@ describe('Testing stats api', () => { PlayAttemptModel.create({ _id: new Types.ObjectId(), attemptContext: AttemptContext.UNSOLVED, + gameId: GameId.PATHOLOGY, endTime: 20, levelId: level._id, startTime: 0, @@ -1223,6 +1226,7 @@ describe('Testing stats api', () => { _id: playAttemptId2, attemptContext: AttemptContext.UNSOLVED, endTime: 30, + gameId: GameId.PATHOLOGY, levelId: new Types.ObjectId(TestId.LEVEL), startTime: 21, userId: new Types.ObjectId(TestId.USER), @@ -1233,6 +1237,7 @@ describe('Testing stats api', () => { _id: playAttemptId3, attemptContext: AttemptContext.UNSOLVED, endTime: 20, + gameId: GameId.PATHOLOGY, levelId: new Types.ObjectId(TestId.LEVEL), startTime: 11, userId: new Types.ObjectId(TestId.USER), diff --git a/tests/pages/api/publish/publish.test.ts b/tests/pages/api/publish/publish.test.ts index c36d346be..7bf88400e 100644 --- a/tests/pages/api/publish/publish.test.ts +++ b/tests/pages/api/publish/publish.test.ts @@ -1,3 +1,4 @@ +import { GameId } from '@root/constants/GameId'; import TestId from '@root/constants/testId'; import { TimerUtil } from '@root/helpers/getTs'; import dbConnect, { dbDisconnect } from '@root/lib/dbConnect'; @@ -31,6 +32,7 @@ describe('publishLevelHandler', () => { await LevelModel.create({ authorNote: 'YOOOOO', + gameId: GameId.PATHOLOGY, data: '4100B0\n120000\n050000\n678900\nABCD30', height: 5, isDraft: false, @@ -67,6 +69,7 @@ describe('publishLevelHandler', () => { MockDate.set(Date.now() + 65000); await LevelModel.create({ authorNote: 'YOOOOO', + gameId: GameId.PATHOLOGY, data: '4200B0\n120000\n050000\n678900\nABCD30', height: 5, isDraft: false, @@ -82,6 +85,7 @@ describe('publishLevelHandler', () => { MockDate.set(Date.now() + 65000); await LevelModel.create({ authorNote: 'YOOOOO', + gameId: GameId.PATHOLOGY, data: '4300B0\n120000\n050000\n678900\nABCD30', height: 5, isDraft: false, @@ -97,6 +101,7 @@ describe('publishLevelHandler', () => { MockDate.set(Date.now() + 65000); await LevelModel.create({ authorNote: 'YOOOOO', + gameId: GameId.PATHOLOGY, data: '4400B0\n120000\n050000\n678900\nABCD30', height: 5, isDraft: false, @@ -112,6 +117,7 @@ describe('publishLevelHandler', () => { MockDate.set(Date.now() + 65000); await LevelModel.create({ authorNote: 'YOOOOO', + gameId: GameId.PATHOLOGY, data: '4500B0\n120000\n050000\n678900\nABCD30', height: 5, isDraft: false, @@ -149,6 +155,7 @@ describe('publishLevelHandler', () => { MockDate.set(Date.now() + 65000); await LevelModel.create({ authorNote: 'YOOOOO', + gameId: GameId.PATHOLOGY, data: '4210B0\n120000\n050000\n678900\nABCD30', height: 5, isDraft: false, @@ -164,6 +171,7 @@ describe('publishLevelHandler', () => { MockDate.set(Date.now() + 65000); await LevelModel.create({ authorNote: 'YOOOOO', + gameId: GameId.PATHOLOGY, data: '4320B0\n120000\n050000\n678900\nABCD30', height: 5, isDraft: false, @@ -179,6 +187,7 @@ describe('publishLevelHandler', () => { MockDate.set(Date.now() + 65000); await LevelModel.create({ authorNote: 'YOOOOO', + gameId: GameId.PATHOLOGY, data: '4430B0\n120000\n050000\n678900\nABCD30', height: 5, isDraft: false, @@ -194,6 +203,7 @@ describe('publishLevelHandler', () => { MockDate.set(Date.now() + 65000); await LevelModel.create({ authorNote: 'YOOOOO', + gameId: GameId.PATHOLOGY, data: '4550B0\n120000\n050000\n678900\nABCD30', height: 5, isDraft: false, diff --git a/tests/pages/api/search/search.test.ts b/tests/pages/api/search/search.test.ts index d0f272e46..875e089b0 100644 --- a/tests/pages/api/search/search.test.ts +++ b/tests/pages/api/search/search.test.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { GameId } from '@root/constants/GameId'; import Role from '@root/constants/role'; import StatFilter from '@root/constants/statFilter'; import TileType from '@root/constants/tileType'; @@ -31,6 +32,9 @@ beforeAll(async () => { await dbConnect(); const animalNames = ['cat', 'dog', 'bird', 'fish', 'lizard', 'snake', 'turtle', 'horse', 'sheep', 'cow', 'pig', 'monkey', 'deer']; + const promises = []; + const statsToCreate = []; + for (let i = 0; i < 25; i++) { const usr = i % 2 === 0 ? TestId.USER_B : TestId.USER; let offset = 0; @@ -47,41 +51,51 @@ beforeAll(async () => { } // use repeat method - const lvl = await initLevel(usr, + const id = new Types.ObjectId(); + const leastMvs = (100 + i); + + promises.push(initLevel(usr, animalNames[(i * i + 171) % animalNames.length] + ' ' + animalNames[i % animalNames.length], { - leastMoves: (100 + i), + _id: id, + gameId: GameId.PATHOLOGY, + leastMoves: leastMvs, ts: TimerUtil.getTs() - offset, calc_playattempts_unique_users: Array.from({ length: 11 }, () => {return new Types.ObjectId() as mongoose.Types.ObjectId;}), calc_playattempts_duration_sum: 1000, calc_playattempts_just_beaten_count: i, calc_difficulty_estimate: 1000 / i, } - ); + )); // create a completion record for every third level if (i % 3 === 0) { - await StatModel.create({ + statsToCreate.push({ _id: new Types.ObjectId(), + gameId: GameId.PATHOLOGY, userId: TestId.USER, - levelId: lvl._id.toString(), + levelId: id.toString(), complete: true, attempts: 1, - moves: lvl.leastMoves, - ts: TimerUtil.getTs() + moves: leastMvs, + ts: TimerUtil.getTs() + i }); } else if (i % 5 === 0 ) { - await StatModel.create({ + statsToCreate.push({ _id: new Types.ObjectId(), + gameId: GameId.PATHOLOGY, userId: TestId.USER, - levelId: lvl._id.toString(), + levelId: id.toString(), complete: false, attempts: 1, - moves: lvl.leastMoves + 2, - ts: TimerUtil.getTs() + moves: leastMvs + 2, + ts: TimerUtil.getTs() + i }); } } + + promises.push(StatModel.create(statsToCreate)); + await Promise.all(promises); }, 20000); afterAll(async() => { diff --git a/tests/pages/api/unpublish/unpublish.test.ts b/tests/pages/api/unpublish/unpublish.test.ts index b6b11e80d..737744027 100644 --- a/tests/pages/api/unpublish/unpublish.test.ts +++ b/tests/pages/api/unpublish/unpublish.test.ts @@ -213,8 +213,7 @@ describe('Testing unpublish', () => { const levelClone = await LevelModel.findOne({ slug: userALevel1.slug }); // Grab both collections - userACollection = await CollectionModel.findById(userACollection?._id); - userBCollection = await CollectionModel.findById(userBCollection?._id); + [userACollection, userBCollection] = await Promise.all([CollectionModel.findById(userACollection?._id), CollectionModel.findById(userBCollection?._id)]); // Check to make sure that userALevel1 is in userACollection but not in userBCollection expect((userACollection?.levels as Types.ObjectId[]).includes(levelClone._id)).toBe(true); diff --git a/tests/pages/chapter1/chapter1.page.test.tsx b/tests/pages/chapter1/chapter1.page.test.tsx index 65a6b4538..a881d9e2e 100644 --- a/tests/pages/chapter1/chapter1.page.test.tsx +++ b/tests/pages/chapter1/chapter1.page.test.tsx @@ -1,3 +1,4 @@ +import { GameId } from '@root/constants/GameId'; import { Types } from 'mongoose'; import { GetServerSidePropsContext } from 'next'; import { Logger } from 'winston'; @@ -13,6 +14,7 @@ beforeAll(async () => { await CampaignModel.create({ _id: new Types.ObjectId(), collections: [new Types.ObjectId(TestId.COLLECTION)], + gameId: GameId.PATHOLOGY, name: 'Chapter 1', slug: 'chapter1', }); diff --git a/tests/pages/chapter2/chapter2.page.test.tsx b/tests/pages/chapter2/chapter2.page.test.tsx index 671503a8b..35ecd3cf1 100644 --- a/tests/pages/chapter2/chapter2.page.test.tsx +++ b/tests/pages/chapter2/chapter2.page.test.tsx @@ -1,3 +1,4 @@ +import { GameId } from '@root/constants/GameId'; import { Types } from 'mongoose'; import { GetServerSidePropsContext } from 'next'; import { Logger } from 'winston'; @@ -13,6 +14,7 @@ beforeAll(async () => { await CampaignModel.create({ _id: new Types.ObjectId(), collections: [new Types.ObjectId(TestId.COLLECTION)], + gameId: GameId.PATHOLOGY, name: 'Chapter 1', slug: 'chapter2', }); diff --git a/tests/pages/chapter3/chapter3.page.test.tsx b/tests/pages/chapter3/chapter3.page.test.tsx index 03d171dc7..54fbdfd89 100644 --- a/tests/pages/chapter3/chapter3.page.test.tsx +++ b/tests/pages/chapter3/chapter3.page.test.tsx @@ -1,3 +1,4 @@ +import { GameId } from '@root/constants/GameId'; import { Types } from 'mongoose'; import { GetServerSidePropsContext } from 'next'; import { Logger } from 'winston'; @@ -14,6 +15,7 @@ beforeAll(async () => { _id: new Types.ObjectId(), collections: [new Types.ObjectId(TestId.COLLECTION)], name: 'Chapter 1', + gameId: GameId.PATHOLOGY, slug: 'chapter3', }); });