mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-17 13:08:49 +03:00
Merge pull request #403 from mchangrh/ratingFix
Fixed issues with ratings
This commit is contained in:
@@ -45,6 +45,7 @@ import { youtubeApiProxy } from "./routes/youtubeApiProxy";
|
|||||||
import { getChapterNames } from "./routes/getChapterNames";
|
import { getChapterNames } from "./routes/getChapterNames";
|
||||||
import { postRating } from "./routes/ratings/postRating";
|
import { postRating } from "./routes/ratings/postRating";
|
||||||
import { getRating } from "./routes/ratings/getRating";
|
import { getRating } from "./routes/ratings/getRating";
|
||||||
|
import { postClearCache as ratingPostClearCache } from "./routes/ratings/postClearCache";
|
||||||
|
|
||||||
export function createServer(callback: () => void): Server {
|
export function createServer(callback: () => void): Server {
|
||||||
// Create a service (the app object is just a callback).
|
// Create a service (the app object is just a callback).
|
||||||
@@ -193,6 +194,7 @@ function setupRoutes(router: Router) {
|
|||||||
// ratings
|
// ratings
|
||||||
router.get("/api/ratings/rate/:prefix", getRating);
|
router.get("/api/ratings/rate/:prefix", getRating);
|
||||||
router.post("/api/ratings/rate", postRateEndpoints);
|
router.post("/api/ratings/rate", postRateEndpoints);
|
||||||
|
router.post("/api/ratings/clearCache", ratingPostClearCache);
|
||||||
|
|
||||||
if (config.postgres) {
|
if (config.postgres) {
|
||||||
router.get("/database", (req, res) => dumpDatabase(req, res, true));
|
router.get("/database", (req, res) => dumpDatabase(req, res, true));
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export async function postClearCache(req: Request, res: Response): Promise<Respo
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
QueryCacher.clearVideoCache({
|
QueryCacher.clearSegmentCache({
|
||||||
videoID,
|
videoID,
|
||||||
hashedVideoID,
|
hashedVideoID,
|
||||||
service
|
service
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export async function postPurgeAllSegments(req: Request, res: Response): Promise
|
|||||||
await db.prepare("run", `UPDATE "sponsorTimes" SET "hidden" = 1 WHERE "videoID" = ?`, [videoID]);
|
await db.prepare("run", `UPDATE "sponsorTimes" SET "hidden" = 1 WHERE "videoID" = ?`, [videoID]);
|
||||||
|
|
||||||
const hashedVideoID: VideoIDHash = getHash(videoID, 1);
|
const hashedVideoID: VideoIDHash = getHash(videoID, 1);
|
||||||
QueryCacher.clearVideoCache({
|
QueryCacher.clearSegmentCache({
|
||||||
videoID,
|
videoID,
|
||||||
hashedVideoID,
|
hashedVideoID,
|
||||||
service
|
service
|
||||||
|
|||||||
@@ -657,7 +657,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
|||||||
videoID, apiVideoInfo?.data?.authorId || "", apiVideoInfo?.data?.title || "", apiVideoInfo?.data?.published || 0, apiVideoInfo?.data?.genreUrl || "", videoID]);
|
videoID, apiVideoInfo?.data?.authorId || "", apiVideoInfo?.data?.title || "", apiVideoInfo?.data?.published || 0, apiVideoInfo?.data?.genreUrl || "", videoID]);
|
||||||
|
|
||||||
// Clear redis cache for this video
|
// Clear redis cache for this video
|
||||||
QueryCacher.clearVideoCache({
|
QueryCacher.clearSegmentCache({
|
||||||
videoID,
|
videoID,
|
||||||
hashedVideoID,
|
hashedVideoID,
|
||||||
service,
|
service,
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ export async function getRating(req: Request, res: Response): Promise<Response>
|
|||||||
: [req.query.type]
|
: [req.query.type]
|
||||||
: [RatingType.Upvote, RatingType.Downvote];
|
: [RatingType.Upvote, RatingType.Downvote];
|
||||||
if (!Array.isArray(types)) {
|
if (!Array.isArray(types)) {
|
||||||
return res.status(400).send("Categories parameter does not match format requirements.");
|
return res.status(400).send("Types parameter does not match format requirements.");
|
||||||
}
|
}
|
||||||
|
|
||||||
types = types.map((type) => parseInt(type as unknown as string, 10));
|
types = types.map((type) => parseInt(type as unknown as string, 10));
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
return res.status(400).send("Bad parameter: categories (invalid JSON)");
|
return res.status(400).send("Bad parameter: types (invalid JSON)");
|
||||||
}
|
}
|
||||||
|
|
||||||
const service: Service = getService(req.query.service, req.body.service);
|
const service: Service = getService(req.query.service, req.body.service);
|
||||||
@@ -53,20 +53,15 @@ export async function getRating(req: Request, res: Response): Promise<Response>
|
|||||||
type: rating.type,
|
type: rating.type,
|
||||||
count: rating.count
|
count: rating.count
|
||||||
}));
|
}));
|
||||||
|
return res.status((ratings.length) ? 200 : 404)
|
||||||
if (ratings) {
|
.send(ratings ?? []);
|
||||||
res.status(200);
|
|
||||||
} else {
|
|
||||||
res.status(404);
|
|
||||||
}
|
|
||||||
return res.send(ratings ?? []);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.error(err as string);
|
Logger.error(err as string);
|
||||||
return res.sendStatus(500);
|
return res.sendStatus(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getRatings(hashPrefix: VideoIDHash, service: Service): Promise<DBRating[]> {
|
function getRatings(hashPrefix: VideoIDHash, service: Service): Promise<DBRating[]> {
|
||||||
const fetchFromDB = () => db
|
const fetchFromDB = () => db
|
||||||
.prepare(
|
.prepare(
|
||||||
"all",
|
"all",
|
||||||
@@ -74,9 +69,7 @@ async function getRatings(hashPrefix: VideoIDHash, service: Service): Promise<DB
|
|||||||
[`${hashPrefix}%`, service]
|
[`${hashPrefix}%`, service]
|
||||||
) as Promise<DBRating[]>;
|
) as Promise<DBRating[]>;
|
||||||
|
|
||||||
if (hashPrefix.length === 4) {
|
return (hashPrefix.length === 4)
|
||||||
return await QueryCacher.get(fetchFromDB, ratingHashKey(hashPrefix, service));
|
? QueryCacher.get(fetchFromDB, ratingHashKey(hashPrefix, service))
|
||||||
}
|
: fetchFromDB();
|
||||||
|
|
||||||
return fetchFromDB();
|
|
||||||
}
|
}
|
||||||
52
src/routes/ratings/postClearCache.ts
Normal file
52
src/routes/ratings/postClearCache.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { Logger } from "../../utils/logger";
|
||||||
|
import { HashedUserID, UserID } from "../../types/user.model";
|
||||||
|
import { getHash } from "../../utils/getHash";
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import { Service, VideoID } from "../../types/segments.model";
|
||||||
|
import { QueryCacher } from "../../utils/queryCacher";
|
||||||
|
import { isUserVIP } from "../../utils/isUserVIP";
|
||||||
|
import { VideoIDHash } from "../../types/segments.model";
|
||||||
|
import { getService } from "../..//utils/getService";
|
||||||
|
|
||||||
|
export async function postClearCache(req: Request, res: Response): Promise<Response> {
|
||||||
|
const videoID = req.query.videoID as VideoID;
|
||||||
|
const userID = req.query.userID as UserID;
|
||||||
|
const service = getService(req.query.service as Service);
|
||||||
|
|
||||||
|
const invalidFields = [];
|
||||||
|
if (typeof videoID !== "string") {
|
||||||
|
invalidFields.push("videoID");
|
||||||
|
}
|
||||||
|
if (typeof userID !== "string") {
|
||||||
|
invalidFields.push("userID");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidFields.length !== 0) {
|
||||||
|
// invalid request
|
||||||
|
const fields = invalidFields.reduce((p, c, i) => p + (i !== 0 ? ", " : "") + c, "");
|
||||||
|
return res.status(400).send(`No valid ${fields} field(s) provided`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash the userID as early as possible
|
||||||
|
const hashedUserID: HashedUserID = getHash(userID);
|
||||||
|
// hash videoID
|
||||||
|
const hashedVideoID: VideoIDHash = getHash(videoID, 1);
|
||||||
|
|
||||||
|
// Ensure user is a VIP
|
||||||
|
if (!(await isUserVIP(hashedUserID))){
|
||||||
|
Logger.warn(`Permission violation: User ${hashedUserID} attempted to clear cache for video ${videoID}.`);
|
||||||
|
return res.status(403).json({ "message": "Not a VIP" });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
QueryCacher.clearRatingCache({
|
||||||
|
hashedVideoID,
|
||||||
|
service
|
||||||
|
});
|
||||||
|
return res.status(200).json({
|
||||||
|
message: `Cache cleared on video ${videoID}`
|
||||||
|
});
|
||||||
|
} catch(err) {
|
||||||
|
return res.sendStatus(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import { getIP } from "../../utils/getIP";
|
|||||||
import { getService } from "../../utils/getService";
|
import { getService } from "../../utils/getService";
|
||||||
import { RatingType, RatingTypes } from "../../types/ratings.model";
|
import { RatingType, RatingTypes } from "../../types/ratings.model";
|
||||||
import { config } from "../../config";
|
import { config } from "../../config";
|
||||||
|
import { QueryCacher } from "../../utils/queryCacher";
|
||||||
|
|
||||||
export async function postRating(req: Request, res: Response): Promise<Response> {
|
export async function postRating(req: Request, res: Response): Promise<Response> {
|
||||||
const privateUserID = req.body.userID as UserID;
|
const privateUserID = req.body.userID as UserID;
|
||||||
@@ -34,26 +35,26 @@ export async function postRating(req: Request, res: Response): Promise<Response>
|
|||||||
// Undo the vote
|
// Undo the vote
|
||||||
await db.prepare("run", `UPDATE "ratings" SET "count" = "count" - 1 WHERE "videoID" = ? AND "service" = ? AND type = ?`, [videoID, service, type]);
|
await db.prepare("run", `UPDATE "ratings" SET "count" = "count" - 1 WHERE "videoID" = ? AND "service" = ? AND type = ?`, [videoID, service, type]);
|
||||||
await privateDB.prepare("run", `DELETE FROM "ratings" WHERE "videoID" = ? AND "service" = ? AND "type" = ? AND "userID" = ?`, [videoID, service, type, hashedUserID]);
|
await privateDB.prepare("run", `DELETE FROM "ratings" WHERE "videoID" = ? AND "service" = ? AND "type" = ? AND "userID" = ?`, [videoID, service, type, hashedUserID]);
|
||||||
|
|
||||||
return res.sendStatus(200);
|
|
||||||
} else if (existingVote.count === 0 && enabled) {
|
} else if (existingVote.count === 0 && enabled) {
|
||||||
// Make sure there hasn't been another vote from this IP
|
// Make sure there hasn't been another vote from this IP
|
||||||
const existingIPVote = (await privateDB.prepare("get", `SELECT count(*) as "count" FROM "ratings" WHERE "videoID" = ? AND "service" = ? AND "type" = ? AND "hashedIP" = ?`, [videoID, service, type, hashedIP]))
|
const existingIPVote = (await privateDB.prepare("get", `SELECT count(*) as "count" FROM "ratings" WHERE "videoID" = ? AND "service" = ? AND "type" = ? AND "hashedIP" = ?`, [videoID, service, type, hashedIP]))
|
||||||
.count > 0;
|
.count > 0;
|
||||||
if (!existingIPVote) {
|
if (existingIPVote) { // if exisiting vote, exit early instead
|
||||||
// Check if general rating already exists, if so increase it
|
return res.sendStatus(200);
|
||||||
const rating = await db.prepare("get", `SELECT count(*) as "count" FROM "ratings" WHERE "videoID" = ? AND "service" = ? AND type = ?`, [videoID, service, type]);
|
}
|
||||||
if (rating.count > 0) {
|
// Check if general rating already exists, if so increase it
|
||||||
await db.prepare("run", `UPDATE "ratings" SET "count" = "count" + 1 WHERE "videoID" = ? AND "service" = ? AND type = ?`, [videoID, service, type]);
|
const rating = await db.prepare("get", `SELECT count(*) as "count" FROM "ratings" WHERE "videoID" = ? AND "service" = ? AND type = ?`, [videoID, service, type]);
|
||||||
} else {
|
if (rating.count > 0) {
|
||||||
await db.prepare("run", `INSERT INTO "ratings" ("videoID", "service", "type", "count", "hashedVideoID") VALUES (?, ?, ?, 1, ?)`, [videoID, service, type, hashedVideoID]);
|
await db.prepare("run", `UPDATE "ratings" SET "count" = "count" + 1 WHERE "videoID" = ? AND "service" = ? AND type = ?`, [videoID, service, type]);
|
||||||
}
|
} else {
|
||||||
|
await db.prepare("run", `INSERT INTO "ratings" ("videoID", "service", "type", "count", "hashedVideoID") VALUES (?, ?, ?, 1, ?)`, [videoID, service, type, hashedVideoID]);
|
||||||
// Create entry in privateDB
|
|
||||||
await privateDB.prepare("run", `INSERT INTO "ratings" ("videoID", "service", "type", "userID", "timeSubmitted", "hashedIP") VALUES (?, ?, ?, ?, ?, ?)`, [videoID, service, type, hashedUserID, Date.now(), hashedIP]);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Create entry in privateDB
|
||||||
|
await privateDB.prepare("run", `INSERT INTO "ratings" ("videoID", "service", "type", "userID", "timeSubmitted", "hashedIP") VALUES (?, ?, ?, ?, ?, ?)`, [videoID, service, type, hashedUserID, Date.now(), hashedIP]);
|
||||||
|
}
|
||||||
|
// clear rating cache
|
||||||
|
QueryCacher.clearRatingCache({ hashedVideoID, service });
|
||||||
return res.sendStatus(200);
|
return res.sendStatus(200);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.error(err as string);
|
Logger.error(err as string);
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export async function shadowBanUser(req: Request, res: Response): Promise<Respon
|
|||||||
// collect list for unshadowbanning
|
// collect list for unshadowbanning
|
||||||
(await db.prepare("all", `SELECT "videoID", "hashedVideoID", "service", "votes", "views", "userID" FROM "sponsorTimes" WHERE "UUID" = ? AND "shadowHidden" = 1 AND "category" in (${categories.map((c) => `'${c}'`).join(",")})`, [UUID]))
|
(await db.prepare("all", `SELECT "videoID", "hashedVideoID", "service", "votes", "views", "userID" FROM "sponsorTimes" WHERE "UUID" = ? AND "shadowHidden" = 1 AND "category" in (${categories.map((c) => `'${c}'`).join(",")})`, [UUID]))
|
||||||
.forEach((videoInfo: {category: Category, videoID: VideoID, hashedVideoID: VideoIDHash, service: Service, userID: UserID}) => {
|
.forEach((videoInfo: {category: Category, videoID: VideoID, hashedVideoID: VideoIDHash, service: Service, userID: UserID}) => {
|
||||||
QueryCacher.clearVideoCache(videoInfo);
|
QueryCacher.clearSegmentCache(videoInfo);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -125,6 +125,6 @@ async function unHideSubmissions(categories: string[], userID: UserID) {
|
|||||||
// clear cache for all old videos
|
// clear cache for all old videos
|
||||||
(await db.prepare("all", `SELECT "videoID", "hashedVideoID", "service", "votes", "views" FROM "sponsorTimes" WHERE "userID" = ?`, [userID]))
|
(await db.prepare("all", `SELECT "videoID", "hashedVideoID", "service", "votes", "views" FROM "sponsorTimes" WHERE "userID" = ?`, [userID]))
|
||||||
.forEach((videoInfo: { category: Category; videoID: VideoID; hashedVideoID: VideoIDHash; service: Service; userID: UserID; }) => {
|
.forEach((videoInfo: { category: Category; videoID: VideoID; hashedVideoID: VideoIDHash; service: Service; userID: UserID; }) => {
|
||||||
QueryCacher.clearVideoCache(videoInfo);
|
QueryCacher.clearSegmentCache(videoInfo);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ async function categoryVote(UUID: SegmentUUID, userID: UserID, isVIP: boolean, i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryCacher.clearVideoCache(videoInfo);
|
QueryCacher.clearSegmentCache(videoInfo);
|
||||||
|
|
||||||
return res.sendStatus(finalResponse.finalStatus);
|
return res.sendStatus(finalResponse.finalStatus);
|
||||||
}
|
}
|
||||||
@@ -473,7 +473,7 @@ export async function voteOnSponsorTime(req: Request, res: Response): Promise<Re
|
|||||||
await db.prepare("run", 'UPDATE "sponsorTimes" SET locked = 0 WHERE "UUID" = ?', [UUID]);
|
await db.prepare("run", 'UPDATE "sponsorTimes" SET locked = 0 WHERE "UUID" = ?', [UUID]);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryCacher.clearVideoCache(videoInfo);
|
QueryCacher.clearSegmentCache(videoInfo);
|
||||||
}
|
}
|
||||||
if (incrementAmount - oldIncrementAmount !== 0) {
|
if (incrementAmount - oldIncrementAmount !== 0) {
|
||||||
sendWebhooks({
|
sendWebhooks({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import redis from "../utils/redis";
|
import redis from "../utils/redis";
|
||||||
import { Logger } from "../utils/logger";
|
import { Logger } from "../utils/logger";
|
||||||
import { skipSegmentsHashKey, skipSegmentsKey, reputationKey } from "./redisKeys";
|
import { skipSegmentsHashKey, skipSegmentsKey, reputationKey, ratingHashKey } from "./redisKeys";
|
||||||
import { Service, VideoID, VideoIDHash } from "../types/segments.model";
|
import { Service, VideoID, VideoIDHash } from "../types/segments.model";
|
||||||
import { UserID } from "../types/user.model";
|
import { UserID } from "../types/user.model";
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ async function get<T>(fetchFromDB: () => Promise<T>, key: string): Promise<T> {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearVideoCache(videoInfo: { videoID: VideoID; hashedVideoID: VideoIDHash; service: Service; userID?: UserID; }): void {
|
function clearSegmentCache(videoInfo: { videoID: VideoID; hashedVideoID: VideoIDHash; service: Service; userID?: UserID; }): void {
|
||||||
if (videoInfo) {
|
if (videoInfo) {
|
||||||
redis.delAsync(skipSegmentsKey(videoInfo.videoID, videoInfo.service));
|
redis.delAsync(skipSegmentsKey(videoInfo.videoID, videoInfo.service));
|
||||||
redis.delAsync(skipSegmentsHashKey(videoInfo.hashedVideoID, videoInfo.service));
|
redis.delAsync(skipSegmentsHashKey(videoInfo.hashedVideoID, videoInfo.service));
|
||||||
@@ -30,7 +30,14 @@ function clearVideoCache(videoInfo: { videoID: VideoID; hashedVideoID: VideoIDHa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearRatingCache(videoInfo: { hashedVideoID: VideoIDHash; service: Service;}): void {
|
||||||
|
if (videoInfo) {
|
||||||
|
redis.delAsync(ratingHashKey(videoInfo.hashedVideoID, videoInfo.service));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const QueryCacher = {
|
export const QueryCacher = {
|
||||||
get,
|
get,
|
||||||
clearVideoCache
|
clearSegmentCache,
|
||||||
|
clearRatingCache
|
||||||
};
|
};
|
||||||
@@ -8,15 +8,19 @@ import { partialDeepEquals } from "../../utils/partialDeepEquals";
|
|||||||
const endpoint = "/api/ratings/rate/";
|
const endpoint = "/api/ratings/rate/";
|
||||||
const getRating = (hash: string, params?: unknown): Promise<AxiosResponse> => client.get(endpoint + hash, { params });
|
const getRating = (hash: string, params?: unknown): Promise<AxiosResponse> => client.get(endpoint + hash, { params });
|
||||||
|
|
||||||
|
const videoOneID = "some-likes-and-dislikes";
|
||||||
|
const videoOneIDHash = getHash(videoOneID, 1);
|
||||||
|
const videoOnePartialHash = videoOneIDHash.substr(0, 4);
|
||||||
|
|
||||||
describe("getRating", () => {
|
describe("getRating", () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const insertUserNameQuery = 'INSERT INTO "ratings" ("videoID", "service", "type", "count", "hashedVideoID") VALUES (?, ?, ?, ?, ?)';
|
const insertUserNameQuery = 'INSERT INTO "ratings" ("videoID", "service", "type", "count", "hashedVideoID") VALUES (?, ?, ?, ?, ?)';
|
||||||
await db.prepare("run", insertUserNameQuery, ["some-likes-and-dislikes", "YouTube", 0, 5, getHash("some-likes-and-dislikes", 1)]); //b3f0
|
await db.prepare("run", insertUserNameQuery, [videoOneID, "YouTube", 0, 5, videoOneIDHash]);
|
||||||
await db.prepare("run", insertUserNameQuery, ["some-likes-and-dislikes", "YouTube", 1, 10, getHash("some-likes-and-dislikes", 1)]);
|
await db.prepare("run", insertUserNameQuery, [videoOneID, "YouTube", 1, 10, videoOneIDHash]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should be able to get dislikes and likes by default", (done) => {
|
it("Should be able to get dislikes and likes by default", (done) => {
|
||||||
getRating("b3f0")
|
getRating(videoOnePartialHash)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const expected = [{
|
const expected = [{
|
||||||
@@ -33,7 +37,7 @@ describe("getRating", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Should be able to filter for only dislikes", (done) => {
|
it("Should be able to filter for only dislikes", (done) => {
|
||||||
getRating("b3f0", { type: 0 })
|
getRating(videoOnePartialHash, { type: 0 })
|
||||||
.then(res => {
|
.then(res => {
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const expected = [{
|
const expected = [{
|
||||||
@@ -46,4 +50,31 @@ describe("getRating", () => {
|
|||||||
})
|
})
|
||||||
.catch(err => done(err));
|
.catch(err => done(err));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should return 400 for invalid hash", (done) => {
|
||||||
|
getRating("a")
|
||||||
|
.then(res => {
|
||||||
|
assert.strictEqual(res.status, 400);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(err => done(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return 404 for nonexitent type", (done) => {
|
||||||
|
getRating(videoOnePartialHash, { type: 100 })
|
||||||
|
.then(res => {
|
||||||
|
assert.strictEqual(res.status, 404);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(err => done(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return 404 for nonexistent videoID", (done) => {
|
||||||
|
getRating("aaaa")
|
||||||
|
.then(res => {
|
||||||
|
assert.strictEqual(res.status, 404);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(err => done(err));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
51
test/cases/ratings/postClearCache.ts
Normal file
51
test/cases/ratings/postClearCache.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { db } from "../../../src/databases/databases";
|
||||||
|
import { getHash } from "../../../src/utils/getHash";
|
||||||
|
import assert from "assert";
|
||||||
|
import { client } from "../../utils/httpClient";
|
||||||
|
|
||||||
|
const VIPUser = "clearCacheVIP";
|
||||||
|
const regularUser = "regular-user";
|
||||||
|
const endpoint = "/api/ratings/clearCache";
|
||||||
|
const postClearCache = (userID: string, videoID: string) => client({ method: "post", url: endpoint, params: { userID, videoID } });
|
||||||
|
|
||||||
|
describe("ratings postClearCache", () => {
|
||||||
|
before(async () => {
|
||||||
|
await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES ('${getHash(VIPUser)}')`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should be able to clear cache amy video", (done) => {
|
||||||
|
postClearCache(VIPUser, "dne-video")
|
||||||
|
.then(res => {
|
||||||
|
assert.strictEqual(res.status, 200);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(err => done(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should get 403 as non-vip", (done) => {
|
||||||
|
postClearCache(regularUser, "clear-test")
|
||||||
|
.then(res => {
|
||||||
|
assert.strictEqual(res.status, 403);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(err => done(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should give 400 with missing videoID", (done) => {
|
||||||
|
client.post(endpoint, { params: { userID: VIPUser } })
|
||||||
|
.then(res => {
|
||||||
|
assert.strictEqual(res.status, 400);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(err => done(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should give 400 with missing userID", (done) => {
|
||||||
|
client.post(endpoint, { params: { videoID: "clear-test" } })
|
||||||
|
.then(res => {
|
||||||
|
assert.strictEqual(res.status, 400);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(err => done(err));
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -9,37 +9,43 @@ const endpoint = "/api/ratings/rate/";
|
|||||||
const postRating = (body: unknown): Promise<AxiosResponse> => client.post(endpoint, body);
|
const postRating = (body: unknown): Promise<AxiosResponse> => client.post(endpoint, body);
|
||||||
const queryDatabase = (videoID: string) => db.prepare("all", `SELECT * FROM "ratings" WHERE "videoID" = ?`, [videoID]);
|
const queryDatabase = (videoID: string) => db.prepare("all", `SELECT * FROM "ratings" WHERE "videoID" = ?`, [videoID]);
|
||||||
|
|
||||||
|
const videoIDOne = "normal-video";
|
||||||
|
const videoIDTwo = "multiple-rates";
|
||||||
|
const ratingUserID = "rating-testman";
|
||||||
|
|
||||||
describe("postRating", () => {
|
describe("postRating", () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const insertUserNameQuery = 'INSERT INTO "ratings" ("videoID", "service", "type", "count", "hashedVideoID") VALUES (?, ?, ?, ?, ?)';
|
const insertUserNameQuery = 'INSERT INTO "ratings" ("videoID", "service", "type", "count", "hashedVideoID") VALUES (?, ?, ?, ?, ?)';
|
||||||
await db.prepare("run", insertUserNameQuery, ["multiple-rates", "YouTube", 0, 3, getHash("multiple-rates", 1)]);
|
await db.prepare("run", insertUserNameQuery, [videoIDTwo, "YouTube", 0, 3, getHash(videoIDTwo, 1)]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should be able to vote on a video", (done) => {
|
it("Should be able to vote on a video", (done) => {
|
||||||
|
const videoID = videoIDOne;
|
||||||
postRating({
|
postRating({
|
||||||
userID: "rating-testman",
|
userID: ratingUserID,
|
||||||
videoID: "normal-video",
|
videoID,
|
||||||
type: 0
|
type: 0
|
||||||
})
|
})
|
||||||
.then(async res => {
|
.then(async res => {
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const expected = [{
|
const expected = [{
|
||||||
hashedVideoID: getHash("normal-video", 1),
|
hashedVideoID: getHash(videoID, 1),
|
||||||
videoID: "normal-video",
|
videoID,
|
||||||
type: 0,
|
type: 0,
|
||||||
count: 1,
|
count: 1,
|
||||||
service: "YouTube"
|
service: "YouTube"
|
||||||
}];
|
}];
|
||||||
assert.ok(partialDeepEquals(await queryDatabase("normal-video"), expected));
|
assert.ok(partialDeepEquals(await queryDatabase(videoID), expected));
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(err => done(err));
|
.catch(err => done(err));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should be able to undo a vote on a video", (done) => {
|
it("Should be able to undo a vote on a video", (done) => {
|
||||||
|
const videoID = videoIDOne;
|
||||||
postRating({
|
postRating({
|
||||||
userID: "rating-testman",
|
userID: ratingUserID,
|
||||||
videoID: "normal-video",
|
videoID,
|
||||||
type: 0,
|
type: 0,
|
||||||
enabled: false
|
enabled: false
|
||||||
})
|
})
|
||||||
@@ -49,16 +55,17 @@ describe("postRating", () => {
|
|||||||
type: 0,
|
type: 0,
|
||||||
count: 0
|
count: 0
|
||||||
}];
|
}];
|
||||||
assert.ok(partialDeepEquals(await queryDatabase("normal-video"), expected));
|
assert.ok(partialDeepEquals(await queryDatabase(videoID), expected));
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(err => done(err));
|
.catch(err => done(err));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should be able to vote after someone else on a video", (done) => {
|
it("Should be able to vote after someone else on a video", (done) => {
|
||||||
|
const videoID = videoIDTwo;
|
||||||
postRating({
|
postRating({
|
||||||
userID: "rating-testman",
|
userID: ratingUserID,
|
||||||
videoID: "multiple-rates",
|
videoID,
|
||||||
type: 0
|
type: 0
|
||||||
})
|
})
|
||||||
.then(async res => {
|
.then(async res => {
|
||||||
@@ -67,16 +74,17 @@ describe("postRating", () => {
|
|||||||
type: 0,
|
type: 0,
|
||||||
count: 4
|
count: 4
|
||||||
}];
|
}];
|
||||||
assert.ok(partialDeepEquals(await queryDatabase("multiple-rates"), expected));
|
assert.ok(partialDeepEquals(await queryDatabase(videoID), expected));
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(err => done(err));
|
.catch(err => done(err));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should be able to vote a different type than existing votes on a video", (done) => {
|
it("Should be able to vote a different type than existing votes on a video", (done) => {
|
||||||
|
const videoID = videoIDTwo;
|
||||||
postRating({
|
postRating({
|
||||||
userID: "rating-testman",
|
userID: ratingUserID,
|
||||||
videoID: "multiple-rates",
|
videoID,
|
||||||
type: 1
|
type: 1
|
||||||
})
|
})
|
||||||
.then(async res => {
|
.then(async res => {
|
||||||
@@ -88,7 +96,21 @@ describe("postRating", () => {
|
|||||||
type: 1,
|
type: 1,
|
||||||
count: 1
|
count: 1
|
||||||
}];
|
}];
|
||||||
assert.ok(partialDeepEquals(await queryDatabase("multiple-rates"), expected));
|
assert.ok(partialDeepEquals(await queryDatabase(videoID), expected));
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(err => done(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should not be able to vote with nonexistent type", (done) => {
|
||||||
|
const videoID = videoIDOne;
|
||||||
|
postRating({
|
||||||
|
userID: ratingUserID,
|
||||||
|
videoID,
|
||||||
|
type: 100
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
assert.strictEqual(res.status, 400);
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(err => done(err));
|
.catch(err => done(err));
|
||||||
|
|||||||
Reference in New Issue
Block a user