Full Add-Goal Tests

This commit is contained in:
zkldi
2022-04-13 20:50:05 +01:00
parent de6fe2ebe3
commit 567ab7ce5d
5 changed files with 345 additions and 26 deletions
+2 -1
View File
@@ -159,7 +159,8 @@ export async function ValidateGoalChartsAndCriteria(
}
chartCount = multiCharts.length;
} else if (charts.type === "any") {
} else {
// (charts.type === "any")
chartCount = await db.charts[game].count({ playtype });
}
@@ -1,6 +1,7 @@
/* eslint-disable no-await-in-loop */
import dm from "deepmerge";
import db from "external/mongo/db";
import { ChartDocument } from "tachi-common";
import { ChartDocument, IIDX_GRADES, IIDX_LAMPS } from "tachi-common";
import t from "tap";
import { CreateFakeAuthCookie } from "test-utils/fake-auth";
import mockApi from "test-utils/mock-api";
@@ -10,6 +11,7 @@ import {
HC511Goal,
HC511UserGoal,
Testing511SPA,
TestingIIDXFolderSP10,
TestingIIDXSPMilestone,
} from "test-utils/test-data";
@@ -57,7 +59,7 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn
const baseInput = {
criteria: {
key: "scoreData.percent",
value: 1,
value: 80,
mode: "single",
},
charts: {
@@ -67,30 +69,181 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn
};
function mkInput(caseName: string, merge: any) {
return {
const input = {
caseName,
content: dm(baseInput, merge),
content: dm(baseInput, merge) as any,
};
// if we've set this prop to undefined, we want to delete it, actually.
if (input.content.charts.data === undefined) {
delete input.content.charts.data;
}
return input;
}
t.beforeEach(() => db.charts.iidx.insert(anotherFakeChart));
// @ts-expect-error weird type issues
t.beforeEach(() => db.folders.insert(dm(TestingIIDXFolderSP10, {})));
const multiCharts = {
type: "multi",
data: [Testing511SPA.chartID, "another_chart"],
};
const anyCharts = {
type: "any",
data: undefined,
};
const folderCharts = {
type: "folder",
data: TestingIIDXFolderSP10.folderID,
};
t.test("Should set valid goals.", async (t) => {
const validInput = [
mkInput("Base Case", baseInput),
mkInput("Percent:Single Case", {}),
mkInput("Score:Single Case", {
criteria: {
key: "scoreData.score",
value: 1,
mode: "",
value: 1000,
},
}),
mkInput("LampIndex:Single Case", {
criteria: {
key: "scoreData.lampIndex",
value: IIDX_LAMPS.EX_HARD_CLEAR,
},
}),
mkInput("GradeIndex:Single Case", {
criteria: {
key: "scoreData.gradeIndex",
value: IIDX_GRADES.AAA,
},
}),
mkInput("Percent:Multi Case", {
charts: multiCharts,
}),
// NOT LEGAL FOR IIDX, due to being nonsense! NEEDS SPECIFIC TESTING.
// mkInput("Score:Multi Case", {
// criteria: {
// key: "scoreData.score",
// value: 1,
// },
// charts: multiCharts,
// }),
mkInput("LampIndex:Multi Case", {
criteria: {
key: "scoreData.lampIndex",
value: IIDX_LAMPS.HARD_CLEAR,
},
charts: multiCharts,
}),
mkInput("GradeIndex:Multi Case", {
criteria: {
key: "scoreData.gradeIndex",
value: IIDX_GRADES.AAA,
},
charts: multiCharts,
}),
mkInput("Percent:Any Case", {
charts: anyCharts,
}),
// also illegal in iidx, bms and pms.
// mkInput("Score:Any Case", {
// criteria: {
// key: "scoreData.score",
// value: 1000,
// },
// charts: anyCharts,
// }),
mkInput("LampIndex:Any Case", {
criteria: {
key: "scoreData.lampIndex",
value: IIDX_LAMPS.HARD_CLEAR,
},
charts: anyCharts,
}),
mkInput("GradeIndex:Any Case", {
criteria: {
key: "scoreData.gradeIndex",
value: IIDX_GRADES.AAA,
},
charts: anyCharts,
}),
mkInput("Percent:Folder Case", {
charts: folderCharts,
}),
// also illegal in iidx, bms and pms.
// mkInput("Score:Folder Case", {
// criteria: {
// key: "scoreData.score",
// value: 1000,
// },
// charts: folderCharts,
// }),
mkInput("LampIndex:Folder Case", {
criteria: {
key: "scoreData.lampIndex",
value: IIDX_LAMPS.HARD_CLEAR,
},
charts: folderCharts,
}),
mkInput("GradeIndex:Folder Case", {
criteria: {
key: "scoreData.gradeIndex",
value: IIDX_GRADES.AAA,
},
charts: folderCharts,
}),
];
for (const input of validInput) {
await t.test(`Valid Goal: ${input.caseName}`, async (t) => {
const res = await mockApi
.post("/api/v1/users/1/games/iidx/SP/targets/goals/add-goal")
.set("Cookie", cookie)
.send(input.content);
t.equal(
// this looks stupid, but results in better errmsgs.
res.statusCode === 200 ? 200 : res.body.description,
200,
"Should return 200"
);
// don't bother doing circular db checks if this is good.
if (res.statusCode !== 200) {
return;
}
const existsInDb = await db.goals.findOne({
charts: input.content.charts,
criteria: input.content.criteria,
});
t.not(existsInDb, null, "A new goal should be added to the database.");
const subscribed = await db["goal-subs"].findOne({
userID: 1,
goalID: existsInDb?.goalID ?? "edge_case",
});
t.not(
subscribed,
null,
`Requesting user 1 should be subscribed to their new goal.`
);
t.end();
});
}
t.end();
});
t.test("Should reject invalid goals.", async (t) => {
await db.charts.iidx.insert(anotherFakeChart);
const invalidInput = [
mkInput("negative percent", {
criteria: {
@@ -107,15 +260,47 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn
value: 100.1,
},
}),
mkInput("too big grade", {
criteria: {
key: "scoreData.gradeIndex",
value: IIDX_GRADES.MAX + 1,
},
}),
mkInput("invalid grade", {
criteria: {
key: "scoreData.gradeIndex",
value: 0.5,
},
}),
mkInput("too big lamp", {
criteria: {
key: "scoreData.lampIndex",
value: IIDX_LAMPS.FULL_COMBO + 1,
},
}),
mkInput("invalid lamp", {
criteria: {
key: "scoreData.lampIndex",
value: 0.5,
},
}),
mkInput("too big score", {
criteria: {
key: "scoreData.gradeIndex",
value: 9_000,
},
}),
mkInput("abs without countNum", {
criteria: {
mode: "abs",
},
charts: multiCharts,
}),
mkInput("proportion without countNum", {
criteria: {
mode: "proportion",
},
charts: multiCharts,
}),
mkInput("single with countNum", {
criteria: {
@@ -123,6 +308,27 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn
countNum: 123,
},
}),
mkInput("proportion with negative countNum", {
criteria: {
mode: "proportion",
countNum: -0.5,
},
charts: multiCharts,
}),
mkInput("proportion with greater than 1 countNum", {
criteria: {
mode: "proportion",
countNum: 1.1,
},
charts: multiCharts,
}),
mkInput("proportion which results in countNum of 0", {
criteria: {
mode: "proportion",
countNum: 0.05,
},
charts: multiCharts,
}),
mkInput("nonsense mode", {
criteria: {
mode: "nonsense",
@@ -151,7 +357,7 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn
mkInput("charts.data array when type == single", {
charts: {
type: "single",
data: [Testing511SPA.chartID, Testing511SPA.chartID],
data: multiCharts.data,
},
}),
mkInput("charts.data array of identical chartIDs", {
@@ -166,7 +372,7 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn
data: [Testing511SPA.chartID],
},
}),
mkInput("charts.data array of chartIDs that don't exist", {
mkInput("charts.data array of chartIDs where some don't exist", {
charts: {
type: "multi",
data: [Testing511SPA.chartID, "not_exist"],
@@ -186,30 +392,90 @@ t.test("POST /api/v1/users/:userID/games/:game/:playtype/targets/add-goal", asyn
mkInput("multi-score for iidx is illegal", {
criteria: {
key: "scoreData.score",
value: 1,
value: 1000,
},
charts: {
type: "multi",
data: [Testing511SPA.chartID, "another_chart"],
charts: multiCharts,
}),
mkInput("folder-score for iidx is illegal", {
criteria: {
key: "scoreData.score",
value: 1000,
},
charts: folderCharts,
}),
mkInput("any-score for iidx is illegal", {
criteria: {
key: "scoreData.score",
value: 1000,
},
charts: anyCharts,
}),
mkInput("charts single but criteria not.", {
criteria: {
mode: "abs",
countNum: 2,
},
}),
];
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);
await t.test(`Invalid Goal: ${input.caseName}`, async (t) => {
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.equal(res.statusCode, 400);
TestSnapshot(t, res.body.description, `Invalid Goal: ${input.caseName}`);
TestSnapshot(t, res.body.description, `Invalid Goal: ${input.caseName}`);
t.end();
});
}
t.end();
});
t.test("Should reject if user tries to subscribe to already subscribed goal.", async (t) => {
const res = await mockApi
.post("/api/v1/users/1/games/iidx/SP/targets/goals/add-goal")
.set("Cookie", cookie)
.send(baseInput);
t.equal(res.statusCode, 200, "Should allow a subscription the first time.");
const res2 = await mockApi
.post("/api/v1/users/1/games/iidx/SP/targets/goals/add-goal")
.set("Cookie", cookie)
.send(baseInput);
t.equal(res2.statusCode, 409, "Should fail the second time.");
t.end();
});
t.test("Should reject if user tries to subscribe to immediately achieved goal.", async (t) => {
const res = await mockApi
.post("/api/v1/users/1/games/iidx/SP/targets/goals/add-goal")
.set("Cookie", cookie)
.send(
dm(baseInput, {
criteria: {
key: "scoreData.score",
value: 1,
},
})
);
t.equal(
res.statusCode,
400,
"Should disallow goal subscriptions that would be instantly achieved."
);
t.end();
});
t.end();
});
@@ -114,12 +114,15 @@ router.post(
"Expected an array of 2 to 5 strings in charts.data due to charts.type being 'multi'."
);
} else if (parent.type === "folder") {
/* istanbul ignore previous */
return (
typeof self === "string" ||
"Expected a string in charts.data due to charts.type being 'folder'."
);
}
// impossible to reach, so doesn't count for coverage.
/* istanbul ignore next */
return "Unknown charts.type.";
},
},
@@ -1 +1,38 @@
[]
[
{
"chartID": "c2311194e3897ddb5745b1760d2c0141f933e683",
"userID": 1,
"calculatedData": {
"ktLampRating": 0,
"BPI": 10.1
},
"composedFrom": {
"scorePB": "TESTING_SCORE_ID",
"lampPB": "TESTING_SCORE_ID"
},
"highlight": false,
"isPrimary": true,
"scoreData": {
"score": 786,
"lamp": "HARD CLEAR",
"percent": 50,
"grade": "C",
"esd": 30,
"gradeIndex": 3,
"lampIndex": 4,
"judgements": {
"pgreat": 50,
"great": 10
},
"hitMeta": {}
},
"rankingData": {
"rank": 1,
"outOf": 2
},
"songID": 1,
"game": "iidx",
"playtype": "SP",
"timeAchieved": 10000
}
]
@@ -9,11 +9,23 @@
"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 when type == single": "[charts.data] Expected a string in charts.data due to charts.type being 'single'. (Received c2311194e3897ddb5745b1760d2c0141f933e683,another_chart)",
"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."
"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.",
"Invalid Goal: too big grade": "Invalid value of 10 for grade goal. No such grade exists at that index.",
"Invalid Goal: invalid grade": "Invalid value of 0.5 for grade goal. No such grade exists at that index.",
"Invalid Goal: too big lamp": "Invalid value of 8 for lamp goal. No such lamp exists at that index.",
"Invalid Goal: invalid lamp": "Invalid value of 0.5 for lamp goal. No such lamp exists at that index.",
"Invalid Goal: too big score": "Invalid value of 9000 for grade goal. No such grade exists at that index.",
"Invalid Goal: proportion with negative countNum": "[criteria.countNum] Expected number to be greater than or equal to 0. (Received -0.5)",
"Invalid Goal: proportion with greater than 1 countNum": "Invalid countNum for goal with criteria.mode of 'proportion'. Expected a decimal in (0, 1]",
"Invalid Goal: proportion which results in countNum of 0": "countNum is too small for goal. Would result in requiring 0 charts to achieve the goal.",
"Invalid Goal: charts.data array of chartIDs where some don't exist": "Expected charts.data to match 2 charts. Instead, it only matched 1. Are all of these chartIDs valid?",
"Invalid Goal: folder-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.",
"Invalid Goal: any-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.",
"Invalid Goal: charts single but criteria not.": "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."
}