From de6fe2ebe3dca3d1040b6254ab004947ff6a93ea Mon Sep 17 00:00:00 2001 From: zkldi <20380519+zkldi@users.noreply.github.com> Date: Wed, 13 Apr 2022 16:31:55 +0100 Subject: [PATCH] Add snapshot testing. --- server/src/external/redis/redis.ts | 10 +++++- .../_playtype/targets/goals/router.test.ts | 3 +- .../{close-connections.ts => cleanup.ts} | 7 +++- server/src/test-utils/mock-api.ts | 2 +- .../src/test-utils/single-process-snapshot.ts | 35 +++++++++++++++++++ server/src/test-utils/single-process-tap.ts | 4 +-- .../test-utils/snapshots/snapshot-data.json | 19 ++++++++++ 7 files changed, 74 insertions(+), 6 deletions(-) rename server/src/test-utils/{close-connections.ts => cleanup.ts} (73%) create mode 100644 server/src/test-utils/single-process-snapshot.ts create mode 100644 server/src/test-utils/snapshots/snapshot-data.json diff --git a/server/src/external/redis/redis.ts b/server/src/external/redis/redis.ts index 03566aed8..05555690f 100644 --- a/server/src/external/redis/redis.ts +++ b/server/src/external/redis/redis.ts @@ -35,5 +35,13 @@ RedisClient.on("connect", () => { }); export function CloseRedisConnection() { - return RedisClient.quit(); + return new Promise((resolve, reject) => + RedisClient.quit((err, reply) => { + if (err) { + reject(err); + } else { + resolve(reply); + } + }) + ); } diff --git a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts index f6c362f7c..3c8c1e561 100644 --- a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts +++ b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts @@ -5,6 +5,7 @@ import t from "tap"; import { CreateFakeAuthCookie } from "test-utils/fake-auth"; import mockApi from "test-utils/mock-api"; import ResetDBState from "test-utils/resets"; +import { TestSnapshot } from "test-utils/single-process-snapshot"; import { HC511Goal, HC511UserGoal, @@ -203,7 +204,7 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn t.equal(res.statusCode, 400); - t.matchSnapshot(res.body.description, `Invalid Goal: ${input.caseName}`); + TestSnapshot(t, res.body.description, `Invalid Goal: ${input.caseName}`); } t.end(); diff --git a/server/src/test-utils/close-connections.ts b/server/src/test-utils/cleanup.ts similarity index 73% rename from server/src/test-utils/close-connections.ts rename to server/src/test-utils/cleanup.ts index b6d8c1e52..d3fba002f 100644 --- a/server/src/test-utils/close-connections.ts +++ b/server/src/test-utils/cleanup.ts @@ -3,8 +3,13 @@ import { CloseRedisPubSub } from "external/redis/redis-IPC"; import { CloseRedisConnection } from "external/redis/redis"; import { CloseServerConnection } from "./mock-api"; import { CloseScoreImportQueue } from "lib/score-import/worker/queue"; +import { WriteSnapshotData } from "./single-process-snapshot"; + +export async function CleanUpAfterTests() { + if (process.env.TAP_SNAPSHOT) { + WriteSnapshotData(); + } -export async function CloseAllConnections() { await CloseMongoConnection(); await CloseServerConnection(); await CloseRedisConnection(); diff --git a/server/src/test-utils/mock-api.ts b/server/src/test-utils/mock-api.ts index c24e4f62f..b5a9593f3 100644 --- a/server/src/test-utils/mock-api.ts +++ b/server/src/test-utils/mock-api.ts @@ -11,7 +11,7 @@ logger.verbose("Connecting to Supertest..."); const mockApi = supertest(connection); export function CloseServerConnection() { - connection.close(); + return connection.close(); } export default mockApi; diff --git a/server/src/test-utils/single-process-snapshot.ts b/server/src/test-utils/single-process-snapshot.ts new file mode 100644 index 000000000..d257a5f35 --- /dev/null +++ b/server/src/test-utils/single-process-snapshot.ts @@ -0,0 +1,35 @@ +import fs from "fs"; +import path from "path"; + +const SNAP_PATH = path.join(__dirname, "./snapshots/snapshot-data.json"); + +type Snapshots = Record; + +function ReadSnapshotData() { + let snapshots: Snapshots = {}; + + if (fs.existsSync(SNAP_PATH)) { + snapshots = JSON.parse(fs.readFileSync(SNAP_PATH, "utf-8")); + } + + return snapshots; +} + +export function WriteSnapshotData() { + fs.writeFileSync(SNAP_PATH, JSON.stringify(snapshotData, null, "\t")); +} + +const snapshotData = ReadSnapshotData(); + +export function TestSnapshot(t: Tap.Test, value: string, testName: string) { + if (process.env.TAP_SNAPSHOT) { + snapshotData[testName] = value; + WriteSnapshotData(); + } else { + if (!snapshotData[testName]) { + return t.fail(`No snapshot exists for ${testName}. Have you ran pnpm snap?`); + } + + t.equal(value, snapshotData[testName], `Snapshot: ${testName}`); + } +} diff --git a/server/src/test-utils/single-process-tap.ts b/server/src/test-utils/single-process-tap.ts index e8f04fcfb..349e134bb 100644 --- a/server/src/test-utils/single-process-tap.ts +++ b/server/src/test-utils/single-process-tap.ts @@ -2,7 +2,7 @@ import "external/mongo/db"; import glob from "glob"; import path from "path"; import t from "tap"; -import { CloseAllConnections } from "./close-connections"; +import { CleanUpAfterTests } from "./cleanup"; const files = glob.sync(path.join(__dirname, "../../", "**/*.test.ts")); @@ -12,4 +12,4 @@ for (const file of files) { require(file); } -t.teardown(CloseAllConnections); +t.teardown(CleanUpAfterTests); diff --git a/server/src/test-utils/snapshots/snapshot-data.json b/server/src/test-utils/snapshots/snapshot-data.json new file mode 100644 index 000000000..ab2122bf6 --- /dev/null +++ b/server/src/test-utils/snapshots/snapshot-data.json @@ -0,0 +1,19 @@ +{ + "Invalid Goal: negative percent": "[criteria.value] Expected number to be greater than or equal to 0. (Received -1)", + "Invalid Goal: percent of 0 is a non-goal": "Invalid value of 0 for percent goal. Percents must be between 0 and 100.", + "Invalid Goal: percent greater than 100": "Invalid value of 100.1 for percent goal. Percents must be between 0 and 100.", + "Invalid Goal: abs without countNum": "[criteria.countNum] Expected number to be greater than or equal to 0. (Received nothing)", + "Invalid Goal: proportion without countNum": "[criteria.countNum] Expected number to be greater than or equal to 0. (Received nothing)", + "Invalid Goal: single with countNum": "[criteria.countNum] Invalid countNum for mode 'single'. Must not have one! (Received 123)", + "Invalid Goal: nonsense mode": "[criteria.mode] Expected any of single, abs, proportion. (Received nonsense)", + "Invalid Goal: abs with countNum but charts.type == single": "Invalid countNum for goal with criteria.mode of 'abs'. Expected a whole number less than the total amount of charts available and greater than 1.", + "Invalid Goal: nonsense charts.type": "[charts.type] Expected any of single, multi, folder, any. (Received nonsense)", + "Invalid Goal: charts.data when type == any": "[charts.data] Invalid charts.data for type 'any'. Must not have any data! (Received foo)", + "Invalid Goal: charts.data array when type == single": "[charts.data] Expected a string in charts.data due to charts.type being 'single'. (Received c2311194e3897ddb5745b1760d2c0141f933e683,c2311194e3897ddb5745b1760d2c0141f933e683)", + "Invalid Goal: charts.data array of identical chartIDs": "Expected charts.data to match 2 charts. Instead, it only matched 1. Are all of these chartIDs valid?", + "Invalid Goal: charts.data array of single chartID": "[charts.data] Expected an array of 2 to 5 strings in charts.data due to charts.type being 'multi'. (Received c2311194e3897ddb5745b1760d2c0141f933e683)", + "Invalid Goal: charts.data array of chartIDs that don't exist": "Expected charts.data to match 2 charts. Instead, it only matched 1. Are all of these chartIDs valid?", + "Invalid Goal: charts.data folder refers to folder that doesn't exist": "A folder with id fake-folder does not exist for iidx:SP.", + "Invalid Goal: nonsense charts.data": "A chart with id nonsense does not exist for iidx:SP.", + "Invalid Goal: multi-score for iidx is illegal": "Invalid key for iidx with multiple charts. Creating score goals on multiple charts where score is relative to notecount is a terrible idea, and has been disabled." +} \ No newline at end of file