mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-08 22:59:52 +03:00
test user endpoints
This commit is contained in:
+317
-2
@@ -1,15 +1,330 @@
|
||||
import t from "tap";
|
||||
import db from "../../../../../../../../../external/mongo/db";
|
||||
import { CloseAllConnections } from "../../../../../../../../../test-utils/close-connections";
|
||||
import mockApi from "../../../../../../../../../test-utils/mock-api";
|
||||
import ResetDBState from "../../../../../../../../../test-utils/resets";
|
||||
import {
|
||||
ScoreDocument,
|
||||
UserGoalDocument,
|
||||
GoalDocument,
|
||||
MilestoneDocument,
|
||||
UserMilestoneDocument,
|
||||
PBScoreDocument,
|
||||
} from "tachi-common";
|
||||
import { Testing511Song, Testing511SPA } from "../../../../../../../../../test-utils/test-data";
|
||||
|
||||
t.test("GET /api/v1/users/:userID/games/:game/:playtype/", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should return a users statistics for that game.", async (t) => {
|
||||
const res = await mockApi.get("/api/v1/users/zkldi/games/iidx/SP");
|
||||
const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP");
|
||||
|
||||
t.strictSame(res.body, {});
|
||||
t.hasStrict(res.body, {
|
||||
success: true,
|
||||
description: "Retrieved user statistics for test_zkldi (iidx SP)",
|
||||
body: {
|
||||
gameStats: {
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
classes: {},
|
||||
ratings: {},
|
||||
},
|
||||
firstScore: null,
|
||||
mostRecentScore: null,
|
||||
totalScores: 1,
|
||||
rankingData: {
|
||||
ranking: 1,
|
||||
outOf: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Should return a users first score if one exists.", async (t) => {
|
||||
await db.scores.insert([
|
||||
{
|
||||
userID: 1,
|
||||
timeAchieved: 100,
|
||||
scoreID: "foo",
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
},
|
||||
{
|
||||
userID: 1,
|
||||
timeAchieved: 200,
|
||||
scoreID: "bar",
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
},
|
||||
{
|
||||
userID: 1,
|
||||
timeAchieved: 300,
|
||||
scoreID: "baz",
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
},
|
||||
] as ScoreDocument[]);
|
||||
|
||||
const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP");
|
||||
|
||||
t.hasStrict(res.body, {
|
||||
success: true,
|
||||
description: "Retrieved user statistics for test_zkldi (iidx SP)",
|
||||
body: {
|
||||
gameStats: {
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
classes: {},
|
||||
ratings: {},
|
||||
},
|
||||
firstScore: {
|
||||
timeAchieved: 100,
|
||||
scoreID: "foo",
|
||||
},
|
||||
mostRecentScore: {
|
||||
timeAchieved: 300,
|
||||
scoreID: "baz",
|
||||
},
|
||||
totalScores: 4,
|
||||
rankingData: {
|
||||
ranking: 1,
|
||||
outOf: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("GET /api/v1/users/:userID/games/:game/:playtype/goals", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test(
|
||||
"Should return all of a users goals, and only unachieved goals if the argument is set.",
|
||||
async (t) => {
|
||||
await db["user-goals"].insert([
|
||||
{
|
||||
goalID: "foo",
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
achieved: false,
|
||||
},
|
||||
{
|
||||
goalID: "bar",
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
achieved: true,
|
||||
},
|
||||
] as UserGoalDocument[]);
|
||||
|
||||
await db.goals.insert([{ goalID: "foo" }, { goalID: "bar" }] as GoalDocument[]);
|
||||
|
||||
const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/goals");
|
||||
|
||||
t.hasStrict(res.body, {
|
||||
success: true,
|
||||
description: "Successfully returned 2 goal(s).",
|
||||
body: {
|
||||
userGoals: [
|
||||
{
|
||||
goalID: "foo",
|
||||
achieved: false,
|
||||
},
|
||||
{
|
||||
goalID: "bar",
|
||||
achieved: true,
|
||||
},
|
||||
],
|
||||
goals: [{ goalID: "bar" }, { goalID: "foo" }],
|
||||
},
|
||||
});
|
||||
|
||||
const resUnachieved = await mockApi.get(
|
||||
"/api/v1/users/1/games/iidx/SP/goals?unachieved=true"
|
||||
);
|
||||
|
||||
t.hasStrict(resUnachieved.body, {
|
||||
success: true,
|
||||
description: "Successfully returned 1 goal(s).",
|
||||
body: {
|
||||
userGoals: [
|
||||
{
|
||||
goalID: "foo",
|
||||
achieved: false,
|
||||
},
|
||||
],
|
||||
goals: [{ goalID: "foo" }],
|
||||
},
|
||||
});
|
||||
|
||||
t.end();
|
||||
}
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("GET /api/v1/users/:userID/games/:game/:playtype/milestones", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test(
|
||||
"Should return all of a users milestones, and only unachieved milestones if the argument is set.",
|
||||
async (t) => {
|
||||
await db["user-milestones"].insert([
|
||||
{
|
||||
milestoneID: "foo",
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
achieved: false,
|
||||
},
|
||||
{
|
||||
milestoneID: "bar",
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
achieved: true,
|
||||
},
|
||||
] as UserMilestoneDocument[]);
|
||||
|
||||
await db.milestones.insert([
|
||||
{ milestoneID: "foo" },
|
||||
{ milestoneID: "bar" },
|
||||
] as MilestoneDocument[]);
|
||||
|
||||
const res = await mockApi.get("/api/v1/users/1/games/iidx/SP/milestones");
|
||||
|
||||
t.hasStrict(res.body, {
|
||||
success: true,
|
||||
description: "Successfully returned 2 milestone(s).",
|
||||
body: {
|
||||
userMilestones: [
|
||||
{
|
||||
milestoneID: "foo",
|
||||
achieved: false,
|
||||
},
|
||||
{
|
||||
milestoneID: "bar",
|
||||
achieved: true,
|
||||
},
|
||||
],
|
||||
milestones: [{ milestoneID: "bar" }, { milestoneID: "foo" }],
|
||||
},
|
||||
});
|
||||
|
||||
const resUnachieved = await mockApi.get(
|
||||
"/api/v1/users/1/games/iidx/SP/milestones?unachieved=true"
|
||||
);
|
||||
|
||||
t.hasStrict(resUnachieved.body, {
|
||||
success: true,
|
||||
description: "Successfully returned 1 milestone(s).",
|
||||
body: {
|
||||
userMilestones: [
|
||||
{
|
||||
milestoneID: "foo",
|
||||
achieved: false,
|
||||
},
|
||||
],
|
||||
milestones: [{ milestoneID: "foo" }],
|
||||
},
|
||||
});
|
||||
|
||||
t.end();
|
||||
}
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("GET /api/v1/users/:userID/games/:game/:playtype/recent-scores", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should return a users 100 most recent scores.", async (t) => {
|
||||
const mockScores: ScoreDocument[] = [];
|
||||
|
||||
for (let i = 0; i < 200; i++) {
|
||||
mockScores.push({
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
timeAchieved: i * 100,
|
||||
scoreID: `scoreID_${i}`,
|
||||
chartID: Testing511SPA.chartID,
|
||||
songID: Testing511Song.id,
|
||||
} as ScoreDocument);
|
||||
}
|
||||
|
||||
await db.scores.insert(mockScores);
|
||||
|
||||
for (const sc of mockScores) {
|
||||
delete sc._id; // lol
|
||||
}
|
||||
|
||||
const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/recent-scores");
|
||||
|
||||
t.hasStrict(res.body, {
|
||||
success: true,
|
||||
description: "Retrieved 100 scores.",
|
||||
body: {
|
||||
scores: mockScores.slice(100).reverse(),
|
||||
songs: [Testing511Song],
|
||||
charts: [Testing511SPA],
|
||||
},
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("GET /api/v1/users/:userID/games/:game/:playtype/best", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should return a users 100 most recent scores.", async (t) => {
|
||||
const mockPBs: PBScoreDocument[] = [];
|
||||
|
||||
for (let i = 0; i < 200; i++) {
|
||||
mockPBs.push({
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
isPrimary: true,
|
||||
chartID: i.toString(), // hack to generate some random chartIDs
|
||||
songID: Testing511Song.id,
|
||||
calculatedData: {
|
||||
ktRating: i,
|
||||
},
|
||||
} as PBScoreDocument);
|
||||
}
|
||||
|
||||
await db["personal-bests"].insert(mockPBs);
|
||||
|
||||
for (const sc of mockPBs) {
|
||||
delete sc._id; // lol
|
||||
}
|
||||
|
||||
const res = await mockApi.get("/api/v1/users/test_zkldi/games/iidx/SP/best");
|
||||
|
||||
t.hasStrict(res.body, {
|
||||
success: true,
|
||||
description: "Retrieved 100 scores.",
|
||||
body: {
|
||||
scores: mockPBs.slice(100).reverse(),
|
||||
songs: [Testing511Song],
|
||||
charts: [],
|
||||
},
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -4,6 +4,8 @@ import { SYMBOL_TachiData } from "../../../../../../../../../lib/constants/tachi
|
||||
import { GetRelevantSongsAndCharts } from "../../../../../../../../../utils/db";
|
||||
import { GetDefaultScoreRatingAlg, GetUsersRanking } from "../../../../../../../../../utils/user";
|
||||
import { CheckUserPlayedGamePlaytype } from "./middleware";
|
||||
import { FilterQuery } from "mongodb";
|
||||
import { UserGoalDocument, UserMilestoneDocument } from "tachi-common";
|
||||
|
||||
const router: Router = Router({ mergeParams: true });
|
||||
|
||||
@@ -30,6 +32,7 @@ router.get("/", async (req, res) => {
|
||||
userID: user.id,
|
||||
game,
|
||||
playtype,
|
||||
timeAchieved: { $ne: null },
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
@@ -42,6 +45,7 @@ router.get("/", async (req, res) => {
|
||||
userID: user.id,
|
||||
game,
|
||||
playtype,
|
||||
timeAchieved: { $ne: null },
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
@@ -76,12 +80,17 @@ router.get("/goals", async (req, res) => {
|
||||
const game = req[SYMBOL_TachiData]!.game!;
|
||||
const playtype = req[SYMBOL_TachiData]!.playtype!;
|
||||
|
||||
const userGoals = await db["user-goals"].find({
|
||||
const query: FilterQuery<UserGoalDocument> = {
|
||||
userID: user.id,
|
||||
game,
|
||||
playtype,
|
||||
achieved: req.query.unachieved ? false : undefined,
|
||||
});
|
||||
};
|
||||
|
||||
if (req.query.unachieved) {
|
||||
query.achieved = false;
|
||||
}
|
||||
|
||||
const userGoals = await db["user-goals"].find(query);
|
||||
|
||||
const goals = await db.goals.find({
|
||||
goalID: { $in: userGoals.map((e) => e.goalID) },
|
||||
@@ -89,7 +98,7 @@ router.get("/goals", async (req, res) => {
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
description: `Successfully returned ${userGoals.length} goals.`,
|
||||
description: `Successfully returned ${userGoals.length} goal(s).`,
|
||||
body: {
|
||||
userGoals,
|
||||
goals,
|
||||
@@ -108,12 +117,17 @@ router.get("/milestones", async (req, res) => {
|
||||
const game = req[SYMBOL_TachiData]!.game!;
|
||||
const playtype = req[SYMBOL_TachiData]!.playtype!;
|
||||
|
||||
const userMilestones = await db["user-milestones"].find({
|
||||
const query: FilterQuery<UserMilestoneDocument> = {
|
||||
userID: user.id,
|
||||
game,
|
||||
playtype,
|
||||
achieved: req.query.unachieved ? false : undefined,
|
||||
});
|
||||
};
|
||||
|
||||
if (req.query.unachieved) {
|
||||
query.achieved = false;
|
||||
}
|
||||
|
||||
const userMilestones = await db["user-milestones"].find(query);
|
||||
|
||||
const milestones = await db.milestones.find({
|
||||
milestoneID: { $in: userMilestones.map((e) => e.milestoneID) },
|
||||
@@ -121,7 +135,7 @@ router.get("/milestones", async (req, res) => {
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
description: `Successfully returned ${userMilestones.length} milestones.`,
|
||||
description: `Successfully returned ${userMilestones.length} milestone(s).`,
|
||||
body: {
|
||||
userMilestones,
|
||||
milestones,
|
||||
|
||||
@@ -79,6 +79,7 @@ interface ExpressJSONErr extends SyntaxError {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const MAIN_ERR_HANDLER: express.ErrorRequestHandler = (err, req, res) => {
|
||||
logger.info("foo");
|
||||
if (err instanceof SyntaxError) {
|
||||
const expErr: ExpressJSONErr = err as ExpressJSONErr;
|
||||
if (expErr.status === 400 && "body" in expErr) {
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
[]
|
||||
[
|
||||
{
|
||||
"userID": 1,
|
||||
"game": "iidx",
|
||||
"playtype": "SP",
|
||||
"classes": {},
|
||||
"ratings": {}
|
||||
}
|
||||
]
|
||||
+14
-11
@@ -163,16 +163,19 @@ export async function GetUsersRanking(stats: UserGameStats) {
|
||||
},
|
||||
},
|
||||
{
|
||||
total: { $sum: 1 },
|
||||
ranking: {
|
||||
$sum: {
|
||||
$cond: {
|
||||
if: {
|
||||
// @ts-expect-error garbage...
|
||||
$gte: [`$ratings.${ratingKey}`, stats.ratings[ratingKey]],
|
||||
$group: {
|
||||
_id: null,
|
||||
outOf: { $sum: 1 },
|
||||
ranking: {
|
||||
$sum: {
|
||||
$cond: {
|
||||
if: {
|
||||
// @ts-expect-error garbage...
|
||||
$gte: [`$ratings.${ratingKey}`, stats.ratings[ratingKey]],
|
||||
},
|
||||
then: 1,
|
||||
else: 0,
|
||||
},
|
||||
then: 1,
|
||||
else: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -180,7 +183,7 @@ export async function GetUsersRanking(stats: UserGameStats) {
|
||||
]);
|
||||
|
||||
return {
|
||||
ranking: aggRes.ranking,
|
||||
total: aggRes.total,
|
||||
ranking: (aggRes[0].ranking + 1) as integer,
|
||||
outOf: aggRes[0].outOf as integer,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user