mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-04 21:08:09 +03:00
Parser tests
This commit is contained in:
@@ -48,7 +48,6 @@
|
||||
"csv-parse": "^4.15.4",
|
||||
"deepmerge": "^4.2.2",
|
||||
"dotenv": "^8.2.0",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"express": "^4.17.1",
|
||||
"express-async-errors": "^3.1.1",
|
||||
"express-session": "^1.17.1",
|
||||
|
||||
Generated
+1
-2
@@ -21,7 +21,6 @@ specifiers:
|
||||
csv-parse: ^4.15.4
|
||||
deepmerge: ^4.2.2
|
||||
dotenv: ^8.2.0
|
||||
escape-string-regexp: ^1.0.5
|
||||
eslint: ^7.25.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-import: ^2.22.1
|
||||
@@ -57,7 +56,6 @@ dependencies:
|
||||
csv-parse: 4.15.4
|
||||
deepmerge: 4.2.2
|
||||
dotenv: 8.2.0
|
||||
escape-string-regexp: 1.0.5
|
||||
express: 4.17.1
|
||||
express-async-errors: 3.1.1_express@4.17.1
|
||||
express-session: 1.17.1
|
||||
@@ -1588,6 +1586,7 @@ packages:
|
||||
/escape-string-regexp/1.0.5:
|
||||
resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=}
|
||||
engines: {node: '>=0.8.0'}
|
||||
dev: true
|
||||
|
||||
/escape-string-regexp/2.0.0:
|
||||
resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
|
||||
|
||||
@@ -90,15 +90,15 @@ function ConvertFn(c: any): ScoreDocument | null {
|
||||
}
|
||||
|
||||
if (base.game === "iidx") {
|
||||
// @ts-expect-error fuck you
|
||||
// @ts-expect-error nope
|
||||
if (base.scoreData.hitMeta.gauge > 200) {
|
||||
// @ts-expect-error fuck you
|
||||
// @ts-expect-error nope
|
||||
base.scoreData.hitMeta.gauge = null;
|
||||
}
|
||||
|
||||
// @ts-expect-error fuck you
|
||||
// @ts-expect-error nope
|
||||
if (base.scoreData.hitMeta.gaugeHistory) {
|
||||
// @ts-expect-error fuck you
|
||||
// @ts-expect-error nope
|
||||
base.scoreData.hitMeta.gaugeHistory = base.scoreData.hitMeta.gaugeHistory.map((e) =>
|
||||
e > 200 ? null : e
|
||||
);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// https://github.com/sindresorhus/escape-string-regexp/blob/main/index.js
|
||||
// the developer of this has migrated everything to Force ES6 style modules,
|
||||
// which really really messes with a lot of the ecosystem.
|
||||
// shim.
|
||||
|
||||
export default function escapeStringRegexp(string: string) {
|
||||
if (typeof string !== "string") {
|
||||
throw new TypeError("Expected a string");
|
||||
}
|
||||
|
||||
// Escape characters with special meaning either inside or outside character sets.
|
||||
// Use a simple backslash escape when it's always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns' stricter grammar.
|
||||
return string.replace(/[|\\{}()[\]^$+*?.]/gu, "\\$&").replace(/-/gu, "\\x2d");
|
||||
}
|
||||
@@ -3,6 +3,9 @@ import { CloseMongoConnection } from "../../../../db/db";
|
||||
import CreateLogCtx from "../../../../logger";
|
||||
import ScoreImportFatalError from "../../../framework/score-importing/score-import-error";
|
||||
import ParserFn from "./parser";
|
||||
import { BatchManual, BatchManualScore } from "./types";
|
||||
import escapeRegex from "../../../../core/escape-string-regexp";
|
||||
import deepmerge from "deepmerge";
|
||||
|
||||
const fileify = (obj: any) =>
|
||||
({
|
||||
@@ -10,8 +13,37 @@ const fileify = (obj: any) =>
|
||||
filename: "fileify.json",
|
||||
} as Express.Multer.File);
|
||||
|
||||
const mockErr = (...msg: string[]) =>
|
||||
(({
|
||||
statusCode: 400,
|
||||
message: new RegExp(msg.map((e) => `${escapeRegex(e)}.*`).join(""), "u"),
|
||||
name: "Error",
|
||||
} as unknown) as ScoreImportFatalError);
|
||||
|
||||
const logger = CreateLogCtx("parser.test.ts");
|
||||
|
||||
const baseBatchManual = {
|
||||
body: [],
|
||||
head: { service: "foo", game: "iidx" },
|
||||
};
|
||||
|
||||
const baseBatchManualScore = {
|
||||
score: 1000,
|
||||
lamp: "HARD CLEAR",
|
||||
matchType: "songID",
|
||||
identifier: "123",
|
||||
playtype: "SP",
|
||||
difficulty: "ANOTHER",
|
||||
};
|
||||
|
||||
function dm(sc: any) {
|
||||
return deepmerge(
|
||||
baseBatchManual,
|
||||
{ body: [deepmerge(baseBatchManualScore, sc)] },
|
||||
{ arrayMerge: (r, c) => c }
|
||||
);
|
||||
}
|
||||
|
||||
t.test("#ParserFn", (t) => {
|
||||
t.test("Non-Object", (t) => {
|
||||
t.throws(
|
||||
@@ -84,7 +116,7 @@ t.test("#ParserFn", (t) => {
|
||||
() => ParserFn(fileify({ body: [], head: { service: "1", game: "iidx" } }), {}, logger),
|
||||
new ScoreImportFatalError(
|
||||
400,
|
||||
"Invalid BATCH-MANUAL (head.service | Expected a string with length between 3 and 15. | Received 1)"
|
||||
"Invalid BATCH-MANUAL: head.service | Expected a string with length between 3 and 15. | Received 1 [string]."
|
||||
),
|
||||
"Should throw an error."
|
||||
);
|
||||
@@ -93,7 +125,7 @@ t.test("#ParserFn", (t) => {
|
||||
() => ParserFn(fileify({ body: [], head: { service: 1, game: "iidx" } }), {}, logger),
|
||||
new ScoreImportFatalError(
|
||||
400,
|
||||
"Invalid BATCH-MANUAL (head.service | Expected a string with length between 3 and 15. | Received 1)"
|
||||
"Invalid BATCH-MANUAL: head.service | Expected a string with length between 3 and 15. | Received 1 [number]."
|
||||
),
|
||||
"Should throw an error."
|
||||
);
|
||||
@@ -122,6 +154,199 @@ t.test("#ParserFn", (t) => {
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Valid BATCH-MANUAL", (t) => {
|
||||
let res = ParserFn(
|
||||
fileify({
|
||||
body: [
|
||||
{
|
||||
score: 1000,
|
||||
lamp: "HARD CLEAR",
|
||||
matchType: "songID",
|
||||
identifier: "123",
|
||||
playtype: "SP",
|
||||
difficulty: "ANOTHER",
|
||||
},
|
||||
{
|
||||
score: 1000,
|
||||
lamp: "HARD CLEAR",
|
||||
matchType: "kamaitachiSongID",
|
||||
identifier: "123",
|
||||
playtype: "DP",
|
||||
difficulty: "HYPER",
|
||||
},
|
||||
{
|
||||
score: 1000,
|
||||
lamp: "HARD CLEAR",
|
||||
matchType: "songTitle",
|
||||
identifier: "5.1.1.",
|
||||
},
|
||||
{
|
||||
score: 1000,
|
||||
lamp: "HARD CLEAR",
|
||||
matchType: "title",
|
||||
identifier: "5.1.1.",
|
||||
},
|
||||
],
|
||||
head: { service: "foo", game: "iidx" },
|
||||
} as BatchManual),
|
||||
{},
|
||||
logger
|
||||
);
|
||||
|
||||
t.hasStrict(res, {
|
||||
game: "iidx",
|
||||
context: {
|
||||
service: "foo",
|
||||
game: "iidx",
|
||||
version: null,
|
||||
},
|
||||
iterable: [],
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Invalid BATCH-MANUAL", (t) => {
|
||||
t.test("Invalid Lamp For Game", (t) => {
|
||||
let fn = () =>
|
||||
ParserFn(
|
||||
fileify({
|
||||
body: [
|
||||
{
|
||||
score: 1000,
|
||||
lamp: "ALL JUSTICE", // not an iidx lamp
|
||||
matchType: "songID",
|
||||
identifier: "123",
|
||||
playtype: "SP",
|
||||
difficulty: "ANOTHER",
|
||||
},
|
||||
],
|
||||
head: { service: "foo", game: "iidx" },
|
||||
}),
|
||||
{},
|
||||
logger
|
||||
);
|
||||
|
||||
t.throws(
|
||||
fn,
|
||||
new ScoreImportFatalError(
|
||||
400,
|
||||
"Invalid BATCH-MANUAL: body[0].lamp | Expected any of NO PLAY, FAILED, ASSIST CLEAR, EASY CLEAR, CLEAR, HARD CLEAR, EX HARD CLEAR, FULL COMBO. | Received ALL JUSTICE [string]."
|
||||
)
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Non-numeric score", (t) => {
|
||||
let fn = () => ParserFn(fileify(dm({ score: "123" })), {}, logger);
|
||||
|
||||
t.throws(
|
||||
fn,
|
||||
new ScoreImportFatalError(
|
||||
400,
|
||||
"Invalid BATCH-MANUAL: body[0].score | Expected number. | Received 123 [string]."
|
||||
)
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Invalid timeAchieved", (t) => {
|
||||
let fn = () => ParserFn(fileify(dm({ timeAchieved: "string" })), {}, logger);
|
||||
|
||||
t.throws(
|
||||
fn,
|
||||
new ScoreImportFatalError(
|
||||
400,
|
||||
"Invalid BATCH-MANUAL: body[0].timeAchieved | Expected a number greater than 1 Trillion - did you pass unix seconds instead of miliseconds? | Received string [string]."
|
||||
)
|
||||
);
|
||||
|
||||
let fn2 = () =>
|
||||
ParserFn(fileify(dm({ timeAchieved: 1_620_768_609_637 / 1000 })), {}, logger);
|
||||
|
||||
t.throws(
|
||||
fn2,
|
||||
new ScoreImportFatalError(
|
||||
400,
|
||||
"Invalid BATCH-MANUAL: body[0].timeAchieved | Expected a number greater than 1 Trillion - did you pass unix seconds instead of miliseconds? | Received 1620768609.637 [number]."
|
||||
),
|
||||
"Should throw if timeAchieved is less than 10_000_000_000."
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Invalid Playtype", (t) => {
|
||||
// this is not a valid playtype for IIDX
|
||||
let fn = () => ParserFn(fileify(dm({ playtype: "Single" })), {}, logger);
|
||||
|
||||
t.throws(
|
||||
fn,
|
||||
new ScoreImportFatalError(
|
||||
400,
|
||||
"Invalid BATCH-MANUAL: body[0].playtype | Expected any of SP, DP. | Received Single [string]."
|
||||
)
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Invalid Identifier", (t) => {
|
||||
// this is not a valid playtype for IIDX
|
||||
let fn = () => ParserFn(fileify(dm({ identifier: null })), {}, logger);
|
||||
|
||||
t.throws(fn, mockErr("body[0].identifier | Expected string", "Received null [null]"));
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Invalid MatchType", (t) => {
|
||||
let fn = () => ParserFn(fileify(dm({ matchType: "Invalid_MatchType" })), {}, logger);
|
||||
|
||||
t.throws(
|
||||
fn,
|
||||
mockErr(
|
||||
"body[0].matchType | Expected any of",
|
||||
"Received Invalid_MatchType [string]"
|
||||
)
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Invalid HitData", (t) => {
|
||||
let fn = () => ParserFn(fileify(dm({ hitData: { not_key: 123 } })), {}, logger);
|
||||
|
||||
t.throws(fn, mockErr("body[0].hitData | Invalid Key not_key"));
|
||||
|
||||
let fn2 = () => ParserFn(fileify(dm({ hitData: { pgreat: "123" } })), {}, logger);
|
||||
|
||||
t.throws(
|
||||
fn2,
|
||||
mockErr("body[0].hitData | Key pgreat had an invalid value of 123 [string]")
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Invalid HitMeta", (t) => {
|
||||
let fn = () => ParserFn(fileify(dm({ hitMeta: { not_key: 123 } })), {}, logger);
|
||||
|
||||
t.throws(fn, mockErr("body[0].hitMeta | Unexpected"));
|
||||
|
||||
let fn2 = () => ParserFn(fileify(dm({ hitMeta: { bp: -1 } })), {}, logger);
|
||||
|
||||
t.throws(fn2, mockErr("body[0].hitMeta.bp | Expected a positive integer"));
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ import deepmerge from "deepmerge";
|
||||
const optNull = (v: ValidSchemaValue) => p.optional(p.nullable(v));
|
||||
|
||||
const BaseValidHitMeta = {
|
||||
fast: p.isPositiveInteger,
|
||||
slow: p.isPositiveInteger,
|
||||
maxCombo: p.isPositiveInteger,
|
||||
fast: optNull(p.isPositiveInteger),
|
||||
slow: optNull(p.isPositiveInteger),
|
||||
maxCombo: optNull(p.isPositiveInteger),
|
||||
};
|
||||
|
||||
const PR_HitMeta = (game: Game): PrudenceSchema => {
|
||||
@@ -73,7 +73,11 @@ const PR_BatchManualScore = (game: Game): PrudenceSchema => ({
|
||||
difficulty: "*?string", // this is checked in converting instead
|
||||
// september 9th 2001 - this saves people not
|
||||
// reading the documentation.
|
||||
timeAchieved: optNull(p.gt(1_000_000_000_000)),
|
||||
timeAchieved: optNull(
|
||||
(self) =>
|
||||
(typeof self === "number" && self > 1_000_000_000_000) ||
|
||||
"Expected a number greater than 1 Trillion - did you pass unix seconds instead of miliseconds?"
|
||||
),
|
||||
hitData: optNull((self) => {
|
||||
if (typeof self !== "object" || self === null) {
|
||||
return "Not a valid object.";
|
||||
@@ -81,13 +85,15 @@ const PR_BatchManualScore = (game: Game): PrudenceSchema => ({
|
||||
|
||||
for (const key in self) {
|
||||
if (!validHitData[game].includes(key)) {
|
||||
return `Invalid Key ${key}. Expected any of ${validHitData[game].toString()}`;
|
||||
return `Invalid Key ${key}. Expected any of ${validHitData[game].join(", ")}`;
|
||||
}
|
||||
|
||||
// @ts-expect-error shush
|
||||
let v = self[key];
|
||||
if (!Number.isSafeInteger(v)) {
|
||||
return `Key ${key} had an invalid value of ${v}.`;
|
||||
if (!Number.isSafeInteger(v) || v < 0) {
|
||||
return `Key ${key} had an invalid value of ${v} [${
|
||||
v === null ? "null" : typeof v
|
||||
}]`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +178,14 @@ function ParseBatchManual(
|
||||
if (err) {
|
||||
throw new ScoreImportFatalError(
|
||||
400,
|
||||
`Invalid BATCH-MANUAL (${err.keychain} | ${err.message} | Received ${err.userVal})`
|
||||
// ouch prettier
|
||||
`Invalid BATCH-MANUAL: ${err.keychain} | ${err.message}${
|
||||
typeof err.userVal === "object" && err.userVal !== null
|
||||
? ""
|
||||
: ` | Received ${err.userVal} [${
|
||||
err.userVal === null ? "null" : typeof err.userVal
|
||||
}]`
|
||||
}.`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user