diff --git a/common/src/lib/schemas.ts b/common/src/lib/schemas.ts index 5278c017d..c7f66023b 100644 --- a/common/src/lib/schemas.ts +++ b/common/src/lib/schemas.ts @@ -250,7 +250,29 @@ const PR_CHART_DOCUMENT = (game: Game) => (self: unknown) => { data: PrudenceZodShim(gptConfig.chartData), - versions: [p.isIn(Object.keys(gptConfig.versions))], + versions: (self) => { + if (!Array.isArray(self)) { + return "Expected an array"; + } + + const versions = Object.keys(gptConfig.versions); + + if (self.find((k) => !versions.includes(k))) { + return "Array contained invalid versions"; + } + + const gameUsesVersions = versions.length > 0; + + if (gameUsesVersions && self.length === 0) { + return "Versions array should not be empty as this game uses versions"; + } + + if (new Set(self).size !== self.length) { + return "Versions array shouldn't contain the same version twice"; + } + + return true; + }, })(self); }; diff --git a/database-seeds/collections/charts-iidx.json b/database-seeds/collections/charts-iidx.json index d6f413b4f..ec42abeed 100755 --- a/database-seeds/collections/charts-iidx.json +++ b/database-seeds/collections/charts-iidx.json @@ -9336,25 +9336,6 @@ "30-omni" ] }, - { - "chartID": "8cbc49c65e02a4c72eac746ae300cdfc24f84612", - "data": { - "2dxtraSet": null, - "bpiCoefficient": null, - "hashSHA256": null, - "inGameID": 2202, - "kaidenAverage": null, - "notecount": 253, - "worldRecord": null - }, - "difficulty": "NORMAL", - "isPrimary": true, - "level": "0", - "levelNum": 0, - "playtype": "DP", - "songID": 55, - "versions": [] - }, { "chartID": "fd562e433d297f838f5bb74b0da6123afc441e61", "data": { @@ -9411,25 +9392,6 @@ "30-omni" ] }, - { - "chartID": "5fe8688fedae2bb08e0f8a82ce4101500610f248", - "data": { - "2dxtraSet": null, - "bpiCoefficient": null, - "hashSHA256": null, - "inGameID": 2202, - "kaidenAverage": null, - "notecount": 239, - "worldRecord": null - }, - "difficulty": "NORMAL", - "isPrimary": true, - "level": "0", - "levelNum": 0, - "playtype": "SP", - "songID": 55, - "versions": [] - }, { "chartID": "26350652a8bd696830e17de89ce9f7e56b6560c1", "data": { @@ -9458,25 +9420,6 @@ "30-omni" ] }, - { - "chartID": "cae0f62390dcbafa71e68197e342fa1bea73802d", - "data": { - "2dxtraSet": null, - "bpiCoefficient": null, - "hashSHA256": null, - "inGameID": 2203, - "kaidenAverage": null, - "notecount": 270, - "worldRecord": null - }, - "difficulty": "NORMAL", - "isPrimary": true, - "level": "3", - "levelNum": 3, - "playtype": "DP", - "songID": 56, - "versions": [] - }, { "chartID": "76ab68595c22487ac8e9caeebedb3d3b165eddc6", "data": { @@ -9505,25 +9448,6 @@ "30-omni" ] }, - { - "chartID": "5e2472c46f86737dd0cb157720a26ed29d632409", - "data": { - "2dxtraSet": null, - "bpiCoefficient": null, - "hashSHA256": null, - "inGameID": 2203, - "kaidenAverage": null, - "notecount": 268, - "worldRecord": null - }, - "difficulty": "NORMAL", - "isPrimary": true, - "level": "3", - "levelNum": 3, - "playtype": "SP", - "songID": 56, - "versions": [] - }, { "chartID": "fe47ead1b4548edab2d4ec3b428d688b156b8571", "data": { @@ -9750,25 +9674,6 @@ "30-omni" ] }, - { - "chartID": "7bae78d0ae7ec0c4b04d8eee5c8398ed48f7ed3f", - "data": { - "2dxtraSet": null, - "bpiCoefficient": null, - "hashSHA256": null, - "inGameID": 2205, - "kaidenAverage": null, - "notecount": 195, - "worldRecord": null - }, - "difficulty": "NORMAL", - "isPrimary": true, - "level": "2", - "levelNum": 2, - "playtype": "DP", - "songID": 58, - "versions": [] - }, { "chartID": "2c2dc63fefb03bf94f4783be08f50b41350b50a1", "data": { @@ -9797,25 +9702,6 @@ "30-omni" ] }, - { - "chartID": "c055985c74a040cf6834ebee693b1c6f56e32203", - "data": { - "2dxtraSet": null, - "bpiCoefficient": null, - "hashSHA256": null, - "inGameID": 2205, - "kaidenAverage": null, - "notecount": 192, - "worldRecord": null - }, - "difficulty": "NORMAL", - "isPrimary": true, - "level": "2", - "levelNum": 2, - "playtype": "SP", - "songID": 58, - "versions": [] - }, { "chartID": "5e61df72196d41f67ddde0540a463dda1f25ff26", "data": { @@ -118856,25 +118742,6 @@ "31" ] }, - { - "chartID": "eb6c16a73809c81f5fbee10d6b34b052aa257241", - "data": { - "2dxtraSet": null, - "bpiCoefficient": null, - "hashSHA256": null, - "inGameID": 13020, - "kaidenAverage": null, - "notecount": 1416, - "worldRecord": null - }, - "difficulty": "ANOTHER", - "isPrimary": true, - "level": "9", - "levelNum": 9, - "playtype": "DP", - "songID": 590, - "versions": [] - }, { "chartID": "0afd91cbe8d18ea696182aa641a7c9425f928470", "data": { @@ -481702,7 +481569,6 @@ "songID": 2, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -482230,7 +482096,6 @@ "songID": 4, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -484008,7 +483873,6 @@ "songID": 10, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -484118,7 +483982,6 @@ "songID": 10, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -484602,7 +484465,6 @@ "songID": 12, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -485746,7 +485608,6 @@ "songID": 15, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -487110,7 +486971,6 @@ "songID": 19, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -487594,7 +487454,6 @@ "songID": 21, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -488770,7 +488629,6 @@ "songID": 25, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -488836,7 +488694,6 @@ "songID": 25, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -491252,7 +491109,6 @@ "songID": 34, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -491340,7 +491196,6 @@ "songID": 34, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -491890,7 +491745,6 @@ "songID": 38, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -492572,7 +492426,6 @@ "songID": 40, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -492704,7 +492557,6 @@ "songID": 41, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -493210,7 +493062,6 @@ "songID": 43, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -496772,7 +496623,6 @@ "songID": 55, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -496816,9 +496666,6 @@ "songID": 55, "versions": [ "27-2dxtra", - "28-2dxtra", - "28-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -496926,7 +496773,6 @@ "songID": 55, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -496992,7 +496838,6 @@ "songID": 55, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -497058,7 +496903,6 @@ "songID": 55, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -497876,7 +497720,6 @@ "songID": 58, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -497920,7 +497763,6 @@ "songID": 58, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -497964,7 +497806,6 @@ "songID": 58, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -498008,7 +497849,6 @@ "songID": 58, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -498052,7 +497892,6 @@ "songID": 58, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -498096,7 +497935,6 @@ "songID": 58, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -500234,7 +500072,6 @@ "songID": 66, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -502302,7 +502139,6 @@ "songID": 72, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -502544,7 +502380,6 @@ "songID": 73, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -505030,7 +504865,6 @@ "songID": 82, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -512686,7 +512520,6 @@ "songID": 99, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -518362,7 +518195,6 @@ "songID": 114, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -520948,7 +520780,6 @@ "songID": 121, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -521476,7 +521307,6 @@ "songID": 122, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -528637,7 +528467,6 @@ "songID": 144, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -544774,7 +544603,6 @@ "songID": 185, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -551626,7 +551454,6 @@ "songID": 203, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -563471,7 +563298,6 @@ "songID": 234, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -565869,7 +565695,6 @@ "songID": 241, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -576300,7 +576125,6 @@ "songID": 269, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -590368,7 +590192,6 @@ "songID": 305, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -613480,7 +613303,6 @@ "songID": 364, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -635983,7 +635805,6 @@ "songID": 421, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -636181,7 +636002,6 @@ "songID": 421, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -640537,7 +640357,6 @@ "songID": 432, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -684042,7 +683861,6 @@ "songID": 547, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -705696,7 +705514,6 @@ "songID": 604, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -732581,7 +732398,6 @@ "songID": 665, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -738122,7 +737938,6 @@ "songID": 679, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -776680,7 +776495,6 @@ "songID": 769, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -784473,7 +784287,6 @@ "songID": 790, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -784496,7 +784309,6 @@ "songID": 790, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, @@ -816717,7 +816529,6 @@ "songID": 865, "versions": [ "27-2dxtra", - "28-2dxtra", "28-2dxtra" ] }, diff --git a/database-seeds/collections/charts-wacca.json b/database-seeds/collections/charts-wacca.json index cc028287e..91e76d714 100644 --- a/database-seeds/collections/charts-wacca.json +++ b/database-seeds/collections/charts-wacca.json @@ -16379,7 +16379,9 @@ "levelNum": 9.8, "playtype": "Single", "songID": 336, - "versions": [] + "versions": [ + "reverse" + ] }, { "chartID": "cf31db26bb5bc6a022a155641bb82adaff01d26f", @@ -16393,7 +16395,9 @@ "levelNum": 6.9, "playtype": "Single", "songID": 336, - "versions": [] + "versions": [ + "reverse" + ] }, { "chartID": "510613910d774deea3479b153e0593c3c81a2b2d", @@ -16407,7 +16411,9 @@ "levelNum": 2, "playtype": "Single", "songID": 336, - "versions": [] + "versions": [ + "reverse" + ] }, { "chartID": "fdd4452046ec225b404e22b8435d0e769f29934c", diff --git a/database-seeds/scripts/test/custom.test.ts b/database-seeds/scripts/test/custom.test.ts new file mode 100644 index 000000000..11fe468e7 --- /dev/null +++ b/database-seeds/scripts/test/custom.test.ts @@ -0,0 +1,132 @@ +import chalk from "chalk"; +import fs from "fs"; +import path from "path"; +import { ReadCollection } from "../util"; +import { FormatFunctions } from "./test-utils"; +import { ChartDocument, Game, GPTStrings, SongDocument } from "tachi-common"; + +type TestFn = (self: T) => boolean; +type Test = { + fn: TestFn; + desc: string; +}; + +function test(desc: string, fn: TestFn): Test { + return { desc, fn }; +} + +const CHART_CHECKS: { [G in Game]?: Array>> } = { + iidx: [ + test("Level should not be 0", (c) => c.level !== "0"), + test("LevelNum should be an integer greater than 0", (c) => + c.levelNum > 0 && Number.isInteger(c.levelNum)), + test("Level and LevelNum should align", (c) => c.level === c.levelNum.toString()), + ], + chunithm: [ + test("Level should not be 0", (c) => c.level !== "0"), + test("LevelNum should be an integer greater than 0", (c) => + c.levelNum > 0 && Number.isInteger(c.levelNum)), + ], + maimaidx: [ + test("Level should not be 0", (c) => c.level !== "0"), + test("LevelNum should be an integer greater than 0", (c) => + c.levelNum > 0 && Number.isInteger(c.levelNum)), + ], +}; + +const SONG_CHECKS: { [G in Game]?: Array>> } = {}; + +function FormatPrError(err, foreword = "Error") { + const receivedText = + typeof err.userVal === "object" && err.userVal !== null + ? "" + : ` | Received ${err.userVal} [${err.userVal === null ? "null" : typeof err.userVal}]`; + + return `${foreword}: ${err.keychain} | ${err.message}${receivedText}.`; +} + +let exitCode = 0; +const suites: Array<{ name: string; good: boolean; report: unknown }> = []; + +const collections = fs + .readdirSync(path.join(__dirname, "../../collections")) + .map((e) => path.basename(e).replace(/\.json$/u, "")); + +for (const collection of collections) { + console.log(`[CUSTOM VALIDATING] ${collection}`); + + let success = 0; + let fails = 0; + + const collectionName = `${collection}.json`; + const formatFn = FormatFunctions[collection] ?? ((v) => JSON.stringify(v)); + + const data = ReadCollection(collectionName, true); + + let game = ""; + + let checks; + + if (collection.startsWith("songs-")) { + checks = SONG_CHECKS; + } else if (collection.startsWith("charts-")) { + checks = CHART_CHECKS; + } else { + continue; + } + + const maybeGame = collection.split("-")[1]; + + if (maybeGame === undefined) { + throw new Error(`Collection passed was literally ${collection}, why?`); + } + + game = maybeGame; + + for (const d of data) { + let failed = false; + + const pretty = formatFn(d, game as Game); + + for (const check of checks[game as Game] ?? []) { + try { + check.fn(d); + } catch (err) { + console.error( + chalk.red( + `[ERR] ${collectionName} | ${check.desc} | ${pretty} | ${FormatPrError( + err + )}.` + ) + ); + failed = true; + } + } + + if (failed) { + fails += 1; + } else { + success += 1; + } + } + + const report = `GOOD: ${success}, BAD: ${fails}(${Math.min( + (success * 100) / fails, + 100 + ).toFixed(2)}%)`; + if (fails > 0) { + console.error(chalk.red(`[FAILED] ${collection}. ${report}.`)); + exitCode++; + } else { + console.log(chalk.green(`[GOOD] ${collection}. ${report}.`)); + } + + suites.push({ name: collection, report, good: fails === 0 }); +} + +console.log(`=== Suite Overview ===`); +for (const suite of suites) { + console.log(chalk[suite.good ? "green" : "red"](`[CUSTOM] ${suite.name}: ${suite.report}`)); +} + +process.exit(exitCode); diff --git a/server/src/lib/score-import/import-types/common/api-kai/reauth.test.ts b/server/src/lib/score-import/import-types/common/api-kai/reauth.test.ts index 6588c0657..4b90a00ce 100644 --- a/server/src/lib/score-import/import-types/common/api-kai/reauth.test.ts +++ b/server/src/lib/score-import/import-types/common/api-kai/reauth.test.ts @@ -32,6 +32,7 @@ t.test("#CreateKaiReauthFunction", (t) => { const mockFetch = MockJSONFetch({ [`${ServerConfig.FLO_API_URL}/oauth/token`]: { access_token: "NEW_ACCESS_TOKEN", + refresh_token: "NEW_REFRESH_TOKEN", }, });