Merge pull request #610 from mini-bomba/warning-history

Keep a history of warnings in the public database
This commit is contained in:
Ajay Ramachandran
2025-09-19 02:15:59 -04:00
committed by GitHub
39 changed files with 1001 additions and 1238 deletions

View File

@@ -31,15 +31,15 @@ module.exports = {
}, },
overrides: [ overrides: [
{ {
files: ["src/**/*.ts"], files: ["**/*.ts"],
parserOptions: { parserOptions: {
project: ["./tsconfig.json"], project: ["./tsconfig.eslint.json"],
}, },
rules: { rules: {
"@typescript-eslint/no-misused-promises": "warn", "@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/no-floating-promises" : "warn" "@typescript-eslint/no-floating-promises" : "error"
} }
}, },
], ],

View File

@@ -56,7 +56,6 @@
] ]
} }
], ],
"hoursAfterWarningExpires": 24,
"rateLimit": { "rateLimit": {
"vote": { "vote": {
"windowMs": 900000, "windowMs": 900000,

View File

@@ -25,8 +25,6 @@
"webhooks": [], "webhooks": [],
"categoryList": ["sponsor", "intro", "outro", "interaction", "selfpromo", "preview", "music_offtopic", "poi_highlight"], // List of supported categories any other category will be rejected "categoryList": ["sponsor", "intro", "outro", "interaction", "selfpromo", "preview", "music_offtopic", "poi_highlight"], // List of supported categories any other category will be rejected
"getTopUsersCacheTimeMinutes": 5, // cacheTime for getTopUsers result in minutes "getTopUsersCacheTimeMinutes": 5, // cacheTime for getTopUsers result in minutes
"maxNumberOfActiveWarnings": 3, // Users with this number of warnings will be blocked until warnings expire
"hoursAfterWarningExpire": 24,
"rateLimit": { "rateLimit": {
"vote": { "vote": {
"windowMs": 900000, // 15 minutes "windowMs": 900000, // 15 minutes

View File

@@ -0,0 +1,7 @@
BEGIN TRANSACTION;
ALTER TABLE "warnings" ADD "disableTime" INTEGER NULL;
UPDATE "config" SET value = 45 WHERE key = 'version';
COMMIT;

View File

@@ -37,8 +37,6 @@ addDefaults(config, {
}, },
deArrowTypes: ["title", "thumbnail"], deArrowTypes: ["title", "thumbnail"],
maxTitleLength: 110, maxTitleLength: 110,
maxNumberOfActiveWarnings: 1,
hoursAfterWarningExpires: 16300000,
adminUserID: "", adminUserID: "",
discordCompletelyIncorrectReportWebhookURL: null, discordCompletelyIncorrectReportWebhookURL: null,
discordFirstTimeSubmissionsWebhookURL: null, discordFirstTimeSubmissionsWebhookURL: null,

View File

@@ -3,7 +3,6 @@ import { CronJob } from "cron";
import { config as serverConfig } from "../config"; import { config as serverConfig } from "../config";
import { Logger } from "../utils/logger"; import { Logger } from "../utils/logger";
import { db } from "../databases/databases"; import { db } from "../databases/databases";
import { DBSegment } from "../types/segments.model";
const jobConfig = serverConfig?.crons?.downvoteSegmentArchive; const jobConfig = serverConfig?.crons?.downvoteSegmentArchive;
@@ -25,7 +24,7 @@ export const archiveDownvoteSegment = async (dayLimit: number, voteLimit: number
timeNow, timeNow,
threshold threshold
] ]
) as DBSegment[]; );
} catch (err) { } catch (err) {
Logger.error("Execption when insert segment in archivedSponsorTimes"); Logger.error("Execption when insert segment in archivedSponsorTimes");
@@ -43,7 +42,7 @@ export const archiveDownvoteSegment = async (dayLimit: number, voteLimit: number
timeNow, timeNow,
threshold threshold
] ]
) as DBSegment[]; );
} catch (err) { } catch (err) {
Logger.error("Execption when deleting segment in sponsorTimes"); Logger.error("Execption when deleting segment in sponsorTimes");

View File

@@ -6,7 +6,10 @@ export interface QueryOption {
export interface IDatabase { export interface IDatabase {
init(): Promise<void>; init(): Promise<void>;
prepare(type: QueryType, query: string, params?: any[], options?: QueryOption): Promise<any | any[] | void>; prepare(type: "run", query: string, params?: any[], options?: QueryOption): Promise<void>;
prepare(type: "get", query: string, params?: any[], options?: QueryOption): Promise<any>;
prepare(type: "all", query: string, params?: any[], options?: QueryOption): Promise<any[]>;
prepare(type: QueryType, query: string, params?: any[], options?: QueryOption): Promise<any>;
highLoad(): boolean; highLoad(): boolean;

View File

@@ -109,7 +109,7 @@ export class Postgres implements IDatabase {
} }
} }
async prepare(type: QueryType, query: string, params?: any[], options: QueryOption = {}): Promise<any[]> { async prepare(type: QueryType, query: string, params?: any[], options: QueryOption = {}): Promise<any> {
// Convert query to use numbered parameters // Convert query to use numbered parameters
let count = 1; let count = 1;
for (let char = 0; char < query.length; char++) { for (let char = 0; char < query.length; char++) {

View File

@@ -13,7 +13,7 @@ export class Sqlite implements IDatabase {
} }
// eslint-disable-next-line require-await // eslint-disable-next-line require-await
async prepare(type: QueryType, query: string, params: any[] = []): Promise<any[]> { async prepare(type: QueryType, query: string, params: any[] = []): Promise<any> {
// Logger.debug(`prepare (sqlite): type: ${type}, query: ${query}, params: ${params}`); // Logger.debug(`prepare (sqlite): type: ${type}, query: ${query}, params: ${params}`);
const preparedQuery = this.db.prepare(Sqlite.processQuery(query)); const preparedQuery = this.db.prepare(Sqlite.processQuery(query));

View File

@@ -122,7 +122,7 @@ function chooseSegment<T extends DBSegment>(choices: T[]): FullVideoSegmentVideo
return { return {
segments: transformDBSegments(choices), segments: transformDBSegments(choices),
hasStartSegment hasStartSegment
} };
} }
// if locked, only choose from locked // if locked, only choose from locked
const locked = choices.filter((segment) => segment.locked); const locked = choices.filter((segment) => segment.locked);

View File

@@ -164,20 +164,15 @@ async function autoModerateSubmission(apiVideoDetails: videoDetails,
} }
async function checkUserActiveWarning(userID: HashedUserID): Promise<CheckResult> { async function checkUserActiveWarning(userID: HashedUserID): Promise<CheckResult> {
const MILLISECONDS_IN_HOUR = 3600000; const warning = await db.prepare("get",
const now = Date.now();
const warnings = (await db.prepare("all",
`SELECT "reason" `SELECT "reason"
FROM warnings FROM warnings
WHERE "userID" = ? AND "issueTime" > ? AND enabled = 1 AND type = 0 WHERE "userID" = ? AND enabled = 1 AND type = 0
ORDER BY "issueTime" DESC`, ORDER BY "issueTime" DESC`,
[ [userID],
userID, ) as {reason: string};
Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR))
],
) as {reason: string}[]).sort((a, b) => (b?.reason?.length ?? 0) - (a?.reason?.length ?? 0));
if (warnings?.length >= config.maxNumberOfActiveWarnings) { if (warning != null) {
const defaultMessage = "Submission rejected due to a tip from a moderator. This means that we noticed you were making some common mistakes" const defaultMessage = "Submission rejected due to a tip from a moderator. This means that we noticed you were making some common mistakes"
+ " that are not malicious, and we just want to clarify the rules. " + " that are not malicious, and we just want to clarify the rules. "
+ "Could you please send a message in discord.gg/SponsorBlock or matrix.to/#/#sponsor:ajay.app so we can further help you? " + "Could you please send a message in discord.gg/SponsorBlock or matrix.to/#/#sponsor:ajay.app so we can further help you? "
@@ -185,7 +180,7 @@ async function checkUserActiveWarning(userID: HashedUserID): Promise<CheckResult
return { return {
pass: false, pass: false,
errorMessage: defaultMessage + (warnings[0]?.reason?.length > 0 ? `\n\nTip message: '${warnings[0].reason}'` : ""), errorMessage: defaultMessage + (warning.reason?.length > 0 ? `\n\nTip message: '${warning.reason}'` : ""),
errorCode: 403 errorCode: 403
}; };
} }

View File

@@ -4,7 +4,6 @@ import { db } from "../databases/databases";
import { isUserVIP } from "../utils/isUserVIP"; import { isUserVIP } from "../utils/isUserVIP";
import { getHashCache } from "../utils/getHashCache"; import { getHashCache } from "../utils/getHashCache";
import { HashedUserID, UserID } from "../types/user.model"; import { HashedUserID, UserID } from "../types/user.model";
import { config } from "../config";
import { generateWarningDiscord, warningData, dispatchEvent } from "../utils/webhookUtils"; import { generateWarningDiscord, warningData, dispatchEvent } from "../utils/webhookUtils";
import { WarningType } from "../types/warning.model"; import { WarningType } from "../types/warning.model";
@@ -16,12 +15,7 @@ type warningEntry = {
reason: string reason: string
} }
function checkExpiredWarning(warning: warningEntry): boolean { const MAX_EDIT_DELAY = 900000; // 15 mins
const MILLISECONDS_IN_HOUR = 3600000;
const now = Date.now();
const expiry = Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR));
return warning.issueTime > expiry && !warning.enabled;
}
const getUsername = (userID: HashedUserID) => db.prepare("get", `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [userID], { useReplica: true }); const getUsername = (userID: HashedUserID) => db.prepare("get", `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [userID], { useReplica: true });
@@ -44,30 +38,30 @@ export async function postWarning(req: Request, res: Response): Promise<Response
try { try {
if (enabled) { if (enabled) {
const previousWarning = await db.prepare("get", 'SELECT * FROM "warnings" WHERE "userID" = ? AND "issuerUserID" = ? AND "type" = ?', [userID, issuerUserID, type]) as warningEntry;
if (!previousWarning) {
if (!reason) { if (!reason) {
return res.status(400).json({ "message": "Missing warning reason" }); return res.status(400).json({ "message": "Missing warning reason" });
} }
const previousWarning = await db.prepare("get", 'SELECT * FROM "warnings" WHERE "userID" = ? AND "type" = ? AND "enabled" = 1', [userID, type]) as warningEntry;
if (!previousWarning) {
await db.prepare( await db.prepare(
"run", "run",
'INSERT INTO "warnings" ("userID", "issueTime", "issuerUserID", "enabled", "reason", "type") VALUES (?, ?, ?, 1, ?, ?)', 'INSERT INTO "warnings" ("userID", "issueTime", "issuerUserID", "enabled", "reason", "type") VALUES (?, ?, ?, 1, ?, ?)',
[userID, issueTime, issuerUserID, reason, type] [userID, issueTime, issuerUserID, reason, type]
); );
resultStatus = "issued to"; resultStatus = "issued to";
// check if warning is still within issue time and warning is not enabled // allow a warning to be edited by the same vip within 15 mins of issuing
} else if (checkExpiredWarning(previousWarning) ) { } else if (issuerUserID === previousWarning.issuerUserID && (Date.now() - MAX_EDIT_DELAY) < previousWarning.issueTime) {
await db.prepare( await db.prepare(
"run", 'UPDATE "warnings" SET "enabled" = 1, "reason" = ? WHERE "userID" = ? AND "issueTime" = ?', "run", 'UPDATE "warnings" SET "reason" = ? WHERE "userID" = ? AND "issueTime" = ?',
[reason, userID, previousWarning.issueTime] [reason, userID, previousWarning.issueTime]
); );
resultStatus = "re-enabled for"; resultStatus = "edited for";
} else { } else {
return res.sendStatus(409); return res.sendStatus(409);
} }
} else { } else {
await db.prepare("run", 'UPDATE "warnings" SET "enabled" = 0 WHERE "userID" = ? AND "type" = ?', [userID, type]); await db.prepare("run", 'UPDATE "warnings" SET "enabled" = 0, "disableTime" = ? WHERE "userID" = ? AND "type" = ? AND "enabled" = 1', [issueTime, userID, type]);
resultStatus = "removed from"; resultStatus = "removed from";
} }

View File

@@ -7,7 +7,7 @@ import { isUserBanned } from "../utils/checkBan";
import { HashedUserID } from "../types/user.model"; import { HashedUserID } from "../types/user.model";
import { isRequestInvalid } from "../utils/requestValidator"; import { isRequestInvalid } from "../utils/requestValidator";
function logUserNameChange(userID: string, newUserName: string, oldUserName: string, updatedByAdmin: boolean): Promise<Response> { function logUserNameChange(userID: string, newUserName: string, oldUserName: string, updatedByAdmin: boolean): Promise<void> {
return privateDB.prepare("run", return privateDB.prepare("run",
`INSERT INTO "userNameLogs"("userID", "newUserName", "oldUserName", "updatedByAdmin", "updatedAt") VALUES(?, ?, ?, ?, ?)`, `INSERT INTO "userNameLogs"("userID", "newUserName", "oldUserName", "updatedByAdmin", "updatedAt") VALUES(?, ?, ?, ?, ?)`,
[userID, newUserName, oldUserName, + updatedByAdmin, new Date().getTime()] [userID, newUserName, oldUserName, + updatedByAdmin, new Date().getTime()]

View File

@@ -379,14 +379,12 @@ export async function vote(ip: IPAddress, UUID: SegmentUUID, paramUserID: UserID
return { status: 400 }; return { status: 400 };
} }
const MILLISECONDS_IN_HOUR = 3600000; const warning = (await db.prepare("get", `SELECT "reason" FROM warnings WHERE "userID" = ? AND enabled = 1 AND type = 0`,
const now = Date.now(); [nonAnonUserID],
const warnings = (await db.prepare("all", `SELECT "reason" FROM warnings WHERE "userID" = ? AND "issueTime" > ? AND enabled = 1 AND type = 0`,
[nonAnonUserID, Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR))],
)); ));
if (warnings.length >= config.maxNumberOfActiveWarnings) { if (warning != null) {
const warningReason = warnings[0]?.reason; const warningReason = warning.reason;
lock.unlock(); lock.unlock();
return { status: 403, message: "Vote rejected due to a tip from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. " + return { status: 403, message: "Vote rejected due to a tip from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. " +
"Could you please send a message in Discord or Matrix so we can further help you?" + "Could you please send a message in Discord or Matrix so we can further help you?" +

View File

@@ -109,8 +109,6 @@ export interface SBSConfig {
categorySupport: Record<string, string[]>; categorySupport: Record<string, string[]>;
maxTitleLength: number; maxTitleLength: number;
getTopUsersCacheTimeMinutes: number; getTopUsersCacheTimeMinutes: number;
maxNumberOfActiveWarnings: number;
hoursAfterWarningExpires: number;
rateLimit: { rateLimit: {
vote: RateLimitConfig; vote: RateLimitConfig;
view: RateLimitConfig; view: RateLimitConfig;

View File

@@ -50,7 +50,6 @@
] ]
} }
], ],
"hoursAfterWarningExpires": 24,
"rateLimit": { "rateLimit": {
"vote": { "vote": {
"windowMs": 900000, "windowMs": 900000,

View File

@@ -73,11 +73,11 @@ describe("getUserID", () => {
) )
); );
it("Should be able to get multiple fuzzy user info from middle", () => { it("Should be able to get multiple fuzzy user info from middle", () =>
validateSearch("user", validateSearch("user",
[users["fuzzy_1"], users["fuzzy_2"], users["specific_1"]] [users["fuzzy_1"], users["fuzzy_2"], users["specific_1"]]
)
); );
});
it("Should be able to get with fuzzy public ID", () => { it("Should be able to get with fuzzy public ID", () => {
const userID = users["public_1"].pubID.substring(0,60); const userID = users["public_1"].pubID.substring(0,60);

View File

@@ -81,19 +81,19 @@ describe("getUserInfo", () => {
// warnings & bans // warnings & bans
// warn-0 // warn-0
insertWarning(db, users["warn-0"].pubID, { reason: "warning0-0", issueTime: 10 }); await insertWarning(db, users["warn-0"].pubID, { reason: "warning0-0", issueTime: 10 });
// warn-1 // warn-1
insertWarning(db, users["warn-1"].pubID, { reason: "warning1-0", issueTime: 20 }); await insertWarning(db, users["warn-1"].pubID, { reason: "warning1-0", issueTime: 20 });
insertWarning(db, users["warn-1"].pubID, { reason: "warning1-1", issueTime: 30 }); await insertWarning(db, users["warn-1"].pubID, { reason: "warning1-1", issueTime: 30 });
// warn -2 // warn -2
insertWarning(db, users["warn-2"].pubID, { reason: "warning2-0", issueTime: 40, enabled: false }); await insertWarning(db, users["warn-2"].pubID, { reason: "warning2-0", issueTime: 40, enabled: false });
// warn-3 // warn-3
insertWarning(db, users["warn-3"].pubID, { reason: "warning3-0", issueTime: 50 }); await insertWarning(db, users["warn-3"].pubID, { reason: "warning3-0", issueTime: 50 });
insertWarning(db, users["warn-3"].pubID, { reason: "warning3-1", issueTime: 60, enabled: false }); await insertWarning(db, users["warn-3"].pubID, { reason: "warning3-1", issueTime: 60, enabled: false });
// ban- // ban-
insertBan(db, users["ban-1"].pubID); await insertBan(db, users["ban-1"].pubID);
insertBan(db, users["ban-2"].pubID); await insertBan(db, users["ban-2"].pubID);
}); });
it("Should be able to get a 200", () => statusTest(200, { userID: users["n-1"].privID })); it("Should be able to get a 200", () => statusTest(200, { userID: users["n-1"].privID }));

View File

@@ -33,11 +33,11 @@ const checkUserViews = (user: User) =>
}); });
describe("getViewsForUser", function() { describe("getViewsForUser", function() {
before(() => { before(async () => {
// add views for users // add views for users
insertSegment(db, { userID: users["u-1"].pubID, views: users["u-1"].info.views1 }); await insertSegment(db, { userID: users["u-1"].pubID, views: users["u-1"].info.views1 });
insertSegment(db, { userID: users["u-1"].pubID, views: users["u-1"].info.views2 }); await insertSegment(db, { userID: users["u-1"].pubID, views: users["u-1"].info.views2 });
insertSegment(db, { userID: users["u-2"].pubID, views: users["u-2"].info.views }); await insertSegment(db, { userID: users["u-2"].pubID, views: users["u-2"].info.views });
}); });
it("Should get back views for user one", () => it("Should get back views for user one", () =>
checkUserViews(users["u-1"]) checkUserViews(users["u-1"])

View File

@@ -15,7 +15,6 @@ describe("postBranding", () => {
const userID5 = `PostBrandingUser5${".".repeat(16)}`; const userID5 = `PostBrandingUser5${".".repeat(16)}`;
const userID6 = `PostBrandingUser6${".".repeat(16)}`; const userID6 = `PostBrandingUser6${".".repeat(16)}`;
const userID7 = `PostBrandingUser7${".".repeat(16)}`; const userID7 = `PostBrandingUser7${".".repeat(16)}`;
const userID8 = `PostBrandingUser8${".".repeat(16)}`;
const bannedUser = `BannedPostBrandingUser${".".repeat(16)}`; const bannedUser = `BannedPostBrandingUser${".".repeat(16)}`;

View File

@@ -50,12 +50,12 @@ describe("postSkipSegments", () => {
const queryDatabaseActionType = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "actionType" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]); const queryDatabaseActionType = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "actionType" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]);
const queryDatabaseVideoInfo = (videoID: string) => db.prepare("get", `SELECT * FROM "videoInfo" WHERE "videoID" = ?`, [videoID]); const queryDatabaseVideoInfo = (videoID: string) => db.prepare("get", `SELECT * FROM "videoInfo" WHERE "videoID" = ?`, [videoID]);
before(() => { before(async () => {
const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "actionType", "videoDuration", "shadowHidden", "hashedVideoID") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "actionType", "videoDuration", "shadowHidden", "hashedVideoID") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
db.prepare("run", insertSponsorTimeQuery, ["full_video_segment", 0, 0, 0, "full-video-uuid-0", submitUserTwoHash, 0, 0, "sponsor", "full", 0, 0, "full_video_segment"]); await db.prepare("run", insertSponsorTimeQuery, ["full_video_segment", 0, 0, 0, "full-video-uuid-0", submitUserTwoHash, 0, 0, "sponsor", "full", 0, 0, "full_video_segment"]);
const insertVipUserQuery = 'INSERT INTO "vipUsers" ("userID") VALUES (?)'; const insertVipUserQuery = 'INSERT INTO "vipUsers" ("userID") VALUES (?)';
db.prepare("run", insertVipUserQuery, [getHash(submitVIPuser)]); await db.prepare("run", insertVipUserQuery, [getHash(submitVIPuser)]);
}); });
it("Should be able to submit a single time (Params method)", (done) => { it("Should be able to submit a single time (Params method)", (done) => {

View File

@@ -19,11 +19,11 @@ describe("postSkipSegments - Automod 80%", () => {
const queryDatabaseCategory = (videoID: string) => db.prepare("all", `SELECT "startTime", "endTime", "category" FROM "sponsorTimes" WHERE "videoID" = ? and "votes" > -1`, [videoID]); const queryDatabaseCategory = (videoID: string) => db.prepare("all", `SELECT "startTime", "endTime", "category" FROM "sponsorTimes" WHERE "videoID" = ? and "votes" > -1`, [videoID]);
before(() => { before(async () => {
const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "actionType", "videoDuration", "shadowHidden", "hashedVideoID") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "actionType", "videoDuration", "shadowHidden", "hashedVideoID") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
db.prepare("run", insertSponsorTimeQuery, [over80VideoID, 0, 1000, 0, "80percent-uuid-0", userIDHash, 0, 0, "interaction", "skip", 0, 0, over80VideoID]); await db.prepare("run", insertSponsorTimeQuery, [over80VideoID, 0, 1000, 0, "80percent-uuid-0", userIDHash, 0, 0, "interaction", "skip", 0, 0, over80VideoID]);
db.prepare("run", insertSponsorTimeQuery, [over80VideoID, 1001, 1005, 0, "80percent-uuid-1", userIDHash, 0, 0, "interaction", "skip", 0, 0, over80VideoID]); await db.prepare("run", insertSponsorTimeQuery, [over80VideoID, 1001, 1005, 0, "80percent-uuid-1", userIDHash, 0, 0, "interaction", "skip", 0, 0, over80VideoID]);
db.prepare("run", insertSponsorTimeQuery, [over80VideoID, 0, 5000, -2, "80percent-uuid-2", userIDHash, 0, 0, "interaction", "skip", 0, 0, over80VideoID]); await db.prepare("run", insertSponsorTimeQuery, [over80VideoID, 0, 5000, -2, "80percent-uuid-2", userIDHash, 0, 0, "interaction", "skip", 0, 0, over80VideoID]);
}); });
it("Should allow multiple times if total is under 80% of video (JSON method)", (done) => { it("Should allow multiple times if total is under 80% of video (JSON method)", (done) => {

View File

@@ -21,10 +21,10 @@ describe("postSkipSegments - duration", () => {
const queryDatabaseDuration = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "videoDuration" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]); const queryDatabaseDuration = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "videoDuration" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]);
before(() => { before(async () => {
const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "actionType", "videoDuration", "shadowHidden", "hashedVideoID") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "actionType", "videoDuration", "shadowHidden", "hashedVideoID") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
db.prepare("run", insertSponsorTimeQuery, ["full_video_duration_segment", 0, 0, 0, "full-video-duration-uuid-0", userIDTwo, 0, 0, "sponsor", "full", 123, 0, "full_video_duration_segment"]); await db.prepare("run", insertSponsorTimeQuery, ["full_video_duration_segment", 0, 0, 0, "full-video-duration-uuid-0", userIDTwo, 0, 0, "sponsor", "full", 123, 0, "full_video_duration_segment"]);
db.prepare("run", insertSponsorTimeQuery, ["full_video_duration_segment", 25, 30, 0, "full-video-duration-uuid-1", userIDTwo, 0, 0, "sponsor", "skip", 123, 0, "full_video_duration_segment"]); await db.prepare("run", insertSponsorTimeQuery, ["full_video_duration_segment", 25, 30, 0, "full-video-duration-uuid-1", userIDTwo, 0, 0, "sponsor", "skip", 123, 0, "full_video_duration_segment"]);
}); });
it("Should be able to submit a single time with a precise duration close to the one from the YouTube API (JSON method)", (done) => { it("Should be able to submit a single time with a precise duration close to the one from the YouTube API (JSON method)", (done) => {

View File

@@ -21,17 +21,17 @@ describe("postSkipSegments Features - Chapters", () => {
}; };
} }
before(() => { before(async () => {
const submitNumberOfTimes = 10; const submitNumberOfTimes = 10;
const submitUser_reputationHash = getHash(submitUser_reputation); const submitUser_reputationHash = getHash(submitUser_reputation);
const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", views, category, "actionType", "shadowHidden") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", views, category, "actionType", "shadowHidden") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
for (let i = 0; i < submitNumberOfTimes; i++) { for (let i = 0; i < submitNumberOfTimes; i++) {
const uuid = `post_reputation_uuid-${i}`; const uuid = `post_reputation_uuid-${i}`;
const videoID = `post_reputation_video-${i}`; const videoID = `post_reputation_video-${i}`;
db.prepare("run", insertSponsorTimeQuery, [videoID, 1, 11, 5, 1, uuid, submitUser_reputationHash, 1597240000000, 50, "sponsor", "skip", 0]); await db.prepare("run", insertSponsorTimeQuery, [videoID, 1, 11, 5, 1, uuid, submitUser_reputationHash, 1597240000000, 50, "sponsor", "skip", 0]);
} }
// user feature // user feature
db.prepare("run", `INSERT INTO "userFeatures" ("userID", "feature", "issuerUserID", "timeSubmitted") VALUES(?, ?, ?, ?)`, [getHash(submitUser_feature), Feature.ChapterSubmitter, "generic-VIP", 0]); await db.prepare("run", `INSERT INTO "userFeatures" ("userID", "feature", "issuerUserID", "timeSubmitted") VALUES(?, ?, ?, ?)`, [getHash(submitUser_feature), Feature.ChapterSubmitter, "generic-VIP", 0]);
}); });
it("Should be able to submit a single chapter due to reputation (JSON method)", (done) => { it("Should be able to submit a single chapter due to reputation (JSON method)", (done) => {

View File

@@ -9,10 +9,10 @@ describe("postSkipSegments - LockedVideos", () => {
const videoID = "lockedVideo"; const videoID = "lockedVideo";
const userID = userIDOne; const userID = userIDOne;
before(() => { before(async () => {
const insertLockCategoriesQuery = `INSERT INTO "lockCategories" ("userID", "videoID", "category", "reason") VALUES(?, ?, ?, ?)`; const insertLockCategoriesQuery = `INSERT INTO "lockCategories" ("userID", "videoID", "category", "reason") VALUES(?, ?, ?, ?)`;
db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "sponsor", "Custom Reason"]); await db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "sponsor", "Custom Reason"]);
db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "intro", ""]); await db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "intro", ""]);
}); });
it("Should return 403 and custom reason for submiting in lockedCategory", (done) => { it("Should return 403 and custom reason for submiting in lockedCategory", (done) => {

View File

@@ -19,8 +19,8 @@ describe("postSkipSegments - shadowban", () => {
const queryDatabaseShadowhidden = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "shadowHidden", "userID" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]); const queryDatabaseShadowhidden = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "shadowHidden", "userID" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]);
before(() => { before(async () => {
db.prepare("run", `INSERT INTO "shadowBannedUsers" ("userID") VALUES(?)`, [banUser01Hash]); await db.prepare("run", `INSERT INTO "shadowBannedUsers" ("userID") VALUES(?)`, [banUser01Hash]);
}); });
it("Should automatically shadowban segments if user is banned", (done) => { it("Should automatically shadowban segments if user is banned", (done) => {

View File

@@ -21,10 +21,10 @@ describe("postSkipSegments - userAgent", () => {
}; };
const dbFormatSegment = convertSingleToDBFormat(segment); const dbFormatSegment = convertSingleToDBFormat(segment);
before(() => { before(async () => {
const insertLockCategoriesQuery = `INSERT INTO "lockCategories" ("userID", "videoID", "category", "reason") VALUES(?, ?, ?, ?)`; const insertLockCategoriesQuery = `INSERT INTO "lockCategories" ("userID", "videoID", "category", "reason") VALUES(?, ?, ?, ?)`;
db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "sponsor", "Custom Reason"]); await db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "sponsor", "Custom Reason"]);
db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "intro", ""]); await db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "intro", ""]);
}); });
it("Should be able to submit with empty user-agent", (done) => { it("Should be able to submit with empty user-agent", (done) => {

View File

@@ -1,20 +1,11 @@
import { config } from "../../src/config";
import { getHash } from "../../src/utils/getHash";
import { db } from "../../src/databases/databases"; import { db } from "../../src/databases/databases";
import assert from "assert"; import assert from "assert";
import { client } from "../utils/httpClient"; import { client } from "../utils/httpClient";
import { usersForSuite } from "../utils/randomUsers";
describe("postSkipSegments Warnings", () => { describe("postSkipSegments Warnings", () => {
// Constant and helpers // Constant and helpers
const warnUser01 = "warn-user01"; const users = usersForSuite("postSkipSegmentsWarnings");
const warnUser01Hash = getHash(warnUser01);
const warnUser02 = "warn-user02";
const warnUser02Hash = getHash(warnUser02);
const warnUser03 = "warn-user03";
const warnUser03Hash = getHash(warnUser03);
const warnUser04 = "warn-user04";
const warnUser04Hash = getHash(warnUser04);
const warnVideoID = "postSkipSegments-warn-video"; const warnVideoID = "postSkipSegments-warn-video";
const endpoint = "/api/skipSegments"; const endpoint = "/api/skipSegments";
@@ -24,104 +15,92 @@ describe("postSkipSegments Warnings", () => {
data data
}); });
before(() => { before(async () => {
const now = Date.now(); const now = Date.now();
const warnVip01Hash = getHash("postSkipSegmentsWarnVIP");
const reason01 = "Reason01"; const reason01 = "Reason01";
const reason02 = ""; const reason02 = "";
const reason03 = "Reason03"; const reason03 = "Reason03";
const MILLISECONDS_IN_HOUR = 3600000;
const WARNING_EXPIRATION_TIME = config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR;
const insertWarningQuery = 'INSERT INTO warnings ("userID", "issuerUserID", "enabled", "reason", "issueTime") VALUES(?, ?, ?, ?, ?)'; const insertWarningQuery = 'INSERT INTO warnings ("userID", "issuerUserID", "enabled", "reason", "issueTime") VALUES(?, ?, ?, ?, ?)';
// User 1 | 1 active | custom reason // User 1 | 1 active | custom reason
db.prepare("run", insertWarningQuery, [warnUser01Hash, warnVip01Hash, 1, reason01, now]); await db.prepare("run", insertWarningQuery, [users.u01.public, users.vip01.public, 1, reason01, now]);
// User 2 | 1 inactive | default reason // User 2 | 1 inactive | default reason
db.prepare("run", insertWarningQuery, [warnUser02Hash, warnVip01Hash, 0, reason02, now]); await db.prepare("run", insertWarningQuery, [users.u02.public, users.vip01.public, 0, reason02, now]);
// User 3 | 1 expired, active | custom reason // User 3 | 1 inactive, 1 active | different reasons
db.prepare("run", insertWarningQuery, [warnUser03Hash, warnVip01Hash, 1, reason03, (now - WARNING_EXPIRATION_TIME - 1000)]); await db.prepare("run", insertWarningQuery, [users.u03.public, users.vip01.public, 0, reason01, now]);
await db.prepare("run", insertWarningQuery, [users.u03.public, users.vip01.public, 1, reason03, now+1]);
// User 4 | 1 active | default reason // User 4 | 1 active | default reason
db.prepare("run", insertWarningQuery, [warnUser04Hash, warnVip01Hash, 1, reason02, now]); await db.prepare("run", insertWarningQuery, [users.u04.public, users.vip01.public, 1, reason02, now]);
}); });
it("Should be rejected with custom message if user has active warnings", (done) => { it("Should be rejected with custom message if user has active warnings", async () => {
postSkipSegmentJSON({ const res = await postSkipSegmentJSON({
userID: warnUser01, userID: users.u01.private,
videoID: warnVideoID, videoID: warnVideoID,
segments: [{ segments: [{
segment: [0, 10], segment: [0, 10],
category: "sponsor", category: "sponsor",
}], }],
}) });
.then(res => {
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
const errorMessage = res.data; const errorMessage = res.data;
const reason = "Reason01"; const reason = "Reason01";
const expected = "Submission rejected due to a tip from a moderator. This means that we noticed you were making some common mistakes" const expected = "Submission rejected due to a tip from a moderator. This means that we noticed you were making some common mistakes"
+ " that are not malicious, and we just want to clarify the rules. " + " that are not malicious, and we just want to clarify the rules. "
+ "Could you please send a message in discord.gg/SponsorBlock or matrix.to/#/#sponsor:ajay.app so we can further help you? " + "Could you please send a message in discord.gg/SponsorBlock or matrix.to/#/#sponsor:ajay.app so we can further help you? "
+ `Your userID is ${warnUser01Hash}.\n\nTip message: '${reason}'`; + `Your userID is ${users.u01.public}.\n\nTip message: '${reason}'`;
assert.strictEqual(errorMessage, expected); assert.strictEqual(errorMessage, expected);
done();
})
.catch(err => done(err));
}); });
it("Should be accepted if user has inactive warning", (done) => { it("Should be accepted if user has inactive warning", async () => {
postSkipSegmentJSON({ const res = await postSkipSegmentJSON({
userID: warnUser02, userID: users.u02.private,
videoID: warnVideoID, videoID: warnVideoID,
segments: [{ segments: [{
segment: [50, 60], segment: [50, 60],
category: "sponsor", category: "sponsor",
}], }],
}) });
.then(res => {
assert.ok(res.status === 200, `Status code was ${res.status} ${res.data}`); assert.ok(res.status === 200, `Status code was ${res.status} ${res.data}`);
done();
})
.catch(err => done(err));
}); });
it("Should be accepted if user has expired warning", (done) => { it("Should be rejected with custom message if user has active warnings, even if has one inactive warning, should use current message", async () => {
postSkipSegmentJSON({ const res = await postSkipSegmentJSON({
userID: warnUser03, userID: users.u03.private,
videoID: warnVideoID, videoID: warnVideoID,
segments: [{ segments: [{
segment: [53, 60], segment: [10, 20],
category: "sponsor", category: "sponsor",
}], }],
}) });
.then(res => { assert.strictEqual(res.status, 403);
assert.ok(res.status === 200, `Status code was ${res.status} ${res.data}`); const errorMessage = res.data;
done(); const reason = "Reason03";
}) const expected = "Submission rejected due to a tip from a moderator. This means that we noticed you were making some common mistakes"
.catch(err => done(err)); + " that are not malicious, and we just want to clarify the rules. "
+ "Could you please send a message in discord.gg/SponsorBlock or matrix.to/#/#sponsor:ajay.app so we can further help you? "
+ `Your userID is ${users.u03.public}.\n\nTip message: '${reason}'`;
assert.strictEqual(errorMessage, expected);
}); });
it("Should be rejected with default message if user has active warning", (done) => { it("Should be rejected with default message if user has active warning", async () => {
postSkipSegmentJSON({ const res = await postSkipSegmentJSON({
userID: warnUser04, userID: users.u04.private,
videoID: warnVideoID, videoID: warnVideoID,
segments: [{ segments: [{
segment: [0, 10], segment: [0, 10],
category: "sponsor", category: "sponsor",
}], }],
}) });
.then(res => {
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
const errorMessage = res.data; const errorMessage = res.data;
const expected = "Submission rejected due to a tip from a moderator. This means that we noticed you were making some common mistakes" const expected = "Submission rejected due to a tip from a moderator. This means that we noticed you were making some common mistakes"
+ " that are not malicious, and we just want to clarify the rules. " + " that are not malicious, and we just want to clarify the rules. "
+ "Could you please send a message in discord.gg/SponsorBlock or matrix.to/#/#sponsor:ajay.app so we can further help you? " + "Could you please send a message in discord.gg/SponsorBlock or matrix.to/#/#sponsor:ajay.app so we can further help you? "
+ `Your userID is ${warnUser04Hash}.`; + `Your userID is ${users.u04.public}.`;
assert.strictEqual(errorMessage, expected); assert.strictEqual(errorMessage, expected);
done();
})
.catch(err => done(err));
}); });
}); });

View File

@@ -3,33 +3,44 @@ import { db } from "../../src/databases/databases";
import { getHash } from "../../src/utils/getHash"; import { getHash } from "../../src/utils/getHash";
import assert from "assert"; import assert from "assert";
import { client } from "../utils/httpClient"; import { client } from "../utils/httpClient";
import { usersForSuite } from "../utils/randomUsers";
describe("postWarning", () => { describe("postWarning", () => {
// constants // constants
const endpoint = "/api/warnUser"; const endpoint = "/api/warnUser";
const getWarning = (userID: string, type = 0) => db.prepare("get", `SELECT "userID", "issueTime", "issuerUserID", enabled, "reason" FROM warnings WHERE "userID" = ? AND "type" = ?`, [userID, type]); const getWarning = (userID: string, type = 0) => db.prepare("all", `SELECT * FROM warnings WHERE "userID" = ? AND "type" = ? ORDER BY "issueTime" ASC`, [userID, type]);
const warneduserOneID = "warning-0"; const users = usersForSuite("postWarning");
const warnedUserTwoID = "warning-1";
const warnedUserOnePublicID = getHash(warneduserOneID);
const warnedUserTwoPublicID = getHash(warnedUserTwoID);
const warningVipOne = "warning-vip-1";
const warningVipTwo = "warning-vip-2";
const nonVipUser = "warning-non-vip";
before(async () => { before(async () => {
await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [getHash(warningVipOne)]); const insertWarningQuery = 'INSERT INTO warnings ("userID", "issuerUserID", "enabled", "reason", "issueTime") VALUES(?, ?, ?, ?, ?)';
await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [getHash(warningVipTwo)]); const insertWarningQueryWithDisableTime = 'INSERT INTO warnings ("userID", "issuerUserID", "enabled", "reason", "issueTime", "disableTime") VALUES(?, ?, ?, ?, ?, ?)';
const HOUR = 60 * 60 * 1000;
await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [users.vip1.public]);
await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [users.vip2.public]);
await db.prepare("run", insertWarningQuery, [users.u1.public, users.vip1.public, 1, "warn reason 1", (Date.now() - 24 * HOUR)]); // 24 hours is much past the edit deadline
await db.prepare("run", insertWarningQuery, [users.u2.public, users.vip1.public, 1, "warn reason 2", (Date.now() - 24 * HOUR)]);
await db.prepare("run", insertWarningQuery, [users.u3.public, users.vip1.public, 1, "warn reason 3", Date.now()]);
await db.prepare("run", insertWarningQuery, [users.u4.public, users.vip1.public, 1, "warn reason 4", Date.now()]);
await db.prepare("run", insertWarningQuery, [users.u6.public, users.vip1.public, 1, "warn reason 6", Date.now()]);
await db.prepare("run", insertWarningQuery, [users.u9.public, users.vip1.public, 0, "warn reason 9", Date.now()]);
await db.prepare("run", insertWarningQuery, [users.u10.public, users.vip1.public, 1, "warn reason 10", Date.now()]);
await db.prepare("run", insertWarningQuery, [users.u11.public, users.vip1.public, 1, "warn reason 11", Date.now()]);
await db.prepare("run", insertWarningQuery, [users.u12.public, users.vip1.public, 0, "warn reason 12", Date.now()]);
await db.prepare("run", insertWarningQuery, [users.u13.public, users.vip1.public, 0, "warn reason 13", Date.now()]);
await db.prepare("run", insertWarningQueryWithDisableTime, [users.u14.public, users.vip1.public, 0, "warn reason 14", 123, 12345]);
await db.prepare("run", insertWarningQuery, [users.u14.public, users.vip1.public, 1, "another reason 14", Date.now()]);
}); });
it("Should be able to create warning if vip (exp 200)", (done) => { it("Should be able to create warning if vip (exp 200)", async () => {
const json = { const json = {
issuerUserID: warningVipOne, issuerUserID: users.vip1.private,
userID: warnedUserOnePublicID, userID: users.u0.public,
reason: "warning-reason-0" reason: "warning-reason-0"
}; };
client.post(endpoint, json) const res = await client.post(endpoint, json);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getWarning(json.userID); const row = await getWarning(json.userID);
const expected = { const expected = {
@@ -37,165 +48,284 @@ describe("postWarning", () => {
issuerUserID: getHash(json.issuerUserID), issuerUserID: getHash(json.issuerUserID),
reason: json.reason, reason: json.reason,
}; };
assert.ok(partialDeepEquals(row, expected)); assert.equal(row.length, 1);
done(); assert.ok(partialDeepEquals(row[0], expected));
})
.catch(err => done(err));
}); });
it("Should be not be able to create a duplicate warning if vip", (done) => { it("Should be not be able to edit a warning if past deadline and same vip", async () => {
const json = { const json = {
issuerUserID: warningVipOne, issuerUserID: users.vip1.private,
userID: warnedUserOnePublicID, userID: users.u1.public,
reason: "edited reason 1",
}; };
client.post(endpoint, json) const res = await client.post(endpoint, json);
.then(async res => {
assert.strictEqual(res.status, 409); assert.strictEqual(res.status, 409);
const row = await getWarning(json.userID); const row = await getWarning(json.userID);
const expected = { const expected = {
enabled: 1, enabled: 1,
issuerUserID: getHash(json.issuerUserID), issuerUserID: users.vip1.public,
}; };
assert.ok(partialDeepEquals(row, expected)); assert.equal(row.length, 1);
done(); assert.ok(partialDeepEquals(row[0], expected));
})
.catch(err => done(err));
}); });
it("Should be able to remove warning if vip", (done) => { it("Should be not be able to edit a warning if past deadline and different vip", async () => {
const json = { const json = {
issuerUserID: warningVipOne, issuerUserID: users.vip2.private,
userID: warnedUserOnePublicID, userID: users.u2.public,
enabled: false reason: "edited reason 2",
}; };
client.post(endpoint, json) const res = await client.post(endpoint, json);
.then(async res => { assert.strictEqual(res.status, 409);
const row = await getWarning(json.userID);
const expected = {
enabled: 1,
issuerUserID: users.vip1.public,
};
assert.equal(row.length, 1);
assert.ok(partialDeepEquals(row[0], expected));
});
it("Should be able to remove warning if same vip as issuer", async () => {
const json = {
issuerUserID: users.vip1.private,
userID: users.u3.public,
enabled: false
};
const beforeTime = Date.now();
const res = await client.post(endpoint, json);
const afterTime = Date.now();
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getWarning(json.userID); const row = await getWarning(json.userID);
const expected = { const expected = {
enabled: 0 enabled: 0
}; };
assert.ok(partialDeepEquals(row, expected)); assert.equal(row.length, 1);
done(); assert.ok(partialDeepEquals(row[0], expected));
}) // check disableTime
.catch(err => done(err)); assert.ok(row[0].disableTime >= beforeTime && row[0].disableTime <= afterTime, "expected disableTime to be somewhere between execution start and end");
}); });
it("Should not be able to create warning if not vip (exp 403)", (done) => { it("Should be able to remove warning if not the same vip as issuer", async () => {
const json = { const json = {
issuerUserID: nonVipUser, issuerUserID: users.vip2.private,
userID: warnedUserOnePublicID, userID: users.u4.public,
};
client.post(endpoint, json)
.then(res => {
assert.strictEqual(res.status, 403);
done();
})
.catch(err => done(err));
});
it("Should return 400 if missing body", (done) => {
client.post(endpoint, {})
.then(res => {
assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
});
it("Should re-enable disabled warning", (done) => {
const json = {
issuerUserID: warningVipOne,
userID: warnedUserOnePublicID,
enabled: true
};
client.post(endpoint, json)
.then(async res => {
assert.strictEqual(res.status, 200);
const data = await getWarning(json.userID);
const expected = {
enabled: 1
};
assert.ok(partialDeepEquals(data, expected));
done();
})
.catch(err => done(err));
});
it("Should be able to remove your own warning", (done) => {
const json = {
userID: warneduserOneID,
enabled: false enabled: false
}; };
const beforeTime = Date.now();
client.post(endpoint, json) const res = await client.post(endpoint, json);
.then(async res => { const afterTime = Date.now();
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const data = await getWarning(warnedUserOnePublicID); const row = await getWarning(json.userID);
const expected = { const expected = {
enabled: 0 enabled: 0
}; };
assert.ok(partialDeepEquals(data, expected)); assert.equal(row.length, 1);
done(); assert.ok(partialDeepEquals(row[0], expected));
}) // check disableTime
.catch(err => done(err)); assert.ok(row[0].disableTime >= beforeTime && row[0].disableTime <= afterTime, "expected disableTime to be somewhere between execution start and end");
}); });
it("Should not be able to add your own warning", (done) => { it("Should not be able to create warning if not vip (exp 403)", async () => {
const json = { const json = {
userID: warneduserOneID, issuerUserID: users.nonvip.private,
enabled: true userID: users.u5.public,
reason: "warn reason 5",
}; };
client.post(endpoint, json) const res = await client.post(endpoint, json);
.then(async res => {
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
const data = await getWarning(warnedUserOnePublicID); });
it("Should return 400 if missing body", async () => {
const res = await client.post(endpoint, {});
assert.strictEqual(res.status, 400);
});
it("Should be able to remove your own warning", async () => {
const json = {
userID: users.u6.private,
enabled: false
};
const beforeTime = Date.now();
const res = await client.post(endpoint, json);
const afterTime = Date.now();
assert.strictEqual(res.status, 200);
const row = await getWarning(users.u6.public);
const expected = { const expected = {
enabled: 0 enabled: 0
}; };
assert.ok(partialDeepEquals(data, expected)); assert.equal(row.length, 1);
done(); assert.ok(partialDeepEquals(row[0], expected));
}) // check disableTime
.catch(err => done(err)); assert.ok(row[0].disableTime >= beforeTime && row[0].disableTime <= afterTime, "expected disableTime to be somewhere between execution start and end");
}); });
it("Should not be able to warn a user without reason", (done) => { it("Should not be able to add your own warning", async () => {
const json = { const json = {
issuerUserID: warningVipOne, userID: users.u7.private,
userID: warnedUserTwoPublicID, enabled: true,
reason: "warn reason 7",
};
const res = await client.post(endpoint, json);
assert.strictEqual(res.status, 403);
const data = await getWarning(users.u7.public);
assert.equal(data.length, 0);
});
it("Should not be able to warn a user without reason", async () => {
const json = {
issuerUserID: users.vip1.private,
userID: users.u8.public,
enabled: true enabled: true
}; };
client.post(endpoint, json) const res = await client.post(endpoint, json);
.then(res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
}); });
it("Should be able to re-warn a user without reason", (done) => { it("Should not be able to re-warn a user without reason", async () => {
const json = { const json = {
issuerUserID: warningVipOne, issuerUserID: users.vip1.private,
userID: warnedUserOnePublicID, userID: users.u9.public,
enabled: true enabled: true
}; };
client.post(endpoint, json) const res = await client.post(endpoint, json);
.then(async res => { assert.strictEqual(res.status, 400);
assert.strictEqual(res.status, 200); });
const data = await getWarning(warnedUserOnePublicID);
const expected = { it("Should be able to edit a warning if within deadline and same vip", async () => {
enabled: 1 const json = {
issuerUserID: users.vip1.private,
userID: users.u10.public,
enabled: true,
reason: "edited reason 10",
}; };
assert.ok(partialDeepEquals(data, expected));
done(); const res = await client.post(endpoint, json);
}) assert.strictEqual(res.status, 200);
.catch(err => done(err)); const row = await getWarning(json.userID);
const expected = {
enabled: 1,
issuerUserID: users.vip1.public,
reason: json.reason,
};
assert.equal(row.length, 1);
assert.ok(partialDeepEquals(row[0], expected));
});
it("Should not be able to edit a warning if within deadline and different vip", async () => {
const json = {
issuerUserID: users.vip2.private,
userID: users.u11.public,
enabled: true,
reason: "edited reason 11",
};
const res = await client.post(endpoint, json);
assert.strictEqual(res.status, 409);
const row = await getWarning(json.userID);
const expected = {
enabled: 1,
issuerUserID: users.vip1.public,
reason: "warn reason 11",
};
assert.equal(row.length, 1);
assert.ok(partialDeepEquals(row[0], expected));
});
it("Should be able to warn a previously warned user again (same vip)", async () => {
const json = {
issuerUserID: users.vip1.private,
userID: users.u12.public,
enabled: true,
reason: "new reason 12",
};
const res = await client.post(endpoint, json);
assert.strictEqual(res.status, 200);
const row = await getWarning(json.userID);
const expected = [
{
enabled: 0,
issuerUserID: users.vip1.public,
reason: "warn reason 12",
},
{
enabled: 1,
issuerUserID: users.vip1.public,
reason: "new reason 12",
}
];
assert.equal(row.length, 2);
assert.ok(partialDeepEquals(row[0], expected[0]), "warning 1");
assert.ok(partialDeepEquals(row[1], expected[1]), "warning 2");
});
it("Should be able to warn a previously warned user again (different vip)", async () => {
const json = {
issuerUserID: users.vip2.private,
userID: users.u13.public,
enabled: true,
reason: "new reason 13",
};
const res = await client.post(endpoint, json);
assert.strictEqual(res.status, 200);
const row = await getWarning(json.userID);
const expected = [
{
enabled: 0,
issuerUserID: users.vip1.public,
reason: "warn reason 13",
},
{
enabled: 1,
issuerUserID: users.vip2.public,
reason: "new reason 13",
}
];
assert.equal(row.length, 2);
assert.ok(partialDeepEquals(row[0], expected[0]), "warning 1");
assert.ok(partialDeepEquals(row[1], expected[1]), "warning 2");
});
it("Disabling a warning should only set disableTime for the active warning", async () => {
const json = {
issuerUserID: users.vip2.private,
userID: users.u14.public,
enabled: false,
};
const beforeTime = Date.now();
const res = await client.post(endpoint, json);
const afterTime = Date.now();
assert.strictEqual(res.status, 200);
const row = await getWarning(json.userID);
const expected = [
{
enabled: 0,
issuerUserID: users.vip1.public,
reason: "warn reason 14",
disableTime: 12345,
},
{
enabled: 0,
issuerUserID: users.vip1.public,
reason: "another reason 14",
}
];
assert.equal(row.length, 2);
assert.ok(partialDeepEquals(row[0], expected[0]), "warning 1");
assert.ok(partialDeepEquals(row[1], expected[1]), "warning 2");
// check disableTime
assert.ok(row[1].disableTime >= beforeTime && row[1].disableTime <= afterTime, "expected disableTime to be somewhere between execution start and end");
}); });
}); });

View File

@@ -9,44 +9,32 @@ const randKey2 = genRandom(16);
const randKey3 = genRandom(); const randKey3 = genRandom();
const randValue3 = genRandom(); const randValue3 = genRandom();
const redisGetCheck = (key: string, expected: string | null, done: Mocha.Done): Promise<void> =>
redis.get(key)
.then(res => {
assert.strictEqual(res, expected);
done();
}).catch(err => done(err));
describe("redis test", function() { describe("redis test", function() {
before(async function() { before(async function() {
if (!config.redis?.enabled) this.skip(); if (!config.redis?.enabled) this.skip();
await redis.set(randKey1, randValue1); await redis.set(randKey1, randValue1);
}); });
it("Should get stored value", (done) => { it("Should get stored value", async () => {
redisGetCheck(randKey1, randValue1, done); const res = await redis.get(randKey1);
assert.strictEqual(res, randValue1);
}); });
it("Should not be able to get not stored value", (done) => { it("Should not be able to get not stored value", async () => {
redis.get(randKey2) const res = await redis.get(randKey2);
.then(res => { assert.strictEqual(res, null, "Value should not be found");
if (res) done("Value should not be found");
done();
}).catch(err => done(err));
}); });
it("Should be able to delete stored value", (done) => { it("Should be able to delete stored value", async () => {
redis.del(randKey1) await redis.del(randKey1);
.catch(err => done(err)) const res = await redis.get(randKey1);
.then(() => redisGetCheck(randKey1, null, done)); assert.strictEqual(res, null, "Deleted key should not be found");
}); });
it("Should be able to set expiring value", (done) => { it("Should be able to set expiring value", async () => {
redis.setEx(randKey3, 8400, randValue3) await redis.setEx(randKey3, 8400, randValue3);
.catch(err => done(err)) const res = await redis.get(randKey3);
.then(() => redisGetCheck(randKey3, randValue3, done)); assert.strictEqual(res, randValue3);
}); });
it("Should continue when undefined value is fetched", (done) => { it("Should continue when undefined value is fetched", async () => {
const undefkey = `undefined.${genRandom()}`; const undefkey = `undefined.${genRandom()}`;
redis.get(undefkey) const res = await redis.get(undefkey);
.then(result => { assert.strictEqual(res, null);
assert.ok(!result); // result should be falsy
done();
});
}); });
}); });

View File

@@ -56,12 +56,11 @@ function wellFormatUserName(userName: string) {
return userName.replace(/[\u0000-\u001F\u007F-\u009F]/g, ""); return userName.replace(/[\u0000-\u001F\u007F-\u009F]/g, "");
} }
async function testUserNameChangelog(userID: string, newUserName: string, oldUserName: string, byAdmin: boolean, done: Mocha.Done) { async function testUserNameChangelog(userID: string, newUserName: string, oldUserName: string, byAdmin: boolean) {
const log = await getLastLogUserNameChange(userID); const log = await getLastLogUserNameChange(userID);
assert.strictEqual(newUserName, log.newUserName); assert.strictEqual(newUserName, log.newUserName);
assert.strictEqual(oldUserName, log.oldUserName); assert.strictEqual(oldUserName, log.oldUserName);
assert.strictEqual(byAdmin, Boolean(log.updatedByAdmin)); assert.strictEqual(byAdmin, Boolean(log.updatedByAdmin));
return done();
} }
const endpoint = "/api/setUsername"; const endpoint = "/api/setUsername";
@@ -111,147 +110,104 @@ describe("setUsername", () => {
} }
}); });
it("Should be able to set username that has never been set", (done) => { it("Should be able to set username that has never been set", async () => {
postSetUserName(user00PrivateUserID, username00) const res = await postSetUserName(user00PrivateUserID, username00);
.then(async res => {
const usernameInfo = await getUsernameInfo(getHash(user00PrivateUserID)); const usernameInfo = await getUsernameInfo(getHash(user00PrivateUserID));
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(usernameInfo.userName, username00); assert.strictEqual(usernameInfo.userName, username00);
assert.notStrictEqual(usernameInfo.locked, 1, "username should not be locked"); assert.notStrictEqual(usernameInfo.locked, 1, "username should not be locked");
done();
})
.catch((err) => done(err));
}); });
it("Should return 200", (done) => { it("Should return 200", async () => {
const username = "Changed%20Username"; const username = "Changed%20Username";
postSetUserName(user01PrivateUserID, username) const res = await postSetUserName(user01PrivateUserID, username);
.then(res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
testUserNameChangelog(user01PrivateUserID, username, username01, false, done); await testUserNameChangelog(user01PrivateUserID, username, username01, false);
})
.catch((err) => done(err));
}); });
it('Should return 400 for missing param "userID"', (done) => { it('Should return 400 for missing param "userID"', async () => {
client({ const res = await client({
method: "POST", method: "POST",
url: endpoint, url: endpoint,
data: { data: {
userName: "MyUsername" userName: "MyUsername"
} }
}) });
.then(res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
done();
})
.catch((err) => done(err));
}); });
it('Should return 400 for missing param "username"', (done) => { it('Should return 400 for missing param "username"', async () => {
client({ const res = await client({
method: "POST", method: "POST",
url: endpoint, url: endpoint,
data: { data: {
userID: "test" userID: "test"
} }
}) });
.then(res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
done();
})
.catch((err) => done(err));
}); });
it('Should return 400 for "username" longer then 64 characters', (done) => { it('Should return 400 for "username" longer then 64 characters', async () => {
const username65 = "0000000000000000000000000000000000000000000000000000000000000000X"; const username65 = "0000000000000000000000000000000000000000000000000000000000000000X";
postSetUserName("test", username65) const res = await postSetUserName("test", username65);
.then(res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
done();
})
.catch((err) => done(err));
}); });
it('Should not change username if it contains "discord"', (done) => { it('Should not change username if it contains "discord"', async () => {
const newUsername = "discord.me"; const newUsername = "discord.me";
postSetUserName(user02PrivateUserID, newUsername) const res = await postSetUserName(user02PrivateUserID, newUsername);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const userNameInfo = await getUsernameInfo(getHash(user02PrivateUserID)); const userNameInfo = await getUsernameInfo(getHash(user02PrivateUserID));
assert.notStrictEqual(userNameInfo.userName, newUsername); assert.notStrictEqual(userNameInfo.userName, newUsername);
done();
})
.catch((err) => done(err));
}); });
it("Should be able to change username", (done) => { it("Should be able to change username", async () => {
const newUsername = "newUsername"; const newUsername = "newUsername";
postSetUserName(user03PrivateUserID, newUsername) await postSetUserName(user03PrivateUserID, newUsername);
.then(async () => {
const usernameInfo = await getUsernameInfo(getHash(user03PrivateUserID)); const usernameInfo = await getUsernameInfo(getHash(user03PrivateUserID));
assert.strictEqual(usernameInfo.userName, newUsername, "Username should change"); assert.strictEqual(usernameInfo.userName, newUsername, "Username should change");
assert.notStrictEqual(usernameInfo.locked, 1, "Username should not be locked"); assert.notStrictEqual(usernameInfo.locked, 1, "Username should not be locked");
testUserNameChangelog(user03PrivateUserID, newUsername, username03, false, done); await testUserNameChangelog(user03PrivateUserID, newUsername, username03, false);
})
.catch((err) => done(err));
}); });
it("Should not be able to change locked username", (done) => { it("Should not be able to change locked username", async () => {
const newUsername = "newUsername"; const newUsername = "newUsername";
postSetUserName(user04PrivateUserID, newUsername) await postSetUserName(user04PrivateUserID, newUsername);
.then(async () => {
const usernameInfo = await getUsernameInfo(getHash(user04PrivateUserID)); const usernameInfo = await getUsernameInfo(getHash(user04PrivateUserID));
assert.notStrictEqual(usernameInfo.userName, newUsername, "Username should not be changed"); assert.notStrictEqual(usernameInfo.userName, newUsername, "Username should not be changed");
assert.strictEqual(usernameInfo.locked, 1, "username should be locked"); assert.strictEqual(usernameInfo.locked, 1, "username should be locked");
done();
})
.catch((err) => done(err));
}); });
it("Should filter out unicode control characters", (done) => { it("Should filter out unicode control characters", async () => {
const newUsername = "This\nUsername+has\tInvalid+Characters"; const newUsername = "This\nUsername+has\tInvalid+Characters";
postSetUserName(user05PrivateUserID, newUsername) await postSetUserName(user05PrivateUserID, newUsername);
.then(async () => {
const usernameInfo = await getUsernameInfo(getHash(user05PrivateUserID)); const usernameInfo = await getUsernameInfo(getHash(user05PrivateUserID));
assert.notStrictEqual(usernameInfo.userName, newUsername, "Username should not contain control characters"); assert.notStrictEqual(usernameInfo.userName, newUsername, "Username should not contain control characters");
testUserNameChangelog(user05PrivateUserID, wellFormatUserName(newUsername), username05, false, done); await testUserNameChangelog(user05PrivateUserID, wellFormatUserName(newUsername), username05, false);
})
.catch((err) => done(err));
}); });
it("Incorrect adminUserID should return 403", (done) => { it("Incorrect adminUserID should return 403", async () => {
const newUsername = "New Username"; const newUsername = "New Username";
postSetUserNameAdmin(getHash(user06PrivateUserID), newUsername,"invalidAdminID") const res = await postSetUserNameAdmin(getHash(user06PrivateUserID), newUsername,"invalidAdminID");
.then(res => {
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
done();
})
.catch((err) => done(err));
}); });
it("Admin should be able to change username", (done) => { it("Admin should be able to change username", async () => {
const newUsername = "New Username"; const newUsername = "New Username";
postSetUserNameAdmin(getHash(user06PrivateUserID), newUsername, adminPrivateUserID) await postSetUserNameAdmin(getHash(user06PrivateUserID), newUsername, adminPrivateUserID);
.then(async () => {
const usernameInfo = await getUsernameInfo(getHash(user06PrivateUserID)); const usernameInfo = await getUsernameInfo(getHash(user06PrivateUserID));
assert.strictEqual(usernameInfo.userName, newUsername, "username should be changed"); assert.strictEqual(usernameInfo.userName, newUsername, "username should be changed");
assert.strictEqual(usernameInfo.locked, 1, "Username should be locked"); assert.strictEqual(usernameInfo.locked, 1, "Username should be locked");
testUserNameChangelog(user06PrivateUserID, newUsername, username06, true, done); await testUserNameChangelog(user06PrivateUserID, newUsername, username06, true);
})
.catch((err) => done(err));
}); });
it("Admin should be able to change locked username", (done) => { it("Admin should be able to change locked username", async () => {
const newUsername = "New Username"; const newUsername = "New Username";
postSetUserNameAdmin(getHash(user07PrivateUserID), newUsername, adminPrivateUserID) await postSetUserNameAdmin(getHash(user07PrivateUserID), newUsername, adminPrivateUserID);
.then(async () => {
const usernameInfo = await getUsernameInfo(getHash(user06PrivateUserID)); const usernameInfo = await getUsernameInfo(getHash(user06PrivateUserID));
assert.strictEqual(usernameInfo.userName, newUsername, "Username should be changed"); assert.strictEqual(usernameInfo.userName, newUsername, "Username should be changed");
assert.strictEqual(usernameInfo.locked, 1, "Username should be locked"); assert.strictEqual(usernameInfo.locked, 1, "Username should be locked");
testUserNameChangelog(user07PrivateUserID, newUsername, username07, true, done); await testUserNameChangelog(user07PrivateUserID, newUsername, username07, true);
})
.catch((err) => done(err));
}); });
it("Should delete existing username if new username is same as publicID", async () => { it("Should delete existing username if new username is same as publicID", async () => {

View File

@@ -59,71 +59,47 @@ describe("setUsernamePrivate tests", () => {
before(() => sinon.stub(config, "minUserIDLength").value(USERID_LIMIT)); before(() => sinon.stub(config, "minUserIDLength").value(USERID_LIMIT));
after(() => sinon.restore()); after(() => sinon.restore());
it("Existing privateID = username under Limit should retreive successfully", (done) => { it("Existing privateID = username under Limit should retreive successfully", async () => {
const privateID = preExisting_underLimit; const privateID = preExisting_underLimit;
hasSetUsername(getHash(privateID)) assert.ok(await hasSetUsername(getHash(privateID)));
.then((usernameInfo) => {
assert.ok(usernameInfo);
done();
});
}); });
it("Existing privateID = username over Limit should retreive successfully", (done) => { it("Existing privateID = username over Limit should retreive successfully", async () => {
const privateID = preExisting_overLimit; const privateID = preExisting_overLimit;
hasSetUsername(getHash(privateID)) assert.ok(await hasSetUsername(getHash(privateID)));
.then((usernameInfo) => {
assert.ok(usernameInfo);
done();
});
}); });
it("Should return error if trying to set userID = username under limit", (done) => { it("Should return error if trying to set userID = username under limit", async () => {
const privateID = newUser_underLimit; const privateID = newUser_underLimit;
postSetUserName(privateID, privateID) const res = await postSetUserName(privateID, privateID);
.then(async (res) => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
const usernameInfo = await hasSetUsername(getHash(privateID)); const usernameInfo = await hasSetUsername(getHash(privateID));
assert.ok(!usernameInfo); assert.ok(!usernameInfo);
done();
})
.catch((err) => done(err));
}); });
it("Should return error if trying to set username = other privateID over limit", (done) => { it("Should return error if trying to set username = other privateID over limit", async () => {
const privateID = newUser_overLimit; const privateID = newUser_overLimit;
postSetUserName(privateID, privateID) const res = await postSetUserName(privateID, privateID);
.then(async (res) => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
const usernameInfo = await hasSetUsername(getHash(privateID)); const usernameInfo = await hasSetUsername(getHash(privateID));
assert.ok(!usernameInfo); assert.ok(!usernameInfo);
done();
})
.catch((err) => done(err));
}); });
it("Should return error if trying to set username = other privateID over limit", (done) => { it("Should return error if trying to set username = other privateID over limit", async () => {
const privateID = otherUser; const privateID = otherUser;
const otherUserPrivate = preExisting_overLimit; const otherUserPrivate = preExisting_overLimit;
postSetUserName(privateID, otherUserPrivate) const res = await postSetUserName(privateID, otherUserPrivate);
.then(async (res) => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
const usernameInfo = await hasSetUsername(getHash(privateID)); const usernameInfo = await hasSetUsername(getHash(privateID));
assert.ok(!usernameInfo); assert.ok(!usernameInfo);
done();
})
.catch((err) => done(err));
}); });
it("Should not return error if trying to set username = other privateID under limit", (done) => { it("Should not return error if trying to set username = other privateID under limit", async () => {
const privateID = otherUser; const privateID = otherUser;
const otherUserPrivate = preExisting_underLimit; const otherUserPrivate = preExisting_underLimit;
postSetUserName(privateID, otherUserPrivate) const res = await postSetUserName(privateID, otherUserPrivate);
.then(async (res) => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const usernameInfo = await hasSetUsername(getHash(privateID)); const usernameInfo = await hasSetUsername(getHash(privateID));
assert.ok(usernameInfo); assert.ok(usernameInfo);
done();
})
.catch((err) => done(err));
}); });
}); });

View File

@@ -1,4 +1,4 @@
import { db, privateDB } from "../../src/databases/databases"; import { db } from "../../src/databases/databases";
import { getHash } from "../../src/utils/getHash"; import { getHash } from "../../src/utils/getHash";
import assert from "assert"; import assert from "assert";
import { Category, Service } from "../../src/types/segments.model"; import { Category, Service } from "../../src/types/segments.model";

View File

@@ -68,37 +68,23 @@ describe("tempVIP test", function() {
await redis.del(tempVIPKey(publicTempVIPOne)); await redis.del(tempVIPKey(publicTempVIPOne));
}); });
it("Should update db version when starting the application", () => { it("Should update db version when starting the application", async () => {
privateDB.prepare("get", "SELECT key, value FROM config where key = ?", ["version"]) const row = await privateDB.prepare("get", "SELECT key, value FROM config where key = ?", ["version"]);
.then(row => {
assert.ok(row.value >= 5, `Versions are not at least 5. private is ${row.value}`); assert.ok(row.value >= 5, `Versions are not at least 5. private is ${row.value}`);
}); });
}); it("User should not already be temp VIP", async () => {
it("User should not already be temp VIP", (done) => { assert.ok(!await checkUserVIP(publicTempVIPOne));
checkUserVIP(publicTempVIPOne)
.then(result => {
assert.ok(!result);
})
.then(async () => {
const row = await privateDB.prepare("get", `SELECT * FROM "tempVipLog" WHERE "targetUserID" = ?`, [publicTempVIPOne]); const row = await privateDB.prepare("get", `SELECT * FROM "tempVipLog" WHERE "targetUserID" = ?`, [publicTempVIPOne]);
assert.ok(!row?.enabled); assert.ok(!row?.enabled);
done();
})
.catch(err => done(err));
}); });
it("Should be able to normal upvote as a user", (done) => { it("Should be able to normal upvote as a user", async () => {
postVote(tempVIPOne, UUID0, 1) const res = await postVote(tempVIPOne, UUID0, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegment(UUID0); const row = await getSegment(UUID0);
assert.strictEqual(row.votes, 1); assert.strictEqual(row.votes, 1);
done();
})
.catch(err => done(err));
}); });
it("Should be able to add tempVIP", (done) => { it("Should be able to add tempVIP", async () => {
addTempVIP("true", permVIP1, publicTempVIPOne) const res = await addTempVIP("true", permVIP1, publicTempVIPOne);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
// check redis // check redis
const vip = await checkUserVIP(publicTempVIPOne); const vip = await checkUserVIP(publicTempVIPOne);
@@ -107,152 +93,93 @@ describe("tempVIP test", function() {
// check privateDB // check privateDB
const row = await privateDB.prepare("get", `SELECT * FROM "tempVipLog" WHERE "targetUserID" = ?`, [publicTempVIPOne]); const row = await privateDB.prepare("get", `SELECT * FROM "tempVipLog" WHERE "targetUserID" = ?`, [publicTempVIPOne]);
assert.ok(row.enabled); assert.ok(row.enabled);
done();
})
.catch(err => done(err));
}); });
it("Should be able to VIP downvote", (done) => { it("Should be able to VIP downvote", async () => {
postVote(tempVIPOne, UUID0, 0) const res = await postVote(tempVIPOne, UUID0, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegment(UUID0); const row = await getSegment(UUID0);
assert.strictEqual(row.votes, -2); assert.strictEqual(row.votes, -2);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to lock segment", (done) => { it("Should not be able to lock segment", async () => {
postVote(tempVIPOne, UUID0, 1) const res = await postVote(tempVIPOne, UUID0, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegment(UUID0); const row = await getSegment(UUID0);
assert.strictEqual(row.votes, 1); assert.strictEqual(row.votes, 1);
assert.strictEqual(row.locked, 0); assert.strictEqual(row.locked, 0);
done();
})
.catch(err => done(err));
}); });
it("Should be able to change category but not lock", (done) => { it("Should be able to change category but not lock", async () => {
postVoteCategory(tempVIPOne, UUID0, "filler") const res = await postVoteCategory(tempVIPOne, UUID0, "filler");
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegment(UUID0); const row = await getSegment(UUID0);
assert.strictEqual(row.category, "filler"); assert.strictEqual(row.category, "filler");
assert.strictEqual(row.locked, 0); assert.strictEqual(row.locked, 0);
done();
})
.catch(err => done(err));
}); });
it("Should be able to remove tempVIP prematurely", (done) => { it("Should be able to remove tempVIP prematurely", async () => {
addTempVIP("false", permVIP1, publicTempVIPOne) const res = await addTempVIP("false", permVIP1, publicTempVIPOne);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const vip = await checkUserVIP(publicTempVIPOne); const vip = await checkUserVIP(publicTempVIPOne);
assert.strictEqual(res.data, "Temp VIP removed"); assert.strictEqual(res.data, "Temp VIP removed");
assert.ok(!vip, "Should be no listed channelID"); assert.ok(!vip, "Should be no listed channelID");
done();
})
.catch(err => done(err));
}); });
it("Should not be able to VIP downvote", (done) => { it("Should not be able to VIP downvote", async () => {
postVote(tempVIPOne, UUID1, 0) const res = await postVote(tempVIPOne, UUID1, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegment(UUID1); const row = await getSegment(UUID1);
assert.strictEqual(row.votes, 0); assert.strictEqual(row.votes, 0);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to VIP change category", (done) => { it("Should not be able to VIP change category", async () => {
postVoteCategory(tempVIPOne, UUID1, "filler") const res = await postVoteCategory(tempVIPOne, UUID1, "filler");
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegment(UUID1); const row = await getSegment(UUID1);
assert.strictEqual(row.category, "sponsor"); assert.strictEqual(row.category, "sponsor");
done();
})
.catch(err => done(err));
}); });
// error code testing // error code testing
it("Should be able to add tempVIP after removal", (done) => { it("Should be able to add tempVIP after removal", async () => {
addTempVIP("true", permVIP1, publicTempVIPOne) const res = await addTempVIP("true", permVIP1, publicTempVIPOne);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const vip = await checkUserVIP(publicTempVIPOne); const vip = await checkUserVIP(publicTempVIPOne);
assert.strictEqual(vip, "ChannelID"); assert.strictEqual(vip, "ChannelID");
done();
})
.catch(err => done(err));
}); });
it("Should not be able to add VIP without existing VIP (403)", (done) => { it("Should not be able to add VIP without existing VIP (403)", async () => {
const privateID = "non-vip-privateid"; const privateID = "non-vip-privateid";
addTempVIP("true", privateID, publicTempVIPOne) const res = await addTempVIP("true", privateID, publicTempVIPOne);
.then(async res => {
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
const vip = await checkUserVIP(getHash(privateID) as HashedUserID); const vip = await checkUserVIP(getHash(privateID) as HashedUserID);
assert.ok(!vip, "Should be no listed channelID"); assert.ok(!vip, "Should be no listed channelID");
done();
})
.catch(err => done(err));
}); });
it("Should not be able to add permanant VIP as temporary VIP (409)", (done) => { it("Should not be able to add permanant VIP as temporary VIP (409)", async () => {
addTempVIP("true", permVIP1, publicPermVIP2) const res = await addTempVIP("true", permVIP1, publicPermVIP2);
.then(async res => {
assert.strictEqual(res.status, 409); assert.strictEqual(res.status, 409);
const vip = await checkUserVIP(publicPermVIP2); const vip = await checkUserVIP(publicPermVIP2);
assert.ok(!vip, "Should be no listed channelID"); assert.ok(!vip, "Should be no listed channelID");
done();
})
.catch(err => done(err));
}); });
it("Temp VIP should not be able to add another Temp VIP (403)", (done) => { it("Temp VIP should not be able to add another Temp VIP (403)", async () => {
const privateID = "non-vip-privateid"; const privateID = "non-vip-privateid";
const publicID = getHash(privateID) as HashedUserID; const publicID = getHash(privateID) as HashedUserID;
addTempVIP("true", tempVIPOne, publicID) const res = await addTempVIP("true", tempVIPOne, publicID);
.then(async res => {
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
const vip = await checkUserVIP(publicID); const vip = await checkUserVIP(publicID);
assert.ok(!vip, "Should be no listed channelID"); assert.ok(!vip, "Should be no listed channelID");
done();
})
.catch(err => done(err));
}); });
// error 40X testing // error 40X testing
it("Should return 404 with invalid videoID", (done) => { it("Should return 404 with invalid videoID", async () => {
const privateID = "non-vip-privateid"; const privateID = "non-vip-privateid";
const publicID = getHash(privateID) as HashedUserID; const publicID = getHash(privateID) as HashedUserID;
addTempVIP("true", permVIP1, publicID, "knownWrongID") const res = await addTempVIP("true", permVIP1, publicID, "knownWrongID");
.then(async res => {
assert.strictEqual(res.status, 404); assert.strictEqual(res.status, 404);
const vip = await checkUserVIP(publicID); const vip = await checkUserVIP(publicID);
assert.ok(!vip, "Should be no listed channelID"); assert.ok(!vip, "Should be no listed channelID");
done();
})
.catch(err => done(err));
}); });
it("Should return 400 with invalid userID", (done) => { it("Should return 400 with invalid userID", async () => {
addTempVIP("true", permVIP1, "" as HashedUserID, "knownWrongID") const res = await addTempVIP("true", permVIP1, "" as HashedUserID, "knownWrongID");
.then(res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
}); });
it("Should return 400 with invalid adminUserID", (done) => { it("Should return 400 with invalid adminUserID", async () => {
addTempVIP("true", "", publicTempVIPOne) const res = await addTempVIP("true", "", publicTempVIPOne);
.then(res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
}); });
it("Should return 400 with invalid channelID", (done) => { it("Should return 400 with invalid channelID", async () => {
addTempVIP("true", permVIP1, publicTempVIPOne, "") const res = await addTempVIP("true", permVIP1, publicTempVIPOne, "");
.then(res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
}); });
}); });

View File

@@ -16,32 +16,24 @@ describe("tokenUtils test", function() {
mock.onGet(/identity/).reply(200, patreon.activeIdentity); mock.onGet(/identity/).reply(200, patreon.activeIdentity);
}); });
it("Should be able to create patreon token", function (done) { it("Should be able to create patreon token", async function () {
if (!config?.patreon) this.skip(); if (!config?.patreon) return this.skip();
tokenUtils.createAndSaveToken(tokenUtils.TokenType.patreon, "test_code").then((licenseKey) => { const licenseKey = await tokenUtils.createAndSaveToken(tokenUtils.TokenType.patreon, "test_code");
assert.ok(validateToken(licenseKey[0])); assert.ok(validateToken(licenseKey[0]));
done();
}); });
}); it("Should be able to create local token", async () => {
it("Should be able to create local token", (done) => { const licenseKey = await tokenUtils.createAndSaveToken(tokenUtils.TokenType.local);
tokenUtils.createAndSaveToken(tokenUtils.TokenType.local).then((licenseKey) => {
assert.ok(validateToken(licenseKey[0])); assert.ok(validateToken(licenseKey[0]));
done();
}); });
}); it("Should be able to get patreon identity", async function () {
it("Should be able to get patreon identity", function (done) { if (!config?.patreon) return this.skip();
if (!config?.patreon) this.skip(); const result = await tokenUtils.getPatreonIdentity("fake_access_token");
tokenUtils.getPatreonIdentity("fake_access_token").then((result) => {
assert.deepEqual(result, patreon.activeIdentity); assert.deepEqual(result, patreon.activeIdentity);
done();
}); });
}); it("Should be able to refresh token", async function () {
it("Should be able to refresh token", function (done) { if (!config?.patreon) return this.skip();
if (!config?.patreon) this.skip(); const result = await tokenUtils.refreshToken(tokenUtils.TokenType.patreon, "fake-licence-Key", "fake_refresh_token");
tokenUtils.refreshToken(tokenUtils.TokenType.patreon, "fake-licence-Key", "fake_refresh_token").then((result) => {
assert.strictEqual(result, true); assert.strictEqual(result, true);
done();
});
}); });
after(function () { after(function () {
@@ -56,17 +48,13 @@ describe("tokenUtils failing tests", function() {
mock.onGet(/identity/).reply(204, patreon.activeIdentity); mock.onGet(/identity/).reply(204, patreon.activeIdentity);
}); });
it("Should fail if patreon is not correctly stubbed", function (done) { it("Should fail if patreon is not correctly stubbed", async () => {
tokenUtils.createAndSaveToken(tokenUtils.TokenType.patreon, "test_code").then((licenseKey) => { const licenseKey = await tokenUtils.createAndSaveToken(tokenUtils.TokenType.patreon, "test_code");
assert.strictEqual(licenseKey, null); assert.strictEqual(licenseKey, null);
done();
}); });
}); it("Should fail if token type is invalid", async () => {
it("Should fail if token type is invalid", (done) => { const licenseKey = await tokenUtils.createAndSaveToken("invalidTokenType" as tokenUtils.TokenType);
tokenUtils.createAndSaveToken("invalidTokenType" as tokenUtils.TokenType).then((licenseKey) => {
assert.strictEqual(licenseKey, null); assert.strictEqual(licenseKey, null);
done();
});
}); });
after(function () { after(function () {

View File

@@ -1,4 +1,3 @@
import { config } from "../../src/config";
import { db, privateDB } from "../../src/databases/databases"; import { db, privateDB } from "../../src/databases/databases";
import { getHash } from "../../src/utils/getHash"; import { getHash } from "../../src/utils/getHash";
import { ImportMock } from "ts-mock-imports"; import { ImportMock } from "ts-mock-imports";
@@ -26,8 +25,6 @@ describe("voteOnSponsorTime", () => {
const warnUser01Hash = getHash("warn-voteuser01"); const warnUser01Hash = getHash("warn-voteuser01");
const warnUser02Hash = getHash("warn-voteuser02"); const warnUser02Hash = getHash("warn-voteuser02");
const categoryChangeUserHash = getHash(categoryChangeUser); const categoryChangeUserHash = getHash(categoryChangeUser);
const MILLISECONDS_IN_HOUR = 3600000;
const warningExpireTime = MILLISECONDS_IN_HOUR * config.hoursAfterWarningExpires;
const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "shadowHidden", "hidden") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "shadowHidden", "hidden") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
await db.prepare("run", insertSponsorTimeQuery, ["vote-testtesttest", 1, 11, 2, 0, "vote-uuid-0", "testman", 0, 50, "sponsor", "skip", 0, 0]); await db.prepare("run", insertSponsorTimeQuery, ["vote-testtesttest", 1, 11, 2, 0, "vote-uuid-0", "testman", 0, 50, "sponsor", "skip", 0, 0]);
@@ -96,8 +93,6 @@ describe("voteOnSponsorTime", () => {
await db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 2000), warnVip01Hash, 1]); await db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 2000), warnVip01Hash, 1]);
await db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 3601000), warnVip01Hash, 1]); await db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 3601000), warnVip01Hash, 1]);
await db.prepare("run", insertWarningQuery, [warnUser02Hash, now, warnVip01Hash, 1]); await db.prepare("run", insertWarningQuery, [warnUser02Hash, now, warnVip01Hash, 1]);
await db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 1000)), warnVip01Hash, 1]);
await db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 2000)), warnVip01Hash, 1]);
await db.prepare("run", 'INSERT INTO "vipUsers" ("userID") VALUES (?)', [getHash(vipUser)]); await db.prepare("run", 'INSERT INTO "vipUsers" ("userID") VALUES (?)', [getHash(vipUser)]);
@@ -133,34 +128,25 @@ describe("voteOnSponsorTime", () => {
const getSegmentCategory = (UUID: string) => db.prepare("get", `SELECT "category" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); const getSegmentCategory = (UUID: string) => db.prepare("get", `SELECT "category" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]);
const getPrivateVoteInfo = (UUID: string) => privateDB.prepare("all", `SELECT * FROM "votes" WHERE "UUID" = ?`, [UUID]); const getPrivateVoteInfo = (UUID: string) => privateDB.prepare("all", `SELECT * FROM "votes" WHERE "UUID" = ?`, [UUID]);
it("Should be able to upvote a segment", (done) => { it("Should be able to upvote a segment", async () => {
const UUID = "vote-uuid-0"; const UUID = "vote-uuid-0";
postVote("randomID", UUID, 1) const res = await postVote("randomID", UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 3); assert.strictEqual(row.votes, 3);
done();
})
.catch(err => done(err));
}); });
it("Should be able to upvote a segment with a partial ID", (done) => { it("Should be able to upvote a segment with a partial ID", async () => {
const UUID = "vote-u34113123"; const UUID = "vote-u34113123";
postVote("randomIDpartial", UUID.substring(0, 9), 1, "vote-testtesttest---sdaas") const res = await postVote("randomIDpartial", UUID.substring(0, 9), 1, "vote-testtesttest---sdaas");
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 3); assert.strictEqual(row.votes, 3);
done();
})
.catch(err => done(err));
}); });
it("Should be able to downvote a segment", (done) => { it("Should be able to downvote a segment", async () => {
const UUID = "vote-uuid-2"; const UUID = "vote-uuid-2";
postVote(randomID2, UUID, 0) const res = await postVote(randomID2, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.ok(row.votes < 10); assert.ok(row.votes < 10);
@@ -168,123 +154,83 @@ describe("voteOnSponsorTime", () => {
assert.strictEqual(voteInfo.length, 1); assert.strictEqual(voteInfo.length, 1);
assert.strictEqual(voteInfo[0].normalUserID, randomID2Hashed); assert.strictEqual(voteInfo[0].normalUserID, randomID2Hashed);
assert.strictEqual(voteInfo[0].type, 0); assert.strictEqual(voteInfo[0].type, 0);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to downvote the same segment when voting from a different user on the same IP", (done) => { it("Should not be able to downvote the same segment when voting from a different user on the same IP", async () => {
const UUID = "vote-uuid-2"; const UUID = "vote-uuid-2";
postVote("randomID3", UUID, 0) const res = await postVote("randomID3", UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 9); assert.strictEqual(row.votes, 9);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to downvote a segment if the user is shadow banned", (done) => { it("Should not be able to downvote a segment if the user is shadow banned", async () => {
const UUID = "vote-uuid-1.6"; const UUID = "vote-uuid-1.6";
postVote("randomID4", UUID, 0) const res = await postVote("randomID4", UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 10); assert.strictEqual(row.votes, 10);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to upvote a segment if the user hasn't submitted yet", (done) => { it("Should not be able to upvote a segment if the user hasn't submitted yet", async () => {
const UUID = "vote-uuid-1"; const UUID = "vote-uuid-1";
postVote("hasNotSubmittedID", UUID, 1) const res = await postVote("hasNotSubmittedID", UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 2); assert.strictEqual(row.votes, 2);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to downvote a segment if the user hasn't submitted yet", (done) => { it("Should not be able to downvote a segment if the user hasn't submitted yet", async () => {
const UUID = "vote-uuid-1.5"; const UUID = "vote-uuid-1.5";
postVote("hasNotSubmittedID", UUID, 0) const res = await postVote("hasNotSubmittedID", UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 10); assert.strictEqual(row.votes, 10);
done();
})
.catch(err => done(err));
}); });
it("VIP should be able to completely downvote a segment", (done) => { it("VIP should be able to completely downvote a segment", async () => {
const UUID = "vote-uuid-3"; const UUID = "vote-uuid-3";
postVote(vipUser, UUID, 0) const res = await postVote(vipUser, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.ok(row.votes <= -2); assert.ok(row.votes <= -2);
done();
})
.catch(err => done(err));
}); });
it("should be able to completely downvote your own segment (segment unlocked)", (done) => { it("should be able to completely downvote your own segment (segment unlocked)", async () => {
const UUID = "own-submission-uuid"; const UUID = "own-submission-uuid";
postVote("own-submission-id", UUID, 0) const res = await postVote("own-submission-id", UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.ok(row.votes <= -2); assert.ok(row.votes <= -2);
done();
})
.catch(err => done(err));
}); });
it("should not be able to completely downvote somebody elses segment", (done) => { it("should not be able to completely downvote somebody elses segment", async () => {
const UUID = "not-own-submission-uuid"; const UUID = "not-own-submission-uuid";
postVote(randomID2, UUID, 0) const res = await postVote(randomID2, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 499); assert.strictEqual(row.votes, 499);
done();
})
.catch(err => done(err));
}); });
it("should be able to completely downvote chapter using malicious", (done) => { it("should be able to completely downvote chapter using malicious", async () => {
const UUID = "chapter-uuid-1"; const UUID = "chapter-uuid-1";
postVote(randomID2, UUID, 30) const res = await postVote(randomID2, UUID, 30);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, -2); assert.strictEqual(row.votes, -2);
done();
})
.catch(err => done(err));
}); });
it("should not be able to completely downvote non-chapter using malicious", (done) => { it("should not be able to completely downvote non-chapter using malicious", async () => {
const UUID = "non-chapter-uuid-2"; const UUID = "non-chapter-uuid-2";
postVote(randomID2, UUID, 30) const res = await postVote(randomID2, UUID, 30);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 0); assert.strictEqual(row.votes, 0);
done();
})
.catch(err => done(err));
}); });
it("Should be able to vote for a category and it should add your vote to the database", (done) => { it("Should be able to vote for a category and it should add your vote to the database", async () => {
const UUID = "vote-uuid-4"; const UUID = "vote-uuid-4";
postVoteCategory(randomID2, UUID, "intro") const res = await postVoteCategory(randomID2, UUID, "intro");
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
const categoryRows = await db.prepare("all", `SELECT votes, category FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]); const categoryRows = await db.prepare("all", `SELECT votes, category FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]);
@@ -294,51 +240,35 @@ describe("voteOnSponsorTime", () => {
assert.strictEqual(categoryRows[0].category, "intro"); assert.strictEqual(categoryRows[0].category, "intro");
assert.strictEqual(categoryRows[1].votes, 1); assert.strictEqual(categoryRows[1].votes, 1);
assert.strictEqual(categoryRows[1].category, "sponsor"); assert.strictEqual(categoryRows[1].category, "sponsor");
done();
})
.catch(err => done(err));
}); });
it("Should not able to change to an invalid category", (done) => { it("Should not able to change to an invalid category", async () => {
const UUID = "incorrect-category"; const UUID = "incorrect-category";
postVoteCategory(randomID2, UUID, "fakecategory") const res = await postVoteCategory(randomID2, UUID, "fakecategory");
.then(async res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, "sponsor"); assert.strictEqual(row.category, "sponsor");
done();
})
.catch(err => done(err));
}); });
it("Should not able to change to highlight category", (done) => { it("Should not able to change to highlight category", async () => {
const UUID = "incorrect-category"; const UUID = "incorrect-category";
postVoteCategory(randomID2, UUID, "highlight") const res = await postVoteCategory(randomID2, UUID, "highlight");
.then(async res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, "sponsor"); assert.strictEqual(row.category, "sponsor");
done();
})
.catch(err => done(err));
}); });
it("Should not able to change to chapter category", (done) => { it("Should not able to change to chapter category", async () => {
const UUID = "incorrect-category"; const UUID = "incorrect-category";
postVoteCategory(randomID2, UUID, "chapter") const res = await postVoteCategory(randomID2, UUID, "chapter");
.then(async res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, "sponsor"); assert.strictEqual(row.category, "sponsor");
done();
})
.catch(err => done(err));
}); });
it("Should be able to change your vote for a category and it should add your vote to the database(segment unlocked, nextCatgeory unlocked)", (done) => { it("Should be able to change your vote for a category and it should add your vote to the database(segment unlocked, nextCatgeory unlocked)", async () => {
const UUID = "vote-uuid-4"; const UUID = "vote-uuid-4";
postVoteCategory(randomID2, UUID, "outro") const res = await postVoteCategory(randomID2, UUID, "outro");
.then(async res => {
assert.strictEqual(res.status, 200, "Status code should be 200"); assert.strictEqual(res.status, 200, "Status code should be 200");
const submissionRow = await getSegmentCategory(UUID); const submissionRow = await getSegmentCategory(UUID);
const categoryRows = await db.prepare("all", `SELECT votes, category FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]); const categoryRows = await db.prepare("all", `SELECT votes, category FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]);
@@ -355,352 +285,240 @@ describe("voteOnSponsorTime", () => {
assert.strictEqual(introVotes, 0); assert.strictEqual(introVotes, 0);
assert.strictEqual(outroVotes, 1); assert.strictEqual(outroVotes, 1);
assert.strictEqual(sponsorVotes, 1); assert.strictEqual(sponsorVotes, 1);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to change your vote to an invalid category", (done) => { it("Should not be able to change your vote to an invalid category", async () => {
const UUID = "incorrect-category-change"; const UUID = "incorrect-category-change";
const vote = (inputCat: string, assertCat: string, callback: Mocha.Done) => { const vote = async (inputCat: string, assertCat: string) => {
postVoteCategory(randomID2, UUID, inputCat) await postVoteCategory(randomID2, UUID, inputCat);
.then(async () => {
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, assertCat); assert.strictEqual(row.category, assertCat);
callback();
})
.catch(err => done(err));
}; };
vote("sponsor", "sponsor", () => { await vote("sponsor", "sponsor");
vote("fakeCategory", "sponsor", done); await vote("fakeCategory", "sponsor");
});
}); });
it("Submitter should be able to vote for a category and it should immediately change (segment unlocked, nextCatgeory unlocked, notVip)", (done) => { it("Submitter should be able to vote for a category and it should immediately change (segment unlocked, nextCatgeory unlocked, notVip)", async () => {
const userID = categoryChangeUser; const userID = categoryChangeUser;
const UUID = "category-change-uuid-1"; const UUID = "category-change-uuid-1";
const category = "sponsor"; const category = "sponsor";
postVoteCategory(userID, UUID, category) const res = await postVoteCategory(userID, UUID, category);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, category); assert.strictEqual(row.category, category);
done();
})
.catch(err => done(err));
}); });
it("Submitter's vote on the category should not work (segment locked, nextCatgeory unlocked, notVip)", (done) => { it("Submitter's vote on the category should not work (segment locked, nextCatgeory unlocked, notVip)", async () => {
const userID = categoryChangeUser; const userID = categoryChangeUser;
const UUID = "category-change-uuid-2"; const UUID = "category-change-uuid-2";
const category = "sponsor"; const category = "sponsor";
postVoteCategory(userID, UUID, category) const res = await postVoteCategory(userID, UUID, category);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, "intro"); assert.strictEqual(row.category, "intro");
done();
})
.catch(err => done(err));
}); });
it("Submitter's vote on the category should not work (segment unlocked, nextCatgeory locked, notVip)", (done) => { it("Submitter's vote on the category should not work (segment unlocked, nextCatgeory locked, notVip)", async () => {
const userID = categoryChangeUser; const userID = categoryChangeUser;
const UUID = "category-change-uuid-3"; const UUID = "category-change-uuid-3";
const category = "preview"; const category = "preview";
postVoteCategory(userID, UUID, category) const res = await postVoteCategory(userID, UUID, category);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, "intro"); assert.strictEqual(row.category, "intro");
done();
})
.catch(err => done(err));
}); });
it("Submitter's vote on the category should not work (segment locked, nextCatgeory locked, notVip)", (done) => { it("Submitter's vote on the category should not work (segment locked, nextCatgeory locked, notVip)", async () => {
const userID = categoryChangeUser; const userID = categoryChangeUser;
const UUID = "category-change-uuid-4"; const UUID = "category-change-uuid-4";
const category = "preview"; const category = "preview";
postVoteCategory(userID, UUID, category) const res = await postVoteCategory(userID, UUID, category);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, "intro"); assert.strictEqual(row.category, "intro");
done();
})
.catch(err => done(err));
}); });
it("Vip should be able to vote for a category and it should immediately change (segment unlocked, nextCatgeory unlocked, Vip)", (done) => { it("Vip should be able to vote for a category and it should immediately change (segment unlocked, nextCatgeory unlocked, Vip)", async () => {
const userID = vipUser; const userID = vipUser;
const UUID = "category-change-uuid-5"; const UUID = "category-change-uuid-5";
const category = "sponsor"; const category = "sponsor";
postVoteCategory(userID, UUID, category) const res = await postVoteCategory(userID, UUID, category);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, category); assert.strictEqual(row.category, category);
done();
})
.catch(err => done(err));
}); });
it("Vip should be able to vote for a category and it should immediately change (segment locked, nextCatgeory unlocked, Vip)", (done) => { it("Vip should be able to vote for a category and it should immediately change (segment locked, nextCatgeory unlocked, Vip)", async () => {
const userID = vipUser; const userID = vipUser;
const UUID = "category-change-uuid-6"; const UUID = "category-change-uuid-6";
const category = "sponsor"; const category = "sponsor";
postVoteCategory(userID, UUID, category) const res = await postVoteCategory(userID, UUID, category);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, category); assert.strictEqual(row.category, category);
done();
})
.catch(err => done(err));
}); });
it("Vip should be able to vote for a category and it should immediately change (segment unlocked, nextCatgeory locked, Vip)", (done) => { it("Vip should be able to vote for a category and it should immediately change (segment unlocked, nextCatgeory locked, Vip)", async () => {
const userID = vipUser; const userID = vipUser;
const UUID = "category-change-uuid-7"; const UUID = "category-change-uuid-7";
const category = "preview"; const category = "preview";
postVoteCategory(userID, UUID, category) const res = await postVoteCategory(userID, UUID, category);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, category); assert.strictEqual(row.category, category);
done();
})
.catch(err => done(err));
}); });
it("Vip should be able to vote for a category and it should immediately change (segment locked, nextCatgeory locked, Vip)", (done) => { it("Vip should be able to vote for a category and it should immediately change (segment locked, nextCatgeory locked, Vip)", async () => {
const userID = vipUser; const userID = vipUser;
const UUID = "category-change-uuid-8"; const UUID = "category-change-uuid-8";
const category = "preview"; const category = "preview";
postVoteCategory(userID, UUID, category) const res = await postVoteCategory(userID, UUID, category);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, category); assert.strictEqual(row.category, category);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to vote for a category of a segment (Too many warning)", (done) => { it("Should not be able to vote for a category of a segment (Too many warning)", async () => {
const UUID = "category-warnvote-uuid-0"; const UUID = "category-warnvote-uuid-0";
const category = "preview"; const category = "preview";
postVoteCategory("warn-voteuser01", UUID, category) const res = await postVoteCategory("warn-voteuser01", UUID, category);
.then(res => {
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
done();
})
.catch(err => done(err));
}); });
it("Should be able to vote for a category as a shadowbanned user, but it shouldn't add your vote to the database", (done) => { it("Should be able to vote for a category as a shadowbanned user, but it shouldn't add your vote to the database", async () => {
const UUID = "category-banvote-uuid-0"; const UUID = "category-banvote-uuid-0";
const category = "preview"; const category = "preview";
postVoteCategory("randomID4", UUID, category) const res = await postVoteCategory("randomID4", UUID, category);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
const categoryRows = await db.prepare("all", `SELECT votes, category FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]); const categoryRows = await db.prepare("all", `SELECT votes, category FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]);
assert.strictEqual(row.category, "intro"); assert.strictEqual(row.category, "intro");
assert.strictEqual(categoryRows.length, 0); assert.strictEqual(categoryRows.length, 0);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to category-vote on an invalid UUID submission", (done) => { it("Should not be able to category-vote on an invalid UUID submission", async () => {
const UUID = "invalid-uuid"; const UUID = "invalid-uuid";
postVoteCategory("randomID3", UUID, "intro") const res = await postVoteCategory("randomID3", UUID, "intro");
.then(res => {
assert.strictEqual(res.status, 404); assert.strictEqual(res.status, 404);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to category-vote on a full video segment", (done) => { it("Should not be able to category-vote on a full video segment", async () => {
const UUID = "full-video-uuid-1"; const UUID = "full-video-uuid-1";
postVoteCategory("randomID3", UUID, "selfpromo") const res = await postVoteCategory("randomID3", UUID, "selfpromo");
.then(res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
}); });
it('Non-VIP should not be able to upvote "dead" submission', (done) => { it('Non-VIP should not be able to upvote "dead" submission', async () => {
const UUID = "vote-uuid-5"; const UUID = "vote-uuid-5";
postVote(randomID2, UUID, 1) const res = await postVote(randomID2, UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, -3); assert.strictEqual(row.votes, -3);
done();
})
.catch(err => done(err));
}); });
it('Non-VIP should not be able to downvote "dead" submission', (done) => { it('Non-VIP should not be able to downvote "dead" submission', async () => {
const UUID = "vote-uuid-5"; const UUID = "vote-uuid-5";
postVote(randomID2, UUID, 0) const res = await postVote(randomID2, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, -3); assert.strictEqual(row.votes, -3);
done();
})
.catch(err => done(err));
}); });
it('VIP should be able to upvote "dead" submission', (done) => { it('VIP should be able to upvote "dead" submission', async () => {
const UUID = "vote-uuid-5"; const UUID = "vote-uuid-5";
postVote(vipUser, UUID, 1) const res = await postVote(vipUser, UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.ok(row.votes > -3); assert.ok(row.votes > -3);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to upvote a segment (Too many warning)", (done) => { it("Should not be able to upvote a segment (Too many warning)", async () => {
const UUID = "warnvote-uuid-0"; const UUID = "warnvote-uuid-0";
postVote("warn-voteuser01", UUID, 1) const res = await postVote("warn-voteuser01", UUID, 1);
.then(res => {
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
done();
})
.catch(err => done(err));
}); });
it("Non-VIP should not be able to downvote on a segment with no-segments category", (done) => { it("Non-VIP should not be able to downvote on a segment with no-segments category", async () => {
const UUID = "no-sponsor-segments-uuid-0"; const UUID = "no-sponsor-segments-uuid-0";
postVote("randomID", UUID, 0) const res = await postVote("randomID", UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 2); assert.strictEqual(row.votes, 2);
done();
})
.catch(err => done(err));
}); });
it("Non-VIP should be able to upvote on a segment with no-segments category", (done) => { it("Non-VIP should be able to upvote on a segment with no-segments category", async () => {
const UUID = "no-sponsor-segments-uuid-0"; const UUID = "no-sponsor-segments-uuid-0";
postVote("randomID", UUID, 1) const res = await postVote("randomID", UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 3); assert.strictEqual(row.votes, 3);
done();
})
.catch(err => done(err));
}); });
it("Non-VIP should not be able to category vote on a segment with no-segments category", (done) => { it("Non-VIP should not be able to category vote on a segment with no-segments category", async () => {
const UUID = "no-sponsor-segments-uuid-0"; const UUID = "no-sponsor-segments-uuid-0";
postVoteCategory("randomID", UUID, "outro") const res = await postVoteCategory("randomID", UUID, "outro");
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentCategory(UUID); const row = await getSegmentCategory(UUID);
assert.strictEqual(row.category, "sponsor"); assert.strictEqual(row.category, "sponsor");
done();
})
.catch(err => done(err));
}); });
it("VIP upvote should lock segment", (done) => { it("VIP upvote should lock segment", async () => {
const UUID = "segment-locking-uuid-1"; const UUID = "segment-locking-uuid-1";
postVote(vipUser, UUID, 1) const res = await postVote(vipUser, UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await db.prepare("get", `SELECT "locked" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); const row = await db.prepare("get", `SELECT "locked" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]);
assert.strictEqual(row.locked, 1); assert.strictEqual(row.locked, 1);
done();
})
.catch(err => done(err));
}); });
it("VIP downvote should unlock segment", (done) => { it("VIP downvote should unlock segment", async () => {
const UUID = "segment-locking-uuid-1"; const UUID = "segment-locking-uuid-1";
postVote(vipUser, UUID, 0) const res = await postVote(vipUser, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await db.prepare("get", `SELECT "locked" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); const row = await db.prepare("get", `SELECT "locked" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]);
assert.strictEqual(row.locked, 0); assert.strictEqual(row.locked, 0);
done();
})
.catch(err => done(err));
}); });
it("VIP upvote should unhide segment", (done) => { it("VIP upvote should unhide segment", async () => {
const UUID = "segment-hidden-uuid-1"; const UUID = "segment-hidden-uuid-1";
postVote(vipUser, UUID, 1) const res = await postVote(vipUser, UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await db.prepare("get", `SELECT "hidden" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); const row = await db.prepare("get", `SELECT "hidden" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]);
assert.strictEqual(row.hidden, 0); assert.strictEqual(row.hidden, 0);
done();
})
.catch(err => done(err));
}); });
it("Should be able to undo-vote a segment", (done) => { it("Should be able to undo-vote a segment", async () => {
const UUID = "vote-uuid-2"; const UUID = "vote-uuid-2";
postVote(randomID2, UUID, 20) const res = await postVote(randomID2, UUID, 20);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 10); assert.strictEqual(row.votes, 10);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to vote with type 10", (done) => { it("Should not be able to vote with type 10", async () => {
const UUID = "segment-locking-uuid-1"; const UUID = "segment-locking-uuid-1";
postVote(vipUser, UUID, 10) const res = await postVote(vipUser, UUID, 10);
.then(res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to vote with type 11", (done) => { it("Should not be able to vote with type 11", async () => {
const UUID = "segment-locking-uuid-1"; const UUID = "segment-locking-uuid-1";
postVote(vipUser, UUID, 11) const res = await postVote(vipUser, UUID, 11);
.then(res => {
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
}); });
it("Should be able to update stored videoDuration with VIP upvote", (done) => { it("Should be able to update stored videoDuration with VIP upvote", async () => {
const UUID = "duration-update-uuid-1"; const UUID = "duration-update-uuid-1";
postVote(vipUser, UUID, 1) const res = await postVote(vipUser, UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const { videoDuration } = await db.prepare("get", `SELECT "videoDuration" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); const { videoDuration } = await db.prepare("get", `SELECT "videoDuration" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]);
assert.strictEqual(videoDuration, 500); assert.strictEqual(videoDuration, 500);
done();
});
}); });
it("Should hide changed submission on any downvote", (done) => { it("Should hide changed submission on any downvote", async () => {
const UUID = "duration-changed-uuid-3"; const UUID = "duration-changed-uuid-3";
const videoID = "duration-changed"; const videoID = "duration-changed";
postVote(randomID2, UUID, 0) const res = await postVote(randomID2, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const hiddenSegments = await db.prepare("all", `SELECT "UUID" FROM "sponsorTimes" WHERE "videoID" = ? AND "hidden" = 1`, [videoID]); const hiddenSegments = await db.prepare("all", `SELECT "UUID" FROM "sponsorTimes" WHERE "videoID" = ? AND "hidden" = 1`, [videoID]);
assert.strictEqual(hiddenSegments.length, 2); assert.strictEqual(hiddenSegments.length, 2);
@@ -710,128 +528,86 @@ describe("voteOnSponsorTime", () => {
"UUID": "duration-changed-uuid-2", "UUID": "duration-changed-uuid-2",
}]; }];
arrayDeepEquals(hiddenSegments, expected); arrayDeepEquals(hiddenSegments, expected);
done();
});
}); });
it("Should be able to downvote segment with ajacent actionType lock", (done) => { it("Should be able to downvote segment with ajacent actionType lock", async () => {
const UUID = "no-sponsor-segments-uuid-2"; const UUID = "no-sponsor-segments-uuid-2";
postVote(randomID2, UUID, 0) const res = await postVote(randomID2, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 1); assert.strictEqual(row.votes, 1);
done();
});
}); });
it("Should not be able to revive full video segment as non-vip", (done) => { it("Should not be able to revive full video segment as non-vip", async () => {
const UUID = "full-video-uuid-1"; const UUID = "full-video-uuid-1";
postVote("VIPUser", UUID, 0).then(() => { await postVote("VIPUser", UUID, 0);
postVote("randomID3", UUID, 1) const res = await postVote("randomID3", UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, -2); assert.strictEqual(row.votes, -2);
done();
})
.catch(err => done(err));
});
}); });
it("Should be able to upvote a segment if the user has submitted that in category", (done) => { it("Should be able to upvote a segment if the user has submitted that in category", async () => {
const UUID = "testing-outro-skip-1"; const UUID = "testing-outro-skip-1";
postVote(outroSubmitter, UUID, 1) const res = await postVote(outroSubmitter, UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 1); assert.strictEqual(row.votes, 1);
done();
})
.catch(err => done(err));
}); });
it("Should be able to downvote a segment if the user has submitted that in category", (done) => { it("Should be able to downvote a segment if the user has submitted that in category", async () => {
const UUID = "testing-outro-skip-2"; const UUID = "testing-outro-skip-2";
postVote(outroSubmitter, UUID, 0) const res = await postVote(outroSubmitter, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, -1); assert.strictEqual(row.votes, -1);
done();
})
.catch(err => done(err));
}); });
it("Should be able to upvote a segment if the user has submitted that in category but different action", (done) => { it("Should be able to upvote a segment if the user has submitted that in category but different action", async () => {
const UUID = "testing-outro-mute-1"; const UUID = "testing-outro-mute-1";
postVote(outroSubmitter, UUID, 1) const res = await postVote(outroSubmitter, UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 1); assert.strictEqual(row.votes, 1);
done();
})
.catch(err => done(err));
}); });
it("Should be able to downvote a segment if the user has submitted that in category but different action", (done) => { it("Should be able to downvote a segment if the user has submitted that in category but different action", async () => {
const UUID = "testing-outro-mute-2"; const UUID = "testing-outro-mute-2";
postVote(outroSubmitter, UUID, 0) const res = await postVote(outroSubmitter, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, -1); assert.strictEqual(row.votes, -1);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to upvote a segment if the user's only submission of that category was downvoted", (done) => { it("Should not be able to upvote a segment if the user's only submission of that category was downvoted", async () => {
const UUID = "testing-intro-skip-1"; const UUID = "testing-intro-skip-1";
postVote(badIntroSubmitter, UUID, 1) const res = await postVote(badIntroSubmitter, UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 0); assert.strictEqual(row.votes, 0);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to downvote a segment if the user's only submission of that category was downvoted", (done) => { it("Should not be able to downvote a segment if the user's only submission of that category was downvoted", async () => {
const UUID = "testing-intro-skip-2"; const UUID = "testing-intro-skip-2";
postVote(badIntroSubmitter, UUID, 0) const res = await postVote(badIntroSubmitter, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 0); assert.strictEqual(row.votes, 0);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to upvote a segment if the user's only submission of that category was hidden", (done) => { it("Should not be able to upvote a segment if the user's only submission of that category was hidden", async () => {
const UUID = "testing-interaction-skip-1"; const UUID = "testing-interaction-skip-1";
postVote(hiddenInteractionSubmitter, UUID, 1) const res = await postVote(hiddenInteractionSubmitter, UUID, 1);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 0); assert.strictEqual(row.votes, 0);
done();
})
.catch(err => done(err));
}); });
it("Should not be able to downvote a segment if the user's only submission of that category was hidden", (done) => { it("Should not be able to downvote a segment if the user's only submission of that category was hidden", async () => {
const UUID = "testing-interaction-skip-2"; const UUID = "testing-interaction-skip-2";
postVote(hiddenInteractionSubmitter, UUID, 0) const res = await postVote(hiddenInteractionSubmitter, UUID, 0);
.then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const row = await getSegmentVotes(UUID); const row = await getSegmentVotes(UUID);
assert.strictEqual(row.votes, 0); assert.strictEqual(row.votes, 0);
done();
})
.catch(err => done(err));
}); });
}); });

View File

@@ -59,12 +59,13 @@ async function init() {
mocha.run((failures) => { mocha.run((failures) => {
mockServer.close(); mockServer.close();
server.close(); server.close();
redis.quit(); redis.quit().finally(() => {
process.exitCode = failures ? 1 : 0; // exit with non-zero status if there were failures process.exitCode = failures ? 1 : 0; // exit with non-zero status if there were failures
process.exit(); process.exit();
}); });
}); });
}); });
});
} }
init(); void init();

51
test/utils/randomUsers.ts Normal file
View File

@@ -0,0 +1,51 @@
import { HashedUserID, UserID } from "../../src/types/user.model";
import { getHash } from "../../src/utils/getHash";
import { genRandom } from "./getRandom";
export interface TestUser {
private: UserID,
public: HashedUserID,
}
interface InternalTestUsers {
map: Map<any, TestUser>,
suiteName: string,
}
const userMapHandler = {
get(target: InternalTestUsers, property: string): TestUser {
const suiteName = Reflect.get(target, "suiteName");
const map = Reflect.get(target, "map");
let user = map.get(property);
if (user !== undefined) {
return user;
}
const priv = `${suiteName}-${property}-${genRandom}` as UserID;
user = {
private: priv,
public: getHash(priv),
};
map.set(property, user);
return user;
},
set: () => false, // nope
deleteProperty: () => false, // nope
has: () => true, // yep
defineProperty: () => false, // nope
preventExtensions: () => false, // nope
setPrototypeOf: () => false, // nope
};
/**
* Creates an object that generates test private/public userID pairs on demand
*
* @param suiteName the suite name, used as a prefix for the private userID
* @returns a proxy that generates & caches private/public userID pairs for each property access
*/
export function usersForSuite(suiteName: string): Record<any, TestUser> {
return new Proxy({
map: new Map<any, TestUser>(),
suiteName,
}, userMapHandler) as unknown as Record<any, TestUser>;
}

4
tsconfig.eslint.json Normal file
View File

@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"include": ["**/*"],
}