test: fix tests and tear out unused endpoints

This commit is contained in:
zkldi
2022-11-25 23:49:33 +00:00
parent 8c52b3ba48
commit 8a0309693f
14 changed files with 38 additions and 386 deletions
+2
View File
@@ -633,6 +633,8 @@ const PRE_SCHEMAS = {
game: p.isIn(games),
playtype: isValidPlaytype,
quests: ["string"],
name: "string",
desc: "string",
}),
quests: prSchemaFnWrap({
game: p.isIn(games),
@@ -117,4 +117,4 @@ an empty array as a result of this process.
Questline are ordered lists of quests. Their purpose is to group quests together
visually.
Users *can not* "subscribe" to quest sets, but they can use sets as a utility for subscribing to all the related quests.
Users *can not* "subscribe" to questlines, but they can use questlines as a utility for subscribing to all the related quests.
@@ -77,7 +77,7 @@ t.test("#UpdateGoalsForUser", (t) => {
},
new: {
progress: 1479,
progressHuman: "1479",
progressHuman: "1,479",
outOf: 1,
outOfHuman: "1",
achieved: true,
@@ -91,7 +91,7 @@ t.test("#UpdateGoalsForUser", (t) => {
r,
{
progress: 1479,
progressHuman: "1479",
progressHuman: "1,479",
outOf: 1,
outOfHuman: "1",
achieved: true,
@@ -31,7 +31,6 @@ t.test("#UpdateUsersQuests", (t) => {
timeAchieved: null,
lastInteraction: null,
userID: 1,
wasAssignedStandalone: false,
})
);
@@ -165,7 +164,6 @@ t.test("#UpdateUsersQuests", (t) => {
timeAchieved: null,
lastInteraction: null,
userID: 1,
wasAssignedStandalone: false,
});
const res = await UpdateUsersQuests(
@@ -276,7 +274,6 @@ t.test("#UpdateUsersQuests", (t) => {
timeAchieved: null,
lastInteraction: null,
userID: 1,
wasAssignedStandalone: false,
},
{
achieved: false,
@@ -289,7 +286,6 @@ t.test("#UpdateUsersQuests", (t) => {
timeAchieved: null,
lastInteraction: null,
userID: 1,
wasAssignedStandalone: false,
},
{
achieved: false,
@@ -302,7 +298,6 @@ t.test("#UpdateUsersQuests", (t) => {
timeAchieved: null,
lastInteraction: null,
userID: 1,
wasAssignedStandalone: false,
},
{
achieved: false,
@@ -314,7 +309,6 @@ t.test("#UpdateUsersQuests", (t) => {
timeAchieved: null,
lastInteraction: null,
userID: 1,
wasAssignedStandalone: false,
},
]);
@@ -96,52 +96,3 @@ t.test("GET /api/v1/games/:game/:playtype/targets/goals/:goalID", (t) => {
t.end();
});
t.test("GET /api/v1/games/:game/:playtype/targets/goals/:goalID/evaluate-for", (t) => {
t.beforeEach(ResetDBState);
t.beforeEach(LoadLazySampleData);
t.test("Should evaluate a goal upon a user that exists.", async (t) => {
const res = await mockApi.get(
"/api/v1/games/iidx/SP/targets/goals/eg_goal_1/evaluate-for?userID=1"
);
t.equal(res.statusCode, 200, "Should return a status code of 200.");
t.strictSame(
res.body.body,
{
achieved: false,
outOf: 5,
outOfHuman: "HARD CLEAR",
progress: null,
progressHuman: "NO DATA",
},
"Should evaluate the goal for the user."
);
t.end();
});
t.test("Should return 404 if the user doesn't exist.", async (t) => {
const res = await mockApi.get(
"/api/v1/games/iidx/SP/targets/goals/eg_goal_1/evaluate-for?userID=unknown"
);
t.equal(res.statusCode, 404);
t.end();
});
t.test("Should return 400 if the user exists but hasn't played this game.", async (t) => {
const res = await mockApi.get(
"/api/v1/games/iidx/SP/targets/goals/eg_goal_1/evaluate-for?userID=2"
);
t.equal(res.statusCode, 400);
t.end();
});
t.end();
});
@@ -2,15 +2,13 @@ import { Router } from "express";
import db from "external/mongo/db";
import CreateLogCtx from "lib/logger/logger";
import { CreateGoalTitle, ValidateGoalChartsAndCriteria } from "lib/targets/goal-utils";
import { EvaluateGoalForUser, GetQuestsThatContainGoal } from "lib/targets/goals";
import { GetQuestsThatContainGoal } from "lib/targets/goals";
import p from "prudence";
import prValidate from "server/middleware/prudence-validate";
import { FormatGame } from "tachi-common";
import { GetMostSubscribedGoals } from "utils/db";
import { AssignToReqTachiData, GetGPT, GetTachiData } from "utils/req-tachi-data";
import { GetUsersWithIDs, ResolveUser } from "utils/user";
import { GetUsersWithIDs } from "utils/user";
import type { RequestHandler } from "express";
import type { EvaluatedGoalReturn } from "lib/targets/goals";
import type { GoalDocument } from "tachi-common";
const logger = CreateLogCtx(__filename);
@@ -181,87 +179,4 @@ router.get("/:goalID", ResolveGoalID, async (req, res) => {
});
});
/**
* Evaluates a goal upon a user, even if they aren't subscribed to it.
*
* @param userID - The userID to evaluate this goal against. Must be a player of this GPT.
*
* @name GET /api/v1/games/:game/:playtype/targets/goals/:goalID/evaluate-for
*/
router.get(
"/:goalID/evaluate-for",
ResolveGoalID,
prValidate({
userID: "string",
}),
async (req, res) => {
const { game, playtype } = GetGPT(req);
const userID = req.query.userID as string;
const user = await ResolveUser(userID);
if (!user) {
return res.status(404).json({
success: false,
description: `The user ${userID} does not exist.`,
});
}
const hasPlayed = await db["game-stats"].findOne({
game,
playtype,
userID: user.id,
});
if (!hasPlayed) {
return res.status(400).json({
success: false,
description: `The user ${user.username} hasn't played ${FormatGame(
game,
playtype
)}.`,
});
}
const goal = GetTachiData(req, "goalDoc");
const goalID = goal.goalID;
const goalSub = await db["goal-subs"].findOne({
userID: user.id,
goalID,
});
let goalResults: EvaluatedGoalReturn;
// shortcut evaluation by using the user goal
if (goalSub) {
goalResults = {
achieved: goalSub.achieved,
outOf: goalSub.outOf,
outOfHuman: goalSub.outOfHuman,
progress: goalSub.progress,
progressHuman: goalSub.progressHuman,
};
} else {
const results = await EvaluateGoalForUser(goal, user.id, logger);
if (!results) {
throw new Error(
`Failed to evaluate goal ${goal.name} (${goal.goalID}) for user ${user.id}. More information above.`
);
}
goalResults = results;
}
return res.status(200).json({
success: true,
description: `Evaluated ${goal.name} for ${user.username}.`,
body: goalResults,
});
}
);
export default router;
@@ -7,7 +7,7 @@ import { TestingIIDXSPQuest } from "test-utils/test-data";
import type { QuestDocument, QuestlineDocument } from "tachi-common";
const TestingIIDXSPQuestline: QuestlineDocument = {
name: "Testing Quest Set",
name: "Testing Questline",
desc: "foo",
game: "iidx",
quests: [TestingIIDXSPQuest.questID, "other_quest"],
@@ -22,7 +22,7 @@ t.test("GET /api/v1/games/:game/:playtype/targets/questlines", (t) => {
return dm(TestingIIDXSPQuestline, merge) as QuestlineDocument;
}
t.test("Should search the loaded quest sets for this game.", async (t) => {
t.test("Should return all questlines for this game.", async (t) => {
await db.questlines.insert([
mkSet({ name: "Testing Set", questlineID: "name" }),
mkSet({ name: "Testing Other Set", questlineID: "similar_name" }),
@@ -35,22 +35,18 @@ t.test("GET /api/v1/games/:game/:playtype/targets/questlines", (t) => {
mkSet({ playtype: "DP", questlineID: "matching name but different playtype" }),
]);
const res = await mockApi.get("/api/v1/games/iidx/SP/targets/questlines?search=Testing");
t.hasStrict(
(res.body.body as Array<QuestlineDocument>).sort((a, b) =>
a.name.localeCompare(b.name)
),
[{ questlineID: "similar_name" }, { questlineID: "name" }]
);
t.end();
});
t.test("Should mandate a search field.", async (t) => {
const res = await mockApi.get("/api/v1/games/iidx/SP/targets/questlines");
t.equal(res.statusCode, 400);
t.hasStrict(
(res.body.body.questlines as Array<QuestlineDocument>).sort((a, b) =>
a.name.localeCompare(b.name)
),
[
{ questlineID: "radically_different_name" },
{ questlineID: "similar_name" },
{ questlineID: "name" },
]
);
t.end();
});
@@ -68,7 +64,7 @@ t.test("GET /api/v1/games/:game/:playtype/targets/questlines/:questlineID", (t)
]);
});
t.test("Should return the quest set and its quests.", async (t) => {
t.test("Should return the questline and its quests.", async (t) => {
const res = await mockApi.get("/api/v1/games/iidx/SP/targets/questlines/quest_set");
t.equal(res.statusCode, 200, "Should return 200.");
@@ -87,7 +83,7 @@ t.test("GET /api/v1/games/:game/:playtype/targets/questlines/:questlineID", (t)
t.end();
});
t.test("Should return 404 if the quest set doesn't exist.", async (t) => {
t.test("Should return 404 if the questline doesn't exist.", async (t) => {
const res = await mockApi.get("/api/v1/games/iidx/SP/targets/questlines/foobar");
t.equal(res.statusCode, 404);
@@ -45,7 +45,7 @@ router.get("/", async (req, res) => {
return res.status(200).json({
success: true,
description: `Returned ${questlines.length} quest sets.`,
description: `Returned ${questlines.length} questlines.`,
body: { questlines, standalone, standaloneGoals },
});
});
@@ -64,7 +64,7 @@ router.get("/:questlineID", ResolveQuestlineID, async (req, res) => {
return res.status(200).json({
success: true,
description: `Retrieved quest set '${questline.name}'.`,
description: `Retrieved questline '${questline.name}'.`,
body: {
quests,
questline,
@@ -9,7 +9,6 @@ import {
IIDXSPQuestGoalSubs,
TestingIIDXSPQuest,
TestingIIDXSPQuestSub,
TestingIIDXSPScorePB,
} from "test-utils/test-data";
import type { QuestDocument, QuestlineDocument, QuestSubscriptionDocument } from "tachi-common";
@@ -38,7 +37,9 @@ t.test("GET /api/v1/games/:game/:playtype/targets/quests", (t) => {
t.equal(res.statusCode, 200);
t.hasStrict(
(res.body.body as Array<QuestDocument>).sort((a, b) => a.name.localeCompare(b.name)),
(res.body.body.quests as Array<QuestDocument>).sort((a, b) =>
a.name.localeCompare(b.name)
),
[{ questID: "similar_name" }, { questID: "name" }]
);
@@ -65,29 +66,6 @@ const LoadLazySampleData = async () => {
] as Array<QuestSubscriptionDocument>);
};
t.test("GET /api/v1/games/:game/:playtype/targets/quests/popular", (t) => {
t.beforeEach(ResetDBState);
t.beforeEach(LoadLazySampleData);
t.test("Should return the most subscribed quests for this GPT.", async (t) => {
const res = await mockApi.get("/api/v1/games/iidx/SP/targets/quests/popular");
t.equal(res.statusCode, 200);
t.hasStrict(
(res.body.body as Array<QuestDocument>).sort((a, b) => a.name.localeCompare(b.name)),
[
{ questID: TestingIIDXSPQuest.questID, __subscriptions: 2 },
{ questID: "other_quest", __subscriptions: 1 },
]
);
t.end();
});
t.end();
});
t.test("GET /api/v1/games/:game/:playtype/targets/quests/:questID", (t) => {
t.beforeEach(ResetDBState);
t.beforeEach(async () => {
@@ -143,121 +121,3 @@ t.test("GET /api/v1/games/:game/:playtype/targets/quests/:questID", (t) => {
t.end();
});
t.test("GET /api/v1/games/:game/:playtype/targets/quests/:questID/evaluate-for", (t) => {
t.beforeEach(ResetDBState);
t.beforeEach(LoadLazySampleData);
t.test("Should evaluate a quest for a user.", async (t) => {
// remove all quest subs -- the user isn't properly
// subscribed to this quest due to test mocking.
// this will force a live calc.
await db["quest-subs"].remove({});
await db["personal-bests"].insert(TestingIIDXSPScorePB);
const res = await mockApi.get(
`/api/v1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}/evaluate-for?userID=1`
);
t.equal(res.statusCode, 200);
t.hasStrict(res.body.body, {
goals: [
{
goalID: "eg_goal_1",
},
{
goalID: "eg_goal_2",
},
{
goalID: "eg_goal_3",
},
{
goalID: "eg_goal_4",
},
],
goalResults: [
{
achieved: true,
progress: 6,
outOf: 5,
progressHuman: "EX HARD CLEAR (BP: 2)",
outOfHuman: "HARD CLEAR",
goalID: "eg_goal_1",
},
{
achieved: true,
progress: 6,
outOf: 2,
progressHuman: "EX HARD CLEAR (BP: 2)",
outOfHuman: "ASSIST CLEAR",
goalID: "eg_goal_2",
},
{
achieved: true,
progress: 1479,
outOf: 300,
outOfHuman: "300",
progressHuman: "1479",
goalID: "eg_goal_3",
},
{
achieved: true,
progress: 1479,
outOf: 1100,
outOfHuman: "1100",
progressHuman: "1479",
goalID: "eg_goal_4",
},
],
achieved: true,
progress: 4,
outOf: 4,
});
t.end();
});
t.test("Should return 404 if the requested quest doesn't exist.", async (t) => {
const res = await mockApi.get(
"/api/v1/games/iidx/SP/targets/quests/fake_quest/evaluate-for?userID=1"
);
t.equal(res.statusCode, 404);
t.end();
});
t.test("Should return 404 if the quest exists but for a different GPT.", async (t) => {
const res = await mockApi.get(
`/api/v1/games/iidx/DP/targets/quests/${TestingIIDXSPQuest.questID}/evaluate-for?userID=1`
);
t.equal(res.statusCode, 404);
t.end();
});
t.test("Should return 404 if the requested user doesn't exist.", async (t) => {
const res = await mockApi.get(
`/api/v1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}/evaluate-for?userID=3`
);
t.equal(res.statusCode, 404);
t.end();
});
t.test("Should return 400 if the requested user hasn't played this gpt.", async (t) => {
const res = await mockApi.get(
`/api/v1/games/iidx/SP/targets/quests/${TestingIIDXSPQuest.questID}/evaluate-for?userID=2`
);
t.equal(res.statusCode, 400);
t.end();
});
t.end();
});
@@ -1,13 +1,10 @@
import { Router } from "express";
import db from "external/mongo/db";
import { SearchCollection } from "lib/search/search";
import { EvaluateQuestProgress, GetGoalsInQuest, GetGoalsInQuests } from "lib/targets/quests";
import prValidate from "server/middleware/prudence-validate";
import { FormatGame } from "tachi-common";
import { GetMostSubscribedQuests } from "utils/db";
import { GetGoalsInQuest, GetGoalsInQuests } from "lib/targets/quests";
import { IsString } from "utils/misc";
import { AssignToReqTachiData, GetGPT, GetTachiData } from "utils/req-tachi-data";
import { GetUsersWithIDs, ResolveUser } from "utils/user";
import { GetUsersWithIDs } from "utils/user";
import type { RequestHandler } from "express";
const router: Router = Router({ mergeParams: true });
@@ -61,24 +58,6 @@ router.get("/", async (req, res) => {
});
});
/**
* Find the most subscribed-to quests for this GPT.
*
* @name GET /api/v1/games/:game/:playtype/targets/quests/popular
*/
router.get("/popular", async (req, res) => {
const { game, playtype } = GetGPT(req);
const quests = await GetMostSubscribedQuests({ game, playtype });
const goals = await GetGoalsInQuests(quests);
return res.status(200).json({
success: true,
description: `Returned ${quests.length} popular quests.`,
body: { quests, goals },
});
});
/**
* Retrieve information about this quest and who is subscribed to it.
*
@@ -112,57 +91,4 @@ router.get("/:questID", ResolveQuestID, async (req, res) => {
});
});
/**
* Evaluates a quest upon a user, even if they aren't subscribed to it.
*
* @param userID - The userID to evaluate this goal against. Must be a player of this GPT.
*
* @name GET /api/v1/games/:game/:playtype/targets/quests/:questID/evaluate-for
*/
router.get(
"/:questID/evaluate-for",
ResolveQuestID,
prValidate({ userID: "string" }),
async (req, res) => {
const { game, playtype } = GetGPT(req);
const userID = req.query.userID as string;
const user = await ResolveUser(userID);
if (!user) {
return res.status(404).json({
success: false,
description: `The user ${userID} does not exist.`,
});
}
const hasPlayed = await db["game-stats"].findOne({
game,
playtype,
userID: user.id,
});
if (!hasPlayed) {
return res.status(400).json({
success: false,
description: `The user ${user.username} hasn't played ${FormatGame(
game,
playtype
)}.`,
});
}
const quest = GetTachiData(req, "questDoc");
const questProgress = await EvaluateQuestProgress(user.id, quest);
return res.status(200).json({
success: true,
description: `Evaluated ${quest.name} for ${user.username}.`,
body: questProgress,
});
}
);
export default router;
@@ -55,6 +55,8 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/goals", (t) => {
t.strictSame(res.body.body, {
goals: [HC511Goal],
goalSubs: [HC511UserGoal],
quests: [],
questSubs: [],
});
t.end();
@@ -4,6 +4,7 @@ import { CreateFakeAuthCookie } from "test-utils/fake-auth";
import mockApi from "test-utils/mock-api";
import ResetDBState from "test-utils/resets";
import {
HC511Goal,
IIDXSPQuestGoals,
IIDXSPQuestGoalSubs,
TestingIIDXSPQuest,
@@ -17,15 +18,21 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/targets/quests", (t) =>
t.test("Should return all quest subscriptions.", async (t) => {
await db.quests.insert(TestingIIDXSPQuest);
await db["quest-subs"].insert(TestingIIDXSPQuestSub);
await db.goals.insert(IIDXSPQuestGoals);
const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/targets/quests");
delete TestingIIDXSPQuest._id;
delete TestingIIDXSPQuestSub._id;
for (const goal of IIDXSPQuestGoals) {
delete goal._id;
}
t.strictSame(res.body.body, {
quests: [TestingIIDXSPQuest],
questSubs: [TestingIIDXSPQuestSub],
goals: IIDXSPQuestGoals,
});
t.end();
@@ -7,7 +7,7 @@
"Invalid Goal: single with countNum": "[criteria.countNum] Invalid countNum for mode 'single'. Must not have one! (Received 123)",
"Invalid Goal: nonsense mode": "[criteria.mode] Expected any of single, absolute, proportion. (Received nonsense)",
"Invalid Goal: abs with countNum but charts.type == single": "Invalid countNum for goal with criteria.mode of 'absolute'. Expected a whole number less than the total amount of charts available and greater than 1. (Got 1, while total charts was 1.)",
"Invalid Goal: nonsense charts.type": "[charts.type] Expected any of single, multi, folder, any. (Received nonsense)",
"Invalid Goal: nonsense charts.type": "[charts.type] Expected any of single, multi, folder. (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'.",
"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?",
-1
View File
@@ -666,7 +666,6 @@ export const TestingIIDXSPQuestSub: QuestSubscriptionDocument = {
progress: 4,
timeAchieved: null,
wasInstantlyAchieved: false,
wasAssignedStandalone: false,
};
let KTDATA_CACHE: