Remove warning expiry, save warning history

This commit is contained in:
mini-bomba
2025-09-10 18:51:08 +02:00
parent 1b99a8534c
commit 3e74a0da58
11 changed files with 267 additions and 114 deletions

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

@@ -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

@@ -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,25 +38,25 @@ 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 (!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) { if (!previousWarning) {
if (!reason) {
return res.status(400).json({ "message": "Missing warning reason" });
}
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);
} }

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

@@ -1,4 +1,3 @@
import { config } from "../../src/config";
import { getHash } from "../../src/utils/getHash"; 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";
@@ -33,16 +32,14 @@ describe("postSkipSegments Warnings", () => {
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]); db.prepare("run", insertWarningQuery, [warnUser01Hash, warnVip01Hash, 1, reason01, now]);
// User 2 | 1 inactive | default reason // User 2 | 1 inactive | default reason
db.prepare("run", insertWarningQuery, [warnUser02Hash, warnVip01Hash, 0, reason02, now]); db.prepare("run", insertWarningQuery, [warnUser02Hash, warnVip01Hash, 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)]); db.prepare("run", insertWarningQuery, [warnUser03Hash, warnVip01Hash, 0, reason01, now]);
db.prepare("run", insertWarningQuery, [warnUser03Hash, warnVip01Hash, 1, reason03, now]);
// User 4 | 1 active | default reason // User 4 | 1 active | default reason
db.prepare("run", insertWarningQuery, [warnUser04Hash, warnVip01Hash, 1, reason02, now]); db.prepare("run", insertWarningQuery, [warnUser04Hash, warnVip01Hash, 1, reason02, now]);
}); });
@@ -87,17 +84,25 @@ describe("postSkipSegments Warnings", () => {
.catch(err => done(err)); .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", (done) => {
postSkipSegmentJSON({ postSkipSegmentJSON({
userID: warnUser03, userID: warnUser03,
videoID: warnVideoID, videoID: warnVideoID,
segments: [{ segments: [{
segment: [53, 60], segment: [10, 20],
category: "sponsor", category: "sponsor",
}], }],
}) })
.then(res => { .then(res => {
assert.ok(res.status === 200, `Status code was ${res.status} ${res.data}`); assert.strictEqual(res.status, 403);
const errorMessage = res.data;
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"
+ " 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 ${warnUser03Hash}.\n\nTip message: '${reason}'`;
assert.strictEqual(errorMessage, expected);
done(); done();
}) })
.catch(err => done(err)); .catch(err => done(err));

View File

@@ -7,25 +7,65 @@ import { client } from "../utils/httpClient";
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 "userID", "issueTime", "issuerUserID", enabled, "reason" FROM warnings WHERE "userID" = ? AND "type" = ? ORDER BY "issueTime" ASC`, [userID, type]);
const warneduserOneID = "warning-0"; const userID0 = "warning-0";
const warnedUserTwoID = "warning-1"; const userID1 = "warning-1";
const warnedUserOnePublicID = getHash(warneduserOneID); const userID2 = "warning-2";
const warnedUserTwoPublicID = getHash(warnedUserTwoID); const userID3 = "warning-3";
const warningVipOne = "warning-vip-1"; const userID4 = "warning-4";
const warningVipTwo = "warning-vip-2"; const userID5 = "warning-5";
const userID6 = "warning-6";
const userID7 = "warning-7";
const userID8 = "warning-8";
const userID9 = "warning-9";
const userID10 = "warning-10";
const userID11 = "warning-11";
const userID12 = "warning-12";
const userID13 = "warning-13";
const publicUserID0 = getHash(userID0);
const publicUserID1 = getHash(userID1);
const publicUserID2 = getHash(userID2);
const publicUserID3 = getHash(userID3);
const publicUserID4 = getHash(userID4);
const publicUserID5 = getHash(userID5);
const publicUserID6 = getHash(userID6);
const publicUserID7 = getHash(userID7);
const publicUserID8 = getHash(userID8);
const publicUserID9 = getHash(userID9);
const publicUserID10 = getHash(userID10);
const publicUserID11 = getHash(userID11);
const publicUserID12 = getHash(userID12);
const publicUserID13 = getHash(userID13);
const vipID1 = "warning-vip-1";
const vipID2 = "warning-vip-2";
const publicVipID1 = getHash(vipID1);
const publicVipID2 = getHash(vipID2);
const nonVipUser = "warning-non-vip"; 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 HOUR = 60 * 60 * 1000;
await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [publicVipID1]);
await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [publicVipID2]);
await db.prepare("run", insertWarningQuery, [publicUserID1, publicVipID1, 1, "warn reason 1", (Date.now() - 24 * HOUR)]); // 24 hours is much past the edit deadline
await db.prepare("run", insertWarningQuery, [publicUserID2, publicVipID1, 1, "warn reason 2", (Date.now() - 24 * HOUR)]);
await db.prepare("run", insertWarningQuery, [publicUserID3, publicVipID1, 1, "warn reason 3", Date.now()]);
await db.prepare("run", insertWarningQuery, [publicUserID4, publicVipID1, 1, "warn reason 4", Date.now()]);
await db.prepare("run", insertWarningQuery, [publicUserID6, publicVipID1, 1, "warn reason 6", Date.now()]);
await db.prepare("run", insertWarningQuery, [publicUserID9, publicVipID1, 0, "warn reason 9", Date.now()]);
await db.prepare("run", insertWarningQuery, [publicUserID10, publicVipID1, 1, "warn reason 10", Date.now()]);
await db.prepare("run", insertWarningQuery, [publicUserID11, publicVipID1, 1, "warn reason 11", Date.now()]);
await db.prepare("run", insertWarningQuery, [publicUserID12, publicVipID1, 0, "warn reason 12", Date.now()]);
await db.prepare("run", insertWarningQuery, [publicUserID13, publicVipID1, 0, "warn reason 13", 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)", (done) => {
const json = { const json = {
issuerUserID: warningVipOne, issuerUserID: vipID1,
userID: warnedUserOnePublicID, userID: publicUserID0,
reason: "warning-reason-0" reason: "warning-reason-0"
}; };
client.post(endpoint, json) client.post(endpoint, json)
@@ -37,16 +77,18 @@ 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);
assert.ok(partialDeepEquals(row[0], expected));
done(); done();
}) })
.catch(err => done(err)); .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", (done) => {
const json = { const json = {
issuerUserID: warningVipOne, issuerUserID: vipID1,
userID: warnedUserOnePublicID, userID: publicUserID1,
reason: "edited reason 1",
}; };
client.post(endpoint, json) client.post(endpoint, json)
@@ -55,18 +97,41 @@ describe("postWarning", () => {
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: publicVipID1,
}; };
assert.ok(partialDeepEquals(row, expected)); assert.equal(row.length, 1);
assert.ok(partialDeepEquals(row[0], expected));
done(); done();
}) })
.catch(err => done(err)); .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", (done) => {
const json = { const json = {
issuerUserID: warningVipOne, issuerUserID: vipID2,
userID: warnedUserOnePublicID, userID: publicUserID2,
reason: "edited reason 2",
};
client.post(endpoint, json)
.then(async res => {
assert.strictEqual(res.status, 409);
const row = await getWarning(json.userID);
const expected = {
enabled: 1,
issuerUserID: publicVipID1,
};
assert.equal(row.length, 1);
assert.ok(partialDeepEquals(row[0], expected));
done();
})
.catch(err => done(err));
});
it("Should be able to remove warning if same vip as issuer", (done) => {
const json = {
issuerUserID: vipID1,
userID: publicUserID3,
enabled: false enabled: false
}; };
@@ -77,7 +142,29 @@ describe("postWarning", () => {
const expected = { const expected = {
enabled: 0 enabled: 0
}; };
assert.ok(partialDeepEquals(row, expected)); assert.equal(row.length, 1);
assert.ok(partialDeepEquals(row[0], expected));
done();
})
.catch(err => done(err));
});
it("Should be able to remove warning if not the same vip as issuer", (done) => {
const json = {
issuerUserID: vipID2,
userID: publicUserID4,
enabled: false
};
client.post(endpoint, json)
.then(async res => {
assert.strictEqual(res.status, 200);
const row = await getWarning(json.userID);
const expected = {
enabled: 0
};
assert.equal(row.length, 1);
assert.ok(partialDeepEquals(row[0], expected));
done(); done();
}) })
.catch(err => done(err)); .catch(err => done(err));
@@ -86,7 +173,8 @@ describe("postWarning", () => {
it("Should not be able to create warning if not vip (exp 403)", (done) => { it("Should not be able to create warning if not vip (exp 403)", (done) => {
const json = { const json = {
issuerUserID: nonVipUser, issuerUserID: nonVipUser,
userID: warnedUserOnePublicID, userID: publicUserID5,
reason: "warn reason 5",
}; };
client.post(endpoint, json) client.post(endpoint, json)
@@ -106,40 +194,21 @@ describe("postWarning", () => {
.catch(err => done(err)); .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) => { it("Should be able to remove your own warning", (done) => {
const json = { const json = {
userID: warneduserOneID, userID: userID6,
enabled: false enabled: false
}; };
client.post(endpoint, json) client.post(endpoint, json)
.then(async res => { .then(async res => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const data = await getWarning(warnedUserOnePublicID); const row = await getWarning(publicUserID6);
const expected = { const expected = {
enabled: 0 enabled: 0
}; };
assert.ok(partialDeepEquals(data, expected)); assert.equal(row.length, 1);
assert.ok(partialDeepEquals(row[0], expected));
done(); done();
}) })
.catch(err => done(err)); .catch(err => done(err));
@@ -147,18 +216,16 @@ describe("postWarning", () => {
it("Should not be able to add your own warning", (done) => { it("Should not be able to add your own warning", (done) => {
const json = { const json = {
userID: warneduserOneID, userID: userID7,
enabled: true enabled: true,
reason: "warn reason 7",
}; };
client.post(endpoint, json) client.post(endpoint, json)
.then(async res => { .then(async res => {
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
const data = await getWarning(warnedUserOnePublicID); const data = await getWarning(publicUserID7);
const expected = { assert.equal(data.length, 0);
enabled: 0
};
assert.ok(partialDeepEquals(data, expected));
done(); done();
}) })
.catch(err => done(err)); .catch(err => done(err));
@@ -166,8 +233,8 @@ describe("postWarning", () => {
it("Should not be able to warn a user without reason", (done) => { it("Should not be able to warn a user without reason", (done) => {
const json = { const json = {
issuerUserID: warningVipOne, issuerUserID: vipID1,
userID: warnedUserTwoPublicID, userID: publicUserID8,
enabled: true enabled: true
}; };
@@ -179,21 +246,128 @@ describe("postWarning", () => {
.catch(err => done(err)); .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", (done) => {
const json = { const json = {
issuerUserID: warningVipOne, issuerUserID: vipID1,
userID: warnedUserOnePublicID, userID: publicUserID9,
enabled: true enabled: true
}; };
client.post(endpoint, json)
.then(res => {
assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
});
it("Should be able to edit a warning if within deadline and same vip", (done) => {
const json = {
issuerUserID: vipID1,
userID: publicUserID10,
enabled: true,
reason: "edited reason 10",
};
client.post(endpoint, json) client.post(endpoint, json)
.then(async res => { .then(async res => {
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: 1 enabled: 1,
issuerUserID: publicVipID1,
reason: json.reason,
}; };
assert.ok(partialDeepEquals(data, expected)); assert.equal(row.length, 1);
assert.ok(partialDeepEquals(row[0], expected));
done();
})
.catch(err => done(err));
});
it("Should not be able to edit a warning if within deadline and different vip", (done) => {
const json = {
issuerUserID: vipID2,
userID: publicUserID11,
enabled: true,
reason: "edited reason 11",
};
client.post(endpoint, json)
.then(async res => {
assert.strictEqual(res.status, 409);
const row = await getWarning(json.userID);
const expected = {
enabled: 1,
issuerUserID: publicVipID1,
reason: "warn reason 11",
};
assert.equal(row.length, 1);
assert.ok(partialDeepEquals(row[0], expected));
done();
})
.catch(err => done(err));
});
it("Should be able to warn a previously warned user again (same vip)", (done) => {
const json = {
issuerUserID: vipID1,
userID: publicUserID12,
enabled: true,
reason: "new reason 12",
};
client.post(endpoint, json)
.then(async res => {
assert.strictEqual(res.status, 200);
const row = await getWarning(json.userID);
const expected = [
{
enabled: 0,
issuerUserID: publicVipID1,
reason: "warn reason 12",
},
{
enabled: 1,
issuerUserID: publicVipID1,
reason: "new reason 12",
}
];
assert.equal(row.length, 2);
assert.ok(partialDeepEquals(row[0], expected[0]));
assert.ok(partialDeepEquals(row[1], expected[1]));
done();
})
.catch(err => done(err));
});
it("Should be able to warn a previously warned user again (different vip)", (done) => {
const json = {
issuerUserID: vipID2,
userID: publicUserID13,
enabled: true,
reason: "new reason 13",
};
client.post(endpoint, json)
.then(async res => {
assert.strictEqual(res.status, 200);
const row = await getWarning(json.userID);
const expected = [
{
enabled: 0,
issuerUserID: publicVipID1,
reason: "warn reason 13",
},
{
enabled: 1,
issuerUserID: publicVipID2,
reason: "new reason 13",
}
];
assert.equal(row.length, 2);
assert.ok(partialDeepEquals(row[0], expected[0]));
assert.ok(partialDeepEquals(row[1], expected[1]));
done(); done();
}) })
.catch(err => done(err)); .catch(err => done(err));

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)]);