From c8d1bb5103d4edf6428dc594f2e638902f86a66b Mon Sep 17 00:00:00 2001 From: tudor <7089284+tudddorrr@users.noreply.github.com> Date: Thu, 23 May 2024 08:32:43 +0100 Subject: [PATCH 1/2] create more natural looking demo events --- package-lock.json | 16 +-- package.json | 2 +- src/lib/demo-data/generateDemoEvents.ts | 138 ++++++++++++++++++++++++ src/services/public/demo.service.ts | 31 +----- tests/fixtures/EventFactory.ts | 15 +-- 5 files changed, 149 insertions(+), 53 deletions(-) create mode 100644 src/lib/demo-data/generateDemoEvents.ts diff --git a/package-lock.json b/package-lock.json index fa335df6..c1d576ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "axios": "^1.6.8", "bcrypt": "^5.1.0", "bullmq": "^3.2.0", + "casual": "^1.6.2", "date-fns": "^2.28.0", "dinero.js": "^2.0.0-alpha.14", "dotenv": "^16.0.0", @@ -56,7 +57,6 @@ "@typescript-eslint/parser": "^5.40.1", "@vitest/coverage-v8": "^1.5.2", "axios-mock-adapter": "^1.22.0", - "casual": "^1.6.2", "eslint": "^8.26.0", "hefty": "^1.1.0", "husky": "^9.0.11", @@ -2665,8 +2665,6 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/casual/-/casual-1.6.2.tgz", "integrity": "sha512-NQObL800rg32KZ9bBajHbyDjxLXxxuShChQg7A4tbSeG3n1t7VYGOSkzFSI9gkSgOHp+xilEJ7G0L5l6M30KYA==", - "dev": true, - "license": "MIT", "dependencies": { "mersenne-twister": "^1.0.1", "moment": "^2.15.2" @@ -5871,9 +5869,7 @@ "node_modules/mersenne-twister": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/mersenne-twister/-/mersenne-twister-1.1.0.tgz", - "integrity": "sha512-mUYWsMKNrm4lfygPkL3OfGzOPTR2DBlTkBNHM//F6hGp8cLThY897crAlk3/Jo17LEOOjQUrNAx6DvgO77QJkA==", - "dev": true, - "license": "MIT" + "integrity": "sha512-mUYWsMKNrm4lfygPkL3OfGzOPTR2DBlTkBNHM//F6hGp8cLThY897crAlk3/Jo17LEOOjQUrNAx6DvgO77QJkA==" }, "node_modules/methods": { "version": "1.1.2", @@ -6031,11 +6027,9 @@ } }, "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", - "dev": true, - "license": "MIT", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { "node": "*" } diff --git a/package.json b/package.json index d1a01eb1..c08b4bac 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "@typescript-eslint/parser": "^5.40.1", "@vitest/coverage-v8": "^1.5.2", "axios-mock-adapter": "^1.22.0", - "casual": "^1.6.2", "eslint": "^8.26.0", "hefty": "^1.1.0", "husky": "^9.0.11", @@ -60,6 +59,7 @@ "axios": "^1.6.8", "bcrypt": "^5.1.0", "bullmq": "^3.2.0", + "casual": "^1.6.2", "date-fns": "^2.28.0", "dinero.js": "^2.0.0-alpha.14", "dotenv": "^16.0.0", diff --git a/src/lib/demo-data/generateDemoEvents.ts b/src/lib/demo-data/generateDemoEvents.ts new file mode 100644 index 00000000..ec230d18 --- /dev/null +++ b/src/lib/demo-data/generateDemoEvents.ts @@ -0,0 +1,138 @@ +import { EntityManager } from '@mikro-orm/mysql' +import { Request } from 'koa-clay' +import Event from '../../entities/event' +import { addDays, differenceInDays, endOfDay, startOfDay, subMonths } from 'date-fns' +import casual from 'casual' +import Prop from '../../entities/prop' +import Game from '../../entities/game' +import randomDate from '../dates/randomDate' +import PlayerAlias from '../../entities/player-alias' + +type DemoEvent = { + name: string + props?: { + [key: string]: () => string + } +} + +const demoEvents: DemoEvent[] = [ + { + name: 'Treasure Discovered', + props: { + zoneId: () => casual.integer(1, 30).toString(), + treasureId: () => casual.integer(1, 255).toString() + } + }, + { + name: 'Levelled up', + props: { + newLevel: () => casual.integer(2, 60).toString(), + timeTaken: () => casual.integer(10, 1000).toString() + } + }, + { + name: 'Potion Used', + props: { + itemId: () => casual.integer(1, 255).toString(), + type: () => casual.random_element(['HP', 'MP']) + } + }, + { + name: 'Item Crafted', + props: { + itemId: () => casual.integer(1, 255).toString(), + quantity: () => casual.integer(1, 5).toString() + } + }, + { + name: 'Quest Completed', + props: { + questId: () => casual.integer(1, 255).toString() + } + } +] + +function getDemoEventProps(demoEvent: DemoEvent) { + const eventProps: Prop[] = [] + + for (const key in demoEvent.props) { + eventProps.push(new Prop(key, demoEvent.props[key]())) + } + + eventProps.push(new Prop('TALO_DEMO_EVENT', '1')) + + return eventProps +} + +export function generateEventData(date: Date): Partial { + const randomEvent: DemoEvent = casual.random_element(demoEvents) + const eventProps: Prop[] = getDemoEventProps(randomEvent) + const createdAt = randomDate(startOfDay(date), endOfDay(date)) + + return { + name: randomEvent.name, + props: eventProps, + createdAt + } +} + +export async function generateDemoEvents(req: Request): Promise { + const em: EntityManager = req.ctx.em + + + const games = await em.getRepository(Game).find({ + organisation: { + name: process.env.DEMO_ORGANISATION_NAME + } + }) + + const startDate = subMonths(new Date(), 1) + + for (const game of games) { + const events = await em.getRepository(Event).find({ + playerAlias: { + player: { + game + } + }, + createdAt: { + $gte: startDate + } + }) + + if (events.length === 0) { + const prev: { [key: string]: number } = {} + + const playerAliases = await em.getRepository(PlayerAlias).find({ + player: { + game + } + }) + + for (let dayStep = 0; dayStep < differenceInDays(new Date(), startDate) + 1; dayStep++) { + const day = addDays(startDate, dayStep) + + for (const demoEvent of demoEvents) { + let numToGenerate = casual.integer(1, 3) + + if (prev[demoEvent.name]) { + const increaseAmount = Math.max(casual.integer(0, 3) === 0 ? 0 : 1, Math.ceil(prev[demoEvent.name] * (casual.integer(0, 30) / 100))) + numToGenerate = prev[demoEvent.name] + (increaseAmount * (casual.integer(0, 2) === 0 ? -1 : 1)) + } + + prev[demoEvent.name] = numToGenerate + + for (let i = 0; i < numToGenerate; i++) { + const event = new Event(demoEvent.name, game) + event.setProps(getDemoEventProps(demoEvent)) + event.playerAlias = casual.random_element(playerAliases) + event.createdAt = randomDate(startOfDay(day), endOfDay(day)) + await em.persist(event) + } + } + } + } + } + + await em.flush() +} diff --git a/src/services/public/demo.service.ts b/src/services/public/demo.service.ts index 54ac1ae8..990a6229 100644 --- a/src/services/public/demo.service.ts +++ b/src/services/public/demo.service.ts @@ -3,15 +3,13 @@ import User, { UserType } from '../../entities/user' import { EntityManager, MikroORM } from '@mikro-orm/mysql' import buildTokenPair from '../../lib/auth/buildTokenPair' import Organisation from '../../entities/organisation' -import { sub } from 'date-fns' import ormConfig from '../../config/mikro-orm.config' import createQueue from '../../lib/queues/createQueue' import UserSession from '../../entities/user-session' -import Event from '../../entities/event' -import randomDate from '../../lib/dates/randomDate' import bcrypt from 'bcrypt' import GameActivity from '../../entities/game-activity' import { Job, Queue } from 'bullmq' +import { generateDemoEvents } from '../../lib/demo-data/generateDemoEvents' interface DemoUserJob { userId: number @@ -24,31 +22,6 @@ async function scheduleDeletion(req: Request, res: Response, caller: DemoService } } -async function updateEventDates(req: Request): Promise { - const em: EntityManager = req.ctx.em - - const events = await em.getRepository(Event).find({ - playerAlias: { - player: { - game: { - organisation: { - name: process.env.DEMO_ORGANISATION_NAME - } - } - } - }, - createdAt: { - $lt: sub(new Date(), { months: 3 }) - } - }) - - for (const event of events) { - event.createdAt = randomDate(sub(new Date(), { months: 2 }), new Date()) - } - - await em.flush() -} - export default class DemoService extends Service { queue: Queue @@ -70,7 +43,7 @@ export default class DemoService extends Service { }) } - @Before(updateEventDates) + @Before(generateDemoEvents) @After(scheduleDeletion) async post(req: Request): Promise { const em: EntityManager = req.ctx.em diff --git a/tests/fixtures/EventFactory.ts b/tests/fixtures/EventFactory.ts index 5a374cbd..183649d5 100644 --- a/tests/fixtures/EventFactory.ts +++ b/tests/fixtures/EventFactory.ts @@ -3,8 +3,8 @@ import casual from 'casual' import Event from '../../src/entities/event' import Player from '../../src/entities/player' import { sub } from 'date-fns' -import Prop from '../../src/entities/prop' import randomDate from '../../src/lib/dates/randomDate' +import { generateEventData } from '../../src/lib/demo-data/generateDemoEvents' export default class EventFactory extends Factory { private availablePlayers: Player[] @@ -24,19 +24,10 @@ export default class EventFactory extends Factory { protected async base(): Promise> { const player: Player = casual.random_element(this.availablePlayers) - const availableProps = ['itemId', 'zoneId', 'treasureId', 'currentLevel', 'timeTaken', 'positionX', 'positionY', 'objectId', 'actionId', 'positionZ', 'currentHealth', 'currentMana', 'currentEnergy', 'npcId'] - const propsCount = casual.integer(0, 4) - const props: Prop[] = [] - - for (let i = 0; i < propsCount; i++) { - props.push(new Prop(casual.random_element(availableProps), String(casual.integer(0, 999)))) - } - return { - name: casual.random_element(this.eventTitles), + ...generateEventData(new Date()), game: player.game, - playerAlias: casual.random_element(player.aliases.getItems()), - props + playerAlias: casual.random_element(player.aliases.getItems()) } } From d7600aad8583c43c96019c86e9eb7e9027dfc06a Mon Sep 17 00:00:00 2001 From: tudor <7089284+tudddorrr@users.noreply.github.com> Date: Sat, 25 May 2024 00:10:08 +0100 Subject: [PATCH 2/2] fix tests --- docker-compose.ci.yml | 4 +-- docker-compose.test.yml | 2 +- docker-compose.yml | 2 +- src/lib/demo-data/generateDemoEvents.ts | 1 - tests/services/_public/demo/post.test.ts | 31 ++++++++++++++++-------- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 3905b528..bd6eb951 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -2,8 +2,8 @@ version: '3.9' services: test-db: - image: mysql:8 - command: --default-authentication-plugin=mysql_native_password --sql_mode= + image: mysql:8.4 + command: --mysql-native-password=ON environment: - MYSQL_DATABASE=${DB_NAME} - MYSQL_ROOT_PASSWORD=${DB_PASS} diff --git a/docker-compose.test.yml b/docker-compose.test.yml index b517559c..9731e55b 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -3,7 +3,7 @@ version: '3.9' services: test-db: image: mysql:8 - command: --default-authentication-plugin=mysql_native_password --sql_mode= + command: --mysql-native-password=ON environment: - MYSQL_DATABASE=${DB_NAME} - MYSQL_ROOT_PASSWORD=${DB_PASS} diff --git a/docker-compose.yml b/docker-compose.yml index b007ea72..ad84e354 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,7 @@ services: db: image: mysql:8 - command: --default-authentication-plugin=mysql_native_password --sql_mode= + command: --mysql-native-password=ON environment: - MYSQL_DATABASE=${DB_NAME} - MYSQL_ROOT_PASSWORD=${DB_PASS} diff --git a/src/lib/demo-data/generateDemoEvents.ts b/src/lib/demo-data/generateDemoEvents.ts index ec230d18..cdfe9263 100644 --- a/src/lib/demo-data/generateDemoEvents.ts +++ b/src/lib/demo-data/generateDemoEvents.ts @@ -79,7 +79,6 @@ export function generateEventData(date: Date): Partial { export async function generateDemoEvents(req: Request): Promise { const em: EntityManager = req.ctx.em - const games = await em.getRepository(Game).find({ organisation: { name: process.env.DEMO_ORGANISATION_NAME diff --git a/tests/services/_public/demo/post.test.ts b/tests/services/_public/demo/post.test.ts index 8d70cbea..c53000c9 100644 --- a/tests/services/_public/demo/post.test.ts +++ b/tests/services/_public/demo/post.test.ts @@ -1,5 +1,6 @@ import { EntityManager } from '@mikro-orm/mysql' import request from 'supertest' +import Event from '../../../../src/entities/event' import Organisation from '../../../../src/entities/organisation' import OrganisationFactory from '../../../fixtures/OrganisationFactory' import User, { UserType } from '../../../../src/entities/user' @@ -7,6 +8,7 @@ import EventFactory from '../../../fixtures/EventFactory' import PlayerFactory from '../../../fixtures/PlayerFactory' import GameFactory from '../../../fixtures/GameFactory' import { sub } from 'date-fns' +import randomDate from '../../../../src/lib/dates/randomDate' describe('Demo service - post', () => { let demoOrg: Organisation @@ -31,24 +33,33 @@ describe('Demo service - post', () => { expect(user).toBeNull() }) - it('should update the createdAt of events older than 3 months', async () => { + it('should insert events if there arent any for the last month', async () => { const game = await new GameFactory(demoOrg).one() const players = await new PlayerFactory([game]).many(2) - let events = await new EventFactory(players).state('this year').many(20) - await (global.em).persistAndFlush(events) - events = events.filter((event) => event.createdAt < sub(new Date(), { months: 3 })) - expect(events.length).toBeGreaterThan(0) + let eventsThisMonth = await (global.em).getRepository(Event).find({ + createdAt: { + $gte: sub(new Date(), { months: 1 }) + } + }) + + expect(eventsThisMonth).toHaveLength(0) + + const randomEvents = await new EventFactory(players).with(() => ({ + createdAt: randomDate(sub(new Date(), { years: 1 }), sub(new Date(), { months: 2 })) + })).many(20) + await (global.em).persistAndFlush(randomEvents) await request(global.app) .post('/public/demo') .expect(200) - for (const event of events) { - await (global.em).refresh(event) - } - events = events.filter((event) => event.createdAt < sub(new Date(), { months: 3 })) + eventsThisMonth = await (global.em).getRepository(Event).find({ + createdAt: { + $gte: sub(new Date(), { months: 1 }) + } + }) - expect(events).toHaveLength(0) + expect(eventsThisMonth.length).toBeGreaterThan(0) }) })