diff --git a/server/make-selfsigned-https.sh b/server/make-selfsigned-https.sh index 437a8566b..e7633657c 100755 --- a/server/make-selfsigned-https.sh +++ b/server/make-selfsigned-https.sh @@ -3,7 +3,6 @@ # This command is for dev servers only! You should use this to # create a self-signed HTTPS certificate for local dev. # That is it. This is not secure. - mkdir -p cert yes "" | openssl req -x509 -newkey rsa:4096 -keyout ./cert/key.pem -out ./cert/cert.pem -sha256 -days 365 --nodes &> /dev/null diff --git a/server/merge-snap.js b/server/merge-snap.js deleted file mode 100755 index 79713e631..000000000 --- a/server/merge-snap.js +++ /dev/null @@ -1,45 +0,0 @@ -const fs = require("fs"); - -// This script takes a path to a .test.ts file, finds its equivalent snapshot file -// and merges it with single-process-tap's snapshot file. - -const toMerge = process.argv[2]; - -if (!toMerge) { - throw new Error("no argv passed to merge-snap. Can't merge with nothing."); -} - -const BASE_FILE = "./tap-snapshots/src/test-utils/single-process-tap.ts.test.cjs"; -const NEW_FILE = `${toMerge.replace("tachi-server/src", "tachi-server/tap-snapshots/src")}.test.cjs`; - -const baseSet = require(BASE_FILE); - -const newSet = require(NEW_FILE); - -for (const [snapshotName, snapshotValue] of Object.entries(newSet)) { - baseSet[snapshotName] = snapshotValue; -} - - -function escapeBacktickString(str) { - return str.replace(/\`\$/, " ") -} - -const cases = []; - -for (const [snapshotName, snapshotValue] of Object.entries(baseSet)) { - cases.push(`exports[\`${escapeBacktickString(snapshotName)}\`] = \`${escapeBacktickString(snapshotValue)}\`\n`) -} - -let outFile = ` -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -${cases.join("\n")}` - -fs.writeFileSync(BASE_FILE, outFile); -fs.rmSync(NEW_FILE); \ No newline at end of file diff --git a/server/package.json b/server/package.json index 65ee8fa63..63acc6978 100644 --- a/server/package.json +++ b/server/package.json @@ -6,7 +6,6 @@ "private": true, "scripts": { "test": "tap", - "snap": "tap", "watchtest": "tap --watch", "build": "tsc --project tsconfig.build.json", "lint": "eslint ./src --ext .ts --fix", diff --git a/server/snap-single-file.sh b/server/snap-single-file.sh deleted file mode 100755 index c87be32ce..000000000 --- a/server/snap-single-file.sh +++ /dev/null @@ -1,27 +0,0 @@ -#! /bin/bash - -# This script is intended to be used as follows -# ./snap-single-file.sh path/to/test.TAP_SNAPSHOT -# This will result in that individual file being snapped. - -# Of course, we need to do this like this because we are abusing -# single process tap to get our tests done faster. - -# Put us in the directory this script is in. -cd "$(dirname "$0")" || exit 1 - -# So, TAP wont exit when ran on a single file because we never close it. -# This can be fixed another time, but until then I'm going to do this insane hack. -onsigint() -{ - node ./merge-snap.js "$1" -} - -# When we recieve a sigint, trap it and call our sigint handler. -# of course, this is bash, so trap onsigint doesn't do what you want. -# instead, we do 'onsigint "$@"', which means call onsigint with arguments like $1. -trap 'onsigint "$@"' SIGINT - -# Snapshot the argument. -TAP_SNAPSHOT=1 tap "$1" - diff --git a/server/src/external/mongo/db.ts b/server/src/external/mongo/db.ts index 92fba9e9c..527db645f 100644 --- a/server/src/external/mongo/db.ts +++ b/server/src/external/mongo/db.ts @@ -24,6 +24,7 @@ import { MilestoneSetDocument, OrphanChart, PBScoreDocument, + PrivateUserInfoDocument, PublicUserDocument, RecentlyViewedFolderDocument, ScoreDocument, @@ -40,7 +41,6 @@ import { UserSettings, } from "tachi-common"; import { GetMillisecondsSince } from "utils/misc"; -import { PrivateUserInfoDocument } from "utils/types"; const logger = CreateLogCtx(__filename); let dbName = ServerConfig.MONGO_DATABASE_NAME; diff --git a/server/src/server/router/api/v1/auth/auth.ts b/server/src/server/router/api/v1/auth/auth.ts index ea721356b..57492b4ae 100644 --- a/server/src/server/router/api/v1/auth/auth.ts +++ b/server/src/server/router/api/v1/auth/auth.ts @@ -2,10 +2,15 @@ import bcrypt from "bcryptjs"; import db from "external/mongo/db"; import CreateLogCtx from "lib/logger/logger"; import { Environment, ServerConfig } from "lib/setup/config"; -import { integer, PublicUserDocument, UserAuthLevels, UserSettings } from "tachi-common"; +import { + integer, + PrivateUserInfoDocument, + PublicUserDocument, + UserAuthLevels, + UserSettings, +} from "tachi-common"; import nodeFetch from "utils/fetch"; import { Random20Hex } from "utils/misc"; -import { PrivateUserInfoDocument } from "utils/types"; import { CreateURLWithParams } from "utils/url"; import { FormatUserDoc } from "utils/user"; 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 deleted file mode 100644 index fd135371c..000000000 --- a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts +++ /dev/null @@ -1,356 +0,0 @@ -import dm from "deepmerge"; -import db from "external/mongo/db"; -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 { - HC511Goal, - HC511UserGoal, - Testing511SPA, - TestingIIDXSPMilestone, -} from "test-utils/test-data"; - -// @ts-expect-error Not sure why the types break here, but they do. -const dupedGoal = dm({}, HC511Goal); -// @ts-expect-error Not sure why the types break here, but they do. -const dupedGoalSub = dm({}, HC511UserGoal); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/goals", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should retrieve this user's goal subscriptions.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); - - const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/targets/goals"); - - t.equal(res.statusCode, 200); - - t.strictSame(res.body.body, { - goals: [HC511Goal], - goalSubs: [HC511UserGoal], - }); - - t.end(); - }); - - t.end(); -}); - -t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - const baseInput = { - criteria: { - key: "scoreData.percent", - value: 1, - mode: "single", - }, - charts: { - type: "single", - data: Testing511SPA.chartID, - }, - }; - - function mkInput(caseName: string, merge: any) { - return { - caseName, - content: dm(baseInput, merge), - }; - } - - t.test("Should set valid goals.", async (t) => { - const validInput = [ - mkInput("Base Case", baseInput), - mkInput("Score:Single Case", { - criteria: { - key: "scoreData.score", - value: 1, - mode: "", - }, - }), - ]; - - t.end(); - }); - - t.test("Should reject invalid goals.", async (t) => { - const invalidInput = [ - mkInput("negative percent", { - criteria: { - value: -1, - }, - }), - mkInput("percent of 0 is a non-goal", { - criteria: { - value: 0, - }, - }), - mkInput("percent greater than 100", { - criteria: { - value: 100.1, - }, - }), - mkInput("abs without countNum", { - criteria: { - mode: "abs", - }, - }), - mkInput("proportion without countNum", { - criteria: { - mode: "proportion", - }, - }), - mkInput("single with countNum", { - criteria: { - mode: "single", - countNum: 123, - }, - }), - mkInput("nonsense mode", { - criteria: { - mode: "nonsense", - }, - }), - mkInput("abs with countNum but charts.type == single", { - criteria: { - mode: "abs", - countNum: 1, - }, - charts: { - type: "single", - }, - }), - mkInput("nonsense charts.type", { - charts: { - type: "nonsense", - }, - }), - mkInput("charts.data when type == any", { - charts: { - type: "any", - data: "foo", - }, - }), - mkInput("charts.data array when type == single", { - charts: { - type: "single", - data: [Testing511SPA.chartID, Testing511SPA.chartID], - }, - }), - mkInput("charts.data array of identical chartIDs", { - charts: { - type: "multi", - data: [Testing511SPA.chartID, Testing511SPA.chartID], - }, - }), - mkInput("charts.data array of single chartID", { - charts: { - type: "multi", - data: [Testing511SPA.chartID], - }, - }), - mkInput("charts.data array of chartIDs that don't exist", { - charts: { - type: "multi", - data: [Testing511SPA.chartID, "not_exist"], - }, - }), - mkInput("charts.data folder refers to folder that doesn't exist", { - charts: { - type: "folder", - data: "fake-folder", - }, - }), - mkInput("nonsense charts.data", { - charts: { - data: "nonsense", - }, - }), - ]; - - for (const input of invalidInput) { - // eslint-disable-next-line no-await-in-loop - const res = await mockApi - .post("/api/v1/users/1/games/iidx/SP/targets/goals/add-goal") - .set("Cookie", cookie) - .send(input.content); - - t.equal(res.statusCode, 400); - - t.matchSnapshot(res.body.description, `Invalid Goal: ${input.caseName}`); - } - - t.end(); - }); - - t.end(); -}); - -t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID", (t) => { - t.beforeEach(ResetDBState); - - t.test("Should retrieve this user's goal subscription.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); - - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/targets/goals/${HC511Goal.goalID}` - ); - - t.equal(res.statusCode, 200); - - t.hasStrict(res.body.body, { - goal: HC511Goal, - goalSub: HC511UserGoal, - milestones: [], - user: { - id: 1, - }, - }); - - t.end(); - }); - - t.test("Should return parent milestones if goal has any.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert( - // @ts-expect-error Not sure why the types break here, but they do. - dm(HC511UserGoal, { - parentMilestones: [TestingIIDXSPMilestone.milestoneID], - }) - ); - await db.milestones.insert(dm(TestingIIDXSPMilestone, {})); - - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/targets/goals/${HC511Goal.goalID}` - ); - - t.equal(res.statusCode, 200); - - t.hasStrict(res.body.body, { - goal: HC511Goal, - goalSub: HC511UserGoal, - milestones: [TestingIIDXSPMilestone], - user: { - id: 1, - }, - }); - - t.end(); - }); - - t.test("Should panic if goal refers to a parent milestone that doesn't exist.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert( - // @ts-expect-error Not sure why the types break here, but they do. - dm(HC511UserGoal, { - parentMilestones: [TestingIIDXSPMilestone.milestoneID], - }) - ); - - const res = await mockApi.get( - `/api/v1/users/1/games/iidx/SP/targets/goals/${HC511Goal.goalID}` - ); - - t.equal(res.statusCode, 500); - - t.end(); - }); - - t.test("Should return 404 if the user is not subscribed to this goal ID.", async (t) => { - const res = await mockApi.get(`/api/v1/users/1/games/iidx/SP/targets/goals/INVALID_GOAL`); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.end(); -}); - -t.test("DELETE /api/v1/users/:userID/games/:game/:playtype/targets/goals/:goalID", async (t) => { - t.beforeEach(ResetDBState); - - const cookie = await CreateFakeAuthCookie(mockApi); - - t.test("Should delete a goal subscription if subscribed.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); - - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP/targets/goals/${dupedGoalSub.goalID}`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 200); - - const dbRes = await db["goal-subs"].findOne({ - userID: 1, - goalID: dupedGoal.goalID, - }); - - t.equal(dbRes, null, "Should delete the goal sub from the database."); - - t.end(); - }); - - t.test("Should reject a goal deletion if goal has parent milestones.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert( - // @ts-expect-error deepmerge type error - dm(dupedGoalSub, { - parentMilestones: [TestingIIDXSPMilestone.milestoneID], - }) - ); - - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP/targets/goals/${dupedGoalSub.goalID}`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 400); - t.equal( - res.body.description, - "This goal is part of a milestone you are subscribed to. It can only be removed by unsubscribing from the relevant milestones." - ); - - const dbRes = await db["goal-subs"].findOne({ - userID: 1, - goalID: dupedGoal.goalID, - }); - - t.not(dbRes, null, "Should NOT delete the goal sub from the database."); - - t.end(); - }); - - t.test("Should reject a goal deletion if not subscribed.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); - - const res = await mockApi - .delete(`/api/v1/users/1/games/iidx/SP/targets/goals/INVALID_GOAL`) - .set("Cookie", cookie); - - t.equal(res.statusCode, 404); - - t.end(); - }); - - t.test("Should reject a goal deletion if not authed.", async (t) => { - await db.goals.insert(dupedGoal); - await db["goal-subs"].insert(dupedGoalSub); - - const res = await mockApi.delete( - `/api/v1/users/1/games/iidx/SP/targets/goals/${dupedGoalSub.goalID}` - ); - - t.equal(res.statusCode, 401); - - t.end(); - }); - - t.end(); -}); diff --git a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.ts b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.ts index 438a9dfb5..2c9e34268 100644 --- a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.ts +++ b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.ts @@ -9,7 +9,7 @@ import p from "prudence"; import { RequirePermissions } from "server/middleware/auth"; import prValidate from "server/middleware/prudence-validate"; import { GoalDocument, MilestoneDocument } from "tachi-common"; -import { GetGoalForIDGuaranteed } from "utils/db"; +import { GetGoalForIDGuaranteed, GetMilestoneForIDGuaranteed } from "utils/db"; import { AssignToReqTachiData, GetUGPT } from "utils/req-tachi-data"; import { RequireAuthedAsUser } from "../../../../../middleware"; @@ -88,7 +88,7 @@ router.post( ); } - // proper valid/ation later. + // proper validation later. return p.gte(0)(self); }, }, @@ -120,8 +120,6 @@ router.post( ); } - // technically impossible, so let's ignore it. - /* istanbul ignore next */ return "Unknown charts.type."; }, }, @@ -220,19 +218,9 @@ router.get("/:goalID", GetGoalSubscription, async (req, res) => { let milestones: MilestoneDocument[] = []; if (goalSub.parentMilestones.length !== 0) { - milestones = await db.milestones.find({ - milestoneID: { $in: goalSub.parentMilestones }, - }); - - if (milestones.length !== goalSub.parentMilestones.length) { - logger.error( - `Goal '${goalSub.goalID} (uid:${goalSub.userID})' refers to parent milestones that don't exist?` - ); - return res.status(500).json({ - success: false, - description: `Something went wrong when fetching parent milestones. This has been reported.`, - }); - } + milestones = await Promise.all( + goalSub.parentMilestones.map((e) => GetMilestoneForIDGuaranteed(e)) + ); } const goal = await GetGoalForIDGuaranteed(goalSub.goalID); @@ -259,6 +247,7 @@ router.delete( RequireAuthedAsUser, GetGoalSubscription, RequirePermissions("manage_targets"), + prValidate({ goalID: "string" }), async (req, res) => { const goalID = req.params.goalID; const { user, game, playtype } = GetUGPT(req); @@ -271,7 +260,7 @@ router.delete( }); if (!goalSub) { - return res.status(404).json({ + return res.status(400).json({ success: false, description: `You aren't subscribed to this goal.`, }); diff --git a/server/src/test-utils/test-data.ts b/server/src/test-utils/test-data.ts index f6b0a5f3d..b40ff3703 100644 --- a/server/src/test-utils/test-data.ts +++ b/server/src/test-utils/test-data.ts @@ -540,7 +540,6 @@ export const uscChart: ChartDocument<"usc:Controller" | "usc:Keyboard"> = { hashSHA1: "USC_CHART_HASH", isOfficial: false, effector: "foo", - tableFolders: [], }, tierlistInfo: {}, isPrimary: true, diff --git a/server/src/utils/types.ts b/server/src/utils/types.ts index b265f5d09..ab8a5869c 100644 --- a/server/src/utils/types.ts +++ b/server/src/utils/types.ts @@ -16,7 +16,6 @@ import { GoalSubscriptionDocument, MilestoneDocument, MilestoneSetDocument, - integer, } from "tachi-common"; declare module "express-session" { @@ -76,11 +75,3 @@ export interface TachiRequestData { apiClientDoc: Omit; } - -// This is only used on tachi-server, and isn't exposed -- so shouldn't be a part -// of common. -export interface PrivateUserInfoDocument { - userID: integer; - password: string; - email: string; -} diff --git a/server/tap-snapshots/src/test-utils/single-process-tap.ts.test.cjs b/server/tap-snapshots/src/test-utils/single-process-tap.ts.test.cjs deleted file mode 100644 index e64f5ce19..000000000 --- a/server/tap-snapshots/src/test-utils/single-process-tap.ts.test.cjs +++ /dev/null @@ -1,135 +0,0 @@ - -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: abs with countNum but charts.type == single 1`] = ` -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. -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: abs without countNum 1`] = ` -[criteria.countNum] Expected number to be greater than or equal to 0. (Received nothing) -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data array of chartIDs that don't exist 1`] = ` -Expected charts.data to match 2 charts. Instead, it only matched 1. Are all of these chartIDs valid? -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data array of identical chartIDs 1`] = ` -Expected charts.data to match 2 charts. Instead, it only matched 1. Are all of these chartIDs valid? -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data array of single chartID 1`] = ` -[charts.data] Expected an array of 2 to 5 strings in charts.data due to charts.type being 'multi'. (Received c2311194e3897ddb5745b1760d2c0141f933e683) -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data array when type == single 1`] = ` -[charts.data] Expected a string in charts.data due to charts.type being 'single'. (Received c2311194e3897ddb5745b1760d2c0141f933e683,c2311194e3897ddb5745b1760d2c0141f933e683) -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data folder refers to folder that doesn't exist 1`] = ` -A folder with id fake-folder does not exist for iidx:SP. -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data when type == any 1`] = ` -[charts.data] Invalid charts.data for type 'any'. Must not have any data! (Received foo) -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: negative percent 1`] = ` -[criteria.value] Expected number to be greater than or equal to 0. (Received -1) -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: nonsense charts.data 1`] = ` -A chart with id nonsense does not exist for iidx:SP. -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: nonsense charts.type 1`] = ` -[charts.type] Expected any of single, multi, folder, any. (Received nonsense) -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: nonsense mode 1`] = ` -[criteria.mode] Expected any of single, abs, proportion. (Received nonsense) -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: percent greater than 100 1`] = ` -Invalid value of 100.1 for percent goal. Percents must be between 0 and 100. -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: percent of 0 is a non-goal 1`] = ` -Invalid value of 0 for percent goal. Percents must be between 0 and 100. -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: proportion without countNum 1`] = ` -[criteria.countNum] Expected number to be greater than or equal to 0. (Received nothing) -` - -exports[`src/test-utils/single-process-tap.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: single with countNum 1`] = ` -[criteria.countNum] Invalid countNum for mode 'single'. Must not have one! (Received 123) -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: abs with countNum but charts.type == single 1`] = ` -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. -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: abs without countNum 1`] = ` -[criteria.countNum] Expected number to be greater than or equal to 0. (Received nothing) -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data array of chartIDs that don't exist 1`] = ` -Expected charts.data to match 2 charts. Instead, it only matched 1. Are all of these chartIDs valid? -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data array of identical chartIDs 1`] = ` -Expected charts.data to match 2 charts. Instead, it only matched 1. Are all of these chartIDs valid? -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data array of single chartID 1`] = ` -[charts.data] Expected an array of 2 to 5 strings in charts.data due to charts.type being 'multi'. (Received c2311194e3897ddb5745b1760d2c0141f933e683) -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data array when type == single 1`] = ` -[charts.data] Expected a string in charts.data due to charts.type being 'single'. (Received c2311194e3897ddb5745b1760d2c0141f933e683,c2311194e3897ddb5745b1760d2c0141f933e683) -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data folder refers to folder that doesn't exist 1`] = ` -A folder with id fake-folder does not exist for iidx:SP. -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: charts.data when type == any 1`] = ` -[charts.data] Invalid charts.data for type 'any'. Must not have any data! (Received foo) -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: negative percent 1`] = ` -[criteria.value] Expected number to be greater than or equal to 0. (Received -1) -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: nonsense charts.data 1`] = ` -A chart with id nonsense does not exist for iidx:SP. -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: nonsense charts.type 1`] = ` -[charts.type] Expected any of single, multi, folder, any. (Received nonsense) -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: nonsense mode 1`] = ` -[criteria.mode] Expected any of single, abs, proportion. (Received nonsense) -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: percent greater than 100 1`] = ` -Invalid value of 100.1 for percent goal. Percents must be between 0 and 100. -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: percent of 0 is a non-goal 1`] = ` -Invalid value of 0 for percent goal. Percents must be between 0 and 100. -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: proportion without countNum 1`] = ` -[criteria.countNum] Expected number to be greater than or equal to 0. (Received nothing) -` - -exports[`src/server/router/api/v1/users/_userID/games/_game/_playtype/targets/goals/router.test.ts TAP POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal Should reject invalid goals. > Invalid Goal: single with countNum 1`] = ` -[criteria.countNum] Invalid countNum for mode 'single'. Must not have one! (Received 123) -`