diff --git a/server/package.json b/server/package.json index f1a3e0386..fbdfee609 100644 --- a/server/package.json +++ b/server/package.json @@ -82,7 +82,7 @@ "rimraf": "3.0.2", "safe-json-stringify": "1.2.0", "seq-logging": "^1.1.1", - "tachi-common": "0.5.4", + "tachi-common": "0.5.5", "ts-node": "10.0.0", "tsconfig-paths": "3.10.1", "typescript": "4.3.4", diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml index a9a1c0ce4..ab20ddb7b 100644 --- a/server/pnpm-lock.yaml +++ b/server/pnpm-lock.yaml @@ -63,7 +63,7 @@ specifiers: safe-json-stringify: 1.2.0 seq-logging: ^1.1.1 supertest: 6.1.3 - tachi-common: 0.5.4 + tachi-common: 0.5.5 tap: 15.0.9 ts-node: 10.0.0 tsconfig-paths: 3.10.1 @@ -107,7 +107,7 @@ dependencies: rimraf: 3.0.2 safe-json-stringify: 1.2.0 seq-logging: 1.1.1 - tachi-common: 0.5.4 + tachi-common: 0.5.5 ts-node: 10.0.0_83f53b0a0c5616d3fa00ed4e30b9ce1b tsconfig-paths: 3.10.1 typescript: 4.3.4 @@ -5386,8 +5386,8 @@ packages: strip-ansi: 6.0.0 dev: true - /tachi-common/0.5.4: - resolution: {integrity: sha512-6QDpdvyGB2JOzPcW+7Hm/jXCKXXrIB6KgTKmvVq86771Iyali/oA2iigSb+oeVUQb8zNX2IM/s9hRD9F+80xTw==} + /tachi-common/0.5.5: + resolution: {integrity: sha512-LFKK+3xhlDQJ07yUij2/H+0cPiHXoXKdFQyR8X1ZNSjsluZoWncR9Rkqwhxlt/WeFBaGyLjkYkpdMkRYZMLVUg==} dependencies: monk: 7.3.4 transitivePeerDependencies: diff --git a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.test.ts b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.test.ts index 6a1a1a4ce..778f29193 100644 --- a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.test.ts +++ b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.test.ts @@ -138,6 +138,114 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => { t.end(); }); + t.test("Should update an IIDX player's bpiTarget settings.", async (t) => { + t.beforeEach(async () => { + await db["api-tokens"].insert({ + userID: 1, + identifier: "api_token", + permissions: { + customise_profile: true, + }, + token: "api_token", + fromAPIClient: null, + }); + }); + + const f = async (target: number) => { + const res = await mockApi + .patch("/api/v1/users/1/games/iidx/SP/settings") + .set("Authorization", "Bearer api_token") + .send({ + gameSpecific: { + bpiTarget: target, + }, + }); + + t.equal(res.statusCode, 200); + + t.strictSame( + res.body.body, + { + userID: 1, + game: "iidx", + playtype: "SP", + preferences: { + preferredScoreAlg: null, + preferredSessionAlg: null, + preferredProfileAlg: null, + scoreBucket: null, + stats: [], + gameSpecific: { + bpiTarget: target, + }, + }, + }, + "Should only update the mutated properties." + ); + + const data = await db["game-settings"].findOne({ + userID: 1, + game: "iidx", + playtype: "SP", + }); + + t.equal(data?.preferences.gameSpecific.bpiTarget, target); + }; + + for (const target of [0, 10, 15, 100]) { + t.test(`Should be able to update the BPI target to ${target}`, async (t) => { + await f(target); + + t.end(); + }); + } + + t.test("Should reject float BPI targets.", async (t) => { + const res = await mockApi + .patch("/api/v1/users/1/games/iidx/SP/settings") + .set("Authorization", "Bearer api_token") + .send({ + gameSpecific: { + bpiTarget: 10.5, + }, + }); + + t.equal(res.statusCode, 400); + }); + + t.test("Should reject negative BPI targets", async (t) => { + const res = await mockApi + .patch("/api/v1/users/1/games/iidx/SP/settings") + .set("Authorization", "Bearer api_token") + .send({ + gameSpecific: { + bpiTarget: -10, + }, + }); + + t.equal(res.statusCode, 400); + + t.end(); + }); + + t.test("Should reject >= 100 BPI targets", async (t) => { + const res = await mockApi + .patch("/api/v1/users/1/games/iidx/SP/settings") + .set("Authorization", "Bearer api_token") + .send({ + gameSpecific: { + bpiTarget: 101, + }, + }); + + t.equal(res.statusCode, 400); + + t.end(); + }); + + t.end(); + }); + t.test("Should reject a arbitrary things on game specific settings.", async (t) => { await db["api-tokens"].insert({ userID: 1, diff --git a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts index 9eeddb29c..dd07f3463 100644 --- a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts +++ b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts @@ -52,14 +52,12 @@ router.patch( if (game === "iidx") { schema = { - display2DXTra: "boolean", - bpiTarget: p.isBoundedInteger(0, 100), + display2DXTra: p.optional("boolean"), + bpiTarget: p.optional(p.isBoundedInteger(0, 100)), }; } - // A limitation in prudence means that validating top-level properties like this isn't - // possible. - // A prudence v1.0. should fix this! - const err = p({ __: req.body.gameSpecific }, { __: p.optional(schema) }, {}); + + const err = p(req.body.gameSpecific, schema); if (err) { return res.status(400).json({