From ecbf809200aaa76fbcfce9e66095dedf999d98f0 Mon Sep 17 00:00:00 2001 From: Michael C Date: Sun, 15 Oct 2023 21:39:06 -0400 Subject: [PATCH 01/31] postPurgeAllSegments, setUsername --- test/cases/postPurgeAllSegments.ts | 54 +++++------ test/cases/setUsername.ts | 144 +++++++++++++++-------------- 2 files changed, 96 insertions(+), 102 deletions(-) diff --git a/test/cases/postPurgeAllSegments.ts b/test/cases/postPurgeAllSegments.ts index 9d9c675a..f486c9ac 100644 --- a/test/cases/postPurgeAllSegments.ts +++ b/test/cases/postPurgeAllSegments.ts @@ -1,52 +1,40 @@ import { db } from "../../src/databases/databases"; -import { getHash } from "../../src/utils/getHash"; import { IDatabase } from "../../src/databases/IDatabase"; import assert from "assert"; import { client } from "../utils/httpClient"; -async function dbSponsorTimesAdd(db: IDatabase, videoID: string, startTime: number, endTime: number, UUID: string, category: string) { - const votes = 0, - userID = 0, - timeSubmitted = 0, - views = 0, - shadowHidden = 0, - hidden = 0, - hashedVideoID = `hash_${UUID}`; - await db.prepare("run", `INSERT INTO - "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", - "userID", "timeSubmitted", "views", "category", "shadowHidden", "hashedVideoID", "hidden") - VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - [videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID, hidden]); -} +import { insertSegment } from "../utils/segmentQueryGen"; +import { genAnonUser, genUser } from "../utils/genUser"; +import { insertVipUser } from "../utils/queryGen"; async function dbSponsorTimesCompareExpect(db: IDatabase, videoId: string, expectdHidden: number) { - const seg = await db.prepare("get", `SELECT "hidden", "UUID" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoId]); - for (let i = 0, len = seg.length; i < len; i++) { - if (seg.hidden !== expectdHidden) { - return `${seg.UUID} hidden expected to be ${expectdHidden} but found ${seg.hidden}`; - } - } + const seg = await db.prepare("get", `SELECT "hidden", "UUID" FROM "sponsorTimes" WHERE "videoID" = ? AND "hidden" != ? `, [videoId, expectdHidden]); + if (seg) return `${seg.UUID} expected to be ${expectdHidden} but found ${seg.hidden}}`; return; } describe("postPurgeAllSegments", function () { - const privateVipUserID = "VIPUser-purgeAll"; - const vipUserID = getHash(privateVipUserID); const endpoint = "/api/purgeAllSegments"; - const postSegmentShift = (videoID: string, userID: string) => client.post(endpoint, { videoID, userID }); + const postPurgeSegments = (videoID: string, userID: string) => client.post(endpoint, { videoID, userID }); + // users + const vipUser = genUser("postPurgeAllSegments", "vipUser"); + const randomUser = genAnonUser(); + // videos + const purgeID = "vsegpurge01"; + const identifier = "vsegpurgetest01"; before(async function () { // startTime and endTime get set in beforeEach for consistency - await dbSponsorTimesAdd(db, "vsegpurge01", 0, 1, "vsegpurgetest01uuid01", "intro"); - await dbSponsorTimesAdd(db, "vsegpurge01", 0, 2, "vsegpurgetest01uuid02", "sponsor"); - await dbSponsorTimesAdd(db, "vsegpurge01", 0, 3, "vsegpurgetest01uuid03", "interaction"); - await dbSponsorTimesAdd(db, "vsegpurge01", 0, 4, "vsegpurgetest01uuid04", "outro"); - await dbSponsorTimesAdd(db, "vseg-not-purged01", 0, 5, "vsegpurgetest01uuid05", "outro"); - await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [vipUserID]); + await insertSegment(db, { videoID: purgeID, category: "intro" }, identifier); + await insertSegment(db, { videoID: purgeID, category: "sponsor" }, identifier); + await insertSegment(db, { videoID: purgeID, category: "interaction" }, identifier); + await insertSegment(db, { videoID: purgeID, category: "outro" }, identifier); + await insertSegment(db, { videoID: "vseg-not-purged01", category: "outro" }, identifier); + await insertVipUser(db, vipUser); }); it("Reject non-VIP user", function (done) { - postSegmentShift("vsegpurge01", "segshift_randomuser001") + postPurgeSegments(purgeID, randomUser.privID) .then(res => { assert.strictEqual(res.status, 403); done(); @@ -55,10 +43,10 @@ describe("postPurgeAllSegments", function () { }); it("Purge all segments success", function (done) { - postSegmentShift("vsegpurge01", privateVipUserID) + postPurgeSegments(purgeID, vipUser.privID) .then(async res => { assert.strictEqual(res.status, 200); - done(await dbSponsorTimesCompareExpect(db, "vsegpurge01", 1) || await dbSponsorTimesCompareExpect(db, "vseg-not-purged01", 0)); + done(await dbSponsorTimesCompareExpect(db, purgeID, 1) || await dbSponsorTimesCompareExpect(db, "vseg-not-purged01", 0)); }) .catch(err => done(err)); }); diff --git a/test/cases/setUsername.ts b/test/cases/setUsername.ts index ab379b76..4e430936 100644 --- a/test/cases/setUsername.ts +++ b/test/cases/setUsername.ts @@ -1,49 +1,41 @@ import { db, privateDB } from "../../src/databases/databases"; -import { getHash } from "../../src/utils/getHash"; import assert from "assert"; import { client } from "../utils/httpClient"; +import { UsernameUser, genAnonUser, genUsersUsername } from "../utils/genUser"; +import { genRandomValue } from "../utils/getRandom"; -const adminPrivateUserID = "testUserId"; -const user00PrivateUserID = "setUsername_00"; -const username00 = "Username 00"; -const user01PrivateUserID = "setUsername_01"; -const username01 = "Username 01"; -const user02PrivateUserID = "setUsername_02"; -const username02 = "Username 02"; -const user03PrivateUserID = "setUsername_03"; -const username03 = "Username 03"; -const user04PrivateUserID = "setUsername_04"; -const username04 = "Username 04"; -const user05PrivateUserID = "setUsername_05"; -const username05 = "Username 05"; -const user06PrivateUserID = "setUsername_06"; -const username06 = "Username 06"; -const user07PrivateUserID = "setUsername_07"; -const username07 = "Username 07"; -const user08PrivateUserID = "setUsername_08"; - -async function addUsername(userID: string, userName: string, locked = 0) { - await db.prepare("run", 'INSERT INTO "userNames" ("userID", "userName", "locked") VALUES(?, ?, ?)', [userID, userName, locked]); - await addLogUserNameChange(userID, userName); +const adminPrivateUserID = "testUserId"; // hardcoded + +const userMap = new Map(); +// generate usermap from 00 to 08 +for (let i = 0; i < 9; i++) { + userMap.set(`user_0${i}`, `username_0${i}`); } -async function getUsernameInfo(userID: string): Promise<{ userName: string, locked: string}> { - const row = await db.prepare("get", 'SELECT "userName", "locked" FROM "userNames" WHERE "userID" = ?', [userID]); +const users = genUsersUsername("setUsername", userMap); + +async function addUsername(user: UsernameUser, locked = 0) { + await db.prepare("run", 'INSERT INTO "userNames" ("userID", "userName", "locked") VALUES(?, ?, ?)', [user.pubID, user.username, locked]); + await addLogUserNameChange(user.pubID, user.username); +} + +async function getUsernameInfo(publicUserID: string): Promise<{ userName: string, locked: string}> { + const row = await db.prepare("get", 'SELECT "userName", "locked" FROM "userNames" WHERE "userID" = ?', [publicUserID]); if (!row) { throw new Error("No username found"); } return row; } -function addLogUserNameChange(userID: string, newUserName: string, oldUserName = "") { +function addLogUserNameChange(publicUserID: string, newUserName: string, oldUserName = "") { privateDB.prepare("run", `INSERT INTO "userNameLogs"("userID", "newUserName", "oldUserName", "updatedAt", "updatedByAdmin") VALUES(?, ?, ?, ?, ?)`, - [getHash(userID), newUserName, oldUserName, new Date().getTime(), + true] + [publicUserID, newUserName, oldUserName, new Date().getTime(), + true] ); } -function getLastLogUserNameChange(userID: string) { - return privateDB.prepare("get", `SELECT * FROM "userNameLogs" WHERE "userID" = ? ORDER BY "updatedAt" DESC LIMIT 1`, [getHash(userID)]); +function getLastLogUserNameChange(publicUserID: string) { + return privateDB.prepare("get", `SELECT * FROM "userNameLogs" WHERE "userID" = ? ORDER BY "updatedAt" DESC LIMIT 1`, [publicUserID]); } function wellFormatUserName(userName: string) { @@ -51,8 +43,8 @@ function wellFormatUserName(userName: string) { return userName.replace(/[\u0000-\u001F\u007F-\u009F]/g, ""); } -async function testUserNameChangelog(userID: string, newUserName: string, oldUserName: string, byAdmin: boolean, done: Mocha.Done) { - const log = await getLastLogUserNameChange(userID); +async function testUserNameChangelog(publicUserID: string, newUserName: string, oldUserName: string, byAdmin: boolean, done: Mocha.Done) { + const log = await getLastLogUserNameChange(publicUserID); assert.strictEqual(newUserName, log.newUserName); assert.strictEqual(oldUserName, log.oldUserName); assert.strictEqual(byAdmin, Boolean(log.updatedByAdmin)); @@ -60,11 +52,11 @@ async function testUserNameChangelog(userID: string, newUserName: string, oldUse } const endpoint = "/api/setUsername"; -const postSetUserName = (userID: string, username: string) => client({ +const postSetUserName = (privateUserID: string, username: string) => client({ method: "POST", url: endpoint, params: { - userID, + userID: privateUserID, username, } }); @@ -81,21 +73,26 @@ const postSetUserNameAdmin = (userID: string, username: string, adminUserID: str describe("setUsername", () => { before(async () => { - await addUsername(getHash(user01PrivateUserID), username01, 0); - await addUsername(getHash(user02PrivateUserID), username02, 0); - await addUsername(getHash(user03PrivateUserID), username03, 0); - await addUsername(getHash(user04PrivateUserID), username04, 1); - await addUsername(getHash(user05PrivateUserID), username05, 0); - await addUsername(getHash(user06PrivateUserID), username06, 0); - await addUsername(getHash(user07PrivateUserID), username07, 1); + // skip user0 + // add unlocked users + await addUsername(users["user_01"], 0); + await addUsername(users["user_02"], 0); + await addUsername(users["user_03"], 0); + await addUsername(users["user_05"], 0); + await addUsername(users["user_06"], 0); + await addUsername(users["user_08"], 0); + // add locked users + await addUsername(users["user_04"], 1); + await addUsername(users["user_07"], 1); }); it("Should be able to set username that has never been set", (done) => { - postSetUserName(user00PrivateUserID, username00) + const user = users["user_00"]; + postSetUserName(user.privID, user.username) .then(async res => { - const usernameInfo = await getUsernameInfo(getHash(user00PrivateUserID)); + const usernameInfo = await getUsernameInfo(user.pubID); assert.strictEqual(res.status, 200); - assert.strictEqual(usernameInfo.userName, username00); + assert.strictEqual(usernameInfo.userName, user.username); assert.notStrictEqual(usernameInfo.locked, 1, "username should not be locked"); done(); }) @@ -103,11 +100,12 @@ describe("setUsername", () => { }); it("Should return 200", (done) => { - const username = "Changed%20Username"; - postSetUserName(user01PrivateUserID, username) + const user = users["user_01"]; + const newUsername = genRandomValue("username", "setUsername01"); + postSetUserName(user.privID, newUsername) .then(res => { assert.strictEqual(res.status, 200); - testUserNameChangelog(user01PrivateUserID, username, username01, false, done); + testUserNameChangelog(user.pubID, newUsername, user.username, false, done); }) .catch((err) => done(err)); }); @@ -143,8 +141,8 @@ describe("setUsername", () => { }); it('Should return 400 for "username" longer then 64 characters', (done) => { - const username65 = "0000000000000000000000000000000000000000000000000000000000000000X"; - postSetUserName("test", username65) + const username65 = "0".repeat(65); + postSetUserName(genAnonUser().privID, username65) .then(res => { assert.strictEqual(res.status, 400); done(); @@ -154,10 +152,11 @@ describe("setUsername", () => { it('Should not change username if it contains "discord"', (done) => { const newUsername = "discord.me"; - postSetUserName(user02PrivateUserID, newUsername) + const user = users["user_02"]; + postSetUserName(user.privID, newUsername) .then(async res => { assert.strictEqual(res.status, 200); - const userNameInfo = await getUsernameInfo(getHash(user02PrivateUserID)); + const userNameInfo = await getUsernameInfo(user.pubID); assert.notStrictEqual(userNameInfo.userName, newUsername); done(); }) @@ -166,21 +165,23 @@ describe("setUsername", () => { it("Should be able to change username", (done) => { const newUsername = "newUsername"; - postSetUserName(user03PrivateUserID, newUsername) + const user = users["user_03"]; + postSetUserName(user.privID, newUsername) .then(async () => { - const usernameInfo = await getUsernameInfo(getHash(user03PrivateUserID)); + const usernameInfo = await getUsernameInfo(user.pubID); assert.strictEqual(usernameInfo.userName, newUsername, "Username should change"); assert.notStrictEqual(usernameInfo.locked, 1, "Username should not be locked"); - testUserNameChangelog(user03PrivateUserID, newUsername, username03, false, done); + testUserNameChangelog(user.pubID, newUsername, user.username, false, done); }) .catch((err) => done(err)); }); it("Should not be able to change locked username", (done) => { const newUsername = "newUsername"; - postSetUserName(user04PrivateUserID, newUsername) + const user = users["user_04"]; + postSetUserName(user.privID, newUsername) .then(async () => { - const usernameInfo = await getUsernameInfo(getHash(user04PrivateUserID)); + const usernameInfo = await getUsernameInfo(user.pubID); assert.notStrictEqual(usernameInfo.userName, newUsername, "Username should not be changed"); assert.strictEqual(usernameInfo.locked, 1, "username should be locked"); done(); @@ -190,18 +191,21 @@ describe("setUsername", () => { it("Should filter out unicode control characters", (done) => { const newUsername = "This\nUsername+has\tInvalid+Characters"; - postSetUserName(user05PrivateUserID, newUsername) + const user = users["user_05"]; + postSetUserName(user.privID, newUsername) .then(async () => { - const usernameInfo = await getUsernameInfo(getHash(user05PrivateUserID)); + const usernameInfo = await getUsernameInfo(user.pubID); assert.notStrictEqual(usernameInfo.userName, newUsername, "Username should not contain control characters"); - testUserNameChangelog(user05PrivateUserID, wellFormatUserName(newUsername), username05, false, done); + testUserNameChangelog(user.pubID, wellFormatUserName(newUsername), user.username, false, done); }) .catch((err) => done(err)); }); it("Incorrect adminUserID should return 403", (done) => { const newUsername = "New Username"; - postSetUserNameAdmin(getHash(user06PrivateUserID), newUsername,"invalidAdminID") + const user = users["user_06"]; + const adminID = genRandomValue("adminID", "setUsername06"); + postSetUserNameAdmin(user.pubID, newUsername, adminID) .then(res => { assert.strictEqual(res.status, 403); done(); @@ -211,34 +215,36 @@ describe("setUsername", () => { it("Admin should be able to change username", (done) => { const newUsername = "New Username"; - postSetUserNameAdmin(getHash(user06PrivateUserID), newUsername, adminPrivateUserID) + const user = users["user_06"]; + postSetUserNameAdmin(user.pubID, newUsername, adminPrivateUserID) .then(async () => { - const usernameInfo = await getUsernameInfo(getHash(user06PrivateUserID)); + const usernameInfo = await getUsernameInfo(user.pubID); assert.strictEqual(usernameInfo.userName, newUsername, "username should be changed"); assert.strictEqual(usernameInfo.locked, 1, "Username should be locked"); - testUserNameChangelog(user06PrivateUserID, newUsername, username06, true, done); + testUserNameChangelog(user.pubID, newUsername, user.username, true, done); }) .catch((err) => done(err)); }); it("Admin should be able to change locked username", (done) => { const newUsername = "New Username"; - postSetUserNameAdmin(getHash(user07PrivateUserID), newUsername, adminPrivateUserID) + const user = users["user_07"]; + postSetUserNameAdmin(user.pubID, newUsername, adminPrivateUserID) .then(async () => { - const usernameInfo = await getUsernameInfo(getHash(user06PrivateUserID)); + const usernameInfo = await getUsernameInfo(user.pubID); assert.strictEqual(usernameInfo.userName, newUsername, "Username should be changed"); assert.strictEqual(usernameInfo.locked, 1, "Username should be locked"); - testUserNameChangelog(user07PrivateUserID, newUsername, username07, true, done); + testUserNameChangelog(user.pubID, newUsername, user.username, true, done); }) .catch((err) => done(err)); }); it("Should delete row if new username is same as publicID", (done) => { - const publicID = getHash(user08PrivateUserID); - postSetUserName(getHash(user08PrivateUserID), publicID) + const user = users["user_08"]; + postSetUserName(user.privID, user.pubID) .then(() => { - getUsernameInfo(getHash(user08PrivateUserID)) - .then(usernameinfo => done(`Username should be deleted - ${usernameinfo})`)) + getUsernameInfo(user.pubID) + .then(usernameinfo => done(`Username should be deleted - ${JSON.stringify(usernameinfo)})`)) .catch(() => done()); }) .catch((err) => done(err)); From db28400a376f7e311d959de5e53aa78d89a9830d Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 13:52:12 -0400 Subject: [PATCH 02/31] use more test helpers - generateVerifyToken - tempVIP - tokenUtils --- test/cases/generateVerifyToken.ts | 90 +++++------ test/cases/tempVip.ts | 243 +++++++++++++----------------- test/cases/tokenUtils.ts | 56 +++---- 3 files changed, 165 insertions(+), 224 deletions(-) diff --git a/test/cases/generateVerifyToken.ts b/test/cases/generateVerifyToken.ts index a4de8e14..eb6745a4 100644 --- a/test/cases/generateVerifyToken.ts +++ b/test/cases/generateVerifyToken.ts @@ -63,27 +63,24 @@ describe("generateToken test", function() { }).catch(err => done(err)); }); - it("Should be able to create new local token", function (done) { + it("Should be able to create new local token", () => createAndSaveToken(TokenType.local).then((licenseKey) => { assert.ok(validateLicenseKeyRegex(licenseKey[0])); localLicense = licenseKey[0]; - done(); - }).catch(err => done(err)); - }); + }) + ); - it("Should return 400 if missing code parameter", function (done) { + it("Should return 400 if missing code parameter", () => getGenerateToken("patreon", null, "").then(res => { assert.strictEqual(res.status, 400); - done(); - }).catch(err => done(err)); - }); + }) + ); - it("Should return 403 if missing adminuserID parameter", function (done) { + it("Should return 403 if missing adminuserID parameter", () => getGenerateToken("local", "fake-code", null).then(res => { assert.strictEqual(res.status, 403); - done(); - }).catch(err => done(err)); - }); + }) + ); it("Should return 403 for invalid adminuserID parameter", function (done) { getGenerateToken("local", "fake-code", "fakeAdminID").then(res => { @@ -94,20 +91,18 @@ describe("generateToken test", function() { }); describe("verifyToken static tests", function() { - it("Should fast reject invalid token", function (done) { + it("Should fast reject invalid token", () => getVerifyToken("00000").then(res => { assert.strictEqual(res.status, 200); assert.ok(!res.data.allowed); - done(); - }).catch(err => done(err)); - }); - - it("Should return 400 if missing code token", function (done) { - getVerifyToken(null).then(res => { - assert.strictEqual(res.status, 400); - done(); - }).catch(err => done(err)); - }); + }) + ); + + it("Should return 400 if missing code token", () => + getVerifyToken(null).then(res => + assert.strictEqual(res.status, 400) + ) + ); }); describe("verifyToken mock tests", function() { @@ -121,69 +116,62 @@ describe("verifyToken mock tests", function() { mock.restore(); }); - it("Should accept current patron", function (done) { + it("Should accept current patron", function () { if (!config?.patreon) this.skip(); mock.onGet(/identity/).reply(200, patreon.activeIdentity); - getVerifyToken(patreonLicense).then(res => { + return getVerifyToken(patreonLicense).then(res => { assert.strictEqual(res.status, 200); assert.ok(res.data.allowed); - done(); - }).catch(err => done(err)); + }); }); - it("Should reject nonexistent patron", function (done) { + it("Should reject nonexistent patron", function () { if (!config?.patreon) this.skip(); mock.onGet(/identity/).reply(200, patreon.invalidIdentity); - getVerifyToken(patreonLicense).then(res => { + return getVerifyToken(patreonLicense).then(res => { assert.strictEqual(res.status, 200); assert.ok(!res.data.allowed); - done(); - }).catch(err => done(err)); + }); }); - it("Should accept qualitying former patron", function (done) { + it("Should accept qualitying former patron", function () { if (!config?.patreon) this.skip(); mock.onGet(/identity/).reply(200, patreon.formerIdentitySucceed); - getVerifyToken(patreonLicense).then(res => { + return getVerifyToken(patreonLicense).then(res => { assert.strictEqual(res.status, 200); assert.ok(res.data.allowed); - done(); - }).catch(err => done(err)); + }); }); - it("Should reject unqualitifed former patron", function (done) { + it("Should reject unqualitifed former patron", function () { if (!config?.patreon) this.skip(); mock.onGet(/identity/).reply(200, patreon.formerIdentityFail); - getVerifyToken(patreonLicense).then(res => { + return getVerifyToken(patreonLicense).then(res => { assert.strictEqual(res.status, 200); assert.ok(!res.data.allowed); - done(); - }).catch(err => done(err)); + }); }); - it("Should accept real gumroad key", function (done) { + it("Should accept real gumroad key", () => { mock.onPost("https://api.gumroad.com/v2/licenses/verify").reply(200, gumroad.licenseSuccess); - getVerifyToken(gumroadLicense).then(res => { + return getVerifyToken(gumroadLicense).then(res => { assert.strictEqual(res.status, 200); assert.ok(res.data.allowed); - done(); - }).catch(err => done(err)); + }); }); - it("Should reject fake gumroad key", function (done) { + it("Should reject fake gumroad key", () => { mock.onPost("https://api.gumroad.com/v2/licenses/verify").reply(200, gumroad.licenseFail); - getVerifyToken(gumroadLicense).then(res => { + return getVerifyToken(gumroadLicense).then(res => { assert.strictEqual(res.status, 200); assert.ok(!res.data.allowed); - done(); - }).catch(err => done(err)); + }); }); - it("Should validate local license", function (done) { + it("Should validate local license", () => getVerifyToken(localLicense).then(res => { assert.strictEqual(res.status, 200); assert.ok(res.data.allowed); - done(); - }).catch(err => done(err)); - }); + }) + ); }); diff --git a/test/cases/tempVip.ts b/test/cases/tempVip.ts index 5448598d..71ba06c6 100644 --- a/test/cases/tempVip.ts +++ b/test/cases/tempVip.ts @@ -1,25 +1,18 @@ import { config } from "../../src/config"; -import { getHash } from "../../src/utils/getHash"; import { tempVIPKey } from "../../src/utils/redisKeys"; import { HashedUserID } from "../../src/types/user.model"; import { client } from "../utils/httpClient"; import { db, privateDB } from "../../src/databases/databases"; import redis from "../../src/utils/redis"; +import { genAnonUser, genUser } from "../utils/genUser"; import assert from "assert"; +import { insertSegment } from "../utils/segmentQueryGen"; +import { genRandomValue } from "../utils/getRandom"; +import { insertVipUser } from "../utils/queryGen"; // helpers const getSegment = (UUID: string) => db.prepare("get", `SELECT "votes", "locked", "category" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); -const permVIP1 = "tempVip_permaVIPOne"; -const publicPermVIP1 = getHash(permVIP1) as HashedUserID; -const permVIP2 = "tempVip_permaVIPTwo"; -const publicPermVIP2 = getHash(permVIP2) as HashedUserID; - -const tempVIPOne = "tempVipTempOne"; -const publicTempVIPOne = getHash(tempVIPOne) as HashedUserID; -const UUID0 = "tempvip-uuid0"; -const UUID1 = "tempvip-uuid1"; - const tempVIPEndpoint = "/api/addUserAsTempVIP"; const addTempVIP = (enabled: string, adminUserID: string, userID: HashedUserID, channelVideoID = "channelid-convert") => client({ url: tempVIPEndpoint, @@ -53,206 +46,178 @@ const postVoteCategory = (userID: string, UUID: string, category: string) => cli const checkUserVIP = async (publicID: HashedUserID): Promise => await redis.get(tempVIPKey(publicID)); +const checkVipLog = async (publicID: string): Promise => { + const row = await privateDB.prepare("get", `SELECT * FROM "tempVipLog" WHERE "targetUserID" = ?`, [publicID]); + return Boolean(row?.enabled); +}; + + +const permVIP1 = genUser("tempVip", "permVIP1"); +const permVIP2 = genUser("tempVip", "permVIP2"); + +const tempVIP1 = genUser("tempVip", "tempVIP1"); + +const targetChannelUUID = genRandomValue("uuid", "channelid-target"); +const otherChannelUUID = genRandomValue("uuid", "otherchannel"); + describe("tempVIP test", function() { before(async function() { if (!config.redis?.enabled) this.skip(); - - const insertSponsorTimeQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "shadowHidden") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; - await db.prepare("run", insertSponsorTimeQuery, ["channelid-convert", 0, 1, 0, 0, UUID0, "testman", 0, 50, "sponsor", 0]); - await db.prepare("run", insertSponsorTimeQuery, ["channelid-convert", 1, 9, 0, 1, "tempvip-submit", publicTempVIPOne, 0, 50, "sponsor", 0]); - await db.prepare("run", insertSponsorTimeQuery, ["otherchannel", 1, 9, 0, 1, UUID1, "testman", 0, 50, "sponsor", 0]); - - await db.prepare("run", 'INSERT INTO "vipUsers" ("userID") VALUES (?)', [publicPermVIP1]); - await db.prepare("run", 'INSERT INTO "vipUsers" ("userID") VALUES (?)', [publicPermVIP2]); + // insert segments + insertSegment(db, { videoID: "channelid-convert", UUID: targetChannelUUID }); + insertSegment(db, { videoID: "channelid-convert", userID: tempVIP1.pubID, locked: true }); + insertSegment(db, { videoID: "otherchannel", UUID: otherChannelUUID, locked: true }); + // add vip user + insertVipUser(db, permVIP1); + insertVipUser(db, permVIP2); // clear redis if running consecutive tests - await redis.del(tempVIPKey(publicTempVIPOne)); + await redis.del(tempVIPKey(tempVIP1.pubID)); }); it("Should update db version when starting the application", () => { 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}`); - }); + .then(row => + assert.ok(row.value >= 5, `Versions are not at least 5. private is ${row.value}`) + ); }); - it("User should not already be temp VIP", (done) => { - checkUserVIP(publicTempVIPOne) + it("User should not already be temp VIP", () => + checkUserVIP(tempVIP1.pubID) .then(result => { assert.ok(!result); }) .then(async () => { - const row = await privateDB.prepare("get", `SELECT * FROM "tempVipLog" WHERE "targetUserID" = ?`, [publicTempVIPOne]); - assert.ok(!row?.enabled); - done(); + const log = await checkVipLog(tempVIP1.pubID); + assert.strictEqual(log, false); }) - .catch(err => done(err)); - }); - it("Should be able to normal upvote as a user", (done) => { - postVote(tempVIPOne, UUID0, 1) + ); + it("Should be able to normal upvote as a user", () => + postVote(tempVIP1.privID, targetChannelUUID, 1) .then(async res => { assert.strictEqual(res.status, 200); - const row = await getSegment(UUID0); + const row = await getSegment(targetChannelUUID); assert.strictEqual(row.votes, 1); - done(); }) - .catch(err => done(err)); - }); - it("Should be able to add tempVIP", (done) => { - addTempVIP("true", permVIP1, publicTempVIPOne) + ); + it("Should be able to add tempVIP", () => + addTempVIP("true", permVIP1.privID, tempVIP1.pubID) .then(async res => { assert.strictEqual(res.status, 200); // check redis - const vip = await checkUserVIP(publicTempVIPOne); + const vip = await checkUserVIP(tempVIP1.pubID); assert.strictEqual(vip, "ChannelID"); assert.strictEqual(res.data, "Temp VIP added on channel ChannelAuthor"); // check privateDB - const row = await privateDB.prepare("get", `SELECT * FROM "tempVipLog" WHERE "targetUserID" = ?`, [publicTempVIPOne]); - assert.ok(row.enabled); - done(); + const log = await checkVipLog(tempVIP1.pubID); + assert.strictEqual(log, true); }) - .catch(err => done(err)); - }); - it("Should be able to VIP downvote", (done) => { - postVote(tempVIPOne, UUID0, 0) + ); + it("Should be able to VIP downvote", () => + postVote(tempVIP1.privID, targetChannelUUID, 0) .then(async res => { assert.strictEqual(res.status, 200); - const row = await getSegment(UUID0); + const row = await getSegment(targetChannelUUID); assert.strictEqual(row.votes, -2); - done(); }) - .catch(err => done(err)); - }); - it("Should not be able to lock segment", (done) => { - postVote(tempVIPOne, UUID0, 1) + ); + it("Should not be able to lock segment", () => + postVote(tempVIP1.privID, targetChannelUUID, 1) .then(async res => { assert.strictEqual(res.status, 200); - const row = await getSegment(UUID0); + const row = await getSegment(targetChannelUUID); assert.strictEqual(row.votes, 1); assert.strictEqual(row.locked, 0); - done(); }) - .catch(err => done(err)); - }); - it("Should be able to change category but not lock", (done) => { - postVoteCategory(tempVIPOne, UUID0, "filler") + ); + it("Should be able to change category but not lock", () => + postVoteCategory(tempVIP1.privID, targetChannelUUID, "filler") .then(async res => { assert.strictEqual(res.status, 200); - const row = await getSegment(UUID0); + const row = await getSegment(targetChannelUUID); assert.strictEqual(row.category, "filler"); assert.strictEqual(row.locked, 0); - done(); }) - .catch(err => done(err)); - }); - it("Should be able to remove tempVIP prematurely", (done) => { - addTempVIP("false", permVIP1, publicTempVIPOne) + ); + it("Should be able to remove tempVIP prematurely", () => + addTempVIP("false", permVIP1.privID, tempVIP1.pubID) .then(async res => { assert.strictEqual(res.status, 200); - const vip = await checkUserVIP(publicTempVIPOne); + const vip = await checkUserVIP(tempVIP1.pubID); assert.strictEqual(res.data, "Temp VIP removed"); assert.ok(!vip, "Should be no listed channelID"); - done(); }) - .catch(err => done(err)); - }); - it("Should not be able to VIP downvote", (done) => { - postVote(tempVIPOne, UUID1, 0) + ); + it("Should not be able to VIP downvote", () => + postVote(tempVIP1.privID, otherChannelUUID, 0) .then(async res => { assert.strictEqual(res.status, 200); - const row = await getSegment(UUID1); + const row = await getSegment(otherChannelUUID); assert.strictEqual(row.votes, 0); - done(); }) - .catch(err => done(err)); - }); - it("Should not be able to VIP change category", (done) => { - postVoteCategory(tempVIPOne, UUID1, "filler") + ); + it("Should not be able to VIP change category", () => + postVoteCategory(tempVIP1.privID, otherChannelUUID, "filler") .then(async res => { assert.strictEqual(res.status, 200); - const row = await getSegment(UUID1); + const row = await getSegment(otherChannelUUID); assert.strictEqual(row.category, "sponsor"); - done(); }) - .catch(err => done(err)); - }); + ); // error code testing - it("Should be able to add tempVIP after removal", (done) => { - addTempVIP("true", permVIP1, publicTempVIPOne) + it("Should be able to add tempVIP after removal", () => + addTempVIP("true", permVIP1.privID, tempVIP1.pubID) .then(async res => { assert.strictEqual(res.status, 200); - const vip = await checkUserVIP(publicTempVIPOne); + const vip = await checkUserVIP(tempVIP1.pubID); assert.strictEqual(vip, "ChannelID"); - done(); }) - .catch(err => done(err)); - }); - it("Should not be able to add VIP without existing VIP (403)", (done) => { - const privateID = "non-vip-privateid"; - addTempVIP("true", privateID, publicTempVIPOne) + ); + it("Should not be able to add VIP without existing VIP (403)", () => { + const testUser = genAnonUser(); + return addTempVIP("true", testUser.privID, tempVIP1.pubID) .then(async res => { assert.strictEqual(res.status, 403); - const vip = await checkUserVIP(getHash(privateID) as HashedUserID); + const vip = await checkUserVIP(testUser.pubID); 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) => { - addTempVIP("true", permVIP1, publicPermVIP2) + it("Should not be able to add permanant VIP as temporary VIP (409)", () => + addTempVIP("true", permVIP1.privID, permVIP2.pubID) .then(async res => { assert.strictEqual(res.status, 409); - const vip = await checkUserVIP(publicPermVIP2); + const vip = await checkUserVIP(permVIP2.pubID); 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) => { - const privateID = "non-vip-privateid"; - const publicID = getHash(privateID) as HashedUserID; - addTempVIP("true", tempVIPOne, publicID) + ); + it("Temp VIP should not be able to add another Temp VIP (403)", () => { + const testUser = genAnonUser(); + return addTempVIP("true", tempVIP1.privID, testUser.pubID) .then(async res => { assert.strictEqual(res.status, 403); - const vip = await checkUserVIP(publicID); + const vip = await checkUserVIP(testUser.pubID); assert.ok(!vip, "Should be no listed channelID"); - done(); - }) - .catch(err => done(err)); + }); }); // error 40X testing - it("Should return 404 with invalid videoID", (done) => { - const privateID = "non-vip-privateid"; - const publicID = getHash(privateID) as HashedUserID; - addTempVIP("true", permVIP1, publicID, "knownWrongID") + it("Should return 404 with invalid videoID", () => { + const testUser = genAnonUser(); + return addTempVIP("true", permVIP1.privID, testUser.pubID, "knownWrongID") .then(async res => { assert.strictEqual(res.status, 404); - const vip = await checkUserVIP(publicID); + const vip = await checkUserVIP(testUser.pubID); assert.ok(!vip, "Should be no listed channelID"); - done(); - }) - .catch(err => done(err)); - }); - it("Should return 400 with invalid userID", (done) => { - addTempVIP("true", permVIP1, "" as HashedUserID, "knownWrongID") - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - it("Should return 400 with invalid adminUserID", (done) => { - addTempVIP("true", "", publicTempVIPOne) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - it("Should return 400 with invalid channelID", (done) => { - addTempVIP("true", permVIP1, publicTempVIPOne, "") - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + }); }); + it("Should return 400 with invalid userID", () => { + const videoID = genRandomValue("videoID", "badVideoID"); + return addTempVIP("true", permVIP1.privID, "" as HashedUserID, videoID) + .then(res => assert.strictEqual(res.status, 400)); + }); + it("Should return 400 with invalid adminUserID", () => + addTempVIP("true", "", tempVIP1.pubID) + .then(res => assert.strictEqual(res.status, 400)) + ); + it("Should return 400 with invalid channelID", () => + addTempVIP("true", permVIP1.privID, tempVIP1.pubID, "") + .then(res => assert.strictEqual(res.status, 400)) + ); }); \ No newline at end of file diff --git a/test/cases/tokenUtils.ts b/test/cases/tokenUtils.ts index c9fa4727..6f45cf06 100644 --- a/test/cases/tokenUtils.ts +++ b/test/cases/tokenUtils.ts @@ -16,32 +16,24 @@ describe("tokenUtils test", function() { 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(); - tokenUtils.createAndSaveToken(tokenUtils.TokenType.patreon, "test_code").then((licenseKey) => { - assert.ok(validateToken(licenseKey[0])); - done(); - }); - }); - it("Should be able to create local token", (done) => { - tokenUtils.createAndSaveToken(tokenUtils.TokenType.local).then((licenseKey) => { - assert.ok(validateToken(licenseKey[0])); - done(); - }); - }); - it("Should be able to get patreon identity", function (done) { + const licenseKey = await tokenUtils.createAndSaveToken(tokenUtils.TokenType.patreon, "test_code"); + return assert.ok(validateToken(licenseKey[0])); + }); + it("Should be able to create local token", () => + tokenUtils.createAndSaveToken(tokenUtils.TokenType.local) + .then((licenseKey) => assert.ok(validateToken(licenseKey[0]))) + ); + it("Should be able to get patreon identity", async function () { if (!config?.patreon) this.skip(); - tokenUtils.getPatreonIdentity("fake_access_token").then((result) => { - assert.deepEqual(result, patreon.activeIdentity); - done(); - }); + const patreon_id = await tokenUtils.getPatreonIdentity("fake_access_token"); + return assert.deepEqual(patreon_id, patreon.activeIdentity); }); - it("Should be able to refresh token", function (done) { + it("Should be able to refresh token", async function () { if (!config?.patreon) this.skip(); - tokenUtils.refreshToken(tokenUtils.TokenType.patreon, "fake-licence-Key", "fake_refresh_token").then((result) => { - assert.strictEqual(result, true); - done(); - }); + const patreon_key_valid = await tokenUtils.refreshToken(tokenUtils.TokenType.patreon, "fake-licence-Key", "fake_refresh_token"); + return assert.strictEqual(patreon_key_valid, true); }); after(function () { @@ -56,18 +48,14 @@ describe("tokenUtils failing tests", function() { mock.onGet(/identity/).reply(204, patreon.activeIdentity); }); - it("Should fail if patreon is not correctly stubbed", function (done) { - tokenUtils.createAndSaveToken(tokenUtils.TokenType.patreon, "test_code").then((licenseKey) => { - assert.strictEqual(licenseKey, null); - done(); - }); - }); - it("Should fail if token type is invalid", (done) => { - tokenUtils.createAndSaveToken("invalidTokenType" as tokenUtils.TokenType).then((licenseKey) => { - assert.strictEqual(licenseKey, null); - done(); - }); - }); + it("Should fail if patreon is not correctly stubbed", () => + tokenUtils.createAndSaveToken(tokenUtils.TokenType.patreon, "test_code") + .then((licenseKey) => assert.strictEqual(licenseKey, null)) + ); + it("Should fail if token type is invalid", () => + tokenUtils.createAndSaveToken("invalidTokenType" as tokenUtils.TokenType) + .then((licenseKey) => assert.strictEqual(licenseKey, null)) + ); after(function () { mock.restore(); From 430ac8ccf3803b3f1e6c184641a5ec2a35259c4d Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 14:11:17 -0400 Subject: [PATCH 03/31] use gha native services --- .github/workflows/test.yaml | 29 ++++++++++++++++++++++------- docker/docker-compose-ci.yml | 4 ++-- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 1e5a11f0..1f24fcd6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -60,13 +60,6 @@ jobs: needs: lint-build steps: - uses: actions/checkout@v3 - - name: Build the docker-compose stack - env: - PG_USER: ci_db_user - PG_PASS: ci_db_pass - run: docker-compose -f docker/docker-compose-ci.yml up -d - - name: Check running containers - run: docker ps - uses: actions/setup-node@v3 with: node-version: 18 @@ -90,6 +83,28 @@ jobs: with: key: nyc-postgres-${{ github.sha }} path: ${{ github.workspace }}/.nyc_output + services: + redis: + image: redis:alpine + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + postgres: + image: postgres:alpine + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + env: + POSTGRES_USER: ci_db_user + POSTGRES_PASSWORD: ci_db_pass codecov: needs: [test-sqlite, test-postgres] name: Run Codecov diff --git a/docker/docker-compose-ci.yml b/docker/docker-compose-ci.yml index beafb2ec..27aaeef7 100644 --- a/docker/docker-compose-ci.yml +++ b/docker/docker-compose-ci.yml @@ -4,8 +4,8 @@ services: container_name: database-co image: postgres:alpine environment: - - POSTGRES_USER=${PG_USER} - - POSTGRES_PASSWORD=${PG_PASS} + - POSTGRES_USER=ci_db_user + - POSTGRES_PASSWORD=ci_db_pass ports: - 5432:5432 redis: From b7f9423dd16b88806c2680056f1e58d29d5220db Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 14:30:06 -0400 Subject: [PATCH 04/31] add setUsername ban test --- test/cases/setUsername.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/cases/setUsername.ts b/test/cases/setUsername.ts index 4e430936..37cbfd06 100644 --- a/test/cases/setUsername.ts +++ b/test/cases/setUsername.ts @@ -1,8 +1,9 @@ import { db, privateDB } from "../../src/databases/databases"; import assert from "assert"; import { client } from "../utils/httpClient"; -import { UsernameUser, genAnonUser, genUsersUsername } from "../utils/genUser"; +import { UsernameUser, genAnonUser, genUser, genUsersUsername } from "../utils/genUser"; import { genRandomValue } from "../utils/getRandom"; +import { insertBan } from "../utils/queryGen"; const adminPrivateUserID = "testUserId"; // hardcoded @@ -11,6 +12,7 @@ const userMap = new Map(); for (let i = 0; i < 9; i++) { userMap.set(`user_0${i}`, `username_0${i}`); } +const bannedUser = genUser("setUsername", "bannedUser"); const users = genUsersUsername("setUsername", userMap); @@ -84,6 +86,8 @@ describe("setUsername", () => { // add locked users await addUsername(users["user_04"], 1); await addUsername(users["user_07"], 1); + // ban user + await insertBan(db, bannedUser.pubID); }); it("Should be able to set username that has never been set", (done) => { @@ -249,4 +253,16 @@ describe("setUsername", () => { }) .catch((err) => done(err)); }); + + it("Should not apply username change if user is banned", (done) => { + const user = bannedUser; + const newUsername = genRandomValue("username", "setUsernameBanned"); + postSetUserName(user.privID, newUsername) + .then(() => { + getUsernameInfo(user.pubID) + .then(usernameinfo => done(`Username should not exist - ${JSON.stringify(usernameinfo)}`)) + .catch(() => done()); + }) + .catch((err) => done(err)); + }); }); From 7c4838c0886f4bcedcfe5c6a44d4eaeb68f7b47a Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 19:01:32 -0400 Subject: [PATCH 05/31] half-broken getLockCategoriesByHash --- test/cases/getLockCategoriesByHash.ts | 205 ++++++++++---------------- test/utils/queryGen.ts | 2 +- 2 files changed, 82 insertions(+), 125 deletions(-) diff --git a/test/cases/getLockCategoriesByHash.ts b/test/cases/getLockCategoriesByHash.ts index f6b2757a..8be3b16a 100644 --- a/test/cases/getLockCategoriesByHash.ts +++ b/test/cases/getLockCategoriesByHash.ts @@ -2,29 +2,30 @@ import { getHash } from "../../src/utils/getHash"; import { db } from "../../src/databases/databases"; import assert from "assert"; import { client } from "../utils/httpClient"; -import { ActionType } from "../../src/types/segments.model"; +import { ActionType, VideoIDHash } from "../../src/types/segments.model"; +import { genUser } from "../utils/genUser"; +import { insertLock, insertVipUser } from "../utils/queryGen"; -const fakeHash = "b05a20424f24a53dac1b059fb78d861ba9723645026be2174c93a94f9106bb35"; +const fakeHash = "b05a20424f24a53dac1b059fb78d861ba9723645026be2174c93a94f9106bb35" as VideoIDHash; const endpoint = "/api/lockCategories"; const getLockCategories = (hash: string, actionType = [ActionType.Mute, ActionType.Skip]) => client.get(`${endpoint}/${hash}`, { params: { actionType } }); +const getLockCategoriesHashed = (videoID: string, hashLength = 6, actionType = [ActionType.Mute, ActionType.Skip]) => getLockCategories(getHash(videoID, 1).substring(0, hashLength), actionType); + +const vip = genUser("getLockCategoriesHash", "VIP"); describe("getLockCategoriesByHash", () => { before(async () => { - const insertVipUserQuery = 'INSERT INTO "vipUsers" ("userID") VALUES (?)'; - await db.prepare("run", insertVipUserQuery, [getHash("getLockCategoriesHashVIP")]); - - const insertLockCategoryQuery = 'INSERT INTO "lockCategories" ("userID", "videoID", "actionType", "category", "reason", "hashedVideoID") VALUES (?, ?, ?, ?, ?, ?)'; - await db.prepare("run", insertLockCategoryQuery, [getHash("getLockCategoriesHashVIP"), "getLockHash1", "skip", "sponsor", "1-reason-short", getHash("getLockHash1", 1)]); - await db.prepare("run", insertLockCategoryQuery, [getHash("getLockCategoriesHashVIP"), "getLockHash1", "skip", "interaction", "1-reason-longer", getHash("getLockHash1", 1)]); - - await db.prepare("run", insertLockCategoryQuery, [getHash("getLockCategoriesHashVIP"), "getLockHash2", "skip", "preview", "2-reason", getHash("getLockHash2", 1)]); - - await db.prepare("run", insertLockCategoryQuery, [getHash("getLockCategoriesHashVIP"), "getLockHash3", "skip", "nonmusic", "3-reason", getHash("getLockHash3", 1)]); - - await db.prepare("run", insertLockCategoryQuery, [getHash("getLockCategoriesHashVIP"), "fakehash-1", "mute", "outro", "fake1-reason", fakeHash]); - await db.prepare("run", insertLockCategoryQuery, [getHash("getLockCategoriesHashVIP"), "fakehash-2", "mute", "intro", "fake2-longer-reason", fakeHash]); - await db.prepare("run", insertLockCategoryQuery, [getHash("getLockCategoriesHashVIP"), "fakehash-2", "mute", "preview", "fake2-short", fakeHash]); - await db.prepare("run", insertLockCategoryQuery, [getHash("getLockCategoriesHashVIP"), "fakehash-2", "full", "sponsor", "fake2-notshown", fakeHash]); + await insertVipUser(db, vip); + // add locks with real hash + await insertLock(db, { userID: vip.pubID, videoID: "getLockHash1", category: "sponsor", reason: "1-reason-short" }); + await insertLock(db, { userID: vip.pubID, videoID: "getLockHash1", category: "interaction", reason: "1-reason-longer" }); + await insertLock(db, { userID: vip.pubID, videoID: "getLockHash2", category: "preview", reason: "2-reason" }); + await insertLock(db, { userID: vip.pubID, videoID: "getLockHash3", category: "nonmusic", reason: "3-reason" }); + // add locks with fake hash + await insertLock(db, { userID: vip.pubID, videoID: "fakehash-1", actionType: "mute", category: "outro", reason: "fake1-reason", hashedVideoID: fakeHash }); + await insertLock(db, { userID: vip.pubID, videoID: "fakehash-2", actionType: "mute", category: "intro", reason: "fake2-longer-reason", hashedVideoID: fakeHash }); + await insertLock(db, { userID: vip.pubID, videoID: "fakehash-2", actionType: "mute", category: "preview", reason: "fake2-short", hashedVideoID: fakeHash }); + await insertLock(db, { userID: vip.pubID, videoID: "fakehash-2", actionType: "full", category: "sponsor", reason: "fake2-notshown", hashedVideoID: fakeHash }); }); it("Database should be greater or equal to version 29", async () => { @@ -34,10 +35,10 @@ describe("getLockCategoriesByHash", () => { `Version isn't greater than 29. Version is ${version}`); }); - it("Should be able to get multiple locks in one object", (done) => { + it("Should be able to get multiple locks in one object", () => { const videoID = "getLockHash1"; const hash = getHash(videoID, 1); - getLockCategories(hash.substring(0,4)) + return getLockCategoriesHashed(videoID, 4) .then(res => { assert.strictEqual(res.status, 200); const expected = [{ @@ -50,15 +51,13 @@ describe("getLockCategoriesByHash", () => { reason: "1-reason-longer" }]; assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); + }); }); - it("Should be able to get single lock", (done) => { + it("Should be able to get single lock", () => { const videoID = "getLockHash2"; const hash = getHash(videoID, 1); - getLockCategories(hash.substring(0,6)) + return getLockCategoriesHashed(videoID) .then(res => { assert.strictEqual(res.status, 200); const expected = [{ @@ -70,15 +69,13 @@ describe("getLockCategoriesByHash", () => { reason: "2-reason" }]; assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); + }); }); - it("Should be able to get by half full hash", (done) => { + it("Should be able to get by half full hash", () => { const videoID = "getLockHash3"; const hash = getHash(videoID, 1); - getLockCategories(hash.substring(0,32)) + return getLockCategoriesHashed(videoID, 32) .then(res => { assert.strictEqual(res.status, 200); const expected = [{ @@ -90,104 +87,70 @@ describe("getLockCategoriesByHash", () => { reason: "3-reason" }]; assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get multiple by similar hash with multiple categories", (done) => { - getLockCategories(fakeHash.substring(0,5)) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID: "fakehash-1", - hash: fakeHash, - categories: [ - "outro" - ], - reason: "fake1-reason" - }, { - videoID: "fakehash-2", - hash: fakeHash, - categories: [ - "intro", - "preview" - ], - reason: "fake2-longer-reason" - }]; - assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); - }); - - it("should return 404 once hash prefix varies", (done) => { + }); + }); + + it("Should be able to get multiple by similar hash with multiple categories", async () => { + const res = await getLockCategories(fakeHash.substring(0,5)); + assert.strictEqual(res.status, 200); + const expected = [{ + videoID: "fakehash-1", + hash: fakeHash, + categories: [ + "outro" + ], + reason: "fake1-reason", + }, { + videoID: "fakehash-2", + hash: fakeHash, + categories: [ + "intro", + "preview" + ], + reason: "fake2-longer-reason", + }]; + assert.deepStrictEqual(res.data, expected); + }); + + it("should return 404 once hash prefix varies", () => getLockCategories("b05aa") - .then(res => { - assert.strictEqual(res.status, 404); - done(); - }) - .catch(err => done(err)); - }); + .then(res => assert.strictEqual(res.status, 404)) + ); - it("should return 404 if no lock exists", (done) => { + it("should return 404 if no lock exists", () => getLockCategories("aaaaaa") - .then(res => { - assert.strictEqual(res.status, 404); - done(); - }) - .catch(err => done(err)); - }); + .then(res => assert.strictEqual(res.status, 404)) + ); - it("should return 400 if full hash sent", (done) => { + it("should return 400 if full hash sent", () => getLockCategories(fakeHash) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); + .then(res => assert.strictEqual(res.status, 400)) + ); - it("should return 400 if hash too short", (done) => { + it("should return 400 if hash too short", () => getLockCategories("00") - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); + .then(res => assert.strictEqual(res.status, 400)) + ); - it("should return 400 if no hash specified", (done) => { + it("should return 400 if no hash specified", () => getLockCategories("") - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); + .then(res => assert.strictEqual(res.status, 400)) + ); - it("should return 400 if invalid actionTypes", (done) => { + it("should return 400 if invalid actionTypes", () => client.get(`${endpoint}/aaaa`, { params: { actionTypes: 3 } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); + .then(res => assert.strictEqual(res.status, 400)) + ); - it("should return 400 if invalid actionTypes JSON", (done) => { + it("should return 400 if invalid actionTypes JSON", () => client.get(`${endpoint}/aaaa`, { params: { actionTypes: "{3}" } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); + .then(res => assert.strictEqual(res.status, 400)) + ); - it("Should be able to get single lock", (done) => { + it("Should be able to get single lock", () => { const videoID = "getLockHash2"; const hash = getHash(videoID, 1); - getLockCategories(hash.substring(0,6)) + return getLockCategoriesHashed(videoID) .then(res => { assert.strictEqual(res.status, 200); const expected = [{ @@ -199,15 +162,13 @@ describe("getLockCategoriesByHash", () => { reason: "2-reason" }]; assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); + }); }); - it("Should be able to get by actionType not in array", (done) => { + it("Should be able to get by actionType not in array", () => { const videoID = "getLockHash2"; const hash = getHash(videoID, 1); - client.get(`${endpoint}/${hash.substring(0,6)}`, { params: { actionType: ActionType.Skip } }) + return client.get(`${endpoint}/${hash.substring(0,6)}`, { params: { actionType: ActionType.Skip } }) .then(res => { assert.strictEqual(res.status, 200); const expected = [{ @@ -219,15 +180,13 @@ describe("getLockCategoriesByHash", () => { reason: "2-reason" }]; assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); + }); }); - it("Should be able to get by no actionType", (done) => { + it("Should be able to get by no actionType", () => { const videoID = "getLockHash2"; const hash = getHash(videoID, 1); - client.get(`${endpoint}/${hash.substring(0,6)}`) + return client.get(`${endpoint}/${hash.substring(0,6)}`) .then(res => { assert.strictEqual(res.status, 200); const expected = [{ @@ -239,8 +198,6 @@ describe("getLockCategoriesByHash", () => { reason: "2-reason" }]; assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); + }); }); }); diff --git a/test/utils/queryGen.ts b/test/utils/queryGen.ts index a820c2be..f9ab5ed9 100644 --- a/test/utils/queryGen.ts +++ b/test/utils/queryGen.ts @@ -67,7 +67,7 @@ export const insertLock = async(db: IDatabase, overrides: lockParams = {}) => { actionType: "skip", category: "sponsor", hashedVideoID: "", reason: "", service: Service.YouTube }; const params = { ...defaults, ...overrides }; - params.hashedVideoID = getHash(params.videoID); + params.hashedVideoID = getHash(params.videoID, 1); await db.prepare("run", query, Object.values(params)); }; From 625dc4329242aa201272bf06d95b067dfb9dd5da Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 20:04:18 -0400 Subject: [PATCH 06/31] replace done() - generateVerifyToken - getStatus --- test/cases/generateVerifyToken.ts | 22 +++++++++------------- test/cases/getStatus.ts | 6 ++---- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/test/cases/generateVerifyToken.ts b/test/cases/generateVerifyToken.ts index eb6745a4..6a231935 100644 --- a/test/cases/generateVerifyToken.ts +++ b/test/cases/generateVerifyToken.ts @@ -43,24 +43,22 @@ describe("generateToken test", function() { mock.restore(); }); - it("Should be able to create patreon token for active patron", function (done) { + it("Should be able to create patreon token for active patron", function () { mock.onGet(/identity/).reply(200, patreon.activeIdentity); if (!config?.patreon) this.skip(); - getGenerateToken("patreon", "patreon_code", "").then(res => { + return getGenerateToken("patreon", "patreon_code", "").then(res => { patreonLicense = extractLicenseKey(res.data); assert.ok(validateLicenseKeyRegex(patreonLicense)); - done(); - }).catch(err => done(err)); + }); }); - it("Should create patreon token for invalid patron", function (done) { + it("Should create patreon token for invalid patron", function () { mock.onGet(/identity/).reply(200, patreon.formerIdentityFail); if (!config?.patreon) this.skip(); - getGenerateToken("patreon", "patreon_code", "").then(res => { + return getGenerateToken("patreon", "patreon_code", "").then(res => { patreonLicense = extractLicenseKey(res.data); assert.ok(validateLicenseKeyRegex(patreonLicense)); - done(); - }).catch(err => done(err)); + }); }); it("Should be able to create new local token", () => @@ -82,11 +80,9 @@ describe("generateToken test", function() { }) ); - it("Should return 403 for invalid adminuserID parameter", function (done) { - getGenerateToken("local", "fake-code", "fakeAdminID").then(res => { - assert.strictEqual(res.status, 403); - done(); - }).catch(err => done(err)); + it("Should return 403 for invalid adminuserID parameter", () => { + getGenerateToken("local", "fake-code", "fakeAdminID") + .then(res => assert.strictEqual(res.status, 403)); }); }); diff --git a/test/cases/getStatus.ts b/test/cases/getStatus.ts index 9b0b5dab..645d3a8b 100644 --- a/test/cases/getStatus.ts +++ b/test/cases/getStatus.ts @@ -104,15 +104,13 @@ describe("getStatus", () => { }); }); - it("Should return commit unkown if not present", (done) => { + it("Should return commit unkown if not present", () => { sinon.stub((global as any), "HEADCOMMIT").value(undefined); client.get(`${endpoint}/commit`) .then(res => { assert.strictEqual(res.status, 200); assert.strictEqual(res.data, "test"); // commit should be test - done(); - }) - .catch(err => done(err)); + }); sinon.restore(); }); }); From afb39ba61021d208080d1a390742b5ab9022e666 Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 20:04:36 -0400 Subject: [PATCH 07/31] fix missing HashedVideoID --- test/utils/queryGen.ts | 2 +- test/utils/segmentQueryGen.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/utils/queryGen.ts b/test/utils/queryGen.ts index f9ab5ed9..fc2817b4 100644 --- a/test/utils/queryGen.ts +++ b/test/utils/queryGen.ts @@ -67,7 +67,7 @@ export const insertLock = async(db: IDatabase, overrides: lockParams = {}) => { actionType: "skip", category: "sponsor", hashedVideoID: "", reason: "", service: Service.YouTube }; const params = { ...defaults, ...overrides }; - params.hashedVideoID = getHash(params.videoID, 1); + params.hashedVideoID = overrides?.hashedVideoID ?? getHash(params.videoID, 1) as VideoIDHash; await db.prepare("run", query, Object.values(params)); }; diff --git a/test/utils/segmentQueryGen.ts b/test/utils/segmentQueryGen.ts index ce6fb385..e7c49939 100644 --- a/test/utils/segmentQueryGen.ts +++ b/test/utils/segmentQueryGen.ts @@ -64,6 +64,8 @@ export const insertSegment = async(db: IDatabase, overrides: insertSegmentParams params.locked = Number(params.locked); params.hidden = Number(params.hidden); params.shadowHidden = Number(params.shadowHidden); + // generate hashedVideoID if not provided + params.hashedVideoID = overrides?.hashedVideoID ?? getHash(params.videoID, 1) as VideoIDHash; await db.prepare("run", query, Object.values(params)); }; export const insertChapter = async(db: IDatabase, description: string, params: insertSegmentParams = {}) => { From 355c154fc81ffba3e55042808893bca6b8b92a33 Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 20:04:50 -0400 Subject: [PATCH 08/31] fix typos --- test/cases/highLoad.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/highLoad.ts b/test/cases/highLoad.ts index cbedd1df..501588e0 100644 --- a/test/cases/highLoad.ts +++ b/test/cases/highLoad.ts @@ -23,7 +23,7 @@ describe("High load test", () => { .then(res => assert.strictEqual(res.status, 503)) ); - it("Should return 0 on getTotalStats", () => + it("Should return 200 on getTotalStats", () => client.get("/api/getTotalStats") .then(res => assert.strictEqual(res.status, 200)) ); From 6a08de7ac68fe411a85db31054d1d980536b2bcd Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 20:05:42 -0400 Subject: [PATCH 09/31] userAgentTest from array --- test/cases/userAgentTest.ts | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/test/cases/userAgentTest.ts b/test/cases/userAgentTest.ts index 6bc91ef7..2f9f957b 100644 --- a/test/cases/userAgentTest.ts +++ b/test/cases/userAgentTest.ts @@ -1,20 +1,17 @@ import assert from "assert"; import { parseUserAgent } from "../../src/utils/userAgent"; -describe("userAgent", () => { - it ("Works for Vanced package", () => { - assert.strictEqual("Vanced/1521081792", parseUserAgent("com.vanced.android.youtube/1521081792 (Linux; U; Android 10)")); - }); - - it ("Works for Android package (root)", () => { - assert.strictEqual("Vanced/1521081792", parseUserAgent("com.google.android.youtube/1521081792 (Linux; U; Android 10)")); - }); +const validateUA = (ua: string, expected: string) => + assert.strictEqual(parseUserAgent(ua), expected); - it ("Works MPV", () => { - assert.strictEqual("mpv_sponsorblock/1.0 (https://github.com/po5/mpv_sponsorblock)", parseUserAgent("mpv_sponsorblock/1.0 (https://github.com/po5/mpv_sponsorblock)")); - }); - - it ("Blank for anything else", () => { - assert.strictEqual("", parseUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36")); - }); +describe("userAgent", () => { + const uaCases = [ + ["Vanced/1521081792", "com.vanced.android.youtube/1521081792 (Linux; U; Android 10)"], // Vanced + ["Vanced/1521081792", "com.google.android.youtube/1521081792 (Linux; U; Android 10)"], // Vanced (root) + ["mpv_sponsorblock/1.0 (https://github.com/po5/mpv_sponsorblock)", "mpv_sponsorblock/1.0 (https://github.com/po5/mpv_sponsorblock)"], // mpv + ["", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"], // blank for everything else + ]; + for (const [expected, ua] of uaCases) { + it ("UA parser works", () => validateUA(ua, expected)); + } }); \ No newline at end of file From 5ec0018e8c9db1dad2b628f88b4d7ec5d4dd9986 Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 20:06:21 -0400 Subject: [PATCH 10/31] use queryGen, userGen - getLockCategoriesByHash - setUsernamePrivate --- test/cases/getLockCategoriesByHash.ts | 144 ++++++++++++-------------- test/cases/setUsernamePrivate.ts | 48 ++++----- 2 files changed, 83 insertions(+), 109 deletions(-) diff --git a/test/cases/getLockCategoriesByHash.ts b/test/cases/getLockCategoriesByHash.ts index 8be3b16a..0eafec7a 100644 --- a/test/cases/getLockCategoriesByHash.ts +++ b/test/cases/getLockCategoriesByHash.ts @@ -9,7 +9,15 @@ import { insertLock, insertVipUser } from "../utils/queryGen"; const fakeHash = "b05a20424f24a53dac1b059fb78d861ba9723645026be2174c93a94f9106bb35" as VideoIDHash; const endpoint = "/api/lockCategories"; const getLockCategories = (hash: string, actionType = [ActionType.Mute, ActionType.Skip]) => client.get(`${endpoint}/${hash}`, { params: { actionType } }); -const getLockCategoriesHashed = (videoID: string, hashLength = 6, actionType = [ActionType.Mute, ActionType.Skip]) => getLockCategories(getHash(videoID, 1).substring(0, hashLength), actionType); + +const verifyGetLockCategories = (hash: string, expected: Record, actionType = [ActionType.Mute, ActionType.Skip]) => + getLockCategories(hash, actionType) + .then(res => { + assert.strictEqual(res.status, 200); + assert.deepStrictEqual(res.data, expected); + }); +const verifyGetLockCategoriesHashed = (videoID: string, expected: Record, hashLength = 6, actionType = [ActionType.Mute, ActionType.Skip]) => + verifyGetLockCategories(getHash(videoID, 1).substring(0, hashLength), expected, actionType); const vip = genUser("getLockCategoriesHash", "VIP"); @@ -38,61 +46,47 @@ describe("getLockCategoriesByHash", () => { it("Should be able to get multiple locks in one object", () => { const videoID = "getLockHash1"; const hash = getHash(videoID, 1); - return getLockCategoriesHashed(videoID, 4) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID, - hash, - categories: [ - "sponsor", - "interaction" - ], - reason: "1-reason-longer" - }]; - assert.deepStrictEqual(res.data, expected); - }); + const expected = [{ + videoID, + hash, + categories: [ + "sponsor", + "interaction" + ], + reason: "1-reason-longer" + }]; + return verifyGetLockCategoriesHashed(videoID, expected, 4); }); it("Should be able to get single lock", () => { const videoID = "getLockHash2"; const hash = getHash(videoID, 1); - return getLockCategoriesHashed(videoID) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID, - hash, - categories: [ - "preview" - ], - reason: "2-reason" - }]; - assert.deepStrictEqual(res.data, expected); - }); + const expected = [{ + videoID, + hash, + categories: [ + "preview" + ], + reason: "2-reason" + }]; + return verifyGetLockCategoriesHashed(videoID, expected); }); it("Should be able to get by half full hash", () => { const videoID = "getLockHash3"; const hash = getHash(videoID, 1); - return getLockCategoriesHashed(videoID, 32) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID, - hash, - categories: [ - "nonmusic" - ], - reason: "3-reason" - }]; - assert.deepStrictEqual(res.data, expected); - }); + const expected = [{ + videoID, + hash, + categories: [ + "nonmusic" + ], + reason: "3-reason" + }]; + return verifyGetLockCategoriesHashed(videoID, expected, 32); }); - it("Should be able to get multiple by similar hash with multiple categories", async () => { - const res = await getLockCategories(fakeHash.substring(0,5)); - assert.strictEqual(res.status, 200); + it("Should be able to get multiple by similar hash with multiple categories", () => { const expected = [{ videoID: "fakehash-1", hash: fakeHash, @@ -109,7 +103,7 @@ describe("getLockCategoriesByHash", () => { ], reason: "fake2-longer-reason", }]; - assert.deepStrictEqual(res.data, expected); + return verifyGetLockCategories(fakeHash.substring(0,5), expected); }); it("should return 404 once hash prefix varies", () => @@ -150,53 +144,45 @@ describe("getLockCategoriesByHash", () => { it("Should be able to get single lock", () => { const videoID = "getLockHash2"; const hash = getHash(videoID, 1); - return getLockCategoriesHashed(videoID) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID, - hash, - categories: [ - "preview" - ], - reason: "2-reason" - }]; - assert.deepStrictEqual(res.data, expected); - }); + const expected = [{ + videoID, + hash, + categories: [ + "preview" + ], + reason: "2-reason" + }]; + return verifyGetLockCategoriesHashed(videoID, expected); }); it("Should be able to get by actionType not in array", () => { const videoID = "getLockHash2"; const hash = getHash(videoID, 1); - return client.get(`${endpoint}/${hash.substring(0,6)}`, { params: { actionType: ActionType.Skip } }) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID, - hash, - categories: [ - "preview" - ], - reason: "2-reason" - }]; - assert.deepStrictEqual(res.data, expected); - }); + const expected = [{ + videoID, + hash, + categories: [ + "preview" + ], + reason: "2-reason" + }]; + return verifyGetLockCategories(hash.substring(0,6), expected, [ActionType.Skip]); }); it("Should be able to get by no actionType", () => { const videoID = "getLockHash2"; const hash = getHash(videoID, 1); + const expected = [{ + videoID, + hash, + categories: [ + "preview" + ], + reason: "2-reason" + }]; return client.get(`${endpoint}/${hash.substring(0,6)}`) .then(res => { assert.strictEqual(res.status, 200); - const expected = [{ - videoID, - hash, - categories: [ - "preview" - ], - reason: "2-reason" - }]; assert.deepStrictEqual(res.data, expected); }); }); diff --git a/test/cases/setUsernamePrivate.ts b/test/cases/setUsernamePrivate.ts index c67e2a56..2a758313 100644 --- a/test/cases/setUsernamePrivate.ts +++ b/test/cases/setUsernamePrivate.ts @@ -4,6 +4,8 @@ import assert from "assert"; import { client } from "../utils/httpClient"; import { config } from "../../src/config"; import sinon from "sinon"; +import { insertUsername } from "../utils/queryGen"; +import { HashedUserID } from "../../src/types/user.model"; const USERID_LIMIT = 30; @@ -18,10 +20,6 @@ const newUser_overLimit = `newUser_over${"*".repeat(USERID_LIMIT)}`; // new username to someone else'e privateID const otherUser = `otherUser${"*".repeat(USERID_LIMIT)}`; - -const addUsername = async (userID: string, userName: string, locked = 0) => - await db.prepare("run", 'INSERT INTO "userNames" ("userID", "userName", "locked") VALUES(?, ?, ?)', [userID, userName, locked]); - async function hasSetUsername(userID: string): Promise { const row = await db.prepare("get", 'SELECT "userName", "locked" FROM "userNames" WHERE "userID" = ?', [userID]); return Boolean(row); @@ -40,78 +38,68 @@ const postSetUserName = (userID: string, username: string) => client({ describe("setUsernamePrivate tests", () => { // add preexisitng usernames before(async () => { - await addUsername(getHash(preExisting_underLimit), preExisting_underLimit, 0); - await addUsername(getHash(preExisting_overLimit), preExisting_overLimit, 0); + await insertUsername(db, getHash(preExisting_underLimit) as HashedUserID, preExisting_underLimit); + await insertUsername(db, getHash(preExisting_overLimit) as HashedUserID, preExisting_overLimit); }); // stub minUserIDLength before(() => sinon.stub(config, "minUserIDLength").value(USERID_LIMIT)); after(() => sinon.restore()); - it("Existing privateID = username under Limit should retreive successfully", (done) => { + it("Existing privateID = username under Limit should retreive successfully", () => { const privateID = preExisting_underLimit; 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", () => { const privateID = preExisting_overLimit; 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", () => { const privateID = newUser_underLimit; - postSetUserName(privateID, privateID) + return postSetUserName(privateID, privateID) .then(async (res) => { assert.strictEqual(res.status, 400); const usernameInfo = await hasSetUsername(getHash(privateID)); 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", () => { const privateID = newUser_overLimit; - postSetUserName(privateID, privateID) + return postSetUserName(privateID, privateID) .then(async (res) => { assert.strictEqual(res.status, 400); const usernameInfo = await hasSetUsername(getHash(privateID)); 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", () => { const privateID = otherUser; const otherUserPrivate = preExisting_overLimit; - postSetUserName(privateID, otherUserPrivate) + return postSetUserName(privateID, otherUserPrivate) .then(async (res) => { assert.strictEqual(res.status, 400); const usernameInfo = await hasSetUsername(getHash(privateID)); 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", () => { const privateID = otherUser; const otherUserPrivate = preExisting_underLimit; - postSetUserName(privateID, otherUserPrivate) + return postSetUserName(privateID, otherUserPrivate) .then(async (res) => { assert.strictEqual(res.status, 200); const usernameInfo = await hasSetUsername(getHash(privateID)); assert.ok(usernameInfo); - done(); - }) - .catch((err) => done(err)); + }); }); }); From 7a7662837962fbf83e1bf4cb9563cbe24467256e Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 20:48:50 -0400 Subject: [PATCH 11/31] refactor getRandom to genRandom --- test/cases/eTag.ts | 2 +- test/cases/getChapterNames.ts | 2 +- test/cases/getHashCache.ts | 2 +- test/cases/getLockCategories.ts | 2 +- test/cases/getSubmissionUUID.ts | 2 +- test/cases/getUserInfo.ts | 2 +- test/cases/lockCategoriesHttp.ts | 2 +- test/cases/postSkipSegmentsFeatures.ts | 2 +- test/cases/redisTest.ts | 2 +- test/cases/setUsername.ts | 2 +- test/cases/tempVip.ts | 2 +- test/utils/{getRandom.ts => genRandom.ts} | 0 test/utils/genUser.ts | 2 +- test/utils/queryGen.ts | 2 +- test/utils/segmentQueryGen.ts | 9 ++++++--- 15 files changed, 19 insertions(+), 16 deletions(-) rename test/utils/{getRandom.ts => genRandom.ts} (100%) diff --git a/test/cases/eTag.ts b/test/cases/eTag.ts index f98dddca..f7a6595f 100644 --- a/test/cases/eTag.ts +++ b/test/cases/eTag.ts @@ -2,7 +2,7 @@ import assert from "assert"; import { client } from "../utils/httpClient"; import redis from "../../src/utils/redis"; import { config } from "../../src/config"; -import { genRandom } from "../utils/getRandom"; +import { genRandom } from "../utils/genRandom"; const validateEtag = (expected: string, actual: string): boolean => { const [actualHashType, actualHashKey, actualService] = actual.split(";"); diff --git a/test/cases/getChapterNames.ts b/test/cases/getChapterNames.ts index 01bb7c53..7ccc9956 100644 --- a/test/cases/getChapterNames.ts +++ b/test/cases/getChapterNames.ts @@ -4,7 +4,7 @@ import { Postgres } from "../../src/databases/Postgres"; import { client } from "../utils/httpClient"; import { partialDeepEquals } from "../utils/partialDeepEquals"; import { insertChapter } from "../utils/segmentQueryGen"; -import { genRandomValue } from "../utils/getRandom"; +import { genRandomValue } from "../utils/genRandom"; import { insertVideoInfo } from "../utils/queryGen"; describe("getChapterNames", function () { diff --git a/test/cases/getHashCache.ts b/test/cases/getHashCache.ts index ed5bf096..ffb7601e 100644 --- a/test/cases/getHashCache.ts +++ b/test/cases/getHashCache.ts @@ -5,7 +5,7 @@ import { getHash } from "../../src/utils/getHash"; import redis from "../../src/utils/redis"; import assert from "assert"; import { setTimeout } from "timers/promises"; -import { genRandom } from "../utils/getRandom"; +import { genRandom } from "../utils/genRandom"; const rand1Hash = genRandom(24); const rand1Hash_Key = getHash(rand1Hash, 1); diff --git a/test/cases/getLockCategories.ts b/test/cases/getLockCategories.ts index 21f0eb7d..5d0a0fcb 100644 --- a/test/cases/getLockCategories.ts +++ b/test/cases/getLockCategories.ts @@ -2,7 +2,7 @@ import { db } from "../../src/databases/databases"; import assert from "assert"; import { client } from "../utils/httpClient"; import { insertLock } from "../utils/queryGen"; -import { multiGenRandomValue } from "../utils/getRandom"; +import { multiGenRandomValue } from "../utils/genRandom"; const endpoint = "/api/lockCategories"; const defaultActionTypes = ["skip", "mute"]; diff --git a/test/cases/getSubmissionUUID.ts b/test/cases/getSubmissionUUID.ts index ac22d5b3..c5c4a0a0 100644 --- a/test/cases/getSubmissionUUID.ts +++ b/test/cases/getSubmissionUUID.ts @@ -5,7 +5,7 @@ import { HashedUserID } from "../../src/types/user.model"; import { getHash } from "../../src/utils/getHash"; import { HashedValue } from "../../src/types/hash.model"; import { genAnonUser } from "../utils/genUser"; -import { genRandomValue } from "../utils/getRandom"; +import { genRandomValue } from "../utils/genRandom"; function testHash (segment: segment, version: number): HashedValue { const manualHash = getHash(Object.values(segment).join(""), 1) as HashedValue; diff --git a/test/cases/getUserInfo.ts b/test/cases/getUserInfo.ts index 6f8249df..4d05c872 100644 --- a/test/cases/getUserInfo.ts +++ b/test/cases/getUserInfo.ts @@ -4,7 +4,7 @@ import assert from "assert"; import { client } from "../utils/httpClient"; import { insertSegment, insertThumbnail, insertThumbnailVote, insertTitle, insertTitleVote } from "../utils/segmentQueryGen"; import { genUsers, User } from "../utils/genUser"; -import { genRandomValue } from "../utils/getRandom"; +import { genRandomValue } from "../utils/genRandom"; import { insertBan, insertUsername, insertWarning } from "../utils/queryGen"; describe("getUserInfo", () => { diff --git a/test/cases/lockCategoriesHttp.ts b/test/cases/lockCategoriesHttp.ts index 496c4c7e..df096c30 100644 --- a/test/cases/lockCategoriesHttp.ts +++ b/test/cases/lockCategoriesHttp.ts @@ -5,7 +5,7 @@ import { UserID } from "../../src/types/user.model"; import { Category, VideoID } from "../../src/types/segments.model"; import { insertVipUser } from "../utils/queryGen"; import { genUser } from "../utils/genUser"; -import { genRandomValue } from "../utils/getRandom"; +import { genRandomValue } from "../utils/genRandom"; interface LockCategory { category: Category, diff --git a/test/cases/postSkipSegmentsFeatures.ts b/test/cases/postSkipSegmentsFeatures.ts index 94ca27f5..fbbaa0ff 100644 --- a/test/cases/postSkipSegmentsFeatures.ts +++ b/test/cases/postSkipSegmentsFeatures.ts @@ -2,7 +2,7 @@ import { getHash } from "../../src/utils/getHash"; import { db } from "../../src/databases/databases"; import assert from "assert"; import { partialDeepEquals } from "../utils/partialDeepEquals"; -import { genRandom } from "../utils/getRandom"; +import { genRandom } from "../utils/genRandom"; import { Feature } from "../../src/types/user.model"; import { Segment, postSkipSegmentJSON, convertSingleToDBFormat } from "./postSkipSegments"; diff --git a/test/cases/redisTest.ts b/test/cases/redisTest.ts index 5288c738..f3f42ecf 100644 --- a/test/cases/redisTest.ts +++ b/test/cases/redisTest.ts @@ -1,7 +1,7 @@ import { config } from "../../src/config"; import redis from "../../src/utils/redis"; import assert from "assert"; -import { genRandom } from "../utils/getRandom"; +import { genRandom } from "../utils/genRandom"; const randKey1 = genRandom(); const randValue1 = genRandom(); diff --git a/test/cases/setUsername.ts b/test/cases/setUsername.ts index 37cbfd06..0d3557f1 100644 --- a/test/cases/setUsername.ts +++ b/test/cases/setUsername.ts @@ -2,7 +2,7 @@ import { db, privateDB } from "../../src/databases/databases"; import assert from "assert"; import { client } from "../utils/httpClient"; import { UsernameUser, genAnonUser, genUser, genUsersUsername } from "../utils/genUser"; -import { genRandomValue } from "../utils/getRandom"; +import { genRandomValue } from "../utils/genRandom"; import { insertBan } from "../utils/queryGen"; const adminPrivateUserID = "testUserId"; // hardcoded diff --git a/test/cases/tempVip.ts b/test/cases/tempVip.ts index 71ba06c6..3c66e571 100644 --- a/test/cases/tempVip.ts +++ b/test/cases/tempVip.ts @@ -7,7 +7,7 @@ import redis from "../../src/utils/redis"; import { genAnonUser, genUser } from "../utils/genUser"; import assert from "assert"; import { insertSegment } from "../utils/segmentQueryGen"; -import { genRandomValue } from "../utils/getRandom"; +import { genRandomValue } from "../utils/genRandom"; import { insertVipUser } from "../utils/queryGen"; // helpers diff --git a/test/utils/getRandom.ts b/test/utils/genRandom.ts similarity index 100% rename from test/utils/getRandom.ts rename to test/utils/genRandom.ts diff --git a/test/utils/genUser.ts b/test/utils/genUser.ts index a4e34043..9844b6cf 100644 --- a/test/utils/genUser.ts +++ b/test/utils/genUser.ts @@ -1,4 +1,4 @@ -import { genRandom } from "./getRandom"; +import { genRandom } from "./genRandom"; import { UserID, HashedUserID } from "../../src/types/user.model"; import { getHash } from "../../src/utils/getHash"; diff --git a/test/utils/queryGen.ts b/test/utils/queryGen.ts index fc2817b4..17e0939e 100644 --- a/test/utils/queryGen.ts +++ b/test/utils/queryGen.ts @@ -3,7 +3,7 @@ import { HashedUserID } from "../../src/types/user.model"; import { User, userArray, usernameUserArray } from "./genUser"; import { Feature } from "../../src/types/user.model"; import { ActionType, Category, Service, VideoIDHash } from "../../src/types/segments.model"; -import { genRandomValue } from "./getRandom"; +import { genRandomValue } from "./genRandom"; import { getHash } from "../../src/utils/getHash"; // segments diff --git a/test/utils/segmentQueryGen.ts b/test/utils/segmentQueryGen.ts index e7c49939..9f660471 100644 --- a/test/utils/segmentQueryGen.ts +++ b/test/utils/segmentQueryGen.ts @@ -1,7 +1,7 @@ import { IDatabase } from "../../src/databases/IDatabase"; import { Service, VideoIDHash } from "../../src/types/segments.model"; import { HashedUserID } from "../../src/types/user.model"; -import { genRandom, genRandomValue } from "./getRandom"; +import { genRandom, genRandomValue } from "./genRandom"; import { getHash } from "../../src/utils/getHash"; interface baseParams { @@ -25,6 +25,8 @@ interface insertSegmentParams extends baseParams { videoDuration?: number, hidden?: boolean | number, shadowHidden?: boolean | number, + userAgent?: string, + hashedVideoID?: VideoIDHash, description?: string } const defaultSegmentParams: insertSegmentParams = { @@ -43,7 +45,8 @@ const defaultSegmentParams: insertSegmentParams = { videoDuration: 0, hidden: false, shadowHidden: false, - hashedVideoID: "", + userAgent: "", + hashedVideoID: "" as VideoIDHash, description: "" }; @@ -55,7 +58,7 @@ const generateDefaults = (identifier: string) => ({ }); export const insertSegment = async(db: IDatabase, overrides: insertSegmentParams = {}, identifier?: string) => { - const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "shadowHidden", "hashedVideoID", "description") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; + const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "shadowHidden", "hashedVideoID", "userAgent", "description") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; // generate defaults identifier = identifier ?? genRandom(); const defaults = generateDefaults(identifier); From f7e0980e82ea05aa6267ff33468290746880b2d8 Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 23:21:49 -0400 Subject: [PATCH 12/31] getSegmetInfo tests - fix UUID regex - use and add test helpers - fix genRandom chars -> bytes - fix segmentQueryGen order --- src/routes/getSegmentInfo.ts | 18 +- test/cases/getSegmentInfo.ts | 503 ++++++++++++++-------------------- test/utils/genRandom.ts | 5 +- test/utils/segmentQueryGen.ts | 5 +- 4 files changed, 228 insertions(+), 303 deletions(-) diff --git a/src/routes/getSegmentInfo.ts b/src/routes/getSegmentInfo.ts index f6625aff..7b206547 100644 --- a/src/routes/getSegmentInfo.ts +++ b/src/routes/getSegmentInfo.ts @@ -2,7 +2,7 @@ import { Request, Response } from "express"; import { db } from "../databases/databases"; import { DBSegment, SegmentUUID } from "../types/segments.model"; -const isValidSegmentUUID = (str: string): boolean => /^([a-f0-9]{64}|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/.test(str); +const isValidSegmentUUID = (str: string): boolean => /^([a-f0-9]{64,65}|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/.test(str); async function getSegmentFromDBByUUID(UUID: SegmentUUID): Promise { try { @@ -31,23 +31,29 @@ async function handleGetSegmentInfo(req: Request, res: Response): Promise 10) UUIDs = UUIDs.slice(0, 10); const DBSegments = await getSegmentsByUUID(UUIDs); // all uuids failed lookup if (!DBSegments?.length) { - res.sendStatus(400); + res.sendStatus(404); return; } // uuids valid but not found if (DBSegments[0] === null || DBSegments[0] === undefined) { - res.sendStatus(400); + res.sendStatus(404); return; } return DBSegments; diff --git a/test/cases/getSegmentInfo.ts b/test/cases/getSegmentInfo.ts index d9420c04..3af85044 100644 --- a/test/cases/getSegmentInfo.ts +++ b/test/cases/getSegmentInfo.ts @@ -2,21 +2,44 @@ import { db } from "../../src/databases/databases"; import { partialDeepEquals } from "../utils/partialDeepEquals"; import assert from "assert"; import { client } from "../utils/httpClient"; +import { insertSegment } from "../utils/segmentQueryGen"; +import { genRandom } from "../utils/genRandom"; -const ENOENTID = "0".repeat(64); -const upvotedID = `a${"0".repeat(63)}`; -const downvotedID = `b${"0".repeat(63)}`; -const lockedupID = `c${"0".repeat(63)}`; -const infvotesID = `d${"0".repeat(63)}`; -const shadowhiddenID = `e${"0".repeat(63)}`; -const lockeddownID = `f${"0".repeat(63)}`; -const hiddenID = `1${"0".repeat(63)}`; -const fillerID1 = `11${"0".repeat(62)}`; -const fillerID2 = `12${"0".repeat(62)}`; -const fillerID3 = `13${"0".repeat(62)}`; -const fillerID4 = `14${"0".repeat(62)}`; -const fillerID5 = `15${"0".repeat(62)}`; -const oldID = `${"0".repeat(8)}-${"0000-".repeat(3)}${"0".repeat(12)}`; +const endpoint = "/api/segmentInfo"; +const singleUUIDLookup = (UUID: string) => client.get(endpoint, { params: { UUID } }); + +const genUUID = () => genRandom(66).substring(0, 65); + +const assertParams = async (params: any, expected: any) => { + const res = await client.get(endpoint, { params }); + assert.strictEqual(res.status, 200); + assert.ok(partialDeepEquals(res.data, expected)); +}; + +const assertSingleUUID = async (UUID: string, expected: any) => { + const res = await singleUUIDLookup(UUID); + assert.strictEqual(res.status, 200); + assert.ok(partialDeepEquals(res.data, expected)); +}; + +const assertStatus = (params: string, status: number) => + client.get(`${endpoint}${params}`) + .then(res => assert.strictEqual(res.status, status)); + +const ENOENTID = genUUID(); +const upvotedID = genUUID(); +const downvotedID = genUUID(); +const lockedupID = genUUID(); +const infvotesID = genUUID(); +const shadowhiddenID = genUUID(); +const lockeddownID = genUUID(); +const hiddenID = genUUID(); +const fillerID1 = genUUID(); +const fillerID2 = genUUID(); +const fillerID3 = genUUID(); +const fillerID4 = genUUID(); +const fillerID5 = genUUID(); +const oldID = `${genRandom(8)}-${genRandom(4)}-${genRandom(4)}-${genRandom(4)}-${genRandom(12)}`; const userAgents = { vanced: "Vanced/5.0", meabot: "Meabot/5.0", @@ -25,326 +48,222 @@ const userAgents = { blank: "" }; -const endpoint = "/api/segmentInfo"; -const singleUUIDLookup = (UUID: string) => client.get(endpoint, { params: { UUID } }); - describe("getSegmentInfo", () => { before(async () => { - const insertQuery = `INSERT INTO - "sponsorTimes"("videoID", "startTime", "endTime", "votes", "locked", - "UUID", "userID", "timeSubmitted", "views", "hidden", "shadowHidden", "userAgent") - VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; - await db.prepare("run", insertQuery, ["segmentInfoUpvoted", 1, 10, 2, 0, upvotedID, "segmentInfoUser", 0, 50, 0, 0, userAgents.vanced]); - await db.prepare("run", insertQuery, ["segmentInfoDownvoted", 1, 10, -2, 0, downvotedID, "segmentInfoUser", 0, 50, 0, 0, userAgents.meabot]); - await db.prepare("run", insertQuery, ["segmentInfoLockedup", 1, 10, 2, 1, lockedupID, "segmentInfoUser", 0, 50, 0, 0, userAgents.mpv]); - await db.prepare("run", insertQuery, ["segmentInfoInfvotes", 1, 10, 100000, 0, infvotesID, "segmentInfoUser", 0, 50, 0, 0, userAgents.nodesb]); - await db.prepare("run", insertQuery, ["segmentInfoHidden", 1, 10, 2, 0, hiddenID, "segmentInfoUser", 0, 50, 1, 0, userAgents.blank]); - await db.prepare("run", insertQuery, ["segmentInfoShadowhidden", 1, 10, 2, 0, shadowhiddenID, "segmentInfoUser", 0, 50, 0, 1, userAgents.blank]); - await db.prepare("run", insertQuery, ["segmentInfoLockedown", 1, 10, -2, 1, lockeddownID, "segmentInfoUser", 0, 50, 0, 0, userAgents.blank]); - await db.prepare("run", insertQuery, ["segmentInfoOldID", 1, 10, 1, 0, oldID, "segmentInfoUser", 0, 50, 0, 0, userAgents.blank]); - await db.prepare("run", insertQuery, ["segmentInfoUpvoted", 1, 2, 1, 0, fillerID1, "segmentInfoUser", 0, 50, 0, 0, userAgents.blank]); - await db.prepare("run", insertQuery, ["segmentInfoFiller", 2, 3, 1, 0, fillerID2, "segmentInfoUser", 0, 50, 0, 0, userAgents.blank]); - await db.prepare("run", insertQuery, ["segmentInfoFiller", 3, 4, 1, 0, fillerID3, "segmentInfoUser", 0, 50, 0, 0, userAgents.blank]); - await db.prepare("run", insertQuery, ["segmentInfoFiller", 4, 5, 1, 0, fillerID4, "segmentInfoUser", 0, 50, 0, 0, userAgents.blank]); - await db.prepare("run", insertQuery, ["segmentInfoFiller", 5, 6, 1, 0, fillerID5, "segmentInfoUser", 0, 50, 0, 0, userAgents.blank]); + await insertSegment(db, { UUID: upvotedID, videoID: "segmentInfoUpvoted", votes: 2, userAgent: userAgents.vanced }); + await insertSegment(db, { UUID: downvotedID, videoID: "segmentInfoDownvoted", votes: -2, userAgent: userAgents.meabot }); + await insertSegment(db, { UUID: lockedupID, videoID: "segmentInfoLockedup", votes: 2, locked: true, userAgent: userAgents.mpv }); + await insertSegment(db, { UUID: infvotesID, videoID: "segmentInfoInfvotes", votes: 100000, userAgent: userAgents.nodesb }); + await insertSegment(db, { UUID: hiddenID, videoID: "segmentInfoHidden", hidden: true }); + await insertSegment(db, { UUID: shadowhiddenID, videoID: "segmentInfoShadowhidden",shadowHidden: true }); + await insertSegment(db, { UUID: lockeddownID, videoID: "segmentInfoLockedown", votes: -2, locked: true }); + await insertSegment(db, { UUID: oldID, videoID: "segmentInfoOldID", votes: 1 }); + await insertSegment(db, { UUID: fillerID1, videoID: "segmentInfoFiller" }); + await insertSegment(db, { UUID: fillerID2, videoID: "segmentInfoFiller" }); + await insertSegment(db, { UUID: fillerID3, videoID: "segmentInfoFiller" }); + await insertSegment(db, { UUID: fillerID4, videoID: "segmentInfoFiller" }); + await insertSegment(db, { UUID: fillerID5, videoID: "segmentInfoFiller" }); }); - it("Should be able to retreive upvoted segment", (done) => { - singleUUIDLookup(upvotedID) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID: "segmentInfoUpvoted", - votes: 2, - userAgent: userAgents.vanced, - }]; - assert.ok(partialDeepEquals(res.data, expected)); - done(); - }) - .catch(err => done(err)); + // test against expected data + it("Should be able to retreive upvoted segment", () => { + const expected = [{ + videoID: "segmentInfoUpvoted", + votes: 2, + userAgent: userAgents.vanced, + }]; + return assertSingleUUID(upvotedID, expected); }); - it("Should be able to retreive downvoted segment", (done) => { - singleUUIDLookup(downvotedID) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID: "segmentInfoDownvoted", - votes: -2, - userAgent: userAgents.meabot, - }]; - assert.ok(partialDeepEquals(res.data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to retreive downvoted segment", () => { + const expected = [{ + videoID: "segmentInfoDownvoted", + votes: -2, + userAgent: userAgents.meabot, + }]; + return assertSingleUUID(downvotedID, expected); }); - it("Should be able to retreive locked up segment", (done) => { - singleUUIDLookup(lockedupID) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID: "segmentInfoLockedup", - locked: 1, - votes: 2, - userAgent: userAgents.mpv, - }]; - assert.ok(partialDeepEquals(res.data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to retreive locked up segment", () => { + const expected = [{ + videoID: "segmentInfoLockedup", + locked: 1, + votes: 2, + userAgent: userAgents.mpv, + }]; + return assertSingleUUID(lockedupID, expected); }); - it("Should be able to retreive infinite vote segment", (done) => { - singleUUIDLookup(infvotesID) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID: "segmentInfoInfvotes", - votes: 100000, - userAgent: userAgents.nodesb, - }]; - assert.ok(partialDeepEquals(res.data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to retreive infinite vote segment", () => { + const expected = [{ + videoID: "segmentInfoInfvotes", + votes: 100000, + userAgent: userAgents.nodesb, + }]; + return assertSingleUUID(infvotesID, expected); }); - it("Should be able to retreive shadowhidden segment", (done) => { - singleUUIDLookup(shadowhiddenID) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID: "segmentInfoShadowhidden", - shadowHidden: 1, - userAgent: userAgents.blank, - }]; - assert.ok(partialDeepEquals(res.data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to retreive shadowhidden segment", () => { + const expected = [{ + videoID: "segmentInfoShadowhidden", + shadowHidden: 1, + userAgent: userAgents.blank, + }]; + return assertSingleUUID(shadowhiddenID, expected); }); - it("Should be able to retreive locked down segment", (done) => { - singleUUIDLookup(lockeddownID) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID: "segmentInfoLockedown", - locked: 1, - votes: -2, - }]; - assert.ok(partialDeepEquals(res.data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to retreive locked down segment", () => { + const expected = [{ + videoID: "segmentInfoLockedown", + locked: 1, + votes: -2, + }]; + return assertSingleUUID(lockeddownID, expected); }); - it("Should be able to retreive hidden segment", (done) => { - singleUUIDLookup(hiddenID) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID: "segmentInfoHidden", - hidden: 1, - }]; - assert.ok(partialDeepEquals(res.data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to retreive hidden segment", () => { + const expected = [{ + videoID: "segmentInfoHidden", + hidden: 1, + }]; + return assertSingleUUID(hiddenID, expected); }); - it("Should be able to retreive segment with old ID", (done) => { - singleUUIDLookup(oldID) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID: "segmentInfoOldID", - votes: 1, - }]; - assert.ok(partialDeepEquals(res.data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to retreive segment with old ID", () => { + const expected = [{ + videoID: "segmentInfoOldID", + votes: 1, + }]; + return assertSingleUUID(oldID, expected); }); - it("Should be able to retreive single segment in array", (done) => { - client.get(endpoint, { params: { UUIDs: `["${upvotedID}"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const expected = [{ - videoID: "segmentInfoUpvoted", - votes: 2, - }]; - assert.strictEqual(data.length, 1); - assert.ok(partialDeepEquals(res.data, expected)); - done(); - }) - .catch(err => done(err)); + // array tests against expected + it("Should be able to retreive single segment in array", async () => { + const res = await client.get(endpoint, { params: { UUIDs: `["${upvotedID}"]` } }); + assert.strictEqual(res.status, 200); + const expected = [{ + videoID: "segmentInfoUpvoted", + votes: 2, + }]; + assert.ok(partialDeepEquals(res.data, expected)); }); - it("Should be able to retreive multiple segments in array", (done) => { - client.get(endpoint, { params: { UUIDs: `["${upvotedID}", "${downvotedID}"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const expected = [{ - videoID: "segmentInfoUpvoted", - votes: 2, - }, { - videoID: "segmentInfoDownvoted", - votes: -2, - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data.length, 2); - done(); - }) - .catch(err => done(err)); + it("Should be able to retreive multiple segments in array", () => { + const expected = [{ + videoID: "segmentInfoUpvoted", + votes: 2, + }, { + videoID: "segmentInfoDownvoted", + votes: -2, + }]; + const UUIDs = `["${upvotedID}", "${downvotedID}"]`; + return assertParams({ UUIDs }, expected); }); - it("Should be possible to send unexpected query parameters", (done) => { - client.get(endpoint, { params: { UUID: upvotedID, fakeparam: "hello", category: "sponsor" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const expected = [{ - videoID: "segmentInfoUpvoted", - votes: 2, - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be possible to send unexpected query parameters", () => { + const expected = [{ + videoID: "segmentInfoUpvoted", + votes: 2, + }]; + return assertParams({ UUID: upvotedID, fakeparam: "hello", category: "sponsor" }, expected); }); - it("Should return 400 if array passed to UUID", (done) => { - client.get(`${endpoint}?UUID=["${upvotedID}", "${downvotedID}"]`) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); + // array tests + // array 200 tests + it("Should return 400 if array passed to UUID", () => + assertStatus(`?UUID=["${upvotedID}", "${downvotedID}"]`, 400) + ); - it("Should return 400 if bad array passed to UUIDs", (done) => { - client.get(`${endpoint}?UUIDs=[not-quoted,not-quoted]`) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); + // array 400 tests + it("Should return 400 if bad array passed to UUIDs", () => + assertStatus("?UUIDs=[not-quoted,not-quoted]", 400) + ); - it("Should return 400 if bad UUID passed", (done) => { - client.get(`${endpoint}?UUID=notarealuuid`) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should return 404 if unknown UUID passed", () => { + const uuid = genUUID(); + assertStatus(`?UUID=${uuid}`, 404); }); - it("Should return 400 if bad UUIDs passed in array", (done) => { - client.get(`${endpoint}?UUIDs=["notarealuuid", "anotherfakeuuid"]`) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should return 404 if unknown UUIDs passed in array", () => { + const uuid1 = genUUID(); + const uuid2 = genUUID(); + assertStatus(`?UUIDs=["${uuid1}", "${uuid2}"]`, 404); }); - it("Should return good UUID when mixed with bad UUIDs", (done) => { - client.get(`${endpoint}?UUIDs=["${upvotedID}", "anotherfakeuuid"]`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const expected = [{ - videoID: "segmentInfoUpvoted", - votes: 2, - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data.length, 1); - done(); - }) - .catch(err => done(err)); + it("Should return good UUID when mixed with bad UUIDs", async () => { + const res = await client.get(`${endpoint}?UUIDs=["${upvotedID}", "anotherfakeuuid"]`); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.data.length, 1); + const expected = [{ + videoID: "segmentInfoUpvoted", + votes: 2, + }]; + assert.ok(partialDeepEquals(res.data, expected)); }); - it("Should cut off array at 10", function(done) { + it("Should cut off array at 10", async function() { this.timeout(10000); - const filledIDArray = `["${upvotedID}", "${downvotedID}", "${lockedupID}", "${shadowhiddenID}", "${lockeddownID}", "${hiddenID}", "${fillerID1}", "${fillerID2}", "${fillerID3}", "${fillerID4}", "${fillerID5}"]`; - client.get(endpoint, { params: { UUIDs: filledIDArray } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 10); - assert.strictEqual(data[0].videoID, "segmentInfoUpvoted"); - assert.strictEqual(data[0].votes, 2); - assert.strictEqual(data[9].videoID, "segmentInfoFiller"); - assert.strictEqual(data[9].UUID, fillerID4); - done(); - }) - .catch(err => done(err)); + const UUIDs = [ + upvotedID, + downvotedID, + lockedupID, + shadowhiddenID, + lockeddownID, + hiddenID, + fillerID1, + fillerID2, + fillerID3, + fillerID4, + fillerID5, + ]; + const res = await client.get(endpoint, { params: { UUIDs: JSON.stringify(UUIDs) } }); + assert.strictEqual(res.status, 200); + const data = res.data; + assert.strictEqual(data.length, 10); + assert.strictEqual(data.length, 10); + assert.strictEqual(data[0].videoID, "segmentInfoUpvoted"); + assert.strictEqual(data[0].votes, 2); + assert.strictEqual(data[9].videoID, "segmentInfoFiller"); + assert.strictEqual(data[9].UUID, fillerID4); }); - it("Should not duplicate reponses", (done) => { - client.get(`${endpoint}?UUIDs=["${upvotedID}", "${upvotedID}", "${upvotedID}", "${upvotedID}", "${upvotedID}", "${upvotedID}", "${upvotedID}", "${upvotedID}", "${upvotedID}", "${upvotedID}", "${downvotedID}"]`) - .then(res => { - assert.strictEqual(res.status, 200); - assert.strictEqual(res.data.length, 2); - done(); - }) - .catch(err => done(err)); + it("Should not duplicate reponses", async () => { + const UUIDs = Array(10).fill(upvotedID); + UUIDs.push(downvotedID); + const res = await client.get(`${endpoint}?UUIDs=${JSON.stringify(UUIDs)}`); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.data.length, 2); }); - it("Should return 400 if UUID not found", (done) => { - client.get(endpoint, { params: { UUID: ENOENTID } }) - .then(res => { - if (res.status !== 400) done(`non 400 response code: ${res.status}`); - else done(); // pass - }) - .catch(err => done(err)); - }); + it("Should return 404 if UUID not found", () => + assertStatus(`?UUID=${ENOENTID}`, 404) + ); - it("Should be able to retreive multiple segments with multiple parameters", (done) => { - client.get(`${endpoint}?UUID=${upvotedID}&UUID=${downvotedID}`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const expected = [{ - videoID: "segmentInfoUpvoted", - votes: 2, - }, { - videoID: "segmentInfoDownvoted", - votes: -2, - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data.length, 2); - done(); - }) - .catch(err => done(err)); + it("Should be able to retreive multiple segments with multiple parameters", async () => { + const res = await client.get(`${endpoint}?UUID=${upvotedID}&UUID=${downvotedID}`); + const expected = [{ + videoID: "segmentInfoUpvoted", + votes: 2, + }, { + videoID: "segmentInfoDownvoted", + votes: -2, + }]; + assert.strictEqual(res.status, 200); + assert.ok(partialDeepEquals(res.data, expected)); + assert.strictEqual(res.data.length, 2); }); - it("Should not parse repeated UUID if UUIDs present", (done) => { - client.get(`${endpoint}?UUID=${downvotedID}&UUID=${lockedupID}&UUIDs=["${upvotedID}"]`) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [{ - videoID: "segmentInfoUpvoted", - votes: 2 - }]; - assert.ok(partialDeepEquals(res.data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should not parse repeated UUID if UUIDs present", async () => { + const res = await client.get(`${endpoint}?UUID=${downvotedID}&UUID=${lockedupID}&UUIDs=["${upvotedID}"]`); + const expected = [{ + videoID: "segmentInfoUpvoted", + votes: 2 + }]; + assert.strictEqual(res.status, 200); + assert.ok(partialDeepEquals(res.data, expected)); }); - it("Should return 400 if no UUIDs not sent", (done) => { - client.get(endpoint) - .then(res => { - if (res.status !== 400) done(`non 400 response code: ${res.status}`); - else done(); // pass - }) - .catch(err => done(err)); - }); + it("Should return 400 if no UUIDs not sent", () => + assertStatus("", 400) + ); }); diff --git a/test/utils/genRandom.ts b/test/utils/genRandom.ts index b08a7f8a..045227ea 100644 --- a/test/utils/genRandom.ts +++ b/test/utils/genRandom.ts @@ -1,8 +1,7 @@ import crypto from "crypto"; -export const genRandom = (bytes=8): string => crypto.pseudoRandomBytes(bytes).toString("hex"); - -export const genRandomValue = (prefix: string, identifier: string, bytes=8): string => `${prefix}-${identifier}-${genRandom(bytes)}`; +export const genRandom = (chars=4): string => crypto.pseudoRandomBytes(chars/2).toString("hex"); +export const genRandomValue = (prefix: string, identifier: string, chars=4): string => `${prefix}-${identifier}-${genRandom(chars)}`; export const multiGenRandomValue = (prefix: string, identifier: string, count: number, bytes=8): string[] => { const arr: string[] = []; for (let i = 0; i < count; i++) arr.push(genRandomValue(prefix, identifier, bytes)); diff --git a/test/utils/segmentQueryGen.ts b/test/utils/segmentQueryGen.ts index 9f660471..652f5308 100644 --- a/test/utils/segmentQueryGen.ts +++ b/test/utils/segmentQueryGen.ts @@ -25,8 +25,8 @@ interface insertSegmentParams extends baseParams { videoDuration?: number, hidden?: boolean | number, shadowHidden?: boolean | number, - userAgent?: string, hashedVideoID?: VideoIDHash, + userAgent?: string, description?: string } const defaultSegmentParams: insertSegmentParams = { @@ -45,8 +45,8 @@ const defaultSegmentParams: insertSegmentParams = { videoDuration: 0, hidden: false, shadowHidden: false, - userAgent: "", hashedVideoID: "" as VideoIDHash, + userAgent: "", description: "" }; @@ -69,6 +69,7 @@ export const insertSegment = async(db: IDatabase, overrides: insertSegmentParams params.shadowHidden = Number(params.shadowHidden); // generate hashedVideoID if not provided params.hashedVideoID = overrides?.hashedVideoID ?? getHash(params.videoID, 1) as VideoIDHash; + // debug await db.prepare("run", query, Object.values(params)); }; export const insertChapter = async(db: IDatabase, description: string, params: insertSegmentParams = {}) => { From 9238cc5997a56054e7f7369ba8805c76ce995c31 Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 23:26:48 -0400 Subject: [PATCH 13/31] merge 404 clauses in getSegmentInfo --- src/routes/getSegmentInfo.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/routes/getSegmentInfo.ts b/src/routes/getSegmentInfo.ts index 7b206547..d45e1564 100644 --- a/src/routes/getSegmentInfo.ts +++ b/src/routes/getSegmentInfo.ts @@ -46,14 +46,9 @@ async function handleGetSegmentInfo(req: Request, res: Response): Promise 10) UUIDs = UUIDs.slice(0, 10); const DBSegments = await getSegmentsByUUID(UUIDs); - // all uuids failed lookup - if (!DBSegments?.length) { - res.sendStatus(404); - return; - } // uuids valid but not found - if (DBSegments[0] === null || DBSegments[0] === undefined) { - res.sendStatus(404); + if (!DBSegments?.length || DBSegments[0] === null || DBSegments[0] === undefined) { + res.status(404).send("UUIDs not found in database."); return; } return DBSegments; From db949180a7d9c662252b22f320a3cc703067340a Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 16 Oct 2023 23:46:28 -0400 Subject: [PATCH 14/31] simplify getSegmentInfo endpoint with parseParams --- src/app.ts | 2 +- src/routes/getSegmentInfo.ts | 41 ++++------------------------ src/utils/parseParams.ts | 52 +++++++++++++++++++++--------------- 3 files changed, 37 insertions(+), 58 deletions(-) diff --git a/src/app.ts b/src/app.ts index 724f26ed..84234b8c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -27,7 +27,7 @@ import { corsMiddleware } from "./middleware/cors"; import { apiCspMiddleware } from "./middleware/apiCsp"; import { rateLimitMiddleware } from "./middleware/requestRateLimit"; import dumpDatabase from "./routes/dumpDatabase"; -import { endpoint as getSegmentInfo } from "./routes/getSegmentInfo"; +import { getSegmentInfo } from "./routes/getSegmentInfo"; import { postClearCache } from "./routes/postClearCache"; import { addUnlistedVideo } from "./routes/addUnlistedVideo"; import { postPurgeAllSegments } from "./routes/postPurgeAllSegments"; diff --git a/src/routes/getSegmentInfo.ts b/src/routes/getSegmentInfo.ts index d45e1564..228c84ec 100644 --- a/src/routes/getSegmentInfo.ts +++ b/src/routes/getSegmentInfo.ts @@ -1,8 +1,7 @@ import { Request, Response } from "express"; import { db } from "../databases/databases"; import { DBSegment, SegmentUUID } from "../types/segments.model"; - -const isValidSegmentUUID = (str: string): boolean => /^([a-f0-9]{64,65}|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/.test(str); +import { parseUUIDs } from "../utils/parseParams"; async function getSegmentFromDBByUUID(UUID: SegmentUUID): Promise { try { @@ -15,32 +14,19 @@ async function getSegmentFromDBByUUID(UUID: SegmentUUID): Promise { async function getSegmentsByUUID(UUIDs: SegmentUUID[]): Promise { const DBSegments: DBSegment[] = []; for (const UUID of UUIDs) { - // if UUID is invalid, skip - if (!isValidSegmentUUID(UUID)) continue; DBSegments.push(await getSegmentFromDBByUUID(UUID as SegmentUUID)); } return DBSegments; } -async function handleGetSegmentInfo(req: Request, res: Response): Promise { +async function getSegmentInfo(req: Request, res: Response): Promise { // If using params instead of JSON, only one UUID can be pulled - let UUIDs = req.query.UUIDs - ? JSON.parse(req.query.UUIDs as string) - : req.query.UUID - ? Array.isArray(req.query.UUID) - ? req.query.UUID - : [req.query.UUID] - : null; + let UUIDs = parseUUIDs(req); // verify format if (!Array.isArray(UUIDs) || !UUIDs?.length) { res.status(400).send("UUIDs parameter does not match format requirements."); return; } - // verify that first entry of UUIDs array conforms to regex - if (!isValidSegmentUUID(UUIDs?.[0])) { - res.status(400).send("UUIDs parameter does not match format requirements."); - return; - } // deduplicate with set UUIDs = [ ...new Set(UUIDs)]; // if more than 10 entries, slice @@ -51,28 +37,11 @@ async function handleGetSegmentInfo(req: Request, res: Response): Promise { - try { - const DBSegments = await handleGetSegmentInfo(req, res); - - // If false, res.send has already been called - if (DBSegments) { - //send result - return res.send(DBSegments); - } - } catch (err) /* istanbul ignore next */ { - if (err instanceof SyntaxError) { // catch JSON.parse error - return res.status(400).send("UUIDs parameter does not match format requirements."); - } else return res.sendStatus(500); - } + return res.send(DBSegments); } export { getSegmentFromDBByUUID, getSegmentsByUUID, - handleGetSegmentInfo, - endpoint + getSegmentInfo }; diff --git a/src/utils/parseParams.ts b/src/utils/parseParams.ts index 0f4979b6..e92d6dcc 100644 --- a/src/utils/parseParams.ts +++ b/src/utils/parseParams.ts @@ -4,6 +4,7 @@ import { config } from "../config"; type fn = (req: Request, fallback: any) => any[]; +// generic parsing handlers const syntaxErrorWrapper = (fn: fn, req: Request, fallback: any) => { try { return fn(req, fallback); } catch (e) { @@ -20,12 +21,24 @@ const getQueryList = (req: Request, fallback: T[], param: string, paramPlural : [req.query[param]] : fallback; +// specfic parsing handlers const getCategories = (req: Request, fallback: Category[] ): string[] | Category[] => getQueryList(req, fallback, "category", "categories"); const getDeArrowTypes = (req: Request, fallback: DeArrowType[] ): string[] | DeArrowType[] => getQueryList(req, fallback, "deArrowType", "deArrowTypes"); +const getActionTypes = (req: Request, fallback: ActionType[]): string[] | ActionType[] => + getQueryList(req, fallback, "actionType", "actionTypes"); + +const getRequiredSegments = (req: Request): string[] | SegmentUUID[] => + getQueryList(req, [], "requiredSegment", "requiredSegments"); + +const getUUIDs = (req: Request): string[] | SegmentUUID[] => + getQueryList(req, [], "UUID", "UUIDs"); + + +// validation handlers const validateString = (array: any[]): any[] => { if (!Array.isArray(array)) return undefined; return array @@ -45,28 +58,17 @@ const filterActionType = (actionTypes: ActionType[]) => { return [...filterCategories]; }; +const validateUUID = (array: string[]): SegmentUUID[] => { + if (!Array.isArray(array)) return undefined; + const filtered = array + .filter((item: string) => typeof item === "string") + .filter((item: string) => /^([a-f0-9]{64,65}|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$/.test(item)); + return filtered as SegmentUUID[]; +}; + export const filterInvalidCategoryActionType = (categories: Category[], actionTypes: ActionType[]): Category[] => categories.filter((category: Category) => filterActionType(actionTypes).includes(category)); -const getActionTypes = (req: Request, fallback: ActionType[]): ActionType[] => - req.query.actionTypes - ? JSON.parse(req.query.actionTypes as string) - : req.query.actionType - ? Array.isArray(req.query.actionType) - ? req.query.actionType - : [req.query.actionType] - : fallback; - -// fallback to empty array -const getRequiredSegments = (req: Request): SegmentUUID[] => - req.query.requiredSegments - ? JSON.parse(req.query.requiredSegments as string) - : req.query.requiredSegment - ? Array.isArray(req.query.requiredSegment) - ? req.query.requiredSegment - : [req.query.requiredSegment] - : []; - export const parseCategories = (req: Request, fallback: Category[]): Category[] => { const categories = syntaxErrorWrapper(getCategories, req, fallback); return categories ? validateString(categories) : undefined; @@ -82,8 +84,16 @@ export const parseDeArrowTypes = (req: Request, fallback: DeArrowType[]): DeArro return deArrowTypes ? validateString(deArrowTypes) : undefined; }; -export const parseRequiredSegments = (req: Request): SegmentUUID[] | undefined => - syntaxErrorWrapper(getRequiredSegments, req, []); // never fall back +export const parseRequiredSegments = (req: Request): SegmentUUID[] | undefined => { + // fall back to empty array + // we do not do regex validation since required segments can be partial UUIDs on videos + return syntaxErrorWrapper(getRequiredSegments, req, []); +}; + +export const parseUUIDs = (req: Request): SegmentUUID[] | undefined => { + const UUIDs = syntaxErrorWrapper(getUUIDs, req, []); // fall back to empty array + return UUIDs ? validateUUID(UUIDs) : undefined; +}; export const validateCategories = (categories: string[]): boolean => categories.every((category: string) => config.categoryList.includes(category)); \ No newline at end of file From 4e5129336637f43bff448bdcc8d27aa2c30cfaaa Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 17 Oct 2023 01:35:12 -0400 Subject: [PATCH 15/31] use ES5 proxies instead of arrays --- test/case_boilerplate.txt | 22 -------------------- test/cases/addFeatures.ts | 9 ++------ test/cases/addUserAsVIP.ts | 7 ++----- test/cases/getIsUserVIP.ts | 8 ++----- test/cases/getLockCategories.ts | 5 ++--- test/cases/getUserInfo.ts | 37 ++++++++++++--------------------- test/cases/getViewsForUser.ts | 9 ++------ test/cases/postClearCache.ts | 8 ++----- test/cases/reputation.ts | 18 ++-------------- test/utils/genRandom.ts | 19 ++++++++++------- test/utils/genUser.ts | 13 +++++++++++- test/utils/segmentQueryGen.ts | 2 +- 12 files changed, 52 insertions(+), 105 deletions(-) delete mode 100644 test/case_boilerplate.txt diff --git a/test/case_boilerplate.txt b/test/case_boilerplate.txt deleted file mode 100644 index 41f9dbcf..00000000 --- a/test/case_boilerplate.txt +++ /dev/null @@ -1,22 +0,0 @@ -import { db } from "../../src/databases/databases"; -import assert from "assert"; -import { client } from "../utils/httpClient"; -import { genUsers, User } from "../utils/genUser"; -import { insertSegment, insertVip } from "../utils/queryGen"; - -const endpoint = "/api/endpoint"; - -const postTestEndpoint = () => client({ - method: "POST", - url: endpoint, - data: { - } -}); - -const cases = [ - "firstCase", - "secondCase", - "thirdCase" -]; -const users = genUsers("endpoint", cases); -const vipUser = genUser("endpoint", "vip"); \ No newline at end of file diff --git a/test/cases/addFeatures.ts b/test/cases/addFeatures.ts index 304222d2..61a63914 100644 --- a/test/cases/addFeatures.ts +++ b/test/cases/addFeatures.ts @@ -4,7 +4,7 @@ import { Feature } from "../../src/types/user.model"; import { hasFeature } from "../../src/utils/features"; import { client } from "../utils/httpClient"; import { grantFeature, insertVip } from "../utils/queryGen"; -import { User, genUser, genUsers } from "../utils/genUser"; +import { User, genUser, genUsersProxy } from "../utils/genUser"; const endpoint = "/api/feature"; @@ -19,12 +19,7 @@ const postAddFeatures = (userID: string, adminUserID: string, feature: Feature, } }); -const cases = [ - "grant", - "remove", - "update" -]; -const users = genUsers("addFeatures", cases); +const users = genUsersProxy("addFeatures"); const vipUser = genUser("addFeatures", "vip"); const testedFeature = Feature.ChapterSubmitter; diff --git a/test/cases/addUserAsVIP.ts b/test/cases/addUserAsVIP.ts index a7ee90ff..c9d5f01d 100644 --- a/test/cases/addUserAsVIP.ts +++ b/test/cases/addUserAsVIP.ts @@ -2,15 +2,12 @@ import { HashedUserID } from "../../src/types/user.model"; import { client } from "../utils/httpClient"; import { db } from "../../src/databases/databases"; import assert from "assert"; -import { genAnonUser, genUsers } from "../utils/genUser"; +import { genAnonUser, genUsersProxy } from "../utils/genUser"; // helpers const checkUserVIP = (publicID: string) => db.prepare("get", `SELECT "userID" FROM "vipUsers" WHERE "userID" = ?`, [publicID]); -const cases = [ - "vip-1", -]; -const users = genUsers("endpoint", cases); +const users = genUsersProxy("addUserAsVIP"); // hardcoded into test code const adminPrivateUserID = "testUserId"; diff --git a/test/cases/getIsUserVIP.ts b/test/cases/getIsUserVIP.ts index ae82239f..4415c1c8 100644 --- a/test/cases/getIsUserVIP.ts +++ b/test/cases/getIsUserVIP.ts @@ -1,5 +1,5 @@ import { db } from "../../src/databases/databases"; -import { genUsers, User } from "../utils/genUser"; +import { genUsersProxy, User } from "../utils/genUser"; import { client } from "../utils/httpClient"; import assert from "assert"; import { insertVip } from "../utils/queryGen"; @@ -13,11 +13,7 @@ const checkVipStatus = (user: User, expected: boolean) => assert.strictEqual(res.data.vip, expected); }); -const cases = [ - "vip", - "normal", -]; -const users = genUsers("endpoint", cases); +const users = genUsersProxy("getIsUserVIP"); describe("getIsUserVIP", () => { before(async () => { diff --git a/test/cases/getLockCategories.ts b/test/cases/getLockCategories.ts index 5d0a0fcb..5b9e2b08 100644 --- a/test/cases/getLockCategories.ts +++ b/test/cases/getLockCategories.ts @@ -2,7 +2,7 @@ import { db } from "../../src/databases/databases"; import assert from "assert"; import { client } from "../utils/httpClient"; import { insertLock } from "../utils/queryGen"; -import { multiGenRandomValue } from "../utils/genRandom"; +import { multiGenProxy } from "../utils/genRandom"; const endpoint = "/api/lockCategories"; const defaultActionTypes = ["skip", "mute"]; @@ -43,9 +43,8 @@ const validate404 = (videoID: string, overrides: lockOverrides = {}): Promise assert.strictEqual(res.status, 404)); }; -const videoIDs = multiGenRandomValue("video", "getLockCategories", 3); - describe("getLockCategories", () => { + const videoIDs = multiGenProxy("video", "getLockCategories"); before(async () => { await insertLock(db, { videoID: videoIDs[0], reason: "1-short" }); await insertLock(db, { videoID: videoIDs[0], reason: "1-longer-reason", actionType: "mute", category: "interaction" }); diff --git a/test/cases/getUserInfo.ts b/test/cases/getUserInfo.ts index 4d05c872..ee56971b 100644 --- a/test/cases/getUserInfo.ts +++ b/test/cases/getUserInfo.ts @@ -3,36 +3,25 @@ import { db } from "../../src/databases/databases"; import assert from "assert"; import { client } from "../utils/httpClient"; import { insertSegment, insertThumbnail, insertThumbnailVote, insertTitle, insertTitleVote } from "../utils/segmentQueryGen"; -import { genUsers, User } from "../utils/genUser"; +import { User, genUsersProxy } from "../utils/genUser"; import { genRandomValue } from "../utils/genRandom"; import { insertBan, insertUsername, insertWarning } from "../utils/queryGen"; describe("getUserInfo", () => { const endpoint = "/api/userInfo"; - const cases = [ - "n-0", - "n-1", - "n-2", - "n-3", - "n-4", - "n-5", - "n-6", - "null", - "vip", - "warn-0", - "warn-1", - "warn-2", - "warn-3", - "ban-1", - "ban-2", - ]; - - const users = genUsers("endpoint", cases); - for (const [id, user] of Object.entries(users)) - // generate last segment UUIDs - user.info["last"] = genRandomValue("uuid", id); - + const baseUsersProxy = genUsersProxy("getUserInfo"); + // override proxy for segment UUIDs + const users = new Proxy(baseUsersProxy, { + get: (target, prop, receiver) => { + const user = Reflect.get(target, prop, receiver); + // generate last segment UUIDs + if (!user.info?.["last"]) { + user.info["last"] = genRandomValue("uuid", prop as string); + } + return user; + } + }); const checkValues = (user: User, expected: Record) => client.get(endpoint, { params: { userID: user.privID, value: Object.keys(expected) } }) .then(res => { diff --git a/test/cases/getViewsForUser.ts b/test/cases/getViewsForUser.ts index a95cb5c9..0e349559 100644 --- a/test/cases/getViewsForUser.ts +++ b/test/cases/getViewsForUser.ts @@ -1,7 +1,7 @@ import { db } from "../../src/databases/databases"; import { client } from "../utils/httpClient"; import assert from "assert"; -import { genUsers, User } from "../utils/genUser"; +import { genUsersProxy, User } from "../utils/genUser"; import { insertSegment } from "../utils/segmentQueryGen"; // helpers @@ -11,12 +11,7 @@ const getViewsForUser = (userID: string) => client({ params: { userID } }); -const cases = [ - "u-1", - "u-2", - "u-3" -]; -const users = genUsers("getViewUser", cases); +const users = genUsersProxy("getViewUser"); // set views for users users["u-1"].info["views1"] = 30; diff --git a/test/cases/postClearCache.ts b/test/cases/postClearCache.ts index 309533f4..92ab3083 100644 --- a/test/cases/postClearCache.ts +++ b/test/cases/postClearCache.ts @@ -1,17 +1,13 @@ import { db } from "../../src/databases/databases"; import assert from "assert"; import { client } from "../utils/httpClient"; -import { genUsers, User } from "../utils/genUser"; +import { User, genUsersProxy } from "../utils/genUser"; import { insertSegment, insertVip } from "../utils/queryGen"; const endpoint = "/api/clearCache"; const postClearCache = (user: User, videoID: string) => client({ method: "post", url: endpoint, params: { userID: user.privID, videoID } }); -const cases = [ - "vip", - "normal", -]; -const users = genUsers("postClearCache", cases); +const users = genUsersProxy("postClearCache"); describe("postClearCache", () => { before(async () => { diff --git a/test/cases/reputation.ts b/test/cases/reputation.ts index 653484da..c70ac449 100644 --- a/test/cases/reputation.ts +++ b/test/cases/reputation.ts @@ -1,24 +1,10 @@ import assert from "assert"; import { db } from "../../src/databases/databases"; import { getReputation, calculateReputationFromMetrics } from "../../src/utils/reputation"; -import { genUsers } from "../utils/genUser"; +import { genUsersProxy } from "../utils/genUser"; describe("reputation", () => { - // user definitions - const cases = [ - "locking-vip", - "low-submissions", - "high-downvotes", - "low-non-self-downvotes", - "high-non-self-downvotes", - "new-submissions", - "low-sum", - "high-rep-before-manual-vote", - "high-rep", - "high-rep-locked", - "have-most-upvoted-in-locked-video" - ]; - const users = genUsers("reputation", cases); + const users = genUsersProxy("reputation"); before(async function() { this.timeout(5000); // this preparation takes longer then usual diff --git a/test/utils/genRandom.ts b/test/utils/genRandom.ts index 045227ea..3b436e90 100644 --- a/test/utils/genRandom.ts +++ b/test/utils/genRandom.ts @@ -1,9 +1,14 @@ import crypto from "crypto"; -export const genRandom = (chars=4): string => crypto.pseudoRandomBytes(chars/2).toString("hex"); -export const genRandomValue = (prefix: string, identifier: string, chars=4): string => `${prefix}-${identifier}-${genRandom(chars)}`; -export const multiGenRandomValue = (prefix: string, identifier: string, count: number, bytes=8): string[] => { - const arr: string[] = []; - for (let i = 0; i < count; i++) arr.push(genRandomValue(prefix, identifier, bytes)); - return arr; -}; \ No newline at end of file +export const genRandom = (chars=8): string => crypto.pseudoRandomBytes(chars/2).toString("hex"); +export const genRandomValue = (prefix: string, identifier: string, chars=8): string => `${prefix}-${identifier}-${genRandom(chars)}`; +export const multiGenProxy = (prefix: string, identifier: string) => + new Proxy({}, { + get(target: Record, prop: string, receiver) { + if (Reflect.has(target, prop)) return Reflect.get(target, prop, receiver); + const longIdentifier = typeof prop === "string" ? identifier + prop : identifier; + const result = genRandomValue(prefix, longIdentifier); + Reflect.set(target, prop, result, receiver); + return result; + } + }); \ No newline at end of file diff --git a/test/utils/genUser.ts b/test/utils/genUser.ts index 9844b6cf..032da70c 100644 --- a/test/utils/genUser.ts +++ b/test/utils/genUser.ts @@ -28,13 +28,24 @@ export const genAnonUser = (info: info = {}): User => { return { privID, pubID, info }; }; -export const genUsers = (fnname: string, testcase: string[]): userArray => { +const genUsers = (fnname: string, testcase: string[]): userArray => { const users: userArray = {}; for (const tc of testcase) users[tc] = genUser(fnname, tc); return users; }; +export const genUsersProxy = (fnname: string) => + new Proxy({}, { + get(target: Record, prop, receiver) { + if (Reflect.has(target, prop)) return Reflect.get(target, prop, receiver); + const identifier = typeof prop === "string" ? prop : ""; + const result = genUser(fnname, identifier); + Reflect.set(target, prop, result, receiver); + return result; + }, + }); + export const genUsersUsername = (fnname: string, case_usernames: Map): usernameUserArray => { const cases = Array.from(case_usernames.keys()); const users = genUsers(fnname, cases); diff --git a/test/utils/segmentQueryGen.ts b/test/utils/segmentQueryGen.ts index 652f5308..a46b31cf 100644 --- a/test/utils/segmentQueryGen.ts +++ b/test/utils/segmentQueryGen.ts @@ -54,7 +54,7 @@ const generateDefaults = (identifier: string) => ({ videoID: `vid-${identifier}`, hashedVideoID: getHash(`vid-${identifier}`), userID: `user-${identifier}`, - UUID: genRandomValue("uuid", identifier, 2), + UUID: genRandomValue("uuid", identifier), }); export const insertSegment = async(db: IDatabase, overrides: insertSegmentParams = {}, identifier?: string) => { From fe3f742cb59bec11fe6017d321ea2ba0dc1bdd42 Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 17 Oct 2023 14:54:38 -0400 Subject: [PATCH 16/31] rewrite tests for voteOnSponsorTime - use queryGen - use segmentGen - use userGen - use randomGen proxies - isolate test cases - create distinct test suites - almost all tests use test helpers --- test/cases/voteOnSponsorTime.ts | 1153 ++++++++++--------------------- test/utils/voteOnSponsorTime.ts | 63 ++ 2 files changed, 441 insertions(+), 775 deletions(-) create mode 100644 test/utils/voteOnSponsorTime.ts diff --git a/test/cases/voteOnSponsorTime.ts b/test/cases/voteOnSponsorTime.ts index 2511332f..9c6d63fc 100644 --- a/test/cases/voteOnSponsorTime.ts +++ b/test/cases/voteOnSponsorTime.ts @@ -1,822 +1,425 @@ -import { config } from "../../src/config"; -import { db, privateDB } from "../../src/databases/databases"; -import { getHash } from "../../src/utils/getHash"; +import { db } from "../../src/databases/databases"; import { ImportMock } from "ts-mock-imports"; import * as YouTubeAPIModule from "../../src/utils/youtubeApi"; import { YouTubeApiMock } from "../mocks/youtubeMock"; import assert from "assert"; -import { client } from "../utils/httpClient"; import { arrayDeepEquals } from "../utils/partialDeepEquals"; +import { genRandomValue, multiGenProxy } from "../utils/genRandom"; +import { insertChapter, insertSegment } from "../utils/segmentQueryGen"; +import { User, genAnonUser, genUser } from "../utils/genUser"; +import { insertBan, insertLock, insertVipUser, insertWarning } from "../utils/queryGen"; +import { assertVotes, assertCategory, assertPrivateVote, assertCategoryVotes, assertSegmentStatus, postVote, postVoteCategory, getSegmentVotes } from "../utils/voteOnSponsorTime"; +// stubs const mockManager = ImportMock.mockStaticClass(YouTubeAPIModule, "YouTubeAPI"); const sinonStub = mockManager.mock("listVideos"); sinonStub.callsFake(YouTubeApiMock.listVideos); -const vipUser = "VIPUser"; -const randomID2 = "randomID2"; -const randomID2Hashed = getHash(randomID2); -const categoryChangeUser = "category-change-user"; -const outroSubmitter = "outro-submitter"; -const badIntroSubmitter = "bad-intro-submitter"; -const hiddenInteractionSubmitter = "hidden-interaction-submitter"; -describe("voteOnSponsorTime", () => { - before(async () => { - const now = Date.now(); - const warnVip01Hash = getHash("warn-vip01"); - const warnUser01Hash = getHash("warn-voteuser01"); - const warnUser02Hash = getHash("warn-voteuser02"); - 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; - 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-testtesttest2", 1, 11, 2, 0, "vote-uuid-1", "testman", 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-testtesttest2", 1, 11, 10, 0, "vote-uuid-1.5", "testman", 0, 50, "outro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-testtesttest2", 1, 11, 10, 0, "vote-uuid-1.6", "testman", 0, 50, "interaction", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-testtesttest3", 20, 33, 10, 0, "vote-uuid-2", "testman", 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-testtesttest,test", 1, 11, 100, 0, "vote-uuid-3", "testman", 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-test3", 1, 11, 2, 0, "vote-uuid-4", "testman", 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-test3", 7, 22, -3, 0, "vote-uuid-5", "testman", 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-multiple", 1, 11, 2, 0, "vote-uuid-6", "testman", 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-multiple", 20, 33, 2, 0, "vote-uuid-7", "testman", 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["voter-submitter", 1, 11, 2, 0, "vote-uuid-8", getHash("randomID"), 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["voter-submitter2", 1, 11, 2, 0, "vote-uuid-9", randomID2Hashed, 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["voter-submitter2", 1, 11, 2, 0, "vote-uuid-10", getHash("randomID3"), 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["voter-submitter2", 1, 11, 2, 0, "vote-uuid-11", getHash("randomID4"), 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["own-submission-video", 1, 11, 500, 0, "own-submission-uuid", getHash("own-submission-id"), 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["not-own-submission-video", 1, 11, 500, 0, "not-own-submission-uuid", getHash("somebody-else-id"), 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["incorrect-category", 1, 11, 500, 0, "incorrect-category", getHash("somebody-else-id"), 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["incorrect-category-change", 1, 11, 500, 0, "incorrect-category-change", getHash("somebody-else-id"), 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-testtesttest", 1, 11, 2, 0, "warnvote-uuid-0", "testman", 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["no-sponsor-segments-video", 1, 11, 2, 0, "no-sponsor-segments-uuid-0", "no-sponsor-segments", 0, 50, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["no-sponsor-segments-video", 1, 11, 2, 0, "no-sponsor-segments-uuid-1", "no-sponsor-segments", 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["no-sponsor-segments-video", 1, 11, 2, 0, "no-sponsor-segments-uuid-2", "no-sponsor-segments", 0, 50, "sponsor", "mute", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["segment-locking-video", 1, 11, 2, 0, "segment-locking-uuid-1", "segment-locking-user", 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["segment-hidden-video", 1, 11, 2, 0, "segment-hidden-uuid-1", "segment-hidden-user", 0, 50, "intro", "skip", 0, 1]); - await db.prepare("run", insertSponsorTimeQuery, ["category-change-test-1", 7, 22, 0, 0, "category-change-uuid-1", categoryChangeUserHash, 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["category-change-test-1", 8, 22, 0, 1, "category-change-uuid-2", categoryChangeUserHash, 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["category-change-test-1", 9, 22, 0, 0, "category-change-uuid-3", categoryChangeUserHash, 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["category-change-test-1", 7, 12, 0, 1, "category-change-uuid-4", categoryChangeUserHash, 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["category-change-test-1", 7, 13, 0, 0, "category-change-uuid-5", categoryChangeUserHash, 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["category-change-test-1", 8, 12, 0, 1, "category-change-uuid-6", categoryChangeUserHash, 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["category-change-test-1", 9, 14, 0, 0, "category-change-uuid-7", categoryChangeUserHash, 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["category-change-test-1", 7, 12, 0, 1, "category-change-uuid-8", categoryChangeUserHash, 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["category-change-test-2", 7, 14, 0, 0, "category-warnvote-uuid-0", categoryChangeUserHash, 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["category-change-test-2", 8, 13, 0, 0, "category-banvote-uuid-0", categoryChangeUserHash, 0, 50, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["duration-update", 1, 10, 0, 0, "duration-update-uuid-1", "testman", 0, 0, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["full-video", 1, 10, 0, 0, "full-video-uuid-1", "testman", 0, 0, "sponsor", "full", 0, 0]); - // videoDuration change - await db.prepare("run", insertSponsorTimeQuery, ["duration-changed", 1, 10, 0, 0, "duration-changed-uuid-1", "testman", 1, 0, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["duration-changed", 1, 11, 0, 0, "duration-changed-uuid-2", "testman", 10, 0, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["duration-changed", 1, 12, 0, 0, "duration-changed-uuid-3", "testman", 20, 0, "sponsor", "skip", 0, 0]); - // add videoDuration to duration-changed-uuid-2 - await db.prepare("run", `UPDATE "sponsorTimes" SET "videoDuration" = 150 WHERE "UUID" = 'duration-changed-uuid-2'`); - await db.prepare("run", insertSponsorTimeQuery, ["chapter-video", 1, 10, 0, 0, "chapter-uuid-1", "testman", 0, 0, "chapter", "chapter", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["chapter-video", 1, 10, 0, 0, "non-chapter-uuid-2", "testman", 0, 0, "sponsor", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["chapter-video", 11, 20, 0, 0, "chapter-uuid-2", randomID2Hashed, 0, 0, "chapter", "chapter", 0, 0]); - // segments for testing stricter voting requirements - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 1, 10, 0, 0, "good-outro-submission", getHash(outroSubmitter), 0, 0, "outro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 1, 10, -2, 0, "bad-intro-submission", getHash(badIntroSubmitter), 0, 0, "intro", "skip", 0, 0]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 1, 10, 0, 0, "hidden-interaction-submission", getHash(hiddenInteractionSubmitter), 0, 0, "interaction", "skip", 0, 1]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 1, 10, 0, 0, "testing-outro-skip-1", "testman", 0, 0, "outro", "skip", 0, 1]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 22, 25, 0, 0, "testing-outro-skip-2", "testman", 0, 0, "outro", "skip", 0, 1]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 1, 10, 0, 0, "testing-outro-mute-1", "testman", 0, 0, "outro", "mute", 0, 1]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 22, 25, 0, 0, "testing-outro-mute-2", "testman", 0, 0, "outro", "mute", 0, 1]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 1, 10, 0, 0, "testing-intro-skip-1", "testman", 0, 0, "intro", "skip", 0, 1]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 22, 25, 0, 0, "testing-intro-skip-2", "testman", 0, 0, "intro", "skip", 0, 1]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 1, 10, 0, 0, "testing-interaction-skip-1", "testman", 0, 0, "interaction", "skip", 0, 1]); - await db.prepare("run", insertSponsorTimeQuery, ["vote-requirements-video", 22, 25, 0, 0, "testing-interaction-skip-2", "testman", 0, 0, "interaction", "skip", 0, 1]); - - const insertWarningQuery = 'INSERT INTO "warnings" ("userID", "issueTime", "issuerUserID", "enabled") VALUES(?, ?, ?, ?)'; - await db.prepare("run", insertWarningQuery, [warnUser01Hash, now, warnVip01Hash, 1]); - await db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 1000), 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, [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 "shadowBannedUsers" ("userID") VALUES (?)', [getHash("randomID4")]); - - const insertlockCategoriesQuery = 'INSERT INTO "lockCategories" ("videoID", "userID", "category", "actionType") VALUES (?, ?, ?, ?)'; - await db.prepare("run", insertlockCategoriesQuery, ["no-sponsor-segments-video", "someUser", "sponsor", "skip"]); - await db.prepare("run", insertlockCategoriesQuery, ["category-change-test-1", "someUser", "preview", "skip"]); // sponsor should stay unlocked - }); - // constants - const endpoint = "/api/voteOnSponsorTime"; - const postVote = (userID: string, UUID: string, type: number) => client({ - method: "POST", - url: endpoint, - params: { - userID, - UUID, - type - } - }); - const postVoteCategory = (userID: string, UUID: string, category: string) => client({ - method: "POST", - url: endpoint, - params: { - userID, - UUID, - category - } - }); - - const getSegmentVotes = (UUID: string) => db.prepare("get", `SELECT "votes" 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]); - - it("Should be able to upvote a segment", (done) => { - const UUID = "vote-uuid-0"; - postVote("randomID", UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 3); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to downvote a segment", (done) => { - const UUID = "vote-uuid-2"; - postVote(randomID2, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.ok(row.votes < 10); - const voteInfo = await getPrivateVoteInfo(UUID); - assert.strictEqual(voteInfo.length, 1); - assert.strictEqual(voteInfo[0].normalUserID, randomID2Hashed); - 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) => { - const UUID = "vote-uuid-2"; - postVote("randomID3", UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - 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) => { - const UUID = "vote-uuid-1.6"; - postVote("randomID4", UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - 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) => { - const UUID = "vote-uuid-1"; - postVote("hasNotSubmittedID", UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - 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) => { - const UUID = "vote-uuid-1.5"; - postVote("hasNotSubmittedID", UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 10); - done(); - }) - .catch(err => done(err)); - }); - - it("VIP should be able to completely downvote a segment", (done) => { - const UUID = "vote-uuid-3"; - postVote(vipUser, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.ok(row.votes <= -2); - done(); - }) - .catch(err => done(err)); - }); - - it("should be able to completely downvote your own segment (segment unlocked)", (done) => { - const UUID = "own-submission-uuid"; - postVote("own-submission-id", UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.ok(row.votes <= -2); - done(); - }) - .catch(err => done(err)); - }); - - it("should not be able to completely downvote somebody elses segment", (done) => { - const UUID = "not-own-submission-uuid"; - postVote(randomID2, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 499); - done(); - }) - .catch(err => done(err)); - }); - - it("should be able to completely downvote chapter using malicious", (done) => { - const UUID = "chapter-uuid-1"; - postVote(randomID2, UUID, 30) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, -2); - done(); - }) - .catch(err => done(err)); - }); - - it("should not be able to completely downvote non-chapter using malicious", (done) => { - const UUID = "non-chapter-uuid-2"; - postVote(randomID2, UUID, 30) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - 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) => { - const UUID = "vote-uuid-4"; - postVoteCategory(randomID2, UUID, "intro") - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - const categoryRows = await db.prepare("all", `SELECT votes, category FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]); - assert.strictEqual(row.category, "sponsor"); - assert.strictEqual(categoryRows.length, 2); - assert.strictEqual(categoryRows[0].votes, 1); - assert.strictEqual(categoryRows[0].category, "intro"); - assert.strictEqual(categoryRows[1].votes, 1); - assert.strictEqual(categoryRows[1].category, "sponsor"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should not able to change to an invalid category", (done) => { - const UUID = "incorrect-category"; - postVoteCategory(randomID2, UUID, "fakecategory") - .then(async res => { - assert.strictEqual(res.status, 400); - const row = await getSegmentCategory(UUID); - assert.strictEqual(row.category, "sponsor"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should not able to change to highlight category", (done) => { - const UUID = "incorrect-category"; - postVoteCategory(randomID2, UUID, "highlight") - .then(async res => { - assert.strictEqual(res.status, 400); - const row = await getSegmentCategory(UUID); - assert.strictEqual(row.category, "sponsor"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should not able to change to chapter category", (done) => { - const UUID = "incorrect-category"; - postVoteCategory(randomID2, UUID, "chapter") - .then(async res => { - assert.strictEqual(res.status, 400); - const row = await getSegmentCategory(UUID); - 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) => { - const UUID = "vote-uuid-4"; - postVoteCategory(randomID2, UUID, "outro") - .then(async res => { - assert.strictEqual(res.status, 200, "Status code should be 200"); - const submissionRow = await getSegmentCategory(UUID); - const categoryRows = await db.prepare("all", `SELECT votes, category FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]); - let introVotes = 0; - let outroVotes = 0; - let sponsorVotes = 0; - for (const row of categoryRows) { - if (row?.category === "intro") introVotes += row?.votes; - if (row?.category === "outro") outroVotes += row?.votes; - if (row?.category === "sponsor") sponsorVotes += row?.votes; - } - assert.strictEqual(submissionRow.category, "sponsor"); - assert.strictEqual(categoryRows.length, 3); - assert.strictEqual(introVotes, 0); - assert.strictEqual(outroVotes, 1); - assert.strictEqual(sponsorVotes, 1); - done(); - }) - .catch(err => done(err)); - }); - - - it("Should not be able to change your vote to an invalid category", (done) => { - const UUID = "incorrect-category-change"; - const vote = (inputCat: string, assertCat: string, callback: Mocha.Done) => { - postVoteCategory(randomID2, UUID, inputCat) - .then(async () => { - const row = await getSegmentCategory(UUID); - assert.strictEqual(row.category, assertCat); - callback(); - }) - .catch(err => done(err)); - }; - vote("sponsor", "sponsor", () => { - vote("fakeCategory", "sponsor", done); - }); - }); - - it("Submitter should be able to vote for a category and it should immediately change (segment unlocked, nextCatgeory unlocked, notVip)", (done) => { - const userID = categoryChangeUser; - const UUID = "category-change-uuid-1"; - const category = "sponsor"; - postVoteCategory(userID, UUID, category) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - 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) => { - const userID = categoryChangeUser; - const UUID = "category-change-uuid-2"; - const category = "sponsor"; - postVoteCategory(userID, UUID, category) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - 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) => { - const userID = categoryChangeUser; - const UUID = "category-change-uuid-3"; - const category = "preview"; - postVoteCategory(userID, UUID, category) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - 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) => { - const userID = categoryChangeUser; - const UUID = "category-change-uuid-4"; - const category = "preview"; - postVoteCategory(userID, UUID, category) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - 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) => { - const userID = vipUser; - const UUID = "category-change-uuid-5"; - const category = "sponsor"; - postVoteCategory(userID, UUID, category) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - 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) => { - const userID = vipUser; - const UUID = "category-change-uuid-6"; - const category = "sponsor"; - postVoteCategory(userID, UUID, category) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - 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) => { - const userID = vipUser; - const UUID = "category-change-uuid-7"; - const category = "preview"; - postVoteCategory(userID, UUID, category) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - 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) => { - const userID = vipUser; - const UUID = "category-change-uuid-8"; - const category = "preview"; - postVoteCategory(userID, UUID, category) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - 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) => { - const UUID = "category-warnvote-uuid-0"; - const category = "preview"; - postVoteCategory("warn-voteuser01", UUID, category) - .then(res => { - 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) => { - const UUID = "category-banvote-uuid-0"; - const category = "preview"; - postVoteCategory("randomID4", UUID, category) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - const categoryRows = await db.prepare("all", `SELECT votes, category FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]); - assert.strictEqual(row.category, "intro"); - assert.strictEqual(categoryRows.length, 0); - done(); - }) - .catch(err => done(err)); - }); - - it("Should not be able to category-vote on an invalid UUID submission", (done) => { +describe("voteOnSponsorTime - 4xx", () => { + const user = genUser("vote", "4xx"); + const uuids = multiGenProxy("uuid", "vote-vip"); + let randomUUID: string; + // before + before (async () => { + await insertSegment(db, { userID: user.pubID }); + await insertSegment(db, { UUID: uuids["full-4xx"], actionType: "full" }); + }); + beforeEach(async () => { + randomUUID = genRandomValue("uuid", "vote-categoryvote"); + await insertSegment(db, { UUID: randomUUID }); + }); + // categoryVotes + it("Should not able to change to an invalid category", () => { + return assertCategory(user, randomUUID, "fakecategory", "sponsor", 400); + }); + it("Should not able to change to highlight category", () => { + return assertCategory(user, randomUUID, "highlight", "sponsor", 400); + }); + it("Should not able to change to chapter category", () => { + return assertCategory(user, randomUUID, "chapter", "sponsor", 400); + }); + // deprecated voteTypes + const assertStatus = async (user: User, UUID: string, voteType: number, status: number) => { + const voteRes = await postVote(user.privID, UUID, voteType); + assert.strictEqual(voteRes.status, status); + }; + it("Should not be able to vote with type 10", () => { + return assertStatus(user, randomUUID, 10, 400); + }); + it("Should not be able to vote with type 11", () => { + return assertStatus(user, randomUUID, 11, 400); + }); + // invalid category-votes + it("Should not be able to category-vote on an invalid UUID submission", async () => { const UUID = "invalid-uuid"; - postVoteCategory("randomID3", UUID, "intro") - .then(res => { - assert.strictEqual(res.status, 404); - done(); - }) - .catch(err => done(err)); - }); - - it("Should not be able to category-vote on a full video segment", (done) => { - const UUID = "full-video-uuid-1"; - postVoteCategory("randomID3", UUID, "selfpromo") - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + const voteRes = await postVoteCategory(user.privID, UUID, "intro"); + return assert.strictEqual(voteRes.status, 404); }); - - it('Non-VIP should not be able to upvote "dead" submission', (done) => { - const UUID = "vote-uuid-5"; - postVote(randomID2, UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 403); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, -3); - done(); - }) - .catch(err => done(err)); + it("Should not be able to category-vote on a full video segment", async () => { + const voteRes = await postVoteCategory(user.privID, uuids["full-4xx"], "selfpromo"); + return assert.strictEqual(voteRes.status, 400); }); - - it('Non-VIP should not be able to downvote "dead" submission', (done) => { - const UUID = "vote-uuid-5"; - postVote(randomID2, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, -3); - done(); - }) - .catch(err => done(err)); + it("Should not be able to change your vote to an invalid category", async () => { + await assertCategory(user, randomUUID, "sponsor", "sponsor"); + return assertCategory(user, randomUUID, "fakecategory", "sponsor", 400); }); +}); - it('VIP should be able to upvote "dead" submission', (done) => { - const UUID = "vote-uuid-5"; - postVote(vipUser, UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.ok(row.votes > -3); - done(); - }) - .catch(err => done(err)); +describe("voteOnSponsorTime - No Previous Submissions", () => { + // constants + const uuid = "uuid-vote-unchanged"; + // helpers + const assertSegmentNotChanged = async (user: User, vote: number) => { + const uuid = "uuid-vote-unchanged"; + const voteRes = await postVote(user.privID, uuid, vote); + assert.strictEqual(voteRes.status, 200); + const row = await getSegmentVotes(uuid); + assert.strictEqual(row.votes, 0); + }; + // before + before(async () => { + await insertSegment(db, { UUID: uuid }); }); - - it("Should not be able to upvote a segment (Too many warning)", (done) => { - const UUID = "warnvote-uuid-0"; - postVote("warn-voteuser01", UUID, 1) - .then(res => { - assert.strictEqual(res.status, 403); - done(); - }) - .catch(err => done(err)); + // tests + it("Should not be able to downvote or upvote a segment if the user hasn't submitted yet", () => { + const hasNotSubmitted = genAnonUser(); + assertSegmentNotChanged(hasNotSubmitted, 0); + assertSegmentNotChanged(hasNotSubmitted, 1); }); +}); - it("Non-VIP should not be able to downvote on a segment with no-segments category", (done) => { - const UUID = "no-sponsor-segments-uuid-0"; - postVote("randomID", UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 2); - done(); - }) - .catch(err => done(err)); +describe("voteOnSponsorTime - VIP", () => { + const vipUser = genUser("vote", "vip"); + const uuids = multiGenProxy("uuid", "vote-vip"); + let randomUUID: string; + // before + before (async () => { + await insertVipUser(db, vipUser); + await insertSegment(db, { UUID: uuids["locked"], locked: true }); + await insertSegment(db, { UUID: uuids["hidden"], hidden: true }); + await insertSegment(db, { UUID: uuids["dead"], votes: -2 }); + await insertSegment(db, { UUID: uuids["duration-update"], videoID: "duration-update", videoDuration: 1000 }); + await insertSegment(db, { UUID: uuids["category-locked"], locked: true }); + // nextCategory locked + await insertLock(db, { videoID: "next-locked", actionType: "skip", category: "outro" }); + await insertSegment(db, { videoID: "next-locked", UUID: uuids["next-locked"] }); + // nextCategory, segment locked + await insertLock(db, { videoID: "locked-next-locked", actionType: "skip", category: "intro" }); + await insertSegment(db, { videoID: "locked-next-locked", UUID: uuids["locked-next-locked"], locked: true }); + }); + beforeEach(async () => { + randomUUID = genRandomValue("uuid", "vote-vip"); + await insertSegment(db, { UUID: randomUUID }); + }); + // voting + it("VIP should be able to completely downvote a segment", () => { + return assertVotes(vipUser, randomUUID, 0, -2); + }); + // locking/ hiding + it("VIP upvote should lock segment", () => { + return assertSegmentStatus(vipUser, randomUUID, 1, "locked", 1); + }); + it("VIP downvote should unlock segment", () => { + return assertSegmentStatus(vipUser, uuids["locked"], 0, "locked", 0); + }); + it("VIP upvote should unhide segment", () => { + return assertSegmentStatus(vipUser, uuids["hidden"], 1, "hidden", 0); + }); + // dead submissions + it('VIP upvote should bring back "dead" submission', () => { + return assertVotes(vipUser, uuids["dead"], 1, -1); + }); + // video duration + it("VIP upvote should updated videoDuration", () => { + return assertSegmentStatus(vipUser, uuids["duration-update"], 1, "videoDuration", 500); + }); + // category change + it("VIP vote for category should change (segment unlocked, nextCatgeory unlocked, VIP)", () => { + return assertCategory(vipUser, randomUUID, "outro", "outro"); + }); + it("VIP vote for category should change (segment locked, nextCatgeory unlocked, VIP)", () => { + return assertCategory(vipUser, uuids["category-locked"], "outro", "outro"); + }); + it("VIP vote for category should change (segment unlocked, nextCatgeory locked, VIP)", () => { + return assertCategory(vipUser, uuids["next-locked"], "outro", "outro"); + }); + it("VIP vote for a category should change (segment locked, nextCatgeory locked, VIP)", () => { + return assertCategory(vipUser, uuids["locked-next-locked"], "intro", "intro"); }); +}); - it("Non-VIP should be able to upvote on a segment with no-segments category", (done) => { - const UUID = "no-sponsor-segments-uuid-0"; - postVote("randomID", UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 3); - done(); - }) - .catch(err => done(err)); +describe("voteOnSponsorTime - user with submissions", () => { + const user = genUser("vote", "userWithSubmissions"); + const uuids = multiGenProxy("uuid", "vote-usersubs"); + const videoIDs = multiGenProxy("video", "vote-usersubs"); + let randomUUID: string; + // before + before (async () => { + await insertSegment(db, { userID: user.pubID }); // user has submission + await insertChapter(db, "", { userID: user.pubID }); // user has chapters + await insertSegment(db, { UUID: uuids["same-ip"], votes: 10 }); + await insertChapter(db, "", { UUID: uuids["chapter"] }); + await insertSegment(db, { UUID: uuids["full-video-dead"], actionType: "full", votes: -2 }); + // ajacent segment lock + await insertLock(db, { videoID: videoIDs["locked-sponsor"], actionType: "skip", category: "sponsor" }); + await insertSegment(db, { videoID: videoIDs["locked-sponsor"], UUID: uuids["locked-sponsor"], actionType: "mute" }); + // category change vote + await insertSegment(db, { UUID: uuids["category-change"], category: "sponsor" }); + }); + beforeEach(async () => { + randomUUID = genRandomValue("uuid", "vote-usersubs"); + await insertSegment(db, { UUID: randomUUID }); + }); + // tests + it("Should be able to upvote a segment", () => { + return assertVotes(user, randomUUID, 1, 1); + }); + it ("Should be able to downvote a segment", async () => { + await assertVotes(user, uuids["same-ip"], 0, 9); + return assertPrivateVote(user, uuids["same-ip"], 0); + }); + it("Should be able to downvote segment with ajacent actionType lock", () => { + return assertVotes(user, uuids["locked-sponsor"], 0, -1); + }); + it ("Should not be able to completely downvote somebody elses segment", () => { + return assertVotes(user, randomUUID, 0, -1); + }); + it("Should not be able to downvote the same segment when voting from a different user on the same IP", () => { + return assertVotes(genAnonUser(), uuids["same-ip"], 0, 9); + }); + // dead segment + it("Should be able to revive full video segment as non-vip", () => { + return assertVotes(user, uuids["full-video-dead"], 1, -1); + }); + // malicious votes + // user needs chapter submissions to malicious vote chapters + it("Should be able to completely downvote chapter using malicious", () => { + return assertVotes(user, uuids["chapter"], 30, -2); + }); + it("Should not be able to completely downvote non-chapter using malicious", () => { + return assertVotes(user, randomUUID, 30, 0); + }); + // category votes + 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 = uuids["category-change"]; + await assertCategory(user, uuid, "outro", "sponsor"); // vote for change + return assertCategoryVotes(uuid, "outro", 1); + }); + // undovote + it("Should be able to undo-vote a segment", async () => { + await assertVotes(user, randomUUID, 1, 1); // upvote + return assertVotes(user, randomUUID, 20, 0); // undo-vote }); +}); - it("Non-VIP should not be able to category vote on a segment with no-segments category", (done) => { - const UUID = "no-sponsor-segments-uuid-0"; - postVoteCategory("randomID", UUID, "outro") - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentCategory(UUID); - assert.strictEqual(row.category, "sponsor"); - done(); - }) - .catch(err => done(err)); +describe("voteOnSponsorTime - duration change", () => { + const user = genUser("vote", "userWithSubmissions"); + const videoID = "duration-changed"; + const uuids = multiGenProxy("uuid", "vote-usersubs"); + // before + before (async () => { + await insertSegment(db, { userID: user.pubID }); // user has submission + // + await insertSegment(db, { UUID: uuids["duration-changed-1"], videoID, timeSubmitted: 10 }); + await insertSegment(db, { UUID: uuids["duration-changed-2"], videoID, timeSubmitted: 20, videoDuration: 150 }); + await insertSegment(db, { UUID: uuids["duration-changed-3"], videoID, timeSubmitted: 30 }); + }); + // tests + it("Should hide changed submission on any downvote", async () => { + const UUID = uuids["duration-changed-3"]; + const videoID = "duration-changed"; + await assertVotes(user, UUID, 0, -1); + const hiddenSegments = await db.prepare("all", `SELECT "UUID" FROM "sponsorTimes" WHERE "videoID" = ? AND "hidden" = 1`, [videoID]); + const expected = [{ + "UUID": "duration-changed-uuid-1", + }, { + "UUID": "duration-changed-uuid-2", + }]; + assert.strictEqual(hiddenSegments.length, 2); + arrayDeepEquals(hiddenSegments, expected); }); +}); - it("VIP upvote should lock segment", (done) => { - const UUID = "segment-locking-uuid-1"; - postVote(vipUser, UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await db.prepare("get", `SELECT "locked" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); - assert.strictEqual(row.locked, 1); - done(); - }) - .catch(err => done(err)); +describe("voteOnSponsorTime - warned", () => { + const user = genUser("vote", "warned"); + let randomUUID: string; + // before + before (async () => { + await insertWarning(db, user.pubID, { issueTime: Date.now() }); // active warning }); - - it("VIP downvote should unlock segment", (done) => { - const UUID = "segment-locking-uuid-1"; - postVote(vipUser, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await db.prepare("get", `SELECT "locked" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); - assert.strictEqual(row.locked, 0); - done(); - }) - .catch(err => done(err)); + beforeEach(async () => { + randomUUID = genRandomValue("uuid", "vote-shadowbanned"); + await insertSegment(db, { UUID: randomUUID }); }); - - it("VIP upvote should unhide segment", (done) => { - const UUID = "segment-hidden-uuid-1"; - postVote(vipUser, UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await db.prepare("get", `SELECT "hidden" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); - assert.strictEqual(row.hidden, 0); - done(); - }) - .catch(err => done(err)); + // tests + it("Should not be able to vote for a category of a segment (Too many warning)", () => { + return assertCategory(user, randomUUID, "outro", "sponsor", 403); }); - - it("Should be able to undo-vote a segment", (done) => { - const UUID = "vote-uuid-2"; - postVote(randomID2, UUID, 20) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 10); - done(); - }) - .catch(err => done(err)); + it("Should not be able to upvote a segment (Too many warning)", () => { + return assertVotes(user, randomUUID, 1, 0, 403); }); +}); - it("Should not be able to vote with type 10", (done) => { - const UUID = "segment-locking-uuid-1"; - postVote(vipUser, UUID, 10) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); +describe("voteOnSponsorTime - shadowbanned", () => { + const user = genUser("vote", "shadowbanned"); + let randomUUID: string; + // before + before (async () => { + await insertBan(db, user.pubID); }); - - it("Should not be able to vote with type 11", (done) => { - const UUID = "segment-locking-uuid-1"; - postVote(vipUser, UUID, 11) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + beforeEach(async () => { + randomUUID = genRandomValue("uuid", "vote-shadowbanned"); + await insertSegment(db, { UUID: randomUUID }); }); - - it("Should be able to update stored videoDuration with VIP upvote", (done) => { - const UUID = "duration-update-uuid-1"; - postVote(vipUser, UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const { videoDuration } = await db.prepare("get", `SELECT "videoDuration" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); - assert.strictEqual(videoDuration, 500); - done(); - }); + // tests + it("Should not be able to downvote a segment", () => { + return assertVotes(user, randomUUID, 0, 0); }); - - it("Should hide changed submission on any downvote", (done) => { - const UUID = "duration-changed-uuid-3"; - const videoID = "duration-changed"; - postVote(randomID2, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const hiddenSegments = await db.prepare("all", `SELECT "UUID" FROM "sponsorTimes" WHERE "videoID" = ? AND "hidden" = 1`, [videoID]); - assert.strictEqual(hiddenSegments.length, 2); - const expected = [{ - "UUID": "duration-changed-uuid-1", - }, { - "UUID": "duration-changed-uuid-2", - }]; - arrayDeepEquals(hiddenSegments, expected); - done(); - }); + it ("Should not be able to upvote a segment", () => { + return assertVotes(user, randomUUID, 1, 0); }); - - it("Should be able to downvote segment with ajacent actionType lock", (done) => { - const UUID = "no-sponsor-segments-uuid-2"; - postVote(randomID2, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 1); - 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 () => { + await assertCategory(user, randomUUID, "outro", "sponsor"); // should pass + assertCategoryVotes(randomUUID, "outro", 1) + .then(() => assert.fail()) + .catch(err => assert.strictEqual(err.message, "vote for category outro not found")); }); +}); - it("Should not be able to revive full video segment as non-vip", (done) => { - const UUID = "full-video-uuid-1"; - postVote("VIPUser", UUID, 0).then(() => { - postVote("randomID3", UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, -2); - done(); - }) - .catch(err => done(err)); - }); +describe("voteOnSponsorTime - vote restrictions", () => { + let newUUID: string, videoID: string; + const user = genUser("vote", "vote-restricted"); + // before + beforeEach(() => { + newUUID = genRandomValue("uuid", "vote-restricted"); + videoID = genRandomValue("video", "vote-restricted"); + }); + // pass + it("Should be able to upvote (same category", async () => { + const category = "outro"; + await insertSegment(db, { category, videoID, userID: user.pubID }); // existing submission + await insertSegment(db, { category, videoID, UUID: newUUID }); // vote target submission + return assertVotes(user, newUUID, 1, 1); + }); + it("Should be able to downvote (same category)", async () => { + const category = "intro"; + await insertSegment(db, { category, videoID, userID: user.pubID }); // existing submission + await insertSegment(db, { category, videoID, UUID: newUUID }); // vote target submission + return assertVotes(user, newUUID, 0, -1); + }); + it("Should be able to upvote (same category different action)", async () => { + const category = "selfpromo"; + await insertSegment(db, { category, videoID, userID: user.pubID, actionType: "mute" }); // existing submission + await insertSegment(db, { category, videoID, UUID: newUUID }); // vote target submission + return assertVotes(user, newUUID, 1, 1); + }); + it("Should be able to downvote (same category different action)", async () => { + const category = "selfpromo"; + await insertSegment(db, { category, videoID, userID: user.pubID, actionType: "mute" }); // existing submission + await insertSegment(db, { category, videoID, UUID: newUUID }); // vote target submission + return assertVotes(user, newUUID, 0, -1); + }); + // fail + it("Should not be able to upvote (only submission in category downvoted)", async () => { + const category = "interaction"; + await insertSegment(db, { category, videoID, userID: user.pubID, votes: -2 }); // existing submission + await insertSegment(db, { category, videoID, UUID: newUUID }); // vote target submission + return assertVotes(user, newUUID, 1, 0); + }); + it("Should not be able to downvote (only submission in category downvoted)", async () => { + const category = "interaction"; + await insertSegment(db, { category, videoID, userID: user.pubID, votes: -2 }); // existing submission + await insertSegment(db, { category, videoID, UUID: newUUID }); // vote target submission + return assertVotes(user, newUUID, 0, 0); + }); + it("Should not be able to upvote (only submission in category hidden)", async () => { + const category = "filler"; + await insertSegment(db, { category, videoID, userID: user.pubID, hidden: true }); // existing submission + await insertSegment(db, { category, videoID, UUID: newUUID }); // vote target submission + return assertVotes(user, newUUID, 1, 0); + }); + it("Should not be able to downvote (only submission in category hidden)", async () => { + const category = "filler"; + await insertSegment(db, { category, videoID, userID: user.pubID, hidden: true }); // existing submission + await insertSegment(db, { category, videoID, UUID: newUUID }); // vote target submission + return assertVotes(user, newUUID, 0, 0); }); +}); - it("Should be able to upvote a segment if the user has submitted that in category", (done) => { - const UUID = "testing-outro-skip-1"; - postVote(outroSubmitter, UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 1); - done(); - }) - .catch(err => done(err)); +describe("voteOnSponsorTime - ownSubmission", () => { + const user = genUser("vote", "own-submission"); + const uuids = multiGenProxy("uuid", "vote-ownsubmission"); + const videoIDs = multiGenProxy("video", "vote-ownsubmission"); + let randomUUID: string; + // before + before (async () => { + // locked segment + await insertSegment(db, { UUID: uuids["locked"], userID: user.pubID, locked: true }); + // nextCategory locked + await insertSegment(db, { videoID: videoIDs["next-locked"], UUID: uuids["next-locked"], userID: user.pubID }); + await insertLock(db, { videoID: videoIDs["next-locked"], actionType: "skip", category: "intro" }); + // locked, nextCategory locked + await insertSegment(db, { videoID: videoIDs["locked-next-locked"], UUID: uuids["locked-next-locked"], userID: user.pubID, locked: true }); + await insertLock(db, { videoID: videoIDs["locked-next-locked"], actionType: "skip", category: "outro" }); + }); + beforeEach(async () => { + randomUUID = genRandomValue("uuid", "vote-ownsubmission"); + await insertSegment(db, { UUID: randomUUID, userID: user.pubID }); + }); + // tests + it("should be able to completely downvote your own segment (unlocked)", () => { + return assertVotes(user, randomUUID, 0, -2); + }); + // category votes + it("Submitter's vote on category should work (segment unlocked, nextCatgeory unlocked, notVip)", () => { + return assertCategory(user, randomUUID, "outro", "outro"); + }); + it("Submitter's vote on category should not work (segment locked, nextCatgeory unlocked, notVip)", () => { + return assertCategory(user, uuids["locked"], "outro", "sponsor"); + }); + it("Submitter's vote on category should not work (segment unlocked, nextCatgeory locked, notVip)", () => { + return assertCategory(user, uuids["next-locked"], "intro", "sponsor"); + }); + it("Submitter's vote on category should not work (segment locked, nextCatgeory locked, notVip)", () => { + return assertCategory(user, uuids["locked-next-locked"], "outro", "sponsor"); }); +}); - it("Should be able to downvote a segment if the user has submitted that in category", (done) => { - const UUID = "testing-outro-skip-2"; - postVote(outroSubmitter, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, -1); - done(); - }) - .catch(err => done(err)); +describe("voteOnSponsorTime - dead-segments", () => { + const user = genUser("vote", "dead-segments"); + const deadUUID = genRandomValue("uuid", "vote-dead"); + // before + before (async () => { + await insertSegment(db, { userID: user.pubID }); + await insertSegment(db, { UUID: deadUUID, votes: -2 }); }); - - it("Should be able to upvote a segment if the user has submitted that in category but different action", (done) => { - const UUID = "testing-outro-mute-1"; - postVote(outroSubmitter, UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 1); - done(); - }) - .catch(err => done(err)); + // tests + it('normal user should not be able to upvote "dead" submission', () => { + return assertVotes(user, deadUUID, 1, -2, 403); }); - - it("Should be able to downvote a segment if the user has submitted that in category but different action", (done) => { - const UUID = "testing-outro-mute-2"; - postVote(outroSubmitter, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, -1); - done(); - }) - .catch(err => done(err)); + it('normal user should not be able to downvote "dead" submission', () => { + return assertVotes(user, deadUUID, 0, -2); }); +}); - it("Should not be able to upvote a segment if the user's only submission of that category was downvoted", (done) => { - const UUID = "testing-intro-skip-1"; - postVote(badIntroSubmitter, UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 0); - done(); - }) - .catch(err => done(err)); +describe("voteOnSponsorTime - locked", () => { + const user = genUser("vote", "locked"); + const lockedVideo = genRandomValue("video", "vote-locked"); + const lockedUUID = genRandomValue("uuid", "vote-locked"); + // before + before (async () => { + await insertSegment(db, { userID: user.pubID }); + await insertLock(db, { videoID: lockedVideo, actionType: "skip", category: "sponsor" }); + await insertSegment(db, { videoID: lockedVideo, UUID: lockedUUID }); }); - - it("Should not be able to downvote a segment if the user's only submission of that category was downvoted", (done) => { - const UUID = "testing-intro-skip-2"; - postVote(badIntroSubmitter, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 0); - done(); - }) - .catch(err => done(err)); + // tests + it("normal user should not be able to downvote on a segment on a locked video+category", () => { + return assertVotes(user, lockedUUID, 0, 0); }); - - it("Should not be able to upvote a segment if the user's only submission of that category was hidden", (done) => { - const UUID = "testing-interaction-skip-1"; - postVote(hiddenInteractionSubmitter, UUID, 1) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 0); - done(); - }) - .catch(err => done(err)); + it("normal user should be able to upvote on a segment on a locked video+category", () => { + return assertVotes(user, lockedUUID, 1, 1); }); - - it("Should not be able to downvote a segment if the user's only submission of that category was hidden", (done) => { - const UUID = "testing-interaction-skip-2"; - postVote(hiddenInteractionSubmitter, UUID, 0) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await getSegmentVotes(UUID); - assert.strictEqual(row.votes, 0); - done(); - }) - .catch(err => done(err)); + it("normal user should not be able to category vote on a segment on a locked video+category", () => { + return assertCategory(user, lockedUUID, "outro", "sponsor"); }); -}); +}); \ No newline at end of file diff --git a/test/utils/voteOnSponsorTime.ts b/test/utils/voteOnSponsorTime.ts new file mode 100644 index 00000000..3eb48969 --- /dev/null +++ b/test/utils/voteOnSponsorTime.ts @@ -0,0 +1,63 @@ +import { client } from "../utils/httpClient"; +import { db, privateDB } from "../../src/databases/databases"; +import assert from "assert"; +import { User } from "../utils/genUser"; + +// generic helpers +const endpoint = "/api/voteOnSponsorTime"; +export const postVote = (userID: string, UUID: string, type: number) => client({ + method: "POST", + url: endpoint, + params: { userID, UUID, type } +}); +export const postVoteCategory = (userID: string, UUID: string, category: string) => client({ + method: "POST", + url: endpoint, + params: { userID, UUID, category } +}); + +export const getSegmentVotes = (UUID: string) => db.prepare("get", `SELECT "votes" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); +export const assertVotes = async (user: User, UUID: string, voteType: number, voteResult: number, status = 200) => { + const voteRes = await postVote(user.privID, UUID, voteType); + assert.strictEqual(voteRes.status, status); + const voteInfo = await getSegmentVotes(UUID); + assert.strictEqual(Number(voteInfo.votes), voteResult, `expect votes value to be ${voteResult}`); +}; + +const getSegmentCategory = (UUID: string) => db.prepare("get", `SELECT "category" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); +export const assertCategory = async (user: User, UUID: string, targetCategory: string, expectedCategory: string, status = 200) => { + const categoryRes = await postVoteCategory(user.privID, UUID, targetCategory); + assert.strictEqual(categoryRes.status, status); + const categoryInfo = await getSegmentCategory(UUID); + assert.strictEqual(categoryInfo.category, expectedCategory); +}; + +const getPrivateVoteInfo = (UUID: string) => privateDB.prepare("all", `SELECT * FROM "votes" WHERE "UUID" = ?`, [UUID]); +export const assertPrivateVote = async (user: User, UUID: string, voteType: number) => { + // assert private vote info + const privateVoteInfo = await getPrivateVoteInfo(UUID); + assert.strictEqual(privateVoteInfo.length, 1); + assert.strictEqual(privateVoteInfo[0].normalUserID, user.pubID); + assert.strictEqual(privateVoteInfo[0].type, voteType, `expect vote type to be ${voteType}`); +}; + +const getCategoryVoteInfo = (UUID: string) => db.prepare("all", `SELECT * FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]); +export const assertCategoryVotes = async (UUID: string, category: string, votes: number) => { + type categoryVoteRow = { + UUID: string, + category: string, + votes: number + }; + const categoryVoteInfo: categoryVoteRow[] = await getCategoryVoteInfo(UUID); + const row = categoryVoteInfo.find((row) => row.category === category); + if (!row) throw new Error(`vote for category ${category} not found`); + assert.strictEqual(row.votes, votes); +}; + +const getSegment = (UUID: string) => db.prepare("get", `SELECT * FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); +export const assertSegmentStatus = async (user: User, UUID: string, vote: number, status: string, value: number) => { + const voteRes = await postVote(user.privID, UUID, vote); + assert.strictEqual(voteRes.status, 200); + const row = await getSegment(UUID); + assert.strictEqual(row[status], value); +}; \ No newline at end of file From ae4e6705c0fcd25fe3613c5ae1301a1cf02bb55d Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 17 Oct 2023 15:25:02 -0400 Subject: [PATCH 17/31] add undovote tests --- test/cases/voteOnSponsorTime.ts | 128 +++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 4 deletions(-) diff --git a/test/cases/voteOnSponsorTime.ts b/test/cases/voteOnSponsorTime.ts index 9c6d63fc..41f838a4 100644 --- a/test/cases/voteOnSponsorTime.ts +++ b/test/cases/voteOnSponsorTime.ts @@ -205,11 +205,131 @@ describe("voteOnSponsorTime - user with submissions", () => { await assertCategory(user, uuid, "outro", "sponsor"); // vote for change return assertCategoryVotes(uuid, "outro", 1); }); +}); + +describe("voteOnSponsorTime - changing votes", () => { + const user = genUser("vote", "changing-user"); + let randomUUID: string; + const uuids = multiGenProxy("uuid", "vote-changing"); + // before + before (async () => { + await insertSegment(db, { userID: user.pubID }); // user has submission + await insertChapter(db, "", { userID: user.pubID }); // user has chapter submission + await insertChapter(db, "chaptername", { UUID: uuids["chapter-undo"] }); + }); + beforeEach(async () => { + randomUUID = genRandomValue("uuid", "vote-changing"); + await insertSegment(db, { UUID: randomUUID }); + }); + // tests // undovote - it("Should be able to undo-vote a segment", async () => { + it("Should be able to undo-vote upvote", async () => { await assertVotes(user, randomUUID, 1, 1); // upvote return assertVotes(user, randomUUID, 20, 0); // undo-vote }); + it("Should be able to undo-vote downvote", async () => { + await assertVotes(user, randomUUID, 0, -1); // downvote + return assertVotes(user, randomUUID, 20, 0); // undo-vote + }); + it("Should be able to undo-vote killing vote", async () => { + const uuid = genRandomValue("uuid", "vote-changing"); + await insertSegment(db, { UUID: uuid, votes: -1 }); + await assertVotes(user, uuid, 0, -2); // downvote + return assertVotes(user, uuid, 20, -1); // undo-vote + }); + it("Should be able to override undo vote with upvote", async () => { + const uuid = genRandomValue("uuid", "vote-changing"); + await insertSegment(db, { UUID: uuid, votes: -1 }); + await assertVotes(user, uuid, 0, -2); // downvote + await assertVotes(user, uuid, 20, -1); // undo-vote + await assertVotes(user, uuid, 1, 0); // upvote + }); + it("Should be able to override undo vote with downvote", async () => { + const uuid = genRandomValue("uuid", "vote-changing"); + await insertSegment(db, { UUID: uuid, votes: -1 }); + await assertVotes(user, uuid, 0, -2); // downvote + await assertVotes(user, uuid, 20, -1); // undo-vote + await assertVotes(user, uuid, 0, -2); // downvote + }); + it("Should be able to continuously undo vote (SLOW)", async () => { + const times = Math.ceil(Math.random() * 10); + const finalVote = (times%2 == 0) ? 0 : 1; + for (let i = 0; i < times; i++) { + if (i%2 == 0) { + await assertVotes(user, randomUUID, 1, 1); // upvote + } else { + await assertVotes(user, randomUUID, 20, 0); // undo-vote + } + } + const votes = await getSegmentVotes(randomUUID); + assert.strictEqual(Number(votes.votes), finalVote); + }); + it("Should be able to undo malicious vote", async () => { + await assertVotes(user, uuids["chapter-undo"], 30, -2); // malicious downvote + return assertVotes(user, uuids["chapter-undo"], 20, 0); // undo-vote + }); +}); + +describe("voteOnSponsorTime - changing votes: VIP", () => { + const user = genUser("vote", "changing-vip"); + let randomUUID: string; + const uuids = multiGenProxy("uuid", "vote-changing-vip"); + // before + before (async () => { + await insertVipUser(db, user); + await insertChapter(db, "chaptername", { UUID: uuids["chapter-undo"] }); + }); + beforeEach(async () => { + randomUUID = genRandomValue("uuid", "vote-changing"); + await insertSegment(db, { UUID: randomUUID }); + }); + // tests + // undovote + it("Should be able to undo-vote upvote", async () => { + await assertVotes(user, randomUUID, 1, 1); // upvote + return assertVotes(user, randomUUID, 20, 0); // undo-vote + }); + it("Should be able to undo-vote downvote", async () => { + await assertVotes(user, randomUUID, 0, -2); // downvote + return assertVotes(user, randomUUID, 20, 0); // undo-vote + }); + it("Should be able to undo-vote killing vote", async () => { + const uuid = genRandomValue("uuid", "vote-changing"); + await insertSegment(db, { UUID: uuid, votes: -1 }); + await assertVotes(user, uuid, 0, -2); // downvote + return assertVotes(user, uuid, 20, -1); // undo-vote + }); + it("Should be able to override undo vote with upvote", async () => { + const uuid = genRandomValue("uuid", "vote-changing"); + await insertSegment(db, { UUID: uuid, votes: -1 }); + await assertVotes(user, uuid, 0, -2); // downvote + await assertVotes(user, uuid, 20, -1); // undo-vote + await assertVotes(user, uuid, 1, 0); // upvote + }); + it("Should be able to override undo vote with downvote", async () => { + const uuid = genRandomValue("uuid", "vote-changing"); + await insertSegment(db, { UUID: uuid, votes: -1 }); + await assertVotes(user, uuid, 0, -2); // downvote + await assertVotes(user, uuid, 20, -1); // undo-vote + await assertVotes(user, uuid, 0, -2); // downvote + }); + it("Should be able to continuously undo vote (SLOW)", async () => { + const times = Math.ceil(Math.random() * 10); + const finalVote = (times%2 == 0) ? 0 : 1; + for (let i = 0; i < times; i++) { + if (i%2 == 0) { + await assertVotes(user, randomUUID, 1, 1); // upvote + } else { + await assertVotes(user, randomUUID, 20, 0); // undo-vote + } + } + const votes = await getSegmentVotes(randomUUID); + assert.strictEqual(Number(votes.votes), finalVote); + }); + it("Should be able to undo malicious vote", async () => { + await assertVotes(user, uuids["chapter-undo"], 30, -2); // malicious downvote + return assertVotes(user, uuids["chapter-undo"], 20, 0); // undo-vote + }); }); describe("voteOnSponsorTime - duration change", () => { @@ -231,12 +351,12 @@ describe("voteOnSponsorTime - duration change", () => { await assertVotes(user, UUID, 0, -1); const hiddenSegments = await db.prepare("all", `SELECT "UUID" FROM "sponsorTimes" WHERE "videoID" = ? AND "hidden" = 1`, [videoID]); const expected = [{ - "UUID": "duration-changed-uuid-1", + "UUID": uuids["duration-changed-1"] }, { - "UUID": "duration-changed-uuid-2", + "UUID": uuids["duration-changed-2"], }]; assert.strictEqual(hiddenSegments.length, 2); - arrayDeepEquals(hiddenSegments, expected); + assert.ok(arrayDeepEquals(hiddenSegments, expected)); }); }); From c4161a793454fc4e926113f60462e8307ba75682 Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 17 Oct 2023 16:47:51 -0400 Subject: [PATCH 18/31] add category vote tests --- test/cases/voteOnSponsorTime.ts | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/cases/voteOnSponsorTime.ts b/test/cases/voteOnSponsorTime.ts index 41f838a4..b39f74f8 100644 --- a/test/cases/voteOnSponsorTime.ts +++ b/test/cases/voteOnSponsorTime.ts @@ -207,6 +207,55 @@ describe("voteOnSponsorTime - user with submissions", () => { }); }); +describe("voteOnSponsorTime - category votes", () => { + // before + let voteForChange: (targetCategory: string, expectedCategory: string, count: number) => Promise; + beforeEach (async () => { + const randomUUID = genRandomValue("uuid", "vote-category"); + // create segment + await insertSegment(db, { UUID: randomUUID, category: "sponsor" }); + voteForChange = async (targetCategory: string, expectedCategory: string, count: number) => { + // insert user for eligibility + const user = genUser("vote", "category-vote"); + await insertSegment(db, { userID: user.pubID }); + // vote + await assertCategory(user, randomUUID, targetCategory, expectedCategory); + await assertCategoryVotes(randomUUID, targetCategory, count); + }; + }); + // tests + it("Three votes should change the category", async () => { + await voteForChange("outro", "sponsor", 1); + await voteForChange("outro", "sponsor", 2); + await voteForChange("outro", "outro", 3); // commit to change + }); + it("2x2 votes should not change the category", async () => { + await voteForChange("outro", "sponsor", 1); + await voteForChange("outro", "sponsor", 2); + await voteForChange("intro", "sponsor", 1); + await voteForChange("intro", "sponsor", 2); + }); + it("Three eventual votes should change the category", async () => { + await voteForChange("outro", "sponsor", 1); + await voteForChange("outro", "sponsor", 2); + await voteForChange("intro", "sponsor", 1); + await voteForChange("intro", "sponsor", 2); + await voteForChange("outro", "outro", 3); // commit to change + }); + it("More votes should flip the category", async () => { + await voteForChange("outro", "sponsor", 1); + await voteForChange("outro", "sponsor", 2); + await voteForChange("outro", "outro", 3); // commit to change + await voteForChange("outro", "outro", 4); // assert + await voteForChange("intro", "outro", 1); + await voteForChange("intro", "outro", 2); + await voteForChange("intro", "outro", 3); + await voteForChange("intro", "outro", 4); // match + await voteForChange("intro", "outro", 5); + await voteForChange("intro", "intro", 6); // overcome by 2 + }); +}); + describe("voteOnSponsorTime - changing votes", () => { const user = genUser("vote", "changing-user"); let randomUUID: string; From f72958553188eee566bf5b362d83946eb1de969c Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 17 Oct 2023 18:45:37 -0400 Subject: [PATCH 19/31] use variables, 1 vote for categories --- test/cases/voteOnSponsorTime.ts | 48 ++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/test/cases/voteOnSponsorTime.ts b/test/cases/voteOnSponsorTime.ts index b39f74f8..fb949971 100644 --- a/test/cases/voteOnSponsorTime.ts +++ b/test/cases/voteOnSponsorTime.ts @@ -209,11 +209,12 @@ describe("voteOnSponsorTime - user with submissions", () => { describe("voteOnSponsorTime - category votes", () => { // before + const originalCategory = "sponsor"; let voteForChange: (targetCategory: string, expectedCategory: string, count: number) => Promise; beforeEach (async () => { const randomUUID = genRandomValue("uuid", "vote-category"); // create segment - await insertSegment(db, { UUID: randomUUID, category: "sponsor" }); + await insertSegment(db, { UUID: randomUUID, category: "sponsor", votes: 1 }); voteForChange = async (targetCategory: string, expectedCategory: string, count: number) => { // insert user for eligibility const user = genUser("vote", "category-vote"); @@ -225,34 +226,37 @@ describe("voteOnSponsorTime - category votes", () => { }); // tests it("Three votes should change the category", async () => { - await voteForChange("outro", "sponsor", 1); - await voteForChange("outro", "sponsor", 2); - await voteForChange("outro", "outro", 3); // commit to change + const targetCategory = "outro"; + await voteForChange(targetCategory, originalCategory, 1); + await voteForChange(targetCategory, originalCategory, 2); + await voteForChange(targetCategory, targetCategory, 3); // commit to change }); it("2x2 votes should not change the category", async () => { - await voteForChange("outro", "sponsor", 1); - await voteForChange("outro", "sponsor", 2); - await voteForChange("intro", "sponsor", 1); - await voteForChange("intro", "sponsor", 2); + await voteForChange("outro", originalCategory, 1); + await voteForChange("outro", originalCategory, 2); + await voteForChange("intro", originalCategory, 1); + await voteForChange("intro", originalCategory, 2); }); it("Three eventual votes should change the category", async () => { - await voteForChange("outro", "sponsor", 1); - await voteForChange("outro", "sponsor", 2); - await voteForChange("intro", "sponsor", 1); - await voteForChange("intro", "sponsor", 2); + await voteForChange("outro", originalCategory, 1); + await voteForChange("outro", originalCategory, 2); + await voteForChange("intro", originalCategory, 1); + await voteForChange("intro", originalCategory, 2); await voteForChange("outro", "outro", 3); // commit to change }); it("More votes should flip the category", async () => { - await voteForChange("outro", "sponsor", 1); - await voteForChange("outro", "sponsor", 2); - await voteForChange("outro", "outro", 3); // commit to change - await voteForChange("outro", "outro", 4); // assert - await voteForChange("intro", "outro", 1); - await voteForChange("intro", "outro", 2); - await voteForChange("intro", "outro", 3); - await voteForChange("intro", "outro", 4); // match - await voteForChange("intro", "outro", 5); - await voteForChange("intro", "intro", 6); // overcome by 2 + const firstTarget = "outro"; + const secondTarget = "intro"; + await voteForChange(firstTarget, originalCategory, 1); + await voteForChange(firstTarget, originalCategory, 2); + await voteForChange(firstTarget, firstTarget, 3); // commit to change + await voteForChange(firstTarget, firstTarget, 4); // assert + await voteForChange(secondTarget,firstTarget, 1); + await voteForChange(secondTarget, firstTarget, 2); + await voteForChange(secondTarget, firstTarget, 3); + await voteForChange(secondTarget, firstTarget, 4); // match + await voteForChange(secondTarget, firstTarget, 5); + await voteForChange(secondTarget, secondTarget, 6); // overcome by 2 }); }); From aa90b6a33505e212a39f5ead19961b4c3c024644 Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 24 Oct 2023 00:20:50 -0400 Subject: [PATCH 20/31] less votes for category re-flipping --- test/cases/voteOnSponsorTime.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/cases/voteOnSponsorTime.ts b/test/cases/voteOnSponsorTime.ts index fb949971..11f4a864 100644 --- a/test/cases/voteOnSponsorTime.ts +++ b/test/cases/voteOnSponsorTime.ts @@ -250,13 +250,11 @@ describe("voteOnSponsorTime - category votes", () => { await voteForChange(firstTarget, originalCategory, 1); await voteForChange(firstTarget, originalCategory, 2); await voteForChange(firstTarget, firstTarget, 3); // commit to change - await voteForChange(firstTarget, firstTarget, 4); // assert - await voteForChange(secondTarget,firstTarget, 1); + await voteForChange(secondTarget, firstTarget, 1); await voteForChange(secondTarget, firstTarget, 2); await voteForChange(secondTarget, firstTarget, 3); await voteForChange(secondTarget, firstTarget, 4); // match - await voteForChange(secondTarget, firstTarget, 5); - await voteForChange(secondTarget, secondTarget, 6); // overcome by 2 + await voteForChange(secondTarget, secondTarget, 5); // overcome by 2 }); }); From b29dea2f3e63e8c35ad29fa66468751393770aa4 Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 24 Oct 2023 15:41:37 -0400 Subject: [PATCH 21/31] convert more functions - getLockReason - getSavedTimeForUser - fix voteOnSponsorTime --- test/cases/getLockReason.ts | 284 ++++++++++++------------------ test/cases/getSavedTimeForUser.ts | 20 +-- test/cases/voteOnSponsorTime.ts | 29 ++- test/utils/genRandom.ts | 1 + test/utils/genUser.ts | 9 + test/utils/queryGen.ts | 9 +- test/utils/voteOnSponsorTime.ts | 8 +- 7 files changed, 163 insertions(+), 197 deletions(-) diff --git a/test/cases/getLockReason.ts b/test/cases/getLockReason.ts index 1b3c36ba..2d286640 100644 --- a/test/cases/getLockReason.ts +++ b/test/cases/getLockReason.ts @@ -1,39 +1,59 @@ -import { getHash } from "../../src/utils/getHash"; import { db } from "../../src/databases/databases"; import assert from "assert"; import { client } from "../utils/httpClient"; -import { partialDeepEquals } from "../utils/partialDeepEquals"; +import { UsernameUser, genUserUsername, emptyUsernameUser } from "../utils/genUser"; +import { deleteUsername, insertLock, insertUsernameUser, insertVipUser } from "../utils/queryGen"; +import { genRandomValue } from "../utils/genRandom"; +import { AxiosResponse } from "axios"; const endpoint = "/api/lockReason"; -const vipUserName1 = "getLockReason-vipUserName_1"; -const vipUserID1 = getHash("getLockReason-vipUserID_1"); -const vipUserName2 = "getLockReason-vipUserName_2"; -const vipUserID2 = getHash("getLockReason-vipUserID_2"); +const vipUser1 = genUserUsername("getLockReason", "vip1", "username-vip1"); +const vipUser2 = genUserUsername("getLockReason", "vip2", "username-vip2"); + +const videoID = genRandomValue("video", "getLockReason"); + +async function validateLocks( + query: Record, + expected: { category: string, locked?: boolean, reason: string, user: UsernameUser }[]) { + const res = await client.get(endpoint, { params: { videoID, ...query } }); + validateBody(res, expected); +} + +function validateBody(res: AxiosResponse, + expected: { category: string, locked?: boolean, reason: string, user: UsernameUser }[]) { + const expectedArray = expected.map(({ category, locked, reason, user }) => ({ + category, + locked: Number(locked ?? true), + reason, + userID: user.pubID, + userName: user.username + })); + assert.strictEqual(res.status, 200); + assert.deepStrictEqual(res.data, expectedArray); +} describe("getLockReason", () => { before(async () => { - const insertVipUserQuery = 'INSERT INTO "vipUsers" ("userID") VALUES (?)'; - await db.prepare("run", insertVipUserQuery, [vipUserID1]); - await db.prepare("run", insertVipUserQuery, [vipUserID2]); - - const insertVipUserNameQuery = 'INSERT INTO "userNames" ("userID", "userName") VALUES (?, ?)'; - await db.prepare("run", insertVipUserNameQuery, [vipUserID1, vipUserName1]); - await db.prepare("run", insertVipUserNameQuery, [vipUserID2, vipUserName2]); - - const insertLockCategoryQuery = 'INSERT INTO "lockCategories" ("userID", "videoID", "actionType", "category", "reason") VALUES (?, ?, ?, ?, ?)'; - await db.prepare("run", insertLockCategoryQuery, [vipUserID1, "getLockReason", "skip", "sponsor", "sponsor-reason"]); - await db.prepare("run", insertLockCategoryQuery, [vipUserID1, "getLockReason", "skip", "interaction", "interaction-reason"]); - await db.prepare("run", insertLockCategoryQuery, [vipUserID1, "getLockReason", "skip", "preview", "preview-reason"]); - await db.prepare("run", insertLockCategoryQuery, [vipUserID1, "getLockReason", "mute", "music_offtopic", "nonmusic-reason"]); - await db.prepare("run", insertLockCategoryQuery, [vipUserID2, "getLockReason", "mute", "outro", "outro-reason"]); - await db.prepare("run", insertLockCategoryQuery, [vipUserID2, "getLockReason", "full", "selfpromo", "selfpromo-reason"]); + // add VIP users + await insertVipUser(db, vipUser1); + await insertVipUser(db, vipUser2); + // add usernames + await insertUsernameUser(db, vipUser1); + await insertUsernameUser(db, vipUser2); + // user 1 + await insertLock(db, { videoID, userID: vipUser1.pubID, category: "sponsor", reason: "sponsor-reason" }); + await insertLock(db, { videoID, userID: vipUser1.pubID, category: "interaction", reason: "interaction-reason" }); + await insertLock(db, { videoID, userID: vipUser1.pubID, category: "preview", reason: "preview-reason" }); + await insertLock(db, { videoID, userID: vipUser1.pubID, category: "music_offtopic", reason: "nonmusic-reason", actionType: "mute" }); + // user 2 + await insertLock(db, { videoID, userID: vipUser2.pubID, category: "outro", reason: "outro-reason", actionType: "mute" }); + await insertLock(db, { videoID, userID: vipUser2.pubID, category: "selfpromo", reason: "selfpromo-reason", actionType: "full" }); }); after(async () => { - const deleteUserNameQuery = 'DELETE FROM "userNames" WHERE "userID" = ? AND "userName" = ?'; - await db.prepare("run", deleteUserNameQuery, [vipUserID1, vipUserName1]); - await db.prepare("run", deleteUserNameQuery, [vipUserID2, vipUserName2]); + await deleteUsername(db, vipUser1.pubID); + await deleteUsername(db, vipUser2.pubID,); }); it("Should update the database version when starting the application", async () => { @@ -42,166 +62,84 @@ describe("getLockReason", () => { else return `Version isn't greater than 20. Version is ${version}`; }); - it("Should be able to get single reason", (done) => { - client.get(endpoint, { params: { videoID: "getLockReason", category: "sponsor" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [ - { category: "sponsor", locked: 1, reason: "sponsor-reason", userID: vipUserID1, userName: vipUserName1 } - ]; - assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get with actionTypes array", (done) => { - client.get(endpoint, { params: { videoID: "getLockReason", category: "selfpromo", actionTypes: '["full"]' } }) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [ - { category: "selfpromo", locked: 1, reason: "selfpromo-reason", userID: vipUserID2, userName: vipUserName2 } - ]; - assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get with actionType", (done) => { - client.get(endpoint, { params: { videoID: "getLockReason", category: "selfpromo", actionType: "full" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [ - { category: "selfpromo", locked: 1, reason: "selfpromo-reason", userID: vipUserID2, userName: vipUserName2 } - ]; - assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get with actionType array", (done) => { - client.get(endpoint, { params: { videoID: "getLockReason", category: "selfpromo", actionType: ["full"] } }) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [ - { category: "selfpromo", locked: 1, reason: "selfpromo-reason", userID: vipUserID2, userName: vipUserName2 } - ]; - assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get empty locks", (done) => { - client.get(endpoint, { params: { videoID: "getLockReason", category: "intro" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [ - { category: "intro", locked: 0, reason: "", userID: "", userName: "" } - ]; - assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); - }); - - it("should get multiple locks with array", (done) => { - client.get(endpoint, { params: { videoID: "getLockReason", categories: `["intro","sponsor","outro"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [ - { category: "intro", locked: 0, reason: "", userID: "", userName: "" }, - { category: "sponsor", locked: 1, reason: "sponsor-reason", userID: vipUserID1, userName: vipUserName1 }, - { category: "outro", locked: 1, reason: "outro-reason", userID: vipUserID2, userName: vipUserName2 } - ]; - assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); - }); - - it("should get multiple locks with repeated category", (done) => { - client.get(`${endpoint}?videoID=getLockReason&category=interaction&category=music_offtopic&category=intro`) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [ - { category: "interaction", locked: 1, reason: "interaction-reason", userID: vipUserID1, userName: vipUserName1 }, - { category: "music_offtopic", locked: 1, reason: "nonmusic-reason", userID: vipUserID1, userName: vipUserName1 }, - { category: "intro", locked: 0, reason: "", userID: "", userName: "" } - ]; - assert.deepStrictEqual(res.data, expected); - done(); - }) - .catch(err => done(err)); - }); - - it("should return all skip + mute categories if none specified", (done) => { - client.get(endpoint, { params: { videoID: "getLockReason" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const expected = [ - { category: "sponsor", locked: 1, reason: "sponsor-reason", userID: vipUserID1, userName: vipUserName1 }, - { category: "selfpromo", locked: 0, reason: "", userID: "", userName: "" }, - { category: "exclusive_access", locked: 0, reason: "", userID: "", userName: "" }, - { category: "interaction", locked: 1, reason: "interaction-reason", userID: vipUserID1, userName: vipUserName1 }, - { category: "intro", locked: 0, reason: "", userID: "", userName: "" }, - { category: "outro", locked: 1, reason: "outro-reason", userID: vipUserID2, userName: vipUserName2 }, - { category: "preview", locked: 1, reason: "preview-reason", userID: vipUserID1, userName: vipUserName1 }, - { category: "music_offtopic", locked: 1, reason: "nonmusic-reason", userID: vipUserID1, userName: vipUserName1 }, - { category: "filler", locked: 0, reason: "", userID: "", userName: "" }, - ]; - partialDeepEquals(res.data, expected, false); - done(); - }) - .catch(err => done(err)); + it("Should be able to get single reason", () => + validateLocks( + { category: "sponsor" }, + [{ category: "sponsor", reason: "sponsor-reason", user: vipUser1 }]) + ); + + it("Should be able to get with actionTypes array", () => + validateLocks( + { category: "selfpromo", actionTypes: '["full"]' }, + [{ category: "selfpromo", reason: "selfpromo-reason", user: vipUser2 }]) + ); + + it("Should be able to get with actionType", () => + validateLocks( + { category: "selfpromo", actionType: "full" }, + [{ category: "selfpromo", reason: "selfpromo-reason", user: vipUser2 }]) + ); + + it("Should be able to get with actionType array", () => + validateLocks( + { category: "selfpromo", actionType: ["full"] }, + [{ category: "selfpromo", reason: "selfpromo-reason", user: vipUser2 }]) + ); + + it("Should be able to get empty locks", () => + validateLocks( + { category: "intro" }, + [{ category: "intro", locked: false, reason: "", user: emptyUsernameUser }] + ) + ); + + it("should get multiple locks with array", () => + validateLocks( + { categories: '["intro","sponsor","outro"]' }, + [ + { category: "intro", locked: false, reason: "", user: emptyUsernameUser }, + { category: "sponsor", reason: "sponsor-reason", user: vipUser1 }, + { category: "outro",reason: "outro-reason", user: vipUser2 } + ] + ) + ); + + it("should get multiple locks with repeated category", async () => { + const categoryString = ["interaction", "music_offtopic", "intro"].join("&category="); + const queryUrl = `${endpoint}?videoID=${videoID}&category=${categoryString}`; + const categoryRes = await client.get(queryUrl); + const expected = [ + { category: "interaction", reason: "interaction-reason", user: vipUser1 }, + { category: "music_offtopic", reason: "nonmusic-reason", user: vipUser1 }, + { category: "intro", locked: false, reason: "", user: emptyUsernameUser } + ]; + validateBody(categoryRes, expected); }); }); describe("getLockReason 400", () => { - it("Should return 400 with missing videoID", (done) => { - client.get(endpoint) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should return 400 with missing videoID", async () => { + const res = await client.get(endpoint); + assert.strictEqual(res.status, 400); }); - it("Should return 400 with invalid actionTypes ", (done) => { - client.get(endpoint, { params: { videoID: "valid-videoid", actionTypes: 3 } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should return 400 with invalid actionTypes ", async () => { + const res = await client.get(endpoint, { params: { videoID: "valid-videoid", actionTypes: 3 } }); + assert.strictEqual(res.status, 400); }); - it("Should return 400 with invalid actionTypes JSON ", (done) => { - client.get(endpoint, { params: { videoID: "valid-videoid", actionTypes: "{3}" } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should return 400 with invalid actionTypes JSON ", async () => { + const res = await client.get(endpoint, { params: { videoID: "valid-videoid", actionTypes: "{3}" } }); + assert.strictEqual(res.status, 400); }); - it("Should return 400 with invalid categories", (done) => { - client.get(endpoint, { params: { videoID: "valid-videoid", categories: 3 } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should return 400 with invalid categories", async () => { + const res = await client.get(endpoint, { params: { videoID: "valid-videoid", categories: 3 } }); + assert.strictEqual(res.status, 400); }); - it("Should return 400 with invalid categories JSON", (done) => { - client.get(endpoint, { params: { videoID: "valid-videoid", categories: "{3}" } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should return 400 with invalid categories JSON", async () => { + const res = await client.get(endpoint, { params: { videoID: "valid-videoid", categories: "{3}" } }); + assert.strictEqual(res.status, 400); }); }); diff --git a/test/cases/getSavedTimeForUser.ts b/test/cases/getSavedTimeForUser.ts index b7e538a9..26b22be4 100644 --- a/test/cases/getSavedTimeForUser.ts +++ b/test/cases/getSavedTimeForUser.ts @@ -1,29 +1,27 @@ import { db } from "../../src/databases/databases"; -import { getHash } from "../../src/utils/getHash"; import { deepStrictEqual } from "assert"; import { client } from "../utils/httpClient"; import assert from "assert"; +import { insertSegment } from "../utils/segmentQueryGen"; +import { genUser } from "../utils/genUser"; // helpers const endpoint = "/api/getSavedTimeForUser"; -const getSavedTimeForUser = (userID: string) => client({ +const getSavedTimeForUser = (privID: string) => client({ url: endpoint, - params: { userID } + params: { userID: privID } }); describe("getSavedTimeForUser", () => { - const user1 = "getSavedTimeForUser1"; - const user2 = "getSavedTimeforUser2"; + const user1 = genUser("getSavedTimeForUser", "user1"); + const user2 = genUser("getSavedTimeForUser", "user2"); const [ start, end, views ] = [1, 11, 50]; before(async () => { - const startOfQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", "views", "shadowHidden") VALUES'; - await db.prepare("run", `${startOfQuery}(?, ?, ?, ?, ?, ?, ?, ?, ?)`, - ["getSavedTimeForUser", start, end, 2, "getSavedTimeUUID0", getHash(user1), 0, views, 0]); - return; + await insertSegment(db, { startTime: start, endTime: end, views, userID: user1.pubID }); }); it("Should be able to get a saved time", (done) => { - getSavedTimeForUser(user1) + getSavedTimeForUser(user1.privID) .then(res => { // (end-start)*minute * views const savedMinutes = ((end-start)/60) * views; @@ -36,7 +34,7 @@ describe("getSavedTimeForUser", () => { .catch((err) => done(err)); }); it("Should return 404 if no submissions", (done) => { - getSavedTimeForUser(user2) + getSavedTimeForUser(user2.privID) .then(res => { assert.strictEqual(res.status, 404); done(); diff --git a/test/cases/voteOnSponsorTime.ts b/test/cases/voteOnSponsorTime.ts index 11f4a864..a86c4296 100644 --- a/test/cases/voteOnSponsorTime.ts +++ b/test/cases/voteOnSponsorTime.ts @@ -211,10 +211,20 @@ describe("voteOnSponsorTime - category votes", () => { // before const originalCategory = "sponsor"; let voteForChange: (targetCategory: string, expectedCategory: string, count: number) => Promise; + let voteNoConfirm: (targetCategory: string, count: number) => Promise; beforeEach (async () => { const randomUUID = genRandomValue("uuid", "vote-category"); // create segment await insertSegment(db, { UUID: randomUUID, category: "sponsor", votes: 1 }); + voteNoConfirm = async (targetCategory: string, count: number) => { + // insert user for eligibility + const user = genUser("vote", "category-vote"); + await insertSegment(db, { userID: user.pubID }); + // vote + const categoryRes = await postVoteCategory(user.privID, randomUUID, targetCategory); + assert.strictEqual(categoryRes.status, 200); + await assertCategoryVotes(randomUUID, targetCategory, count); + }; voteForChange = async (targetCategory: string, expectedCategory: string, count: number) => { // insert user for eligibility const user = genUser("vote", "category-vote"); @@ -247,14 +257,17 @@ describe("voteOnSponsorTime - category votes", () => { it("More votes should flip the category", async () => { const firstTarget = "outro"; const secondTarget = "intro"; - await voteForChange(firstTarget, originalCategory, 1); - await voteForChange(firstTarget, originalCategory, 2); - await voteForChange(firstTarget, firstTarget, 3); // commit to change - await voteForChange(secondTarget, firstTarget, 1); - await voteForChange(secondTarget, firstTarget, 2); - await voteForChange(secondTarget, firstTarget, 3); - await voteForChange(secondTarget, firstTarget, 4); // match - await voteForChange(secondTarget, secondTarget, 5); // overcome by 2 + await voteNoConfirm(firstTarget, 1); + await voteNoConfirm(firstTarget, 2); + await voteNoConfirm(firstTarget, 3); // commit to change + await voteForChange(firstTarget, firstTarget, 4); // vote and assert change + await voteNoConfirm(secondTarget, 1); + await voteNoConfirm(secondTarget, 2); + await voteNoConfirm(secondTarget, 3); + await voteNoConfirm(secondTarget, 4); // match + await voteNoConfirm(secondTarget, 5); + await voteNoConfirm(secondTarget, 6); + await voteForChange(secondTarget, secondTarget, 7); // overcome by 3 and assert }); }); diff --git a/test/utils/genRandom.ts b/test/utils/genRandom.ts index 3b436e90..f828cd73 100644 --- a/test/utils/genRandom.ts +++ b/test/utils/genRandom.ts @@ -1,6 +1,7 @@ import crypto from "crypto"; export const genRandom = (chars=8): string => crypto.pseudoRandomBytes(chars/2).toString("hex"); +export const genRandomNumber = (min: number, max: number) => Math.floor(Math.random() * (max - min) + min); export const genRandomValue = (prefix: string, identifier: string, chars=8): string => `${prefix}-${identifier}-${genRandom(chars)}`; export const multiGenProxy = (prefix: string, identifier: string) => new Proxy({}, { diff --git a/test/utils/genUser.ts b/test/utils/genUser.ts index 032da70c..978dae9b 100644 --- a/test/utils/genUser.ts +++ b/test/utils/genUser.ts @@ -16,12 +16,21 @@ export interface UsernameUser extends User { } export type usernameUserArray = Record +export const emptyUser: User = { privID: "" as UserID, pubID: "" as HashedUserID, info: {} }; +export const emptyUsernameUser: UsernameUser = { ...emptyUser, username: "" }; + export const genUser = (fnname: string, testcase: string, info: info = {}): User => { const privID = `${fnname}-${testcase}-${genRandom(2)}` as UserID; const pubID = getHash(privID); return { privID, pubID, info }; }; +export const genUserUsername = (fnname: string, testcase: string, username: string, info: info = {}): UsernameUser => { + const user = genUser(fnname, testcase, info) as UsernameUser; + user.username = username; + return user; +}; + export const genAnonUser = (info: info = {}): User => { const privID = `user-${genRandom()}` as UserID; const pubID = getHash(privID); diff --git a/test/utils/queryGen.ts b/test/utils/queryGen.ts index 17e0939e..66f0ec0f 100644 --- a/test/utils/queryGen.ts +++ b/test/utils/queryGen.ts @@ -1,6 +1,6 @@ import { IDatabase } from "../../src/databases/IDatabase"; import { HashedUserID } from "../../src/types/user.model"; -import { User, userArray, usernameUserArray } from "./genUser"; +import { User, UsernameUser, userArray, usernameUserArray } from "./genUser"; import { Feature } from "../../src/types/user.model"; import { ActionType, Category, Service, VideoIDHash } from "../../src/types/segments.model"; import { genRandomValue } from "./genRandom"; @@ -38,10 +38,17 @@ export const insertUsername = async (db: IDatabase, userID: HashedUserID, userNa const lockedValue = Number(locked); await db.prepare("run", query, [userID, userName, lockedValue]); }; +export const insertUsernameUser = async (db: IDatabase, user: UsernameUser, lock = false) => { + await insertUsername(db, user.pubID, user.username, lock); +}; export const insertUsernameBulk = async (db: IDatabase, users: usernameUserArray) => { for (const user of Object.values(users)) await insertUsername(db, user.pubID, user.username, false); }; +export const deleteUsername = async (db: IDatabase, userID: HashedUserID) => { + const query = 'DELETE FROM "userNames" WHERE "userID" = ?'; + await db.prepare("run", query, [userID]); +}; // videoInfo export const insertVideoInfo = async (db: IDatabase, videoID: string, channelID: string, title = "", published = 0) => { diff --git a/test/utils/voteOnSponsorTime.ts b/test/utils/voteOnSponsorTime.ts index 3eb48969..e4055241 100644 --- a/test/utils/voteOnSponsorTime.ts +++ b/test/utils/voteOnSponsorTime.ts @@ -41,16 +41,16 @@ export const assertPrivateVote = async (user: User, UUID: string, voteType: numb assert.strictEqual(privateVoteInfo[0].type, voteType, `expect vote type to be ${voteType}`); }; -const getCategoryVoteInfo = (UUID: string) => db.prepare("all", `SELECT * FROM "categoryVotes" WHERE "UUID" = ?`, [UUID]); +const getCategoryVoteInfo = (UUID: string, category: string) => db.prepare("get", `SELECT * FROM "categoryVotes" WHERE "UUID" = ? AND "category" = ?`, [UUID, category]); export const assertCategoryVotes = async (UUID: string, category: string, votes: number) => { type categoryVoteRow = { UUID: string, category: string, votes: number }; - const categoryVoteInfo: categoryVoteRow[] = await getCategoryVoteInfo(UUID); - const row = categoryVoteInfo.find((row) => row.category === category); - if (!row) throw new Error(`vote for category ${category} not found`); + const categoryVoteInfo: categoryVoteRow = await getCategoryVoteInfo(UUID, category); + const row = categoryVoteInfo; + if (!row) throw new Error(`categoryVotes row not found for UUID ${UUID} and category ${category} at count ${votes}`); assert.strictEqual(row.votes, votes); }; From bc191eadfafb094c6908d5f540e3311c4571dd78 Mon Sep 17 00:00:00 2001 From: Michael C Date: Thu, 2 Nov 2023 21:24:45 -0400 Subject: [PATCH 22/31] add getUserInfoFree --- test/cases/getUserInfoFree.ts | 73 ++++++++++++++--------------------- test/utils/segmentQueryGen.ts | 4 +- 2 files changed, 31 insertions(+), 46 deletions(-) diff --git a/test/cases/getUserInfoFree.ts b/test/cases/getUserInfoFree.ts index 6556eb25..b8f5a765 100644 --- a/test/cases/getUserInfoFree.ts +++ b/test/cases/getUserInfoFree.ts @@ -1,65 +1,48 @@ import { db } from "../../src/databases/databases"; -import { getHash } from "../../src/utils/getHash"; import assert from "assert"; import { client } from "../utils/httpClient"; +import { genAnonUser } from "../utils/genUser"; +import { insertSegment } from "../utils/segmentQueryGen"; +import { insertVip } from "../utils/queryGen"; describe("getUserInfo Free Chapters", () => { const endpoint = "/api/userInfo"; - - const newQualifyUserID = "getUserInfo-Free-newQualify"; - const vipQualifyUserID = "getUserInfo-Free-VIP"; - const repQualifyUserID = "getUserInfo-Free-RepQualify"; - const oldQualifyUserID = "getUserInfo-Free-OldQualify"; const postOldQualify = 1600000000000; - before(async () => { - const sponsorTimesQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "actionType", "reputation", "shadowHidden") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; - await db.prepare("run", sponsorTimesQuery, ["getUserInfoFree", 1, 2, 0, "uuid-guif-0", getHash(repQualifyUserID), postOldQualify, 0, "sponsor", "skip", 20, 0]); - await db.prepare("run", sponsorTimesQuery, ["getUserInfoFree", 1, 2, 0, "uuid-guif-1", getHash(oldQualifyUserID), 0, 0, "sponsor", "skip", 0, 0]); // submit at epoch - await db.prepare("run", sponsorTimesQuery, ["getUserInfoFree", 1, 2, 0, "uuid-guif-2", getHash(newQualifyUserID), postOldQualify, 0, "sponsor", "skip", 0, 0]); - - await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [getHash(vipQualifyUserID)]); - }); - const getUserInfo = (userID: string) => client.get(endpoint, { params: { userID, value: "freeChaptersAccess" } }); - - it("Should get free access under new rule (newQualify)", (done) => { - getUserInfo(newQualifyUserID) + const assertChapterAccess = (pubID: string) => + getUserInfo(pubID) .then(res => { assert.strictEqual(res.status, 200); assert.strictEqual(res.data.freeChaptersAccess, true); - done(); - }) - .catch(err => done(err)); + }); + + it("Should get free access under new rule (newQualify)", async () => { + const user = genAnonUser(); + await insertSegment(db, { userID: user.pubID, timeSubmitted: postOldQualify }); + return assertChapterAccess(user.pubID); }); - it("Should get free access (VIP)", (done) => { - getUserInfo(vipQualifyUserID) - .then(res => { - assert.strictEqual(res.status, 200); - assert.strictEqual(res.data.freeChaptersAccess, true); - done(); - }) - .catch(err => done(err)); + it("Should get free access (VIP)", async () => { + const user = genAnonUser(); + await insertVip(db, user.pubID); + return assertChapterAccess(user.pubID); }); - it("Should get free access (rep)", (done) => { - getUserInfo(repQualifyUserID) - .then(res => { - assert.strictEqual(res.status, 200); - assert.strictEqual(res.data.freeChaptersAccess, true); - done(); - }) - .catch(err => done(err)); + it("Should get free access (rep)", async () => { + const user = genAnonUser(); + await insertSegment(db, { userID: user.pubID, reputation: 20 }); + return assertChapterAccess(user.pubID); }); - it("Should get free access (old)", (done) => { - getUserInfo(oldQualifyUserID) - .then(res => { - assert.strictEqual(res.status, 200); - assert.strictEqual(res.data.freeChaptersAccess, true); - done(); - }) - .catch(err => done(err)); + it("Should get free access (old)", async () => { + const user = genAnonUser(); + await insertSegment(db, { userID: user.pubID, timeSubmitted: 0 }); + return assertChapterAccess(user.pubID); + }); + + it("Everyone should get free access", async() => { + const user = genAnonUser(); + return assertChapterAccess(user.pubID); }); }); diff --git a/test/utils/segmentQueryGen.ts b/test/utils/segmentQueryGen.ts index a46b31cf..335fd64b 100644 --- a/test/utils/segmentQueryGen.ts +++ b/test/utils/segmentQueryGen.ts @@ -24,6 +24,7 @@ interface insertSegmentParams extends baseParams { actionType?: string, videoDuration?: number, hidden?: boolean | number, + reputation?: number, shadowHidden?: boolean | number, hashedVideoID?: VideoIDHash, userAgent?: string, @@ -44,6 +45,7 @@ const defaultSegmentParams: insertSegmentParams = { service: Service.YouTube, videoDuration: 0, hidden: false, + reputation: 0, shadowHidden: false, hashedVideoID: "" as VideoIDHash, userAgent: "", @@ -58,7 +60,7 @@ const generateDefaults = (identifier: string) => ({ }); export const insertSegment = async(db: IDatabase, overrides: insertSegmentParams = {}, identifier?: string) => { - const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "shadowHidden", "hashedVideoID", "userAgent", "description") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; + const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "reputation", "shadowHidden", "hashedVideoID", "userAgent", "description") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; // generate defaults identifier = identifier ?? genRandom(); const defaults = generateDefaults(identifier); From c664d83c9e9ab8b2cf08555546371dc164eace02 Mon Sep 17 00:00:00 2001 From: Michael C Date: Sat, 4 Nov 2023 02:28:04 -0400 Subject: [PATCH 23/31] update getSearchSegment, fix parseParams - update getSearchSegments to use helpers - fix long-standing bug in parseParams - update userInfoFree --- src/utils/parseParams.ts | 16 +- test/cases/getSearchSegments.ts | 940 +++++++++++++------------------- test/cases/getUserInfoFree.ts | 13 +- test/utils/segmentQueryGen.ts | 2 +- 4 files changed, 405 insertions(+), 566 deletions(-) diff --git a/src/utils/parseParams.ts b/src/utils/parseParams.ts index e92d6dcc..ebabe12c 100644 --- a/src/utils/parseParams.ts +++ b/src/utils/parseParams.ts @@ -12,9 +12,23 @@ const syntaxErrorWrapper = (fn: fn, req: Request, fallback: any) => { } }; +/** + * This function acts as a parser for singular and plural query parameters + * either as arrays, strings as arrays or singular values with optional fallback + * The priority is to parse the plural parameter natively, then with JSON + * then the singular parameter as an array, then as a single value + * and then finally fall back to the fallback value + * @param req Axios Request object + * @param fallback fallback value in case all parsing fail + * @param param Name of singular parameter + * @param paramPlural Name of plural parameter + * @returns Array of values + */ const getQueryList = (req: Request, fallback: T[], param: string, paramPlural: string): string[] | T[] => req.query[paramPlural] - ? JSON.parse(req.query[paramPlural] as string) + ? Array.isArray(req.query[paramPlural]) + ? req.query[paramPlural] + : JSON.parse(req.query[paramPlural] as string) : req.query[param] ? Array.isArray(req.query[param]) ? req.query[param] diff --git a/test/cases/getSearchSegments.ts b/test/cases/getSearchSegments.ts index 6dbef4f5..38df5d27 100644 --- a/test/cases/getSearchSegments.ts +++ b/test/cases/getSearchSegments.ts @@ -1,564 +1,390 @@ import { db } from "../../src/databases/databases"; import { client } from "../utils/httpClient"; import assert from "assert"; +import { insertSegment, insertSegmentParams } from "../utils/segmentQueryGen"; +import { AxiosResponse } from "axios"; +import { multiGenProxy } from "../utils/genRandom"; +import { genAnonUser } from "../utils/genUser"; + +const endpoint = "/api/searchSegments"; +const videoIDs = multiGenProxy("video", "getSearchSegments"); + +const getSearchSegments = (videoID: string, params: Record) => client.get(endpoint, { params: { videoID, ...params } }); +const assertSegments = (uuids: string[], actual: AxiosResponse, count?: number) => { + assert.strictEqual(actual.status, 200); + const segments = actual.data.segments; + const segmentCount = count ?? uuids.length; + assert.strictEqual(actual.data.segmentCount, segmentCount); + assert.strictEqual(actual.data.page, 0); + for (const i in uuids) { + assert.strictEqual(segments[i].UUID, uuids[i]); + } +}; + +describe("getSearchSegments - variedVideo", () => { + const videoID = videoIDs["variedVideo"]; + const getVideoSearch = (params: Record) => getSearchSegments(videoID, params); + before(async () => { + await insertSegment(db, { videoID, votes: 2, timeSubmitted: 1, UUID: "search-normal", category: "sponsor" }); + await insertSegment(db, { videoID, votes: -2, timeSubmitted: 2, UUID: "search-downvote", category: "selfpromo" }); + await insertSegment(db, { videoID, votes: 1, timeSubmitted: 3, UUID: "search-locked", category: "interaction", locked: true }); + await insertSegment(db, { videoID, votes: 1, timeSubmitted: 4, UUID: "search-hidden", category: "sponsor", hidden: true }); + await insertSegment(db, { videoID, votes: 1, timeSubmitted: 5, UUID: "search-shadowhidden", category: "sponsor", shadowHidden: true }); + }); + + it("Should be able to filter by lock status", async () => { + const res = await getVideoSearch({ locked: false }); + const expected = [ + "search-normal", + "search-downvote", + "search-hidden", + "search-shadowhidden" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter by hide status", async () => { + const res = await getVideoSearch({ hidden: false }); + const expected = [ + "search-normal", + "search-downvote", + "search-locked", + "search-shadowhidden" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter by ignored status", async () => { + const res = await getVideoSearch({ ignored: false }); + const expected = [ + "search-normal", + "search-locked" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to show all segments under searchTest0", async () => { + const res = await getVideoSearch({}); + const expected = [ + "search-normal", + "search-downvote", + "search-locked", + "search-hidden", + "search-shadowhidden" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter by category", async () => { + const res = await getVideoSearch({ category: "selfpromo" }); + const expected = [ + "search-downvote" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter by category array", async () => { + const res = await getVideoSearch({ category: ["selfpromo"] }); + const expected = [ + "search-downvote" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter by category with categories array string", async () => { + const res = await getVideoSearch({ categories: `["selfpromo"]` }); + const expected = [ + "search-downvote" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter by category with categories array", async () => { + const res = await getVideoSearch({ categories: ["selfpromo"] }); + const expected = [ + "search-downvote" + ]; + return assertSegments(expected, res); + }); +}); + +describe("getSearchSegments - views", () => { + const videoID = videoIDs["views"]; + const getVideoSearch = (params: Record) => getSearchSegments(videoID, params); + before(async () => { + await insertSegment(db, { videoID, timeSubmitted: 1, views: 5, UUID: "search-lowview" }); + await insertSegment(db, { videoID, timeSubmitted: 2, views: 50, UUID: "search-highview" }); + }); + + it("Should be able to filter segments by min views", async () => { + const res = await getVideoSearch({ minViews: 6 }); + const expected = [ + "search-highview" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter segments by max views", async () => { + const res = await getVideoSearch({ maxViews: 10 }); + const expected = [ + "search-lowview" + ]; + return assertSegments(expected, res); + }); -describe("getSearchSegments", () => { - const endpoint = "/api/searchSegments"; + it("Should be able to filter segments by min and max views", async () => { + const res = await getVideoSearch({ minViews: 10, maxViews: 100 }); + const expected = [ + "search-highview" + ]; + return assertSegments(expected, res); + }); +}); + +describe("getSearchSegments - votes", () => { + const videoID = videoIDs["votes"]; + const getVideoSearch = (params: Record) => getSearchSegments(videoID, params); before(async () => { - const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "views", "locked", "hidden", "shadowHidden", "timeSubmitted", "UUID", "userID", "category", "actionType") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; - await db.prepare("run", query, ["searchTest0", 0, 1, 2, 0, 0, 0, 0, 1, "search-normal", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest0", 0, 2, -2, 0, 0, 0, 0, 2, "search-downvote", "searchTestUser", "selfpromo", "skip",]); - await db.prepare("run", query, ["searchTest0", 0, 3, 1, 0, 1, 0, 0, 3, "search-locked", "searchTestUser", "interaction", "skip"]); - await db.prepare("run", query, ["searchTest0", 0, 4, 1, 0, 0, 1, 0, 4, "search-hidden", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest0", 0, 5, 1, 0, 0, 0, 1, 5, "search-shadowhidden", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest1", 1, 2, 1, 5, 0, 0, 0, 6, "search-lowview", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest1", 1, 3, 1, 50, 0, 0, 0, 7, "search-highview", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest2", 1, 4, -1, 0, 0, 0, 0, 8, "search-lowvote", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest2", 2, 3, 0, 0, 0, 0, 0, 9, "search-zerovote", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest2", 2, 4, 50, 0, 0, 0, 0, 10, "search-highvote", "searchTestUser", "sponsor", "skip"]); - // page - await db.prepare("run", query, ["searchTest4", 3, 4, 1, 0, 0, 0, 0, 10, "search-page1-1", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 5, 1, 0, 0, 0, 0, 11, "search-page1-2", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 6, 1, 0, 0, 0, 0, 12, "search-page1-3", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 7, 1, 0, 0, 0, 0, 13, "search-page1-4", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 8, 1, 0, 0, 0, 0, 14, "search-page1-5", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 9, 1, 0, 0, 0, 0, 15, "search-page1-6", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 10, 1, 0, 0, 0, 0, 16, "search-page1-7", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 11, 1, 0, 0, 0, 0, 17, "search-page1-8", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 12, 1, 0, 0, 0, 0, 18, "search-page1-9", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 13, 1, 0, 0, 0, 0, 19, "search-page1-10", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 14, 1, 0, 0, 0, 0, 20, "search-page2-1", "searchTestUser", "sponsor", "skip"]); - await db.prepare("run", query, ["searchTest4", 3, 15, 1, 0, 0, 0, 0, 21, "search-page2-2", "searchTestUser", "sponsor", "skip"]); - // test all values - await db.prepare("run", query, ["searchTest5", 0, 10, 1, 1, 1, 0, 0, 22, "search-values", "searchTestUser", "filler", "mute"]); - return; - }); - - it("Should be able to show all segments under searchTest0", (done) => { - client.get(endpoint, { params: { videoID: "searchTest0" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 5); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-normal"); - assert.strictEqual(segments[1].UUID, "search-downvote"); - assert.strictEqual(segments[2].UUID, "search-locked"); - assert.strictEqual(segments[3].UUID, "search-hidden"); - assert.strictEqual(segments[4].UUID, "search-shadowhidden"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter by category", (done) => { - client.get(endpoint, { params: { videoID: "searchTest0", category: "selfpromo" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 1); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-downvote"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter by category", (done) => { - client.get(endpoint, { params: { videoID: "searchTest0", category: "selfpromo" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 1); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-downvote"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter by category with categories string", (done) => { - client.get(endpoint, { params: { videoID: "searchTest0", categories: `["selfpromo"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 1); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-downvote"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter by category with categories array", (done) => { - client.get(endpoint, { params: { videoID: "searchTest0", category: ["selfpromo"] } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 1); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-downvote"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter by category with actionTypes JSON", (done) => { - client.get(endpoint, { params: { videoID: "searchTest5", actionTypes: `["mute"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.segmentCount, 1); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter by category with actionType array", (done) => { - client.get(endpoint, { params: { videoID: "searchTest5", actionType: ["mute"] } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.segmentCount, 1); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter by category with actionType string", (done) => { - client.get(endpoint, { params: { videoID: "searchTest5", actionType: "mute" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.segmentCount, 1); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter by lock status", (done) => { - client.get(endpoint, { params: { videoID: "searchTest0", locked: false } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 4); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-normal"); - assert.strictEqual(segments[1].UUID, "search-downvote"); - assert.strictEqual(segments[2].UUID, "search-hidden"); - assert.strictEqual(segments[3].UUID, "search-shadowhidden"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter by hide status", (done) => { - client.get(endpoint, { params: { videoID: "searchTest0", hidden: false } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 4); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-normal"); - assert.strictEqual(segments[1].UUID, "search-downvote"); - assert.strictEqual(segments[2].UUID, "search-locked"); - assert.strictEqual(segments[3].UUID, "search-shadowhidden"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter by ignored status", (done) => { - client.get(endpoint, { params: { videoID: "searchTest0", ignored: false } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 2); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-normal"); - assert.strictEqual(segments[1].UUID, "search-locked"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter segments by min views", (done) => { - client.get(endpoint, { params: { videoID: "searchTest1", minViews: 6 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 1); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-highview"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter segments by max views", (done) => { - client.get(endpoint, { params: { videoID: "searchTest1", maxViews: 10 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 1); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-lowview"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter segments by min and max views", (done) => { - client.get(endpoint, { params: { videoID: "searchTest1", maxViews: 10, minViews: 1 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 1); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-lowview"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter segments by min votes", (done) => { - client.get(endpoint, { params: { videoID: "searchTest2", minVotes: 0 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 2); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-zerovote"); - assert.strictEqual(segments[1].UUID, "search-highvote"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter segments by max votes", (done) => { - client.get(endpoint, { params: { videoID: "searchTest2", maxVotes: 10 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 2); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-lowvote"); - assert.strictEqual(segments[1].UUID, "search-zerovote"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to filter segments by both min and max votes", (done) => { - client.get(endpoint, { params: { videoID: "searchTest2", maxVotes: 10, minVotes: 0 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 1); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-zerovote"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get first page of results", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-page1-1"); - assert.strictEqual(segments[1].UUID, "search-page1-2"); - assert.strictEqual(segments[2].UUID, "search-page1-3"); - assert.strictEqual(segments[3].UUID, "search-page1-4"); - assert.strictEqual(segments[4].UUID, "search-page1-5"); - assert.strictEqual(segments[5].UUID, "search-page1-6"); - assert.strictEqual(segments[6].UUID, "search-page1-7"); - assert.strictEqual(segments[7].UUID, "search-page1-8"); - assert.strictEqual(segments[8].UUID, "search-page1-9"); - assert.strictEqual(segments[9].UUID, "search-page1-10"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get second page of results", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", page: 1 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 1); - assert.strictEqual(segments[0].UUID, "search-page2-1"); - assert.strictEqual(segments[1].UUID, "search-page2-2"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should return all wanted values from searchTest5", (done) => { - client.get(endpoint, { params: { videoID: "searchTest5" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 1); - assert.strictEqual(data.page, 0); - const segment0 = segments[0]; - const expected = { - UUID: "search-values", - timeSubmitted: 22, - startTime: 0, - endTime: 10, - category: "filler", - actionType: "mute", - votes: 1, - views: 1, - locked: 1, - hidden: 0, - shadowHidden: 0, - userID: "searchTestUser", - description: "" - }; - assert.deepStrictEqual(segment0, expected); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get with custom limit", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", limit: 2 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments.length, 2); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get with custom limit(2) and page(2)", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", limit: 2, page: 2 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 2); - assert.strictEqual(segments.length, 2); - assert.strictEqual(segments[0].UUID, "search-page1-5"); - assert.strictEqual(segments[1].UUID, "search-page1-6"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get with over range page", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", limit: 2, page: 2000 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 2000); - assert.strictEqual(segments.length, 0); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get with invalid page (=-100)", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", page: -100 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments.length, 10); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get with invalid page (=text)", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", page: "hello" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments.length, 10); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be use default limit if invalid limit query (=0)", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", limit: 0 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-page1-1"); - assert.strictEqual(segments[1].UUID, "search-page1-2"); - assert.strictEqual(segments[2].UUID, "search-page1-3"); - assert.strictEqual(segments[3].UUID, "search-page1-4"); - assert.strictEqual(segments[4].UUID, "search-page1-5"); - assert.strictEqual(segments[5].UUID, "search-page1-6"); - assert.strictEqual(segments[6].UUID, "search-page1-7"); - assert.strictEqual(segments[7].UUID, "search-page1-8"); - assert.strictEqual(segments[8].UUID, "search-page1-9"); - assert.strictEqual(segments[9].UUID, "search-page1-10"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be use default limit if invalid limit query (=-100)", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", limit: -100 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-page1-1"); - assert.strictEqual(segments[1].UUID, "search-page1-2"); - assert.strictEqual(segments[2].UUID, "search-page1-3"); - assert.strictEqual(segments[3].UUID, "search-page1-4"); - assert.strictEqual(segments[4].UUID, "search-page1-5"); - assert.strictEqual(segments[5].UUID, "search-page1-6"); - assert.strictEqual(segments[6].UUID, "search-page1-7"); - assert.strictEqual(segments[7].UUID, "search-page1-8"); - assert.strictEqual(segments[8].UUID, "search-page1-9"); - assert.strictEqual(segments[9].UUID, "search-page1-10"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be use default limit if invalid limit query (=text)", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", limit: "hello" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-page1-1"); - assert.strictEqual(segments[1].UUID, "search-page1-2"); - assert.strictEqual(segments[2].UUID, "search-page1-3"); - assert.strictEqual(segments[3].UUID, "search-page1-4"); - assert.strictEqual(segments[4].UUID, "search-page1-5"); - assert.strictEqual(segments[5].UUID, "search-page1-6"); - assert.strictEqual(segments[6].UUID, "search-page1-7"); - assert.strictEqual(segments[7].UUID, "search-page1-8"); - assert.strictEqual(segments[8].UUID, "search-page1-9"); - assert.strictEqual(segments[9].UUID, "search-page1-10"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be use default limit if invalid limit query (=2000)", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", limit: 2000 } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-page1-1"); - assert.strictEqual(segments[1].UUID, "search-page1-2"); - assert.strictEqual(segments[2].UUID, "search-page1-3"); - assert.strictEqual(segments[3].UUID, "search-page1-4"); - assert.strictEqual(segments[4].UUID, "search-page1-5"); - assert.strictEqual(segments[5].UUID, "search-page1-6"); - assert.strictEqual(segments[6].UUID, "search-page1-7"); - assert.strictEqual(segments[7].UUID, "search-page1-8"); - assert.strictEqual(segments[8].UUID, "search-page1-9"); - assert.strictEqual(segments[9].UUID, "search-page1-10"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get sorted result (desc)", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", sortBy: "endTime", sortDir: "desc" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-page2-2"); - assert.strictEqual(segments[1].UUID, "search-page2-1"); - assert.strictEqual(segments[2].UUID, "search-page1-10"); - assert.strictEqual(segments[3].UUID, "search-page1-9"); - assert.strictEqual(segments[4].UUID, "search-page1-8"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get sorted result (asc)", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", sortBy: "endTime" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-page1-1"); - assert.strictEqual(segments[1].UUID, "search-page1-2"); - assert.strictEqual(segments[2].UUID, "search-page1-3"); - assert.strictEqual(segments[3].UUID, "search-page1-4"); - assert.strictEqual(segments[4].UUID, "search-page1-5"); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be use default sorted if invalid sort field", (done) => { - client.get(endpoint, { params: { videoID: "searchTest4", sortBy: "not exist", sortDir: "desc" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const segments = data.segments; - assert.strictEqual(data.segmentCount, 12); - assert.strictEqual(data.page, 0); - assert.strictEqual(segments[0].UUID, "search-page1-1"); - assert.strictEqual(segments[1].UUID, "search-page1-2"); - assert.strictEqual(segments[2].UUID, "search-page1-3"); - assert.strictEqual(segments[3].UUID, "search-page1-4"); - assert.strictEqual(segments[4].UUID, "search-page1-5"); - done(); - }) - .catch(err => done(err)); + await insertSegment(db, { videoID, timeSubmitted: 1, votes: -1, UUID: "search-lowvote" }); + await insertSegment(db, { videoID, timeSubmitted: 2, votes: 0, UUID: "search-zerovote" }); + await insertSegment(db, { videoID, timeSubmitted: 3, votes: 50, UUID: "search-highvote" }); + }); + + it("Should be able to filter segments by min votes", async () => { + const res = await getVideoSearch({ minVotes: 0 }); + const expected = [ + "search-zerovote", + "search-highvote" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter segments by max votes", async () => { + const res = await getVideoSearch({ maxVotes: 10 }); + const expected = [ + "search-lowvote", + "search-zerovote" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter segments by both min and max votes", async () => { + const res = await getVideoSearch({ minVotes: 0, maxVotes: 10 }); + const expected = [ + "search-zerovote" + ]; + return assertSegments(expected, res); + }); +}); + +describe("getSearchSegments - paginated", () => { + const videoID = videoIDs["paginated"]; + const getVideoSearch = (params: Record) => getSearchSegments(videoID, params); + before(async () => { + await insertSegment(db, { videoID, timeSubmitted: 11, UUID: "search1-1", startTime: 3, endTime: 4 }); + await insertSegment(db, { videoID, timeSubmitted: 12, UUID: "search1-2", startTime: 3, endTime: 5 }); + await insertSegment(db, { videoID, timeSubmitted: 13, UUID: "search1-3", startTime: 3, endTime: 6 }); + await insertSegment(db, { videoID, timeSubmitted: 14, UUID: "search1-4", startTime: 3, endTime: 7 }); + await insertSegment(db, { videoID, timeSubmitted: 15, UUID: "search1-5", startTime: 3, endTime: 8 }); + await insertSegment(db, { videoID, timeSubmitted: 16, UUID: "search1-6", startTime: 3, endTime: 9 }); + await insertSegment(db, { videoID, timeSubmitted: 17, UUID: "search1-7", startTime: 3, endTime: 10 }); + await insertSegment(db, { videoID, timeSubmitted: 18, UUID: "search1-8", startTime: 3, endTime: 11 }); + await insertSegment(db, { videoID, timeSubmitted: 19, UUID: "search1-9", startTime: 3, endTime: 12 }); + await insertSegment(db, { videoID, timeSubmitted: 20, UUID: "search1-10", startTime: 3, endTime: 13 }); + await insertSegment(db, { videoID, timeSubmitted: 21, UUID: "search2-1", startTime: 3, endTime: 14 }); + await insertSegment(db, { videoID, timeSubmitted: 22, UUID: "search2-2", startTime: 3, endTime: 15 }); + }); + + const firstPage = [ + "search1-1", + "search1-2", + "search1-3", + "search1-4", + "search1-5", + "search1-6", + "search1-7", + "search1-8", + "search1-9", + "search1-10" + ]; + + it("Should be able to get first page of results", async () => { + const res = await getVideoSearch({}); + const expected = firstPage; + return assertSegments(expected, res, 12); + }); + + it("Should be able to get second page of results", async () => { + const res = await getVideoSearch({ page: 1 }); + const expected = [ + "search2-1", + "search2-2" + ]; + assert.strictEqual(res.status, 200); + const segments = res.data.segments; + assert.strictEqual(res.data.segmentCount, 12); + assert.strictEqual(res.data.page, 1); + for (const i in expected) { + assert.strictEqual(segments[i].UUID, expected[i]); + } + }); + + it("Should be able to get with custom limit", async () => { + const res = await getVideoSearch({ limit: 2 }); + const expected = firstPage.slice(0, 2); + return assertSegments(expected, res, 12); + }); + + it("Should be able to get with custom limit(2) and page(2)", async () => { + const res = await getVideoSearch({ limit: 2, page: 2 }); + const expected = firstPage.slice(2, 4); + assert.strictEqual(res.status, 200); + const segments = res.data.segments; + assert.strictEqual(res.data.segmentCount, 12); + assert.strictEqual(res.data.page, 2); + for (const i in expected) { + assert.strictEqual(segments[i].UUID, expected[i]); + } + }); + + it("Should be able to get with over range page", async () => { + const res = await getVideoSearch({ limit: 2, page: 2000 }); + assert.strictEqual(res.status, 200); + const data = res.data; + const segments = data.segments; + assert.strictEqual(data.segmentCount, 12); + assert.strictEqual(data.page, 2000); + assert.strictEqual(segments.length, 0); + }); + + it("Should be able to get with invalid page (-100)", async () => { + const res = await getVideoSearch({ page: -100 }); + const expected = firstPage; + return assertSegments(expected, res, 12); + }); + + it("Should be able to get with invalid page (text)", async () => { + const res = await getVideoSearch({ page: "hello" }); + const expected = firstPage; + return assertSegments(expected, res, 12); + }); + + it("Should be use default limit if invalid limit query (0)", async () => { + const res = await getVideoSearch({ limit: 0 }); + const expected = firstPage; + return assertSegments(expected, res, 12); + }); + + it("Should be use default limit if invalid limit query (-100)", async () => { + const res = await getVideoSearch({ limit: -100 }); + const expected = firstPage; + return assertSegments(expected, res, 12); + }); + + it("Should be use default limit if invalid limit query (text)", async () => { + const res = await getVideoSearch({ limit: "hello" }); + const expected = firstPage; + return assertSegments(expected, res, 12); + }); + + it("Should be use default limit if invalid limit query (2000)", async () => { + const res = await getVideoSearch({ limit: 2000 }); + const expected = firstPage; + return assertSegments(expected, res, 12); + }); + + it("Should be able to get sorted result (desc)", async () => { + const res = await getVideoSearch({ sortBy: "endTime", sortDir: "desc" }); + const expected = [ + "search2-2", + "search2-1", + "search1-10", + "search1-9", + "search1-8", + "search1-7", + "search1-6", + "search1-5", + "search1-4", + "search1-3" + ]; + return assertSegments(expected, res, 12); + }); + + it("Should be able to get sorted result (asc)", async () => { + const res = await getVideoSearch({ sortBy: "endTime" }); + const expected = [ + "search1-1", + "search1-2", + "search1-3", + "search1-4", + "search1-5", + "search1-6", + "search1-7", + "search1-8", + "search1-9", + "search1-10" + ]; + return assertSegments(expected, res, 12); + }); + + it("Should be use default sorted if invalid sort field", async () => { + const res = await getVideoSearch({ sortBy: "not exist", sortDir: "desc" }); + const expected = firstPage; + return assertSegments(expected, res, 12); + }); +}); + +describe("getSearchSegments - specific", () => { + const user = genAnonUser(); + const videoID = videoIDs["specific"]; + const getVideoSearch = (params: Record) => getSearchSegments(videoID, params); + const segment: insertSegmentParams = { + UUID: "search-values", + timeSubmitted: 22, + startTime: 0, + endTime: 10, + category: "filler", + actionType: "mute", + votes: 1, + views: 1, + locked: 1, + shadowHidden: 0, + userID: user.pubID, + }; + before(async () => { + await insertSegment(db, segment); + }); + + it("Should be able to filter by category with actionTypes JSON", async () => { + const res = await getVideoSearch({ actionTypes: `["mute"]` }); + const expected = [ + "search-values" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter by category with actionType array", async () => { + const res = await getVideoSearch({ actionTypes: ["mute"] }); + const expected = [ + "search-values" + ]; + return assertSegments(expected, res); + }); + + it("Should be able to filter by category with actionType string", async () => { + const res = await getVideoSearch({ actionType: "mute" }); + const expected = [ + "search-values" + ]; + return assertSegments(expected, res); + }); + + it("Should return all wanted values from searchTest5", async () => { + const res = await getVideoSearch({}); + assert.strictEqual(res.status, 200); + const data = res.data; + assert.strictEqual(data.segmentCount, 1); + assert.strictEqual(data.page, 0); + assert.strictEqual(segment, data.segment); }); }); diff --git a/test/cases/getUserInfoFree.ts b/test/cases/getUserInfoFree.ts index b8f5a765..74c97467 100644 --- a/test/cases/getUserInfoFree.ts +++ b/test/cases/getUserInfoFree.ts @@ -10,12 +10,11 @@ describe("getUserInfo Free Chapters", () => { const postOldQualify = 1600000000000; const getUserInfo = (userID: string) => client.get(endpoint, { params: { userID, value: "freeChaptersAccess" } }); - const assertChapterAccess = (pubID: string) => - getUserInfo(pubID) - .then(res => { - assert.strictEqual(res.status, 200); - assert.strictEqual(res.data.freeChaptersAccess, true); - }); + async function assertChapterAccess (pubID: string) { + const res = await getUserInfo(pubID); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.data.freeChaptersAccess, true); + } it("Should get free access under new rule (newQualify)", async () => { const user = genAnonUser(); @@ -41,7 +40,7 @@ describe("getUserInfo Free Chapters", () => { return assertChapterAccess(user.pubID); }); - it("Everyone should get free access", async() => { + it("Everyone should get free access", () => { const user = genAnonUser(); return assertChapterAccess(user.pubID); }); diff --git a/test/utils/segmentQueryGen.ts b/test/utils/segmentQueryGen.ts index 335fd64b..90db84b7 100644 --- a/test/utils/segmentQueryGen.ts +++ b/test/utils/segmentQueryGen.ts @@ -14,7 +14,7 @@ interface baseParams { } // sponsorTimes -interface insertSegmentParams extends baseParams { +export interface insertSegmentParams extends baseParams { startTime?: number, endTime?: number, votes?: number, From d7a4b674797425d7a6f2bead0540bc4e8f6970fa Mon Sep 17 00:00:00 2001 From: Michael C Date: Sat, 4 Nov 2023 17:44:05 -0400 Subject: [PATCH 24/31] update tests - fix getSearchSegments - postSKipSegmentsUserAgent - shadowBanUser4xx --- test/cases/getSearchSegments.ts | 10 +- test/cases/postSkipSegmentsUserAgent.ts | 133 ++++++++++-------------- test/cases/shadowBanUser4xx.ts | 33 ++---- 3 files changed, 73 insertions(+), 103 deletions(-) diff --git a/test/cases/getSearchSegments.ts b/test/cases/getSearchSegments.ts index 38df5d27..ca4f410d 100644 --- a/test/cases/getSearchSegments.ts +++ b/test/cases/getSearchSegments.ts @@ -237,7 +237,7 @@ describe("getSearchSegments - paginated", () => { it("Should be able to get with custom limit(2) and page(2)", async () => { const res = await getVideoSearch({ limit: 2, page: 2 }); - const expected = firstPage.slice(2, 4); + const expected = firstPage.slice(4, 6); assert.strictEqual(res.status, 200); const segments = res.data.segments; assert.strictEqual(res.data.segmentCount, 12); @@ -339,6 +339,7 @@ describe("getSearchSegments - specific", () => { const videoID = videoIDs["specific"]; const getVideoSearch = (params: Record) => getSearchSegments(videoID, params); const segment: insertSegmentParams = { + videoID, UUID: "search-values", timeSubmitted: 22, startTime: 0, @@ -348,6 +349,8 @@ describe("getSearchSegments - specific", () => { votes: 1, views: 1, locked: 1, + hidden: 0, + description: "", shadowHidden: 0, userID: user.pubID, }; @@ -379,12 +382,13 @@ describe("getSearchSegments - specific", () => { return assertSegments(expected, res); }); - it("Should return all wanted values from searchTest5", async () => { + it("Should return all wanted values", async () => { const res = await getVideoSearch({}); assert.strictEqual(res.status, 200); const data = res.data; assert.strictEqual(data.segmentCount, 1); assert.strictEqual(data.page, 0); - assert.strictEqual(segment, data.segment); + delete segment.videoID; // videoID not returned in searchSegments + assert.deepEqual(segment, data.segments[0]); }); }); diff --git a/test/cases/postSkipSegmentsUserAgent.ts b/test/cases/postSkipSegmentsUserAgent.ts index c5e68c9d..b2189ddd 100644 --- a/test/cases/postSkipSegmentsUserAgent.ts +++ b/test/cases/postSkipSegmentsUserAgent.ts @@ -1,104 +1,81 @@ import assert from "assert"; import { convertSingleToDBFormat } from "./postSkipSegments"; -import { getHash } from "../../src/utils/getHash"; import { db } from "../../src/databases/databases"; import { partialDeepEquals } from "../utils/partialDeepEquals"; import { client } from "../utils/httpClient"; +import { multiGenProxy } from "../utils/genRandom"; +import { genAnonUser } from "../utils/genUser"; const endpoint = "/api/skipSegments"; const queryUseragent = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "userAgent" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]); +const videoIDs = multiGenProxy("videoID", "postSkipSegmentsUserAgent"); describe("postSkipSegments - userAgent", () => { - const userIDOne = "postSkip-DurationUserOne"; - const VIPLockUser = "VIPUser-lockCategories"; - const videoID = "lockedVideo"; - const userID = userIDOne; - const segment = { segment: [0, 10], category: "sponsor", }; const dbFormatSegment = convertSingleToDBFormat(segment); - - before(() => { - const insertLockCategoriesQuery = `INSERT INTO "lockCategories" ("userID", "videoID", "category", "reason") VALUES(?, ?, ?, ?)`; - db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "sponsor", "Custom Reason"]); - db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "intro", ""]); - }); - - it("Should be able to submit with empty user-agent", (done) => { - const videoID = "userAgent-3"; - client(endpoint, { + async function postSubmitSegment(videoID: string, addlData: Record) { + const user = genAnonUser(); + const res = await client(endpoint, { method: "POST", data: { - userID, + userID: user.privID, videoID, segments: [segment], - userAgent: "", - } - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await queryUseragent(videoID); - const expected = { - ...dbFormatSegment, - userAgent: "", - }; - assert.ok(partialDeepEquals(row, expected)); - done(); - }) - .catch(err => done(err)); + }, + ...addlData, + }); + return assert.strictEqual(res.status, 200); + } + + it("Should be able to submit with empty user-agent", async () => { + const videoID = videoIDs["ua-empty"]; + const user = genAnonUser(); + await postSubmitSegment(videoID, { data: { + userID: user.privID, + videoID, + segments: [segment], + userAgent: "" + } }); + const row = await queryUseragent(videoID); + const expected = { + ...dbFormatSegment, + userAgent: "", + }; + assert.ok(partialDeepEquals(row, expected)); }); - it("Should be able to submit with custom userAgent in body", (done) => { - const videoID = "userAgent-4"; - client(endpoint, { - method: "POST", - data: { - userID, - videoID, - segments: [segment], - userAgent: "MeaBot/5.0" - } - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await queryUseragent(videoID); - const expected = { - ...dbFormatSegment, - userAgent: "MeaBot/5.0", - }; - assert.ok(partialDeepEquals(row, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to submit with custom userAgent in body", async () => { + const videoID = videoIDs["ua-body"]; + const user = genAnonUser(); + await postSubmitSegment(videoID, { data: { + userID: user.privID, + videoID, + segments: [segment], + userAgent: "MeaBot/5.0" + } }); + const row = await queryUseragent(videoID); + const expected = { + ...dbFormatSegment, + userAgent: "MeaBot/5.0", + }; + assert.ok(partialDeepEquals(row, expected)); }); - it("Should be able to submit with custom user-agent 1", (done) => { - const videoID = "userAgent-1"; - client(endpoint, { - method: "POST", - headers: { - "Content-Type": "application/json", - "User-Agent": "com.google.android.youtube/5.0" - }, - data: { - userID, - videoID, - segments: [segment], - } - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await queryUseragent(videoID); - const expected = { - ...dbFormatSegment, - userAgent: "Vanced/5.0", - }; - assert.ok(partialDeepEquals(row, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to submit with custom user-agent 1", async () => { + const videoID = videoIDs["ua-headers"]; + await postSubmitSegment(videoID, { headers: { + "Content-Type": "application/json", + "User-Agent": "com.google.android.youtube/5.0" + } }); + const row = await queryUseragent(videoID); + const expected = { + ...dbFormatSegment, + userAgent: "Vanced/5.0", + }; + assert.ok(partialDeepEquals(row, expected)); }); }); \ No newline at end of file diff --git a/test/cases/shadowBanUser4xx.ts b/test/cases/shadowBanUser4xx.ts index c9cdda9c..6cb4c737 100644 --- a/test/cases/shadowBanUser4xx.ts +++ b/test/cases/shadowBanUser4xx.ts @@ -1,7 +1,8 @@ import { db } from "../../src/databases/databases"; -import { getHash } from "../../src/utils/getHash"; import assert from "assert"; import { client } from "../utils/httpClient"; +import { genUser } from "../utils/genUser"; +import { insertVip } from "../utils/queryGen"; const endpoint = "/api/shadowBanUser"; @@ -12,37 +13,25 @@ const postShadowBan = (params: Record) => client({ }); describe("shadowBanUser 4xx", () => { - const VIPuserID = "shadow-ban-4xx-vip"; + const vip = genUser("shadowBanUser", "4xx"); before(async () => { - await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES(?)`, [getHash(VIPuserID)]); + await insertVip(db, vip.pubID); }); - it("Should return 400 if no adminUserID", (done) => { + it("Should return 400 if no adminUserID", () => { const userID = "shadowBanned"; postShadowBan({ userID }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + .then(res => assert.strictEqual(res.status, 400)); }); - it("Should return 400 if no userID", (done) => { - postShadowBan({ adminUserID: VIPuserID }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should return 400 if no userID", () => { + postShadowBan({ adminUserID: vip.privID }) + .then(res => assert.strictEqual(res.status, 400)); }); - it("Should return 403 if not authorized", (done) => { + it("Should return 403 if not authorized", () => { postShadowBan({ adminUserID: "notVIPUserID", userID: "shadowBanned" }) - .then(res => { - assert.strictEqual(res.status, 403); - done(); - }) - .catch(err => done(err)); + .then(res => assert.strictEqual(res.status, 403)); }); }); From 88a74352a0bcaa674da619cc086feead9a0474b1 Mon Sep 17 00:00:00 2001 From: Michael C Date: Sat, 4 Nov 2023 17:56:06 -0400 Subject: [PATCH 25/31] add more small tests - getUsername - postSkipSegments400Stub - downvoteSegmentArchive --- test/cases/downvoteSegmentArchiveJob.ts | 55 +++++++++++++------------ test/cases/getUsername.ts | 44 +++++++------------- test/cases/postSkipSegments400Stub.ts | 13 +++--- 3 files changed, 49 insertions(+), 63 deletions(-) diff --git a/test/cases/downvoteSegmentArchiveJob.ts b/test/cases/downvoteSegmentArchiveJob.ts index 4a3ad8e1..2130b05c 100644 --- a/test/cases/downvoteSegmentArchiveJob.ts +++ b/test/cases/downvoteSegmentArchiveJob.ts @@ -2,45 +2,46 @@ import { strictEqual, ok } from "assert"; import { db } from "../../src/databases/databases"; import { archiveDownvoteSegment } from "../../src/cronjob/downvoteSegmentArchiveJob"; import { DBSegment } from "../../src/types/segments.model"; +import { insertSegment, insertSegmentParams } from "../utils/segmentQueryGen"; +import { multiGenProxy } from "../utils/genRandom"; const oct2021 = new Date("October 1, 2021").getTime(); const nov2021 = new Date("November 1, 2021").getTime(); const dec2021 = new Date("December 17, 2021").getTime(); const dec2022 = new Date("December 17, 2022").getTime(); -const records = [ - ["dsajVideo0", 0, 0, 2, 0, "dsaj00", "dsajUser", dec2021, 0, 0, 0], - ["dsajVideo0", 0, 0, 2, 0, "dsaj01", "dsajUser", dec2021, 0, 0, 0], - ["dsajVideo0", 0, 0, 2, 0, "dsaj02", "dsajUser", dec2021, 0, 0, 0], - ["dsajVideo0", 0, 0, 2, 0, "dsaj03", "dsajUser", dec2021, 0, 0, 0], - ["dsajVideo0", 0, 0, 2, 0, "dsaj04", "dsajUser", dec2021, 0, 0, 0,], - - ["dsajVideo1", 0, 0, 2, 0, "dsaj10", "dsajUser", dec2021, 0, 0, 0], - ["dsajVideo1", 0, 0, -3, 0, "dsaj11", "dsajUser", dec2021, 0, 0, 0], - - ["dsajVideo2", 0, 0, 2, 0, "dsaj20", "dsajUser", dec2021, 0, 0, 0], - ["dsajVideo2", 0, 0, -4, 0, "dsaj21", "dsajUser", oct2021, 0, 0, 0], - - ["dsajVideo3", 0, 0, 2, 1, "dsaj30", "dsajUser", dec2021, 0, 0, 0], - ["dsajVideo3", 0, 0, 100000, 0, "dsaj31", "dsajUser", dec2021, 0, 0, 0], - - ["dsajVideo4", 0, 0, 100000, 0, "dsaj40", "dsajUser", dec2021, 0, 1, 0], - - ["dsajVideo5", 0, 0, 2, 0, "dsaj50", "dsajUser", dec2021, 0, 0, 0], - ["dsajVideo5", 0, 0, -1, 0, "dsaj51", "dsajUser", dec2021, 0, 0, 0], - ["dsajVideo5", 0, 0, -2, 0, "dsaj52", "dsajUser", nov2021, 0, 0, 0], - ["dsajVideo5", 0, 0, 2, 0, "dsaj53", "dsajUser", dec2021, 0, 0, 0] +const videoIDs = multiGenProxy("videoID", "downvoteSegmentArchiveJob"); + +const records: insertSegmentParams[] = [ + // video0 + { videoID: videoIDs[0], votes: 2, timeSubmitted: dec2021 }, + { videoID: videoIDs[0], votes: 2, timeSubmitted: dec2021 }, + { videoID: videoIDs[0], votes: 2, timeSubmitted: dec2021 }, + { videoID: videoIDs[0], votes: 2, timeSubmitted: dec2021 }, + { videoID: videoIDs[0], votes: 2, timeSubmitted: dec2021 }, + // video1 + { videoID: videoIDs[1], votes: 2, timeSubmitted: dec2021 }, + { videoID: videoIDs[1], votes: -3, timeSubmitted: dec2021 }, + // video2 + { videoID: videoIDs[2], votes: 2, timeSubmitted: dec2021 }, + { videoID: videoIDs[2], votes: -4, timeSubmitted: oct2021 }, + // video3 + { videoID: videoIDs[3], votes: 2, timeSubmitted: dec2021, locked: true }, + { videoID: videoIDs[3], votes: 100000, timeSubmitted: dec2021 }, + // video4 + { videoID: videoIDs[4], votes: 100000, timeSubmitted: dec2021, hidden: true }, + // video5 + { videoID: videoIDs[5], votes: 2, timeSubmitted: dec2021 }, + { videoID: videoIDs[5], votes: -1, timeSubmitted: dec2021 }, + { videoID: videoIDs[5], votes: -2, timeSubmitted: nov2021 }, + { videoID: videoIDs[5], votes: 2, timeSubmitted: dec2021 }, ]; describe("downvoteSegmentArchiveJob", () => { beforeEach(async () => { - const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "hidden", "shadowHidden") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; - for (const record of records) { - await db.prepare("run", query, record); + await insertSegment(db, record); } - - return; }); it("Should update the database version when starting the application", async () => { diff --git a/test/cases/getUsername.ts b/test/cases/getUsername.ts index 5394434d..5d1add17 100644 --- a/test/cases/getUsername.ts +++ b/test/cases/getUsername.ts @@ -1,4 +1,5 @@ -import { getHash } from "../../src/utils/getHash"; +import { genRandomValue } from "../utils/genRandom"; +import { genAnonUser } from "../utils/genUser"; import { client } from "../utils/httpClient"; import assert from "assert"; @@ -17,37 +18,24 @@ const postSetUserName = (userID: string, username: string) => client({ } }); -const userOnePrivate = "getUsername_0"; -const userOnePublic = getHash(userOnePrivate); -const userOneUsername = "getUsername_username"; - describe("getUsername test", function() { - it("Should get back publicUserID if not set", (done) => { - getUsername(userOnePrivate) - .then(result => { - assert.strictEqual(result.data.userName, userOnePublic); - done(); - }) - .catch(err => done(err)); + it("Should get back publicUserID if not set", async () => { + const user = genAnonUser(); + const result = await getUsername(user.privID); + return assert.strictEqual(result.data.userName, user.pubID); }); - it("Should be able to get username after setting", (done) => { - postSetUserName(userOnePrivate, userOneUsername) + it("Should be able to get username after setting", () => { + const user = genAnonUser(); + const username = genRandomValue("username", "getUsername"); + return postSetUserName(user.privID, username) .then(async () => { - const result = await getUsername(userOnePrivate); + const result = await getUsername(user.privID); const actual = result.data.userName; - assert.strictEqual(actual, userOneUsername); - done(); - }) - .catch(err => done(err)); + assert.strictEqual(actual, username); + }); }); - it("Should return 400 if no userID provided", (done) => { - client({ - url: "/api/getUsername" - }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should return 400 if no userID provided", () => { + client({ url: "/api/getUsername" }) + .then(res => assert.strictEqual(res.status, 400)); }); }); \ No newline at end of file diff --git a/test/cases/postSkipSegments400Stub.ts b/test/cases/postSkipSegments400Stub.ts index 53f2b4ee..1722375d 100644 --- a/test/cases/postSkipSegments400Stub.ts +++ b/test/cases/postSkipSegments400Stub.ts @@ -2,8 +2,9 @@ import assert from "assert"; import { postSkipSegmentParam } from "./postSkipSegments"; import { config } from "../../src/config"; import sinon from "sinon"; +import { genRandomValue } from "../utils/genRandom"; -const videoID = "postSkipSegments-404-video"; +const videoID = genRandomValue("videoID", "postSkipSegments400Stub"); describe("postSkipSegments 400 - stubbed config", () => { const USERID_LIMIT = 30; @@ -14,19 +15,15 @@ describe("postSkipSegments 400 - stubbed config", () => { sinon.restore(); }); - it("Should return 400 if userID is too short", (done) => { + it("Should return 400 if userID is too short", () => { const userID = "a".repeat(USERID_LIMIT - 10); - postSkipSegmentParam({ + return postSkipSegmentParam({ videoID, startTime: 1, endTime: 5, category: "sponsor", userID }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + .then(res => assert.strictEqual(res.status, 400)); }); }); From 2969266206e07683072353541f820790af66f482 Mon Sep 17 00:00:00 2001 From: Michael C Date: Mon, 20 Nov 2023 23:53:05 -0500 Subject: [PATCH 26/31] postSkipSegments, postSkipSegmentsLocked - add fix for postSkipSegment 500 if userID was not a string --- src/routes/postSkipSegments.ts | 8 +- test/cases/postSkipSegments.ts | 437 ++++++++++++--------------- test/cases/postSkipSegmentsLocked.ts | 38 +-- test/utils/genRandom.ts | 3 +- test/utils/genUser.ts | 2 +- 5 files changed, 218 insertions(+), 270 deletions(-) diff --git a/src/routes/postSkipSegments.ts b/src/routes/postSkipSegments.ts index 61ccc67d..27d45bcf 100644 --- a/src/routes/postSkipSegments.ts +++ b/src/routes/postSkipSegments.ts @@ -187,7 +187,7 @@ async function checkUserActiveWarning(userID: HashedUserID): Promise { const invalidFields = []; const errors = []; @@ -493,10 +493,10 @@ export async function postSkipSegments(req: Request, res: Response): Promise { // Constant and helpers - const submitUserOne = `PostSkipUser1${".".repeat(18)}`; - const submitUserTwo = `PostSkipUser2${".".repeat(18)}`; - const submitUserTwoHash = getHash(submitUserTwo); - - const submitVIPuser = `VIPPostSkipUser${".".repeat(16)}`; + const submitVIPuser = genUser("postSkipSegments", "vipuser"); - const queryDatabase = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "votes", "userID", "locked", "category", "actionType" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]); + const queryDatabase = (videoID: string) => db.prepare("get", `SELECT "videoID", "startTime", "endTime", "votes", "userID", "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]); before(() => { - 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"]); - - const insertVipUserQuery = 'INSERT INTO "vipUsers" ("userID") VALUES (?)'; - db.prepare("run", insertVipUserQuery, [getHash(submitVIPuser)]); + insertVipUser(db, submitVIPuser); }); - it("Should be able to submit a single time (Params method)", (done) => { - const videoID = "postSkipParamSingle"; - postSkipSegmentParam({ + it("Should be able to submit a single time (Params method)", async () => { + const videoID = genRandomValue("video", "post-skip-param"); + const segment = { videoID, startTime: 2, endTime: 10, - userID: submitUserOne, category: "sponsor" - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await queryDatabase(videoID); - const expected = { - startTime: 2, - endTime: 10, - category: "sponsor", - }; - assert.ok(partialDeepEquals(row, expected)); - - const videoInfo = await queryDatabaseVideoInfo(videoID); - const expectedVideoInfo = { - videoID, - title: "Example Title", - channelID: "ExampleChannel", - published: 123, - }; - assert.ok(partialDeepEquals(videoInfo, expectedVideoInfo)); - - done(); - }) - .catch(err => done(err)); + }; + // submit + const submit = await postSkipSegmentParam({ + ...segment, + userID: genAnonUser().privID, + }); + assert.strictEqual(submit.status, 200); + // check database + const row = await queryDatabase(videoID); + const expected = { + ...segment, + }; + assert.ok(partialDeepEquals(row, expected)); + // query videoInfo + const videoInfo = await queryDatabaseVideoInfo(videoID); + const expectedVideoInfo = { + videoID, + title: "Example Title", + channelID: "ExampleChannel", + published: 123, + }; + assert.ok(partialDeepEquals(videoInfo, expectedVideoInfo)); }); - it("Should be able to submit a single time (JSON method)", (done) => { - const videoID = "postSkipJSONSingle"; - postSkipSegmentJSON({ - userID: submitUserOne, + it("Should be able to submit a single time (JSON method)", async () => { + const videoID = genRandomValue("video", "post-skip-json"); + const user = genAnonUser(); + const submit = await postSkipSegmentJSON({ + userID: user.privID, videoID, segments: [{ segment: [0, 10], category: "sponsor", }], - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await queryDatabase(videoID); - const expected = { - startTime: 0, - endTime: 10, - locked: 0, - category: "sponsor", - }; - assert.ok(partialDeepEquals(row, expected)); - done(); - }) - .catch(err => done(err)); + }); + assert.strictEqual(submit.status, 200); + const row = await queryDatabase(videoID); + const expected = { + startTime: 0, + endTime: 10, + locked: 0, + category: "sponsor", + }; + assert.ok(partialDeepEquals(row, expected)); }); - it("Should be able to submit a single time with an action type (JSON method)", (done) => { - const videoID = "postSkipJSONSingleActionType"; - postSkipSegmentJSON({ - userID: submitUserOne, + it("Should be able to submit a single time with an action type (JSON method)", async () => { + const videoID = genRandomValue("video", "post-skip-json-action-type"); + const submit = await postSkipSegmentJSON({ + userID: genAnonUser().privID, videoID, segments: [{ segment: [0, 10], category: "sponsor", actionType: "mute" }], - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await queryDatabaseActionType(videoID); - const expected = { - startTime: 0, - endTime: 10, - category: "sponsor", - actionType: "mute", - }; - assert.ok(partialDeepEquals(row, expected)); - done(); - }) - .catch(err => done(err)); + }); + assert.strictEqual(submit.status, 200); + const row = await queryDatabaseActionType(videoID); + const expected = { + startTime: 0, + endTime: 10, + category: "sponsor", + actionType: "mute", + }; + assert.ok(partialDeepEquals(row, expected)); }); - it("Should be able to submit a single time under a different service (JSON method)", (done) => { - const videoID = "postSkip7"; - postSkipSegmentJSON({ - userID: submitUserOne, + it("Should be able to submit a single time under a different service (JSON method)", async () => { + const videoID = genRandomValue("video", "post-skip-json-service"); + const submit = await postSkipSegmentJSON({ + userID: genAnonUser().privID, videoID, service: "PeerTube", segments: [{ segment: [0, 10], category: "sponsor", }], - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "service" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]); - const expected = { - startTime: 0, - endTime: 10, - locked: 0, - category: "sponsor", - service: "PeerTube", - }; - assert.ok(partialDeepEquals(row, expected)); - done(); - }) - .catch(err => done(err)); + }); + assert.strictEqual(submit.status, 200); + const row = await db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "service" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]); + const expected = { + startTime: 0, + endTime: 10, + locked: 0, + category: "sponsor", + service: "PeerTube", + }; + assert.ok(partialDeepEquals(row, expected)); }); - it("VIP submission should start locked", (done) => { - const videoID = "vipuserIDSubmission"; - postSkipSegmentJSON({ - userID: submitVIPuser, + it("VIP submission should start locked", async () => { + const videoID = genRandomValue("video", "post-skip-vip"); + const segment = { videoID, - segments: [{ - segment: [0, 10], - category: "sponsor", - }], - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await queryDatabase(videoID); - const expected = { - startTime: 0, - endTime: 10, - locked: 1, - category: "sponsor", - }; - assert.ok(partialDeepEquals(row, expected)); - done(); - }) - .catch(err => done(err)); + startTime: 0, + endTime: 10, + category: "sponsor" + }; + const submit = await postSkipSegmentParam({ + userID: submitVIPuser.privID, + ...segment + }); + assert.strictEqual(submit.status, 200); + const row = await queryDatabase(videoID); + assert.ok(partialDeepEquals(row, segment)); }); - it("Should be able to submit multiple times (JSON method)", (done) => { - const videoID = "postSkipJSONMultiple"; - postSkipSegmentJSON({ - userID: submitUserOne, + it("Should be able to submit multiple times (JSON method)", async () => { + const videoID = genRandomValue("video", "post-skip-json-multiple"); + const submit = await postSkipSegmentJSON({ + userID: genAnonUser().privID, videoID, segments: [{ segment: [3, 10], @@ -206,172 +181,152 @@ describe("postSkipSegments", () => { segment: [30, 60], category: "intro", }], - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const rows = await db.prepare("all", `SELECT "startTime", "endTime", "category" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]); - const expected = [{ - startTime: 3, - endTime: 10, - category: "sponsor" - }, { - startTime: 30, - endTime: 60, - category: "intro" - }]; - assert.ok(arrayDeepEquals(rows, expected)); - done(); - }) - .catch(err => done(err)); + }); + assert.strictEqual(submit.status, 200); + const rows = await db.prepare("all", `SELECT "startTime", "endTime", "category" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]); + const expected = [{ + startTime: 3, + endTime: 10, + category: "sponsor" + }, { + startTime: 30, + endTime: 60, + category: "intro" + }]; + assert.ok(arrayDeepEquals(rows, expected)); }).timeout(5000); - it("Should be accepted if a non-sponsor is less than 1 second", (done) => { - const videoID = "qqwerty"; - postSkipSegmentParam({ + it("Should be accepted if a non-sponsor is less than 1 second", async () => { + const videoID = genRandomValue("video", "post-short-non-sponsor"); + const submit = await postSkipSegmentParam({ videoID, startTime: 30, endTime: 30.5, - userID: submitUserTwo, + userID: genAnonUser().privID, category: "intro" - }) - .then(res => { - assert.strictEqual(res.status, 200); - done(); - }) - .catch(err => done(err)); + }); + assert.strictEqual(submit.status, 200); }); - it("Should be accepted if highlight segment starts and ends at the same time", (done) => { - const videoID = "qqwerty"; - postSkipSegmentParam({ + it("Should be accepted if highlight segment starts and ends at the same time", async () => { + const videoID = genRandomValue("video", "post-skip-highlight"); + const submit = await postSkipSegmentParam({ videoID, startTime: 30, endTime: 30, - userID: submitUserTwo, + userID: genAnonUser().privID, category: "poi_highlight" - }) - .then(res => { - assert.strictEqual(res.status, 200); - done(); - }) - .catch(err => done(err)); + }); + assert.strictEqual(submit.status, 200); }); - it("Should be able to submit with commas in timestamps", (done) => { - const videoID = "commas-1"; - postSkipSegmentJSON({ - userID: submitUserOne, + it("Should be able to submit with commas in timestamps", async () => { + const videoID = genRandomValue("video", "post-skip-commas"); + const submit = await postSkipSegmentJSON({ + userID: genAnonUser().privID, videoID, segments: [{ segment: ["0,2", "10,392"], category: "sponsor", }] - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await queryDatabase(videoID); - const expected = { - startTime: 0.2, - endTime: 10.392 - }; - assert.ok(partialDeepEquals(row, expected)); - done(); - }) - .catch(err => done(err)); + }); + assert.strictEqual(submit.status, 200); + const row = await queryDatabase(videoID); + const expected = { + startTime: 0.2, + endTime: 10.392 + }; + assert.ok(partialDeepEquals(row, expected)); }); - it("Should allow submitting full video sponsor", (done) => { - const videoID = "qqwerth"; - postSkipSegmentParam({ + it("Should allow submitting full video sponsor", async () => { + const videoID = genRandomValue("video", "post-skip-full-video"); + const user = genAnonUser(); + const segment = { videoID, startTime: 0, endTime: 0, category: "sponsor", actionType: "full", - userID: submitUserTwo - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await queryDatabase(videoID); - const expected = { - startTime: 0, - endTime: 0, - votes: 0, - userID: submitUserTwoHash, - category: "sponsor", - actionType: "full" - }; - assert.ok(partialDeepEquals(row, expected)); - done(); - }) - .catch(err => done(err)); + }; + const submit = await postSkipSegmentParam({ + ...segment, + userID: user.privID + }); + assert.strictEqual(submit.status, 200); + const row = await queryDatabase(videoID); + const expected = { + ...segment, + userID: user.pubID + }; + assert.ok(partialDeepEquals(row, expected)); }); - it("Submitting duplicate full video sponsor should count as an upvote", (done) => { - const videoID = "full_video_segment"; - postSkipSegmentParam({ + it("Submitting duplicate full video sponsor should count as an upvote", async () => { + const videoID = genRandomValue("video", "vote-dupe-full-video"); + const submitUser = genAnonUser(); + const voteUser = genAnonUser(); + const segment = { videoID, startTime: 0, endTime: 0, category: "sponsor", - actionType: "full", - userID: submitUserOne - }) - .then(async res => { - assert.strictEqual(res.status, 200); - const row = await queryDatabase(videoID); - const expected = { - startTime: 0, - endTime: 0, - votes: 1, - userID: submitUserTwoHash, - category: "sponsor", - actionType: "full" - }; - assert.ok(partialDeepEquals(row, expected)); - done(); - }) - .catch(err => done(err)); + actionType: "full" + }; + // submitting + const submitOne = await postSkipSegmentParam({ + ...segment, + userID: submitUser.privID + }); + assert.strictEqual(submitOne.status, 200); + // add eligibility for voteUser + await postSkipSegmentParam({ + videoID: genRandomValue("video", "vote-eligible"), + userID: voteUser.privID, + category: "sponsor", + startTime: 0, + endTime: 10, + }); + // submit second segment + const submitTwo = await postSkipSegmentParam({ + ...segment, + userID: voteUser.privID + }); + assert.strictEqual(submitTwo.status, 200); + // checking + const row = await queryDatabase(videoID); + const expected = { + ...segment, + votes: 1 + }; + assert.ok(partialDeepEquals(row, expected)); }); - it("Should not be able to submit with colons in timestamps", (done) => { - const videoID = "colon-1"; - postSkipSegmentJSON({ - userID: submitUserOne, + it("Should not be able to submit with colons in timestamps", async () => { + const videoID = genRandomValue("video", "post-skip-colons"); + const submit = await postSkipSegmentJSON({ + userID: genAnonUser().privID, videoID, segments: [{ segment: ["0:2.000", "3:10.392"], category: "sponsor", }] - }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + }); + assert.strictEqual(submit.status, 400); }); - it("Should throw 409 on duplicate submission", (done) => { + it("Should throw 409 on duplicate submission", async () => { const videoID = "private-video"; - postSkipSegmentParam({ + const submitSegment = { videoID, startTime: 5.555, endTime: 8.888, category: "sponsor", - userID: submitUserTwo - }) - .then(res => assert.strictEqual(res.status, 200) ) - .then(() => postSkipSegmentParam({ - videoID, - startTime: 5.555, - endTime: 8.888, - category: "sponsor", - userID: submitUserTwo - })) - .then(res => { - assert.strictEqual(res.status, 409); - done(); - }) - .catch(err => done(err)); + userID: genAnonUser().privID + }; + const firstSubmit = await postSkipSegmentParam(submitSegment); + assert.strictEqual(firstSubmit.status, 200); + const secondSubmit = await postSkipSegmentParam(submitSegment); + assert.strictEqual(secondSubmit.status, 409); }); }); diff --git a/test/cases/postSkipSegmentsLocked.ts b/test/cases/postSkipSegmentsLocked.ts index 0fc0ab82..e31e2bb1 100644 --- a/test/cases/postSkipSegmentsLocked.ts +++ b/test/cases/postSkipSegmentsLocked.ts @@ -1,23 +1,21 @@ import assert from "assert"; import { postSkipSegmentJSON } from "./postSkipSegments"; -import { getHash } from "../../src/utils/getHash"; import { db } from "../../src/databases/databases"; +import { insertLock } from "../utils/queryGen"; +import { genAnonUser } from "../utils/genUser"; +import { genRandomValue } from "../utils/genRandom"; describe("postSkipSegments - LockedVideos", () => { - const userIDOne = "postSkip-DurationUserOne"; - const VIPLockUser = "VIPUser-lockCategories"; - const videoID = "lockedVideo"; - const userID = userIDOne; + const videoID = genRandomValue("video", "postSkipLocked"); before(() => { - const insertLockCategoriesQuery = `INSERT INTO "lockCategories" ("userID", "videoID", "category", "reason") VALUES(?, ?, ?, ?)`; - db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "sponsor", "Custom Reason"]); - db.prepare("run", insertLockCategoriesQuery, [getHash(VIPLockUser), videoID, "intro", ""]); + insertLock(db, { videoID, category: "sponsor", reason: "Custom Reason" }); + insertLock(db, { videoID, category: "intro", reason: "" }); }); - it("Should return 403 and custom reason for submiting in lockedCategory", (done) => { + it("Should return 403 and custom reason for submiting in lockedCategory", () => postSkipSegmentJSON({ - userID, + userID: genAnonUser().privID, videoID, segments: [{ segment: [1, 10], @@ -28,14 +26,12 @@ describe("postSkipSegments - LockedVideos", () => { assert.strictEqual(res.status, 403); assert.match(res.data, /Reason: /); assert.match(res.data, /Custom Reason/); - done(); }) - .catch(err => done(err)); - }); + ); - it("Should return not be 403 when submitting with locked category but unlocked actionType", (done) => { + it("Should return not be 403 when submitting with locked category but unlocked actionType", () => postSkipSegmentJSON({ - userID, + userID: genAnonUser().privID, videoID, segments: [{ segment: [1, 10], @@ -45,14 +41,12 @@ describe("postSkipSegments - LockedVideos", () => { }) .then(res => { assert.strictEqual(res.status, 200); - done(); }) - .catch(err => done(err)); - }); + ); - it("Should return 403 for submiting in lockedCategory", (done) => { + it("Should return 403 for submiting in lockedCategory", () => postSkipSegmentJSON({ - userID, + userID: genAnonUser().privID, videoID, segments: [{ segment: [1, 10], @@ -63,8 +57,6 @@ describe("postSkipSegments - LockedVideos", () => { assert.strictEqual(res.status, 403); assert.doesNotMatch(res.data, /Lock reason: /); assert.doesNotMatch(res.data, /Custom Reason/); - done(); }) - .catch(err => done(err)); - }); + ); }); \ No newline at end of file diff --git a/test/utils/genRandom.ts b/test/utils/genRandom.ts index f828cd73..b1d1ae26 100644 --- a/test/utils/genRandom.ts +++ b/test/utils/genRandom.ts @@ -1,8 +1,9 @@ import crypto from "crypto"; export const genRandom = (chars=8): string => crypto.pseudoRandomBytes(chars/2).toString("hex"); -export const genRandomNumber = (min: number, max: number) => Math.floor(Math.random() * (max - min) + min); +export const genRandomNumber = (min = 0, max = 100) => Math.floor(Math.random() * (max - min) + min); export const genRandomValue = (prefix: string, identifier: string, chars=8): string => `${prefix}-${identifier}-${genRandom(chars)}`; +export const selectRandom = (arr: any[]) => arr[genRandomNumber(0, arr.length)]; export const multiGenProxy = (prefix: string, identifier: string) => new Proxy({}, { get(target: Record, prop: string, receiver) { diff --git a/test/utils/genUser.ts b/test/utils/genUser.ts index 978dae9b..66c3abef 100644 --- a/test/utils/genUser.ts +++ b/test/utils/genUser.ts @@ -32,7 +32,7 @@ export const genUserUsername = (fnname: string, testcase: string, username: stri }; export const genAnonUser = (info: info = {}): User => { - const privID = `user-${genRandom()}` as UserID; + const privID = `user-${genRandom(16)}` as UserID; const pubID = getHash(privID); return { privID, pubID, info }; }; From e3937085f3f8e19385f6f64769d20d728ec0fd7a Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 12 Dec 2023 14:52:04 -0500 Subject: [PATCH 27/31] getSkipSegments tests --- test/cases/getSkipSegments.ts | 623 ++++++++++------------------------ test/utils/segmentQueryGen.ts | 2 +- 2 files changed, 186 insertions(+), 439 deletions(-) diff --git a/test/cases/getSkipSegments.ts b/test/cases/getSkipSegments.ts index 275f2310..fac374e6 100644 --- a/test/cases/getSkipSegments.ts +++ b/test/cases/getSkipSegments.ts @@ -1,498 +1,245 @@ import { db } from "../../src/databases/databases"; -import { partialDeepEquals } from "../utils/partialDeepEquals"; +import { arrayPartialDeepEquals, partialDeepEquals } from "../utils/partialDeepEquals"; import assert from "assert"; import { client } from "../utils/httpClient"; +import { genAnonUser } from "../utils/genUser"; +import { genRandomNumber, genRandomValue } from "../utils/genRandom"; +import { insertSegment, insertSegmentParams } from "../utils/segmentQueryGen"; +import { AxiosRequestConfig } from "axios"; +import { Service } from "../../src/types/segments.model"; + +type ResponseSegment = { + segment?: [number, number] | number[], + category?: string, + UUID?: string, + videoDuration?: number, + actionType?: string, + votes?: number, + locked?: number, + description?: string +}; + +const defaultResponseSegment = { + segment: [1, 10] as [number, number], + category: "sponsor", + UUID: "", + videoDuration: 0, + actionType: "skip", + votes: 0, + locked: 0, + description: "" +}; + +const genRandomStartEnd = (min = 0, max = 100): [number, number] => { + const start = genRandomNumber(min, max); + const end = genRandomNumber(start + 1, max); + return [start, end]; +}; describe("getSkipSegments", () => { const endpoint = "/api/skipSegments"; - before(async () => { - const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "shadowHidden", "description") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; - await db.prepare("run", query, ["getSkipSegmentID0", 1, 11, 1, 0, "uuid01", "testman", 0, 50, "sponsor", "skip", "YouTube", 100, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentID0", 12, 14, 2, 0, "uuid02", "testman", 0, 50, "sponsor", "mute", "YouTube", 100, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentID0", 20, 33, 2, 0, "uuid03", "testman", 0, 50, "intro", "skip", "YouTube", 101, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentID1", 1, 11, 2, 0, "uuid10", "testman", 0, 50, "sponsor", "skip", "PeerTube", 120, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentID2", 1, 11, 2, 1, "uuid20", "testman", 0, 50, "sponsor", "skip", "YouTube", 140, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentID3", 1, 11, 2, 0, "uuid30", "testman", 0, 50, "sponsor", "skip", "YouTube", 200, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentID3", 7, 22, -3, 0, "uuid31", "testman", 0, 50, "sponsor", "skip", "YouTube", 300, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentMultiple", 1, 11, 2, 0, "uuid40", "testman", 0, 50, "intro", "skip", "YouTube", 400, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentMultiple", 20, 33, 2, 0, "uuid41", "testman", 0, 50, "intro", "skip", "YouTube", 500, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentLocked", 20, 33, 2, 1, "uuid50", "testman", 0, 50, "intro", "skip", "YouTube", 230, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentLocked", 20, 34, 100000, 0, "uuid51", "testman", 0, 50, "intro", "skip", "YouTube", 190, 0, 0, ""]); - await db.prepare("run", query, ["getSkipSegmentID6", 20, 34, 100000, 0, "uuid60", "testman", 0, 50, "sponsor", "skip", "YouTube", 190, 1, 0, ""]); - await db.prepare("run", query, ["requiredSegmentVid", 60, 70, 2, 0, "requiredSegmentVid1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]); - await db.prepare("run", query, ["requiredSegmentVid", 60, 70, -2, 0, "requiredSegmentVid2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]); - await db.prepare("run", query, ["requiredSegmentVid", 80, 90, -2, 0, "requiredSegmentVid3", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]); - await db.prepare("run", query, ["requiredSegmentVid", 80, 90, 2, 0, "requiredSegmentVid4", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]); - await db.prepare("run", query, ["requiredSegmentVid", 60, 70, 0, 0, "requiredSegmentVid-hidden", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 1, 0, ""]); - await db.prepare("run", query, ["requiredSegmentVid", 80, 90, 0, 0, "requiredSegmentVid-shadowhidden", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 1, ""]); - await db.prepare("run", query, ["chapterVid", 60, 80, 2, 0, "chapterVid-1", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "Chapter 1"]); - await db.prepare("run", query, ["chapterVid", 70, 75, 2, 0, "chapterVid-2", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "Chapter 2"]); - await db.prepare("run", query, ["chapterVid", 71, 75, 2, 0, "chapterVid-3", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "Chapter 3"]); - await db.prepare("run", query, ["requiredSegmentHashVid", 10, 20, -2, 0, "1d04b98f48e8f8bcc15c6ae5ac050801cd6dcfd428fb5f9e65c4e16e7807340fa", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]); - await db.prepare("run", query, ["requiredSegmentHashVid", 20, 30, -2, 0, "1ebde8e8ae03096b6c866aa2c8cc7ee1d720ca1fca27bea3f39a6a1b876577e71", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]); - return; - }); + let randomVideoID: string; + + beforeEach(() => { + randomVideoID = genRandomValue("video", "getSkipSegments"); + }); + + const createSegment = async(segment: ResponseSegment = {}, overrides: insertSegmentParams = {}): Promise => { + // setup random values + const user = genAnonUser(); + const UUID = genRandomValue("uuid", "getSkipSegments"); + // if description, add chapter + if (segment.description) { + segment.category = "chapter"; + segment.actionType = "chapter"; + } + // insert segments + const responseSegmentOverride: ResponseSegment = { + ...defaultResponseSegment, + UUID, + ...segment + }; + const sanitizedResponseSegment = responseSegmentOverride as ResponseSegment; + delete sanitizedResponseSegment.segment; + const result: insertSegmentParams = { + ...sanitizedResponseSegment, + videoID: randomVideoID, + startTime: segment.segment?.[0] ?? 0, + endTime: segment.segment?.[1] ?? 10, + userID: user.pubID, + ...overrides + }; + await insertSegment(db, result); + return responseSegmentOverride; + }; - it("Should be able to get a time by category 1", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID0", category: "sponsor" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 1); - assert.strictEqual(data[0].segment[0], 1); - assert.strictEqual(data[0].segment[1], 11); - assert.strictEqual(data[0].category, "sponsor"); - assert.strictEqual(data[0].UUID, "uuid01"); - assert.strictEqual(data[0].votes, 1); - assert.strictEqual(data[0].locked, 0); - assert.strictEqual(data[0].videoDuration, 100); - done(); - }) - .catch(err => done(err)); - }); + const createAndAssert = async (segments: ResponseSegment[] = [{}], request: AxiosRequestConfig, overrides: insertSegmentParams = {}) => { + const parsedSegments: ResponseSegment[] = []; + for (const segment of segments) { + parsedSegments.push(await createSegment(segment, overrides)); + } + // fetch response + const obj = { url: endpoint, method: "get", ...request }; + const res = await client(obj); + // assert response + assert.strictEqual(res.status, 200); + const data = res.data; + assert.ok(arrayPartialDeepEquals(data, parsedSegments), `Expected \n${JSON.stringify(parsedSegments, null, 2)} \nto equal \n${JSON.stringify(data, null, 2)}`); + assert.strictEqual(data.length, parsedSegments.length); + }; - it("Should be able to get a time by category and action type", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID0", category: "sponsor", actionType: "mute" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const expected = [{ - segment: [12, 14], - category: "sponsor", - UUID: "uuid02", - videoDuration: 100 - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data.length, 1); - done(); - }) - .catch(err => done(err)); + it("Should be able to get a time by category", () => { + const overrides = { category: "sponsor" }; + return createAndAssert([overrides], { params: { videoID: randomVideoID, ...overrides } }); }); - it("Should be able to get a time by category and getSkipSegmentMultiple action types", (done) => { - client.get(`${endpoint}?videoID=getSkipSegmentID0&category=sponsor&actionType=mute&actionType=skip`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const expected = [{ - segment: [1, 11], - category: "sponsor", - UUID: "uuid01", - videoDuration: 100 - }, { - UUID: "uuid02" - }]; - assert.strictEqual(data.length, 2); - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get a time by category and action type", () => { + const overrides = { category: "sponsor", actionType: "mute" }; + return createAndAssert([overrides], { params: { videoID: randomVideoID, ...overrides } }); }); - it("Should be able to get a time by category and getSkipSegmentMultiple action types (JSON array)", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID0", category: "sponsor", actionTypes: `["mute","skip"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const expected = [{ - segment: [1, 11], - category: "sponsor", - UUID: "uuid01", - videoDuration: 100 - }, { - UUID: "uuid02" - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get a time by category and multiple action types", () => { + const override1 = { segment: genRandomStartEnd(), category: "intro", actionType: "mute" }; + const override2 = { segment: genRandomStartEnd(), category: "intro", actionType: "skip" }; + return createAndAssert([override1, override2], { url: `${endpoint}?videoID=${randomVideoID}&category=intro&actionType=mute&actionType=skip` }); }); - it("Should be able to get a time by category for a different service 1", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID1", category: "sponsor", service: "PeerTube" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - const expected = [{ - segment: [1, 11], - category: "sponsor", - UUID: "uuid10", - videoDuration: 120 - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data.length, 1); - done(); - }) - .catch(err => done(err)); + it("Should be able to get a time by category and getSkipSegmentMultiple action types (JSON array)", () => { + const override1 = { segment: genRandomStartEnd(), category: "sponsor", actionType: "mute" }; + const override2 = { segment: genRandomStartEnd(), category: "sponsor", actionType: "skip" }; + return createAndAssert([override1, override2], { params: { videoID: randomVideoID, category: "sponsor", actionTypes: `["mute","skip"]` } }); }); - it("Should be able to get a time by category 2", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID0", category: "intro" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 1); - const expected = [{ - segment: [20, 33], - category: "intro", - UUID: "uuid03" - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get a time by category for a different service", () => { + const serviceOverride = { service: "PeerTube" as Service }; + const override = { category: "sponsor" }; + return createAndAssert([override], { params: { videoID: randomVideoID, ...override, ...serviceOverride } }, serviceOverride); }); - it("Should be able to get a time by categories array", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID0", categories: `["sponsor"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 1); - const expected = [{ - segment: [1, 11], - category: "sponsor", - UUID: "uuid01", - videoDuration: 100 - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get a time by category", () => { + const override = { category: "intro" }; + return createAndAssert([override], { params: { videoID: randomVideoID, ...override } }); }); - it("Should be able to get a time by categories array 2", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID0", categories: `["intro"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 1); - const expected = [{ - segment: [20, 33], - category: "intro", - UUID: "uuid03", - videoDuration: 101 - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get a time by categories array", () => { + return createAndAssert([{}], { params: { videoID: randomVideoID, categories: `["sponsor"]` } }); }); - it("Should return 404 if all submissions are hidden", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID6" } }) - .then(res => { - assert.strictEqual(res.status, 404); - done(); - }) - .catch(err => done(err)); + it("Should be able to get a time by categories array", () => { + const override = { category: "intro" }; + return createAndAssert([override], { params: { videoID: randomVideoID, categories: `["intro"]` } }); }); - it("Should be able to get getSkipSegmentMultiple times by category", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentMultiple", categories: `["intro"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 2); - const expected = [{ - segment: [1, 11], - category: "intro", - UUID: "uuid40", - }, { - segment: [20, 33], - category: "intro", - UUID: "uuid41", - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should return 404 if all submissions are hidden", async () => { + await createSegment({}, { videoID: randomVideoID, hidden: true }); + client.get(endpoint, { params: { videoID: randomVideoID } }) + .then(res => assert.strictEqual(res.status, 404)); }); - it("Should be able to get getSkipSegmentMultiple times by getSkipSegmentMultiple categories", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID0", categories: `["sponsor", "intro"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 2); - const expected = [{ - segment: [1, 11], - category: "sponsor", - UUID: "uuid01", - }, { - segment: [20, 33], - category: "intro", - UUID: "uuid03", - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get getSkipSegmentMultiple times by category", () => { + const category = { category: "intro" }; + const override1 = { segment: genRandomStartEnd(10), ...category }; + const override2 = { segment: genRandomStartEnd(20), ...category }; + return createAndAssert([override1, override2], { params: { videoID: randomVideoID, categories: `["intro"]` } }); }); - it("Should be possible to send unexpected query parameters", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID0", fakeparam: "hello", category: "sponsor" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 1); - const expected = [{ - segment: [1, 11], - category: "sponsor", - UUID: "uuid01", - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get getSkipSegmentMultiple times by getSkipSegmentMultiple categories", () => { + const override1 = { segment: genRandomStartEnd(), category: "sponsor" }; + const override2 = { segment: genRandomStartEnd(), category: "intro" }; + return createAndAssert([override1, override2], { params: { videoID: randomVideoID, categories: `["sponsor", "intro"]` } }); }); - it("Low voted submissions should be hidden", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID3", category: "sponsor" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 1); - const expected = [{ - segment: [1, 11], - category: "sponsor", - UUID: "uuid30", - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be possible to send unexpected query parameters", () => { + return createAndAssert([{}], { params: { videoID: randomVideoID, fakeparam: "hello" } }); }); - it("Should return 404 if no segment found", (done) => { - client.get(endpoint, { params: { videoID: "notarealvideo" } }) - .then(res => { - assert.strictEqual(res.status, 404); - done(); - }) - .catch(err => done(err)); + it("Low voted submissions should be hidden", async () => { + await createSegment({}, { videoID: randomVideoID, votes: -3 }); + return createAndAssert([{}], { params: { videoID: randomVideoID } }); }); - it("Should return 400 if bad categories argument", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID0", categories: `[not-quoted,not-quoted]` } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should return 404 if no segment found", () => { + client.get(endpoint, { params: { videoID: randomVideoID } }) + .then(res => assert.strictEqual(res.status, 404)); }); - it("Should be able send a comma in a query param", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID2", category: "sponsor" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 1); - const expected = [{ - segment: [1, 11], - category: "sponsor", - UUID: "uuid20", - votes: 2, - locked: 1 - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should return 400 if bad categories argument", () => { + client.get(endpoint, { params: { videoID: randomVideoID, categories: `[not-quoted,not-quoted]` } }) + .then(res => assert.strictEqual(res.status, 400)); }); - it("Should always get getSkipSegmentLocked segment", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentLocked", category: "intro" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 1); - const expected = [{ - segment: [20, 33], - category: "intro", - UUID: "uuid50", - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should always get getSkipSegmentLocked segment", () => { + const nonLocked = { category: "intro", votes: 10000 }; + const locked = { category: "intro", locked: 1 }; + createAndAssert([nonLocked, locked], { params: { videoID: randomVideoID } }); }); - it("Should be able to get getSkipSegmentMultiple categories with repeating parameters", (done) => { - client.get(`${endpoint}?category=sponsor&category=intro`, { params: { videoID: "getSkipSegmentID0" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 2); - const expected = [{ - segment: [1, 11], - category: "sponsor", - UUID: "uuid01", - }, { - segment: [20, 33], - category: "intro", - UUID: "uuid03", - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get getSkipSegmentMultiple categories with repeating parameters", () => { + const override1 = { segment: genRandomStartEnd(), category: "sponsor" }; + const override2 = { segment: genRandomStartEnd(), category: "intro" }; + return createAndAssert([override1, override2], { url: `${endpoint}?category=sponsor&category=intro`, params: { videoID: randomVideoID } }); }); - it("Should be able to get, categories param overriding repeating category", (done) => { - client.get(`${endpoint}?videoID=getSkipSegmentID0&categories=["sponsor"]&category=intro`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 1); - const expected = [{ - segment: [1, 11], - category: "sponsor", - UUID: "uuid01", - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get, categories param overriding repeating category", () => { + const override1 = { segment: genRandomStartEnd(), category: "sponsor" }; + const override2 = { segment: genRandomStartEnd(), category: "intro" }; + createSegment({ ...override2 }, { videoID: randomVideoID }); + return createAndAssert([override1], { params: { videoID: randomVideoID, categories: ["sponsor"], category: "intro" } }); }); - it("Should be able to get specific segments with requiredSegments", (done) => { - const required2 = "requiredSegmentVid2"; - const required3 = "requiredSegmentVid3"; - client.get(endpoint, { params: { videoID: "requiredSegmentVid", requiredSegments: `["${required2}","${required3}"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 2); - const expected = [{ - UUID: required2, - }, { - UUID: required3, - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get specific segments with requiredSegments", () => { + const required1 = { segment: genRandomStartEnd(10, 20), UUID: genRandomValue("uuid", "getSkipSegments-req"), votes: -2 }; + const required2 = { segment: genRandomStartEnd(20, 30), UUID: genRandomValue("uuid", "getSkipSegments-req"), votes: -2 }; + return createAndAssert([required1, required2], + { params: { videoID: randomVideoID, requiredSegments: `["${required1.UUID}","${required2.UUID}"]` } }); }); - it("Should be able to get specific segments with repeating requiredSegment", (done) => { - const required2 = "requiredSegmentVid2"; - const required3 = "requiredSegmentVid3"; - client.get(`${endpoint}?videoID=requiredSegmentVid&requiredSegment=${required2}&requiredSegment=${required3}`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 2); - const expected = [{ - UUID: required2, - }, { - UUID: required3, - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get specific segments with repeating requiredSegment", () => { + const required1 = { segment: genRandomStartEnd(10, 20), UUID: genRandomValue("uuid", "getSkipSegments-req"), votes: -2 }; + const required2 = { segment: genRandomStartEnd(20, 30), UUID: genRandomValue("uuid", "getSkipSegments-req"), votes: -2 }; + return createAndAssert([required1, required2], + { url: `${endpoint}?requiredSegment=${required1.UUID}&requiredSegment=${required2.UUID}`, params: { videoID: randomVideoID } }); }); - it("Should be able to get overlapping chapter segments if very different", (done) => { - client.get(`${endpoint}?videoID=chapterVid&category=chapter&actionType=chapter`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 2); - const expected = [{ - UUID: "chapterVid-1", - description: "Chapter 1" - }, { - UUID: "chapterVid-2", - description: "Chapter 2" - }]; - const expected2 = [{ - UUID: "chapterVid-1", - description: "Chapter 1" - }, { - UUID: "chapterVid-3", - description: "Chapter 3" - }]; - - assert.ok(partialDeepEquals(data, expected, false) || partialDeepEquals(data, expected2)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get overlapping chapter segments if very different", async () => { + const chapter1 = { segment: [60, 80], UUID: genRandomValue("uuid", "getSkipSegments-req"), description: "Chapter 1" }; + const chapter2 = { segment: [70, 75], UUID: genRandomValue("uuid", "getSkipSegments-req"), description: "Chapter 2" }; + const chapter3 = { segment: [71, 75], UUID: genRandomValue("uuid", "getSkipSegments-req"), description: "Chapter 3" }; + for (const chapter of [chapter1, chapter2, chapter3]) { + createSegment(chapter, { videoID: randomVideoID }); + } + const videoResponse = await client.get(endpoint, { params: { videoID: randomVideoID, actionType: "chapter", category: "chapter" } }); + const ch12 = [chapter1, chapter2]; + const ch13 = [chapter1, chapter3]; + const data = videoResponse.data; + assert.strictEqual(data.length, 2); + return assert.ok(partialDeepEquals(data, ch12, false) || partialDeepEquals(data, ch13)); }); - it("Should get 400 if no videoID passed in", (done) => { - client.get(endpoint) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should get 400 if no videoID passed in", () => { + return client.get(endpoint) + .then(res => assert.strictEqual(res.status, 400)); }); - it("Should be able to get requiredSegment by partial", (done) => { - const required1 = "1d04b98f48e8f8bcc15c6ae5ac050801cd6dcfd428fb5f9e65c4e16e7807340fa"; - const required2 = "1ebde8e8ae03096b6c866aa2c8cc7ee1d720ca1fca27bea3f39a6a1b876577e71"; - client.get(`${endpoint}?videoID=requiredSegmentHashVid&requiredSegment=${required1.slice(0,8)}&requiredSegment=${required2.slice(0,8)}`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 2); - const expected = [{ - UUID: required1, - }, { - UUID: required2, - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get requiredSegment by partial", () => { + const required1 = { segment: genRandomStartEnd(10, 20), UUID: genRandomValue("uuid", "getSkipSegments-req"), votes: -2 }; + const required2 = { segment: genRandomStartEnd(20, 30), UUID: genRandomValue("uuid", "getSkipSegments-req"), votes: -2 }; + return createAndAssert([required1, required2], + { url: `${endpoint}?requiredSegment=${required1.UUID.slice(0,8)}&requiredSegment=${required2.UUID.slice(0,8)}`, params: { videoID: randomVideoID } }); }); - it("Should be able to get hidden segments with requiredSegments", (done) => { - const required3 = "requiredSegmentVid3"; - const requiredHidden = "requiredSegmentVid-hidden"; - client.get(endpoint, { params: { videoID: "requiredSegmentVid", requiredSegments: `["${requiredHidden}","${required3}"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 2); - const expected = [{ - UUID: requiredHidden, - }, { - UUID: required3, - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get hidden segments with requiredSegments", () => { + const hidden = { UUID: genRandomValue("uuid", "getSkipSegments-req") }; + return createAndAssert([hidden], { params: { videoID: randomVideoID, requiredSegment: hidden.UUID } }, { hidden: true }); }); - it("Should be able to get shadowhidden segments with requiredSegments", (done) => { - const required2 = "requiredSegmentVid2"; - const requiredShadowHidden = "requiredSegmentVid-shadowhidden"; - client.get(endpoint, { params: { videoID: "requiredSegmentVid", requiredSegments: `["${required2}","${requiredShadowHidden}"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = res.data; - assert.strictEqual(data.length, 2); - const expected = [{ - UUID: required2, - }, { - UUID: requiredShadowHidden, - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get shadowhidden segments with requiredSegments", () => { + const shadowHidden = { UUID: genRandomValue("uuid", "getSkipSegments-req") }; + return createAndAssert([shadowHidden], { params: { videoID: randomVideoID, requiredSegment: shadowHidden.UUID } }, { shadowHidden: true }); }); - it("Should get 400 for invalid category type", (done) => { - client.get(endpoint, { params: { videoID: "getSkipSegmentID0", category: 1 } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); + it("Should get 400 for invalid category type", () => { + return client.get(endpoint, { params: { videoID: randomVideoID, category: 1 } }) + .then(res => assert.strictEqual(res.status, 400)); }); }); diff --git a/test/utils/segmentQueryGen.ts b/test/utils/segmentQueryGen.ts index 90db84b7..ecbdd8c4 100644 --- a/test/utils/segmentQueryGen.ts +++ b/test/utils/segmentQueryGen.ts @@ -33,7 +33,7 @@ export interface insertSegmentParams extends baseParams { const defaultSegmentParams: insertSegmentParams = { videoID: "", startTime: 0, - endTime: 0, + endTime: 10, votes: 0, locked: false, UUID: "", From be3490a3f5580f2ab40ca59aa9a0489dd27a2e0f Mon Sep 17 00:00:00 2001 From: Michael C Date: Thu, 14 Dec 2023 22:43:36 -0500 Subject: [PATCH 28/31] partial skipsegments byhash rewrite - split 4XX tests out --- test/cases/getSkipSegmentByHash400.ts | 93 +++++++++ test/cases/getSkipSegmentsByHash.ts | 284 +++++++++++--------------- test/utils/segmentQueryGen.ts | 6 +- 3 files changed, 212 insertions(+), 171 deletions(-) create mode 100644 test/cases/getSkipSegmentByHash400.ts diff --git a/test/cases/getSkipSegmentByHash400.ts b/test/cases/getSkipSegmentByHash400.ts new file mode 100644 index 00000000..a6d1c30b --- /dev/null +++ b/test/cases/getSkipSegmentByHash400.ts @@ -0,0 +1,93 @@ +import { db } from "../../src/databases/databases"; +import { getHash } from "../../src/utils/getHash"; +import { ImportMock, } from "ts-mock-imports"; +import * as YouTubeAPIModule from "../../src/utils/youtubeApi"; +import { genRandomValue, genRandom } from "../utils/genRandom"; +import { insertSegment } from "../utils/segmentQueryGen"; +import { YouTubeApiMock } from "../mocks/youtubeMock"; +import { AxiosRequestConfig } from "axios"; +import assert from "assert"; +import { client } from "../utils/httpClient"; + +const mockManager = ImportMock.mockStaticClass(YouTubeAPIModule, "YouTubeAPI"); +const sinonStub = mockManager.mock("listVideos"); +sinonStub.callsFake(YouTubeApiMock.listVideos); + +const endpoint = "/api/skipSegments"; + +const genRandomHash = () => genRandom(4); + +describe("getSkipSegmentsByHash 4XX", () => { + const realVideoID = genRandomValue("video", "getSkipSegments"); + const realVideoIDHashed = getHash(realVideoID); + const realVideo = { + videoID: realVideoID, + hashedID: realVideoIDHashed, + partialHash: realVideoIDHashed.substring(0, 5) + }; + + before(async() => { + await insertSegment(db, { videoID: realVideo.videoID }); + }); + + const assertStatus = (axiosConfig: AxiosRequestConfig, status: number, hash = genRandomHash()) => { + axiosConfig.validateStatus = () => true; + return client.get(`${endpoint}/${hash}`, axiosConfig) + .then(res => { + //console.log(res) + assert.notStrictEqual(res.data, "videoID not specified"); + assert.ok(!res.data?.[0]?.segments?.length); + assert.strictEqual(res.status, status, res.data); + }); + }; + + it("Should return 400 for bad format categories", () => { + return assertStatus({ params: { categories: "shilling" } }, 400); + }); + + it("Should return 400 prefix too short", () => { + return assertStatus({ params: { categories: `["shilling"]` } }, 400, "11"); + }); + + it("Should return 400 prefix too long", () => { + const prefix = "1".repeat(50); + assert.ok(prefix.length > 33, "failed to generate long enough string"); + return assertStatus({ params: { category: "sponsor" } }, 400, prefix); + }); + + it("Should return 404 prefix in range", () => { + const prefix = "1".repeat(4); + return assertStatus({ params: { categories: `["sponsor"]` } }, 404, prefix); + }); + + it("Should return 400 if categories are is number", () => { + return assertStatus({ params: { categories: 3 } }, 400); + }); + + it("Should return 400 if actionTypes is number", () => { + return assertStatus({ params: { actionTypes: 3 } }, 400); + }); + + it("Should return 400 if actionTypes are invalid json", () => { + return assertStatus({ params: { actionTypes: "{test}" } }, 400); + }); + + it("Should return 400 if requiredSegments is number", () => { + return assertStatus({ url: `${endpoint}/${genRandomHash()}`, params: { requiredSegments: 3 } }, 400); + }); + + it("Should return 400 if requiredSegments is invalid json", () => { + return assertStatus({ url: `${endpoint}/${genRandomHash()}`, params: { requiredSegments: "{test}" } }, 400); + }); + + it("Should 404 instead of 400 if requiredSegments is invalid", () => { + return assertStatus({ url: `${endpoint}/${realVideo.partialHash}`, params: { requiredSegments: undefined } }, 404); + }); + + it("Should return 404 if all videos hidden", () => { + const videoID = genRandomValue("video", "getSkipSegmentsHash404"); + const hashedIDPrefix = getHash(videoID).substring(0, 5); + insertSegment(db, { hidden: 1, videoID }); + return assertStatus({}, 404, hashedIDPrefix); + }); +}); diff --git a/test/cases/getSkipSegmentsByHash.ts b/test/cases/getSkipSegmentsByHash.ts index 3129f91f..b799b767 100644 --- a/test/cases/getSkipSegmentsByHash.ts +++ b/test/cases/getSkipSegmentsByHash.ts @@ -3,33 +3,131 @@ import { partialDeepEquals, arrayPartialDeepEquals } from "../utils/partialDeepE import { getHash } from "../../src/utils/getHash"; import { ImportMock, } from "ts-mock-imports"; import * as YouTubeAPIModule from "../../src/utils/youtubeApi"; +import { multiGenProxy } from "../utils/genRandom"; +import { insertSegment, insertSegmentParams } from "../utils/segmentQueryGen"; import { YouTubeApiMock } from "../mocks/youtubeMock"; +import { AxiosRequestConfig } from "axios"; import assert from "assert"; import { client } from "../utils/httpClient"; +import { Service, VideoIDHash } from "../../src/types/segments.model"; const mockManager = ImportMock.mockStaticClass(YouTubeAPIModule, "YouTubeAPI"); const sinonStub = mockManager.mock("listVideos"); sinonStub.callsFake(YouTubeApiMock.listVideos); +type HashResponseSegment = { + videoID: string, + segments: ResponseSegment[] +} + +type ResponseSegment = { + segment?: [number, number] | number[], + category?: string, + UUID?: string, + videoDuration?: number, + actionType?: string, + votes?: number, + locked?: number, + description?: string +}; + +const defaultResponseSegment = { + segment: [1, 10] as [number, number], + category: "sponsor", + UUID: "", + videoDuration: 0, + actionType: "skip", + votes: 0, + locked: 0, + description: "" +}; + +interface segmentInsertSegmentParams extends insertSegmentParams { + segment: [number, number] | number[], +} + describe("getSkipSegmentsByHash", () => { const endpoint = "/api/skipSegments"; + const videoIDs = multiGenProxy("video", "getSegmentsByHash"); + const hashedVideoIDs = new Proxy({}, { + get(target: Record, prop: string, receiver) { + if (Reflect.has(target, prop)) return Reflect.get(target, prop, receiver); + const videoID = videoIDs[prop]; + const hashedVideoID = getHash(videoID, 1); + const hashPrefix = hashedVideoID.substring(0, 5); + const result = { videoID, hashedVideoID, hashPrefix }; + Reflect.set(target, prop, result, receiver); + return result; + } + }); + const getSegmentsByHash0Hash = "fdaff4dee1043451faa7398324fb63d8618ebcd11bddfe0491c488db12c6c910"; const requiredSegmentVidHash = "d51822c3f681e07aef15a8855f52ad12db9eb9cf059e65b16b64c43359557f61"; const requiredSegmentHashVidHash = "17bf8d9090e050257772f8bff277293c29c7ce3b25eb969a8fae111a2434504d"; const differentCategoryVidHash = "7fac44d1ee3257ec7f18953e2b5f991828de6854ad57193d1027c530981a89c0"; const nonMusicOverlapVidHash = "306151f778f9bfd19872b3ccfc83cbab37c4f370717436bfd85e0a624cd8ba3c"; const fullCategoryVidHash = "278fa987eebfe07ae3a4a60cf0663989ad874dd0c1f0430831d63c2001567e6f"; + const insertSegments: insertSegmentParams[] = [ + // videoID: "getSegmentsByHash-0" + { videoID: "getSegmentsByHash-0", startTime: 1, endTime: 10, UUID: "getSegmentsByHash-01" }, + { videoID: "getSegmentsByHash-0", startTime: 1, endTime: 10, UUID: "getSegmentsByHash-02", service: Service.PeerTube }, + { videoID: "getSegmentsByHash-0", startTime: 20, endTime: 30, UUID: "getSegmentsByHash-03", category: "intro" }, + { videoID: "getSegmentsByHash-0", startTime: 40, endTime: 50, UUID: "getSegmentsByHash-04", actionType: "mute" }, + // getSegmentsByHash-noMatchHash + { videoID: "getSegmentsByHash-noMatchHash", startTime: 40, endTime: 50, UUID: "getSegmentsByHash-noMatchHash", hashedVideoID: "fdaffnoMatchHash" }, + ]; + + const assertSegmentsEqual = async (hashPrefix: string, expectedUUIDs: string[], axiosConfig: AxiosRequestConfig = {}) => { + // fetch segments + const res = await client.get(`${endpoint}/${hashPrefix}`, axiosConfig); + assert.strictEqual(res.status, 200); + const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); + // sort out videos + const expected = insertSegments.filter(segment => segment.UUID && expectedUUIDs.includes(segment.UUID)); + const expectedArray: HashResponseSegment[] = []; + for (const segment of expected) { + const videoID = segment.videoID; + if (!videoID) throw new Error("VideoID is undefined"); + const rawResponse = { + ...defaultResponseSegment, + ...segment + }; + if (!rawResponse.startTime || !rawResponse.endTime) throw new Error("startTime or endTime is undefined"); + const sanitizedResponse: ResponseSegment = { + segment: [rawResponse.startTime, rawResponse.endTime], + category: rawResponse.category, + UUID: rawResponse.UUID, + videoDuration: rawResponse.videoDuration, + actionType: rawResponse.actionType, + votes: rawResponse.votes, + locked: Number(rawResponse.locked), + description: rawResponse.description + }; + // insert into array + const match = expectedArray.findIndex((s: insertSegmentParams) => s.videoID === videoID); + if (match !== -1) { + expectedArray[match].segments.push(sanitizedResponse); + } else { + expectedArray.push({ + videoID, + segments: [sanitizedResponse] + }); + } + } + assert.ok(partialDeepEquals(data, expectedArray)); + }; + before(async () => { - const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", views, category, "actionType", "service", "hidden", "shadowHidden", "hashedVideoID", "description") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; - await db.prepare("run", query, ["getSegmentsByHash-0", 1, 10, 2, 0, "getSegmentsByHash-01", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, getSegmentsByHash0Hash, ""]); - await db.prepare("run", query, ["getSegmentsByHash-0", 1, 10, 2, 0, "getSegmentsByHash-02", "testman", 0, 50, "sponsor", "skip", "PeerTube", 0, 0, getSegmentsByHash0Hash, ""]); - await db.prepare("run", query, ["getSegmentsByHash-0", 20, 30, 2, 0, "getSegmentsByHash-03", "testman", 100, 150, "intro", "skip", "YouTube", 0, 0, getSegmentsByHash0Hash, ""]); - await db.prepare("run", query, ["getSegmentsByHash-0", 40, 50, 2, 0, "getSegmentsByHash-04", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getSegmentsByHash0Hash, ""]); - await db.prepare("run", query, ["getSegmentsByHash-noMatchHash", 40, 50, 2, 0, "getSegmentsByHash-noMatchHash", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, "fdaffnoMatchHash", ""]); + for (const segment of insertSegments) { + await insertSegment(db, segment); + } + const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", views, category, "actionType", "service", "hidden", "shadowHidden", "hashedVideoID", "description") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' + await db.prepare("run", query, ["getSegmentsByHash-1", 60, 70, 2, 0, "getSegmentsByHash-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, "3272fa85ee0927f6073ef6f07ad5f3146047c1abba794cfa364d65ab9921692b", ""]); await db.prepare("run", query, ["onlyHidden", 60, 70, 2, 0, "onlyHidden", "testman", 0, 50, "sponsor", "skip", "YouTube", 1, 0, "f3a199e1af001d716cdc6599360e2b062c2d2b3fa2885f6d9d2fd741166cbbd3", ""]); - await db.prepare("run", query, ["highlightVid", 60, 60, 2, 0, "highlightVid-1", "testman", 0, 50, "poi_highlight", "poi", "YouTube", 0, 0, getHash("highlightVid", 1), ""]); - await db.prepare("run", query, ["highlightVid", 70, 70, 2, 0, "highlightVid-2", "testman", 0, 50, "poi_highlight", "poi", "YouTube", 0, 0, getHash("highlightVid", 1), ""]); + + await insertSegment(db, { videoID: "highlightVid", startTime: 60, endTime: 60, UUID: "highlightVid-1", category: "poi_highlight", actionType: "poi" }); + await insertSegment(db, { videoID: "highlightVid", startTime: 70, endTime: 70, UUID: "highlightVid-2", category: "poi_highlight", actionType: "poi" }); await db.prepare("run", query, ["requiredSegmentVid", 60, 70, 2, 0, "requiredSegmentVid-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]); await db.prepare("run", query, ["requiredSegmentVid", 60, 70, -2, 0, "requiredSegmentVid-2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]); await db.prepare("run", query, ["requiredSegmentVid", 80, 90, -2, 0, "requiredSegmentVid-3", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]); @@ -41,10 +139,12 @@ describe("getSkipSegmentsByHash", () => { await db.prepare("run", query, ["longMuteVid-hash", 30, 35, 2, 0, "longMuteVid-hash-2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613 await db.prepare("run", query, ["longMuteVid-hash", 2, 80, 2, 0, "longMuteVid-hash-3", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613 await db.prepare("run", query, ["longMuteVid-hash", 3, 78, 2, 0, "longMuteVid-hash-4", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613 - await db.prepare("run", query, ["longMuteVid-2-hash", 1, 15, 2, 0, "longMuteVid-2-hash-1", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-2-hash", 1), ""]); //ab0c - await db.prepare("run", query, ["longMuteVid-2-hash", 30, 35, 2, 0, "longMuteVid-2-hash-2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, getHash("longMuteVid-2-hash", 1), ""]); //ab0c - await db.prepare("run", query, ["longMuteVid-2-hash", 2, 80, 2, 0, "longMuteVid-2-hash-3", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-2-hash", 1), ""]); //ab0c - await db.prepare("run", query, ["longMuteVid-2-hash", 3, 78, 2, 0, "longMuteVid-2-hash-4", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-2-hash", 1), ""]); //ab0c + + await insertSegment(db, { videoID: "longMuteVid-2-hash", startTime: 1, endTime: 15, UUID: "longMuteVid-2-hash-1", category: "sponsor", actionType: "mute" }); + await insertSegment(db, { videoID: "longMuteVid-2-hash", startTime: 30, endTime: 35, UUID: "longMuteVid-2-hash-2", category: "sponsor", actionType: "skip" }); + await insertSegment(db, { videoID: "longMuteVid-2-hash", startTime: 2, endTime: 80, UUID: "longMuteVid-2-hash-3", category: "sponsor", actionType: "mute" }); + await insertSegment(db, { videoID: "longMuteVid-2-hash", startTime: 3, endTime: 78, UUID: "longMuteVid-2-hash-4", category: "sponsor", actionType: "mute" }); + await db.prepare("run", query, ["requiredSegmentHashVid", 10, 20, -2, 0, "fbf0af454059733c8822f6a4ac8ec568e0787f8c0a5ee915dd5b05e0d7a9a388", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentHashVidHash, ""]); await db.prepare("run", query, ["requiredSegmentHashVid", 20, 30, -2, 0, "7e1ebc5194551d2d0a606d64f675e5a14952e4576b2959f8c9d51e316c14f8da", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentHashVidHash, ""]); await db.prepare("run", query, ["differentCategoryVid", 60, 70, 2, 0, "differentCategoryVid-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, differentCategoryVidHash, ""]); @@ -55,111 +155,13 @@ describe("getSkipSegmentsByHash", () => { await db.prepare("run", query, ["fullCategoryVid", 60, 70, 2, 1, "fullCategoryVid-2", "testman", 0, 50, "selfpromo", "full", "YouTube", 0, 0, fullCategoryVidHash, ""]); }); - it("Should be able to get a 200", (done) => { - client.get(`${endpoint}/3272f`, { params: { categories: `["sponsor", "intro"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - done(); - }) - .catch(err => done(err)); - }); - - it("Should return 404 if no segments are found even if a video for the given hash is known", (done) => { - client.get(`${endpoint}/3272f`, { params: { categories: `["shilling"]` } }) - .then(res => { - assert.strictEqual(res.status, 404); - assert.equal(res.data.length, 0); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get an empty array if no videos", (done) => { - client.get(`${endpoint}/11111`, { params: { categories: `["shilling"]` } }) - .then(res => { - assert.strictEqual(res.status, 404); - const body = res.data; - assert.strictEqual(body.length, 0); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get an empty array if only hidden videos", (done) => { - client.get(`${endpoint}/f3a1`, { params: { categories:`["sponsor"]` } }) - .then(res => { - if (res.status !== 404) done(`non 404 status code, was ${res.status}`); - else { - const body = res.data; - if (body.length === 0) done(); // pass - else done("non empty array returned"); - } - }) - .catch(err => done(err)); - }); - - it("Should return 400 prefix too short", (done) => { - client.get(`${endpoint}/11`, { params: { categories: `["shilling"]` } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - - it("Should return 400 prefix too long", (done) => { - const prefix = "1".repeat(50); - assert.ok(prefix.length > 33, "failed to generate long enough string"); - client.get(`${endpoint}/${prefix}`, { params: { categories: `["shilling"]` } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - - it("Should return 404 prefix in range", (done) => { - const prefix = "1".repeat(5); - client.get(`${endpoint}/${prefix}`, { params: { categories: `["shilling"]` } }) - .then(res => { - assert.strictEqual(res.status, 404); - done(); - }) - .catch(err => done(err)); - }); - - it("Should return 400 for no hash", (done) => { - client.get(`${endpoint}`, { params: { categories: `["shilling"]` } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - - it("Should return 400 for bad format categories", (done) => { - client.get(`${endpoint}/fdaf`, { params: { categories: "shilling" } }) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get multiple videos", (done) => { - client.get(`${endpoint}/fdaf`, { params: { categories: `["sponsor","intro"]` } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 2); - assert.strictEqual(data[0].segments.length, 2); - assert.strictEqual(data[1].segments.length, 1); - done(); - }) - .catch(err => done(err)); + it("Should be able to get multiple videos", () => { + const prefix = getSegmentsByHash0Hash.substring(0, 5); + return assertSegmentsEqual(prefix, ["getSegmentsByHash-01", "getSegmentsByHash-03", "getSegmentsByHash-noMatchHash"], { params: { categories: `["sponsor","intro"]` } }); }); it("Should be able to get 200 for no categories (default sponsor)", (done) => { + const prefix = getSegmentsByHash0Hash.substring(0, 5); client.get(`${endpoint}/fdaf`) .then(res => { assert.strictEqual(res.status, 200); @@ -601,58 +603,4 @@ describe("getSkipSegmentsByHash", () => { .catch(err => done(err)); }); - it("Should return 400 if categories are is number", (done) => { - client.get(`${endpoint}/17bf?categories=3`) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - - it("Should return 400 if actionTypes is number", (done) => { - client.get(`${endpoint}/17bf?actionTypes=3`) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - - it("Should return 400 if actionTypes are invalid json", (done) => { - client.get(`${endpoint}/17bf?actionTypes={test}`) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - - it("Should return 400 if requiredSegments is number", (done) => { - client.get(`${endpoint}/17bf?requiredSegments=3`) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - - - it("Should return 404 if requiredSegments is invalid json", (done) => { - client.get(`${endpoint}/17bf?requiredSegments={test}`) - .then(res => { - assert.strictEqual(res.status, 400); - done(); - }) - .catch(err => done(err)); - }); - - it("Should return 400 if requiredSegments is not present", (done) => { - client.get(`${endpoint}/17bf?requiredSegment=${fullCategoryVidHash}`) - .then(res => { - assert.strictEqual(res.status, 404); - done(); - }) - .catch(err => done(err)); - }); }); diff --git a/test/utils/segmentQueryGen.ts b/test/utils/segmentQueryGen.ts index ecbdd8c4..2643dad9 100644 --- a/test/utils/segmentQueryGen.ts +++ b/test/utils/segmentQueryGen.ts @@ -6,7 +6,7 @@ import { getHash } from "../../src/utils/getHash"; interface baseParams { videoID?: string - hashedVideoID?: VideoIDHash | "" + hashedVideoID?: string | VideoIDHash | "" userID?: HashedUserID | "" service?: Service timeSubmitted?: number @@ -26,7 +26,7 @@ export interface insertSegmentParams extends baseParams { hidden?: boolean | number, reputation?: number, shadowHidden?: boolean | number, - hashedVideoID?: VideoIDHash, + hashedVideoID?: string | VideoIDHash, userAgent?: string, description?: string } @@ -70,7 +70,7 @@ export const insertSegment = async(db: IDatabase, overrides: insertSegmentParams params.hidden = Number(params.hidden); params.shadowHidden = Number(params.shadowHidden); // generate hashedVideoID if not provided - params.hashedVideoID = overrides?.hashedVideoID ?? getHash(params.videoID, 1) as VideoIDHash; + params.hashedVideoID = (overrides?.hashedVideoID ?? getHash(params.videoID, 1)) as VideoIDHash; // debug await db.prepare("run", query, Object.values(params)); }; From 30f86933461db3db3f11cb718229026b59e42311 Mon Sep 17 00:00:00 2001 From: Michael C Date: Sat, 16 Dec 2023 07:23:12 -0500 Subject: [PATCH 29/31] insomnia fueled tests --- test/cases/getSkipSegmentsByHash.ts | 324 ++++++++++------------------ 1 file changed, 115 insertions(+), 209 deletions(-) diff --git a/test/cases/getSkipSegmentsByHash.ts b/test/cases/getSkipSegmentsByHash.ts index b799b767..20b06198 100644 --- a/test/cases/getSkipSegmentsByHash.ts +++ b/test/cases/getSkipSegmentsByHash.ts @@ -3,7 +3,7 @@ import { partialDeepEquals, arrayPartialDeepEquals } from "../utils/partialDeepE import { getHash } from "../../src/utils/getHash"; import { ImportMock, } from "ts-mock-imports"; import * as YouTubeAPIModule from "../../src/utils/youtubeApi"; -import { multiGenProxy } from "../utils/genRandom"; +import { genRandomNumber, genRandomValue, multiGenProxy } from "../utils/genRandom"; import { insertSegment, insertSegmentParams } from "../utils/segmentQueryGen"; import { YouTubeApiMock } from "../mocks/youtubeMock"; import { AxiosRequestConfig } from "axios"; @@ -54,27 +54,68 @@ describe("getSkipSegmentsByHash", () => { if (Reflect.has(target, prop)) return Reflect.get(target, prop, receiver); const videoID = videoIDs[prop]; const hashedVideoID = getHash(videoID, 1); - const hashPrefix = hashedVideoID.substring(0, 5); + const hashPrefix = hashedVideoID.substring(0, 4); const result = { videoID, hashedVideoID, hashPrefix }; Reflect.set(target, prop, result, receiver); return result; } }); + console.log(hashedVideoIDs[0].videoID) + console.log(hashedVideoIDs[0].hashedVideoID) + console.log(hashedVideoIDs[0].hashPrefix) + console.log(`${hashedVideoIDs[0].hashPrefix}noMatchHash`) - const getSegmentsByHash0Hash = "fdaff4dee1043451faa7398324fb63d8618ebcd11bddfe0491c488db12c6c910"; - const requiredSegmentVidHash = "d51822c3f681e07aef15a8855f52ad12db9eb9cf059e65b16b64c43359557f61"; - const requiredSegmentHashVidHash = "17bf8d9090e050257772f8bff277293c29c7ce3b25eb969a8fae111a2434504d"; + const requiredSegmentVidHash = getHash("requiredSegmentsVid", 1); + const requiredSegmentHashVidHash = getHash("requiredSegmentsHashVid", 1); const differentCategoryVidHash = "7fac44d1ee3257ec7f18953e2b5f991828de6854ad57193d1027c530981a89c0"; const nonMusicOverlapVidHash = "306151f778f9bfd19872b3ccfc83cbab37c4f370717436bfd85e0a624cd8ba3c"; const fullCategoryVidHash = "278fa987eebfe07ae3a4a60cf0663989ad874dd0c1f0430831d63c2001567e6f"; const insertSegments: insertSegmentParams[] = [ - // videoID: "getSegmentsByHash-0" - { videoID: "getSegmentsByHash-0", startTime: 1, endTime: 10, UUID: "getSegmentsByHash-01" }, - { videoID: "getSegmentsByHash-0", startTime: 1, endTime: 10, UUID: "getSegmentsByHash-02", service: Service.PeerTube }, - { videoID: "getSegmentsByHash-0", startTime: 20, endTime: 30, UUID: "getSegmentsByHash-03", category: "intro" }, - { videoID: "getSegmentsByHash-0", startTime: 40, endTime: 50, UUID: "getSegmentsByHash-04", actionType: "mute" }, + // videoID 0 + { videoID: hashedVideoIDs[0].videoID, startTime: 1, endTime: 10, UUID: "getSegmentsByHash-01" }, + { videoID: hashedVideoIDs[0].videoID, startTime: 1, endTime: 10, UUID: "getSegmentsByHash-02", service: Service.PeerTube }, + { videoID: hashedVideoIDs[0].videoID, startTime: 20, endTime: 30, UUID: "getSegmentsByHash-03", category: "intro" }, + { videoID: hashedVideoIDs[0].videoID, startTime: 40, endTime: 50, UUID: "getSegmentsByHash-04", actionType: "mute" }, // getSegmentsByHash-noMatchHash - { videoID: "getSegmentsByHash-noMatchHash", startTime: 40, endTime: 50, UUID: "getSegmentsByHash-noMatchHash", hashedVideoID: "fdaffnoMatchHash" }, + { videoID: "getSegmentsByHash-noMatchHash", startTime: 40, endTime: 50, UUID: "getSegmentsByHash-noMatchHash", hashedVideoID: `${hashedVideoIDs[0].hashedVideoID.substring(0,5)}noMatchHash` }, + // videoID: "getSegmentsByHash-1" + { videoID: "getSegmentsByHash-1", startTime: 60, endTime: 70, UUID: "getSegmentsByHash-1" }, + // videoID: onlyHidden + { videoID: "onlyHidden", startTime: 60, endTime: 70, UUID: "onlyHidden", hidden: true }, + // videoID: highlightVid + { videoID: hashedVideoIDs["highlight"].videoID, startTime: 60, endTime: 60, votes: 1, UUID: "highlightVid-1", category: "poi_highlight", actionType: "poi" }, + { videoID: hashedVideoIDs["highlight"].videoID, startTime: 70, endTime: 70, votes: 2, UUID: "highlightVid-2", category: "poi_highlight", actionType: "poi" }, + // videoID: requiredSegmentsVid + { videoID: "requiredSegmentsVid", startTime: 60, endTime: 70, UUID: "requiredSegmentsVid-1", votes: 2 }, + { videoID: "requiredSegmentsVid", startTime: 60, endTime: 70, UUID: "requiredSegmentsVid-2", votes: -2 }, + { videoID: "requiredSegmentsVid", startTime: 80, endTime: 90, UUID: "requiredSegmentsVid-3", votes: -2 }, + { videoID: "requiredSegmentsVid", startTime: 80, endTime: 90, UUID: "requiredSegmentsVid-4", votes: 2 }, + // specific UUIDs + { videoID: "requiredSegmentsHashVid", startTime: 10, endTime: 20, UUID: "fbf0af454059733c8822f6a4ac8ec568e0787f8c0a5ee915dd5b05e0d7a9a388", votes: -2 }, + { videoID: "requiredSegmentsHashVid", startTime: 20, endTime: 30, UUID: "7e1ebc5194551d2d0a606d64f675e5a14952e4576b2959f8c9d51e316c14f8da", votes: -2 }, + // videoID: chapterVid-hash + { videoID: "chapterVid-hash", startTime: 60, endTime: 80, UUID: "chapterVid-hash-1", actionType: "chapter", category: "chapter", description: "Chapter 1" }, + { videoID: "chapterVid-hash", startTime: 70, endTime: 75, UUID: "chapterVid-hash-2", actionType: "chapter", category: "chapter", description: "Chapter 2" }, + { videoID: "chapterVid-hash", startTime: 71, endTime: 75, UUID: "chapterVid-hash-3", actionType: "chapter", category: "chapter", description: "Chapter 3" }, + // videoID: longMuteVid-hash + { videoID: "longMuteVid-hash", startTime: 40, endTime: 45, UUID: "longMuteVid-hash-1" }, + { videoID: "longMuteVid-hash", startTime: 30, endTime: 35, UUID: "longMuteVid-hash-2" }, + { videoID: "longMuteVid-hash", startTime: 2, endTime: 80, UUID: "longMuteVid-hash-3", actionType: "mute" }, + { videoID: "longMuteVid-hash", startTime: 3, endTime: 78, UUID: "longMuteVid-hash-4", actionType: "mute" }, + // videoID: longMuteVid-2-hash + { videoID: "longMuteVid-2-hash", startTime: 1, endTime: 15, UUID: "longMuteVid-2-hash-1", actionType: "mute" }, + { videoID: "longMuteVid-2-hash", startTime: 30, endTime: 35, UUID: "longMuteVid-2-hash-2" }, + { videoID: "longMuteVid-2-hash", startTime: 2, endTime: 80, UUID: "longMuteVid-2-hash-3", actionType: "mute" }, + { videoID: "longMuteVid-2-hash", startTime: 3, endTime: 78, UUID: "longMuteVid-2-hash-4", actionType: "mute" }, + // videoID: differentCategoryVid + { videoID: "differentCategoryVid", startTime: 60, endTime: 70, UUID: "differentCategoryVid-1", category: "sponsor" }, + { videoID: "differentCategoryVid", startTime: 60, endTime: 70, UUID: "differentCategoryVid-2", category: "intro" }, + // videoID: nonMusicOverlapVid + { videoID: "nonMusicOverlapVid", startTime: 60, endTime: 70, UUID: "nonMusicOverlapVid-1", votes: 0, category: "sponsor" }, + { videoID: "nonMusicOverlapVid", startTime: 60, endTime: 70, UUID: "nonMusicOverlapVid-2", votes: 1, category: "music_offtopic" }, + // videoID: fullCategoryVid + { videoID: "fullCategoryVid", startTime: 60, endTime: 70, UUID: "fullCategoryVid-1", votes: 0, category: "sponsor", actionType: "full" }, + { videoID: "fullCategoryVid", startTime: 60, endTime: 70, UUID: "fullCategoryVid-2", votes: 1, category: "selfpromo", actionType: "full" }, ]; const assertSegmentsEqual = async (hashPrefix: string, expectedUUIDs: string[], axiosConfig: AxiosRequestConfig = {}) => { @@ -84,7 +125,7 @@ describe("getSkipSegmentsByHash", () => { const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); // sort out videos const expected = insertSegments.filter(segment => segment.UUID && expectedUUIDs.includes(segment.UUID)); - const expectedArray: HashResponseSegment[] = []; + let expectedArray: HashResponseSegment[] = []; for (const segment of expected) { const videoID = segment.videoID; if (!videoID) throw new Error("VideoID is undefined"); @@ -113,230 +154,91 @@ describe("getSkipSegmentsByHash", () => { segments: [sanitizedResponse] }); } + // sort array to match + expectedArray = expectedArray.sort((a, b) => a.videoID.localeCompare(b.videoID)); } - assert.ok(partialDeepEquals(data, expectedArray)); + assert.ok(partialDeepEquals(data, expectedArray, true)); }; before(async () => { for (const segment of insertSegments) { await insertSegment(db, segment); } - const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", views, category, "actionType", "service", "hidden", "shadowHidden", "hashedVideoID", "description") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' - - await db.prepare("run", query, ["getSegmentsByHash-1", 60, 70, 2, 0, "getSegmentsByHash-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, "3272fa85ee0927f6073ef6f07ad5f3146047c1abba794cfa364d65ab9921692b", ""]); - await db.prepare("run", query, ["onlyHidden", 60, 70, 2, 0, "onlyHidden", "testman", 0, 50, "sponsor", "skip", "YouTube", 1, 0, "f3a199e1af001d716cdc6599360e2b062c2d2b3fa2885f6d9d2fd741166cbbd3", ""]); - - await insertSegment(db, { videoID: "highlightVid", startTime: 60, endTime: 60, UUID: "highlightVid-1", category: "poi_highlight", actionType: "poi" }); - await insertSegment(db, { videoID: "highlightVid", startTime: 70, endTime: 70, UUID: "highlightVid-2", category: "poi_highlight", actionType: "poi" }); - await db.prepare("run", query, ["requiredSegmentVid", 60, 70, 2, 0, "requiredSegmentVid-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]); - await db.prepare("run", query, ["requiredSegmentVid", 60, 70, -2, 0, "requiredSegmentVid-2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]); - await db.prepare("run", query, ["requiredSegmentVid", 80, 90, -2, 0, "requiredSegmentVid-3", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]); - await db.prepare("run", query, ["requiredSegmentVid", 80, 90, 2, 0, "requiredSegmentVid-4", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]); - await db.prepare("run", query, ["chapterVid-hash", 60, 80, 2, 0, "chapterVid-hash-1", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, getHash("chapterVid-hash", 1), "Chapter 1"]); //7258 - await db.prepare("run", query, ["chapterVid-hash", 70, 75, 2, 0, "chapterVid-hash-2", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, getHash("chapterVid-hash", 1), "Chapter 2"]); //7258 - await db.prepare("run", query, ["chapterVid-hash", 71, 75, 2, 0, "chapterVid-hash-3", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, getHash("chapterVid-hash", 1), "Chapter 3"]); //7258 - await db.prepare("run", query, ["longMuteVid-hash", 40, 45, 2, 0, "longMuteVid-hash-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613 - await db.prepare("run", query, ["longMuteVid-hash", 30, 35, 2, 0, "longMuteVid-hash-2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613 - await db.prepare("run", query, ["longMuteVid-hash", 2, 80, 2, 0, "longMuteVid-hash-3", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613 - await db.prepare("run", query, ["longMuteVid-hash", 3, 78, 2, 0, "longMuteVid-hash-4", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613 - - await insertSegment(db, { videoID: "longMuteVid-2-hash", startTime: 1, endTime: 15, UUID: "longMuteVid-2-hash-1", category: "sponsor", actionType: "mute" }); - await insertSegment(db, { videoID: "longMuteVid-2-hash", startTime: 30, endTime: 35, UUID: "longMuteVid-2-hash-2", category: "sponsor", actionType: "skip" }); - await insertSegment(db, { videoID: "longMuteVid-2-hash", startTime: 2, endTime: 80, UUID: "longMuteVid-2-hash-3", category: "sponsor", actionType: "mute" }); - await insertSegment(db, { videoID: "longMuteVid-2-hash", startTime: 3, endTime: 78, UUID: "longMuteVid-2-hash-4", category: "sponsor", actionType: "mute" }); - - await db.prepare("run", query, ["requiredSegmentHashVid", 10, 20, -2, 0, "fbf0af454059733c8822f6a4ac8ec568e0787f8c0a5ee915dd5b05e0d7a9a388", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentHashVidHash, ""]); - await db.prepare("run", query, ["requiredSegmentHashVid", 20, 30, -2, 0, "7e1ebc5194551d2d0a606d64f675e5a14952e4576b2959f8c9d51e316c14f8da", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentHashVidHash, ""]); - await db.prepare("run", query, ["differentCategoryVid", 60, 70, 2, 0, "differentCategoryVid-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, differentCategoryVidHash, ""]); - await db.prepare("run", query, ["differentCategoryVid", 60, 70, 2, 1, "differentCategoryVid-2", "testman", 0, 50, "intro", "skip", "YouTube", 0, 0, differentCategoryVidHash, ""]); - await db.prepare("run", query, ["nonMusicOverlapVid", 60, 70, 2, 0, "nonMusicOverlapVid-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, nonMusicOverlapVidHash, ""]); - await db.prepare("run", query, ["nonMusicOverlapVid", 60, 70, 2, 1, "nonMusicOverlapVid-2", "testman", 0, 50, "music_offtopic", "skip", "YouTube", 0, 0, nonMusicOverlapVidHash, ""]); - await db.prepare("run", query, ["fullCategoryVid", 60, 70, 2, 0, "fullCategoryVid-1", "testman", 0, 50, "sponsor", "full", "YouTube", 0, 0, fullCategoryVidHash, ""]); - await db.prepare("run", query, ["fullCategoryVid", 60, 70, 2, 1, "fullCategoryVid-2", "testman", 0, 50, "selfpromo", "full", "YouTube", 0, 0, fullCategoryVidHash, ""]); }); it("Should be able to get multiple videos", () => { - const prefix = getSegmentsByHash0Hash.substring(0, 5); + const prefix = hashedVideoIDs[0].hashPrefix; return assertSegmentsEqual(prefix, ["getSegmentsByHash-01", "getSegmentsByHash-03", "getSegmentsByHash-noMatchHash"], { params: { categories: `["sponsor","intro"]` } }); }); - it("Should be able to get 200 for no categories (default sponsor)", (done) => { - const prefix = getSegmentsByHash0Hash.substring(0, 5); - client.get(`${endpoint}/fdaf`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - const expected = [{ - segments: [{ - category: "sponsor", - UUID: "getSegmentsByHash-01" - }] - }, { - segments: [{ - category: "sponsor" - }] - }]; - assert.strictEqual(data.length, 2); - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data[0].segments.length, 1); - assert.strictEqual(data[1].segments.length, 1); - done(); - }) - .catch(err => done(err)); + it("Should be able to get 200 for no categories (default sponsor)", () => { + return assertSegmentsEqual(hashedVideoIDs[0].hashPrefix, ["getSegmentsByHash-01", "getSegmentsByHash-noMatchHash"]); }); - it("Should be able to get 200 for no categories (default sponsor) with action type", (done) => { - client.get(`${endpoint}/fdaf`, { params: { actionType: "skip" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 2); - assert.strictEqual(data[0].segments.length, 1); - assert.strictEqual(data[1].segments.length, 1); - const expected = [{ - segments: [{ - category: "sponsor", - UUID: "getSegmentsByHash-01", - }] - }, { - segments: [{ - category: "sponsor", - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get 200 for no categories (default sponsor) with multiple action types", (done) => { - client.get(`${endpoint}/fdaf?actionType=skip&actionType=mute`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 2); - assert.strictEqual(data[0].segments.length, 2); - assert.strictEqual(data[1].segments.length, 1); - const expected = [{ - segments: [{ - category: "sponsor", - UUID: "getSegmentsByHash-01", - }, { - UUID: "getSegmentsByHash-04", - }] - }, { - segments: [{ - category: "sponsor", - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get 200 for no categories (default sponsor) with action type", () => { + return assertSegmentsEqual(hashedVideoIDs[0].hashPrefix, ["getSegmentsByHash-01", "getSegmentsByHash-noMatchHash"], { params: { actionType: "skip" } }); }); - it("Should be able to get 200 for no categories (default sponsor) with multiple action types (JSON array)", (done) => { - client.get(`${endpoint}/fdaf?actionTypes=["skip","mute"]`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 2); - const expected = [{ - segments: [{ - category: "sponsor", - UUID: "getSegmentsByHash-01", - }, { - UUID: "getSegmentsByHash-04", - }] - }, { - segments: [{ - category: "sponsor", - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + it("Should be able to get 200 for no categories (default sponsor) with multiple action types", () => { + return assertSegmentsEqual(hashedVideoIDs[0].hashPrefix, ["getSegmentsByHash-01", "getSegmentsByHash-04", "getSegmentsByHash-noMatchHash"], { params: { actionTypes: `["skip","mute"]` } }); }); - it("Should be able to get 200 for no categories (default sponsor) for a non YouTube service", (done) => { - client.get(`${endpoint}/fdaf`, { params: { service: "PeerTube" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - const expected = [{ - segments: [{ - UUID: "getSegmentsByHash-02" - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data[0].segments.length, 1); - done(); - }) - .catch(err => done(err)); + it("Should be able to get 200 for no categories (default sponsor) for a non YouTube service", () => { + return assertSegmentsEqual(hashedVideoIDs[0].hashPrefix, ["getSegmentsByHash-02"], { params: { service: "PeerTube" } }); }); - it("Should only return one segment when fetching highlight segments", (done) => { - client.get(`${endpoint}/c962`, { params: { category: "poi_highlight", actionType: "poi" } }) + it("Should only return one segment when fetching highlight segments", () => { + return client.get(`${endpoint}/${hashedVideoIDs["highlight"].hashPrefix}`, { params: { category: "poi_highlight", actionType: "poi" } }) .then(res => { assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - assert.strictEqual(data[0].segments.length, 1); - assert.strictEqual(data[0].segments[0].category, "poi_highlight"); - assert.strictEqual(data[0].segments[0].actionType, "poi"); - done(); - }) - .catch(err => done(err)); + assert.strictEqual(res.data.length, 1); + assert.strictEqual(res.data[0].segments.length, 1); + assert.strictEqual(res.data[0].segments[0].category, "poi_highlight"); + assert.strictEqual(res.data[0].segments[0].actionType, "poi"); + }); }); - it("Should return skip actionType for highlight for old clients", (done) => { - client.get(`${endpoint}/c962`, { params: { category: "poi_highlight" } }) + it("Should return skip actionType for highlight for old clients", () => { + return client.get(`${endpoint}/${hashedVideoIDs["highlight"].hashPrefix}`, { params: { category: "poi_highlight" } }) .then(res => { assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - assert.strictEqual(data[0].segments.length, 1); - assert.strictEqual(data[0].segments[0].category, "poi_highlight"); - assert.strictEqual(data[0].segments[0].actionType, "skip"); - done(); - }) - .catch(err => done(err)); + assert.strictEqual(res.data.length, 1); + assert.strictEqual(res.data[0].segments.length, 1); + assert.strictEqual(res.data[0].segments[0].category, "poi_highlight"); + assert.strictEqual(res.data[0].segments[0].actionType, "skip"); + }); }); - it("Should be able to post a segment and get it using endpoint", (done) => { - const testID = "abc123goodVideo"; - client.post("/api/skipSegments", { - userID: "test-qwertyuiopasdfghjklzxcvbnm", - videoID: testID, + it("Should be able to post a segment and get it using endpoint", async () => { + const videoID = genRandomValue("video", "getSegmentsByHash"); + const segment = [genRandomNumber(0, 100), genRandomNumber(100, 200)]; + const submit = await client.post("/api/skipSegments", { + userID: genRandomValue("user", "getSegmentsByHash"), + videoID, segments: [{ - segment: [13, 17], + segment, category: "sponsor", }], - }) - .then(() => { - client.get(`${endpoint}/${getHash(testID, 1).substring(0, 3)}`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - const expected = [{ - segments: [{ - category: "sponsor", - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data[0].segments.length, 1); - done(); - }) - .catch(err => done(`(get) ${err}`)); - }) - .catch(err => done(`(post) ${err}`)); + }); + // check if segment was inserted + assert.strictEqual(submit.status, 200); + const videoIDHash = getHash(videoID, 1); + const prefix = videoIDHash.substring(0, 3); + const retreive = await client.get(`${endpoint}/${prefix}`); + assert.strictEqual(retreive.status, 200); + assert.strictEqual(retreive.data.length, 1); + const expected = [{ + segments: [{ + segment, + category: "sponsor", + }] + }]; + assert.ok(partialDeepEquals(retreive.data, expected)); + assert.strictEqual(retreive.data[0].segments.length, 1); }); it("Should be able to get multiple categories with repeating parameters", (done) => { - client.get(`${endpoint}/fdaff4?&category=sponsor&category=intro`) + client.get(`${endpoint}/${hashedVideoIDs[0].hashedVideoID.substring(0,7)}?&category=sponsor&category=intro`) .then(res => { assert.strictEqual(res.status, 200); const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); @@ -359,16 +261,17 @@ describe("getSkipSegmentsByHash", () => { }); it("Should be able to get specific segments with requiredSegments", (done) => { - client.get(`${endpoint}/d518?requiredSegments=["requiredSegmentVid-2","requiredSegmentVid-3"]`) + const prefix = requiredSegmentVidHash.substring(0, 5); + client.get(`${endpoint}/${prefix}?requiredSegments=["requiredSegmentsVid-2","requiredSegmentsVid-3"]`) .then(res => { assert.strictEqual(res.status, 200); const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); assert.strictEqual(data.length, 1); const expected = [{ segments: [{ - UUID: "requiredSegmentVid-2" + UUID: "requiredSegmentsVid-2" }, { - UUID: "requiredSegmentVid-3" + UUID: "requiredSegmentsVid-3" }] }]; assert.ok(partialDeepEquals(data, expected)); @@ -379,7 +282,8 @@ describe("getSkipSegmentsByHash", () => { }); it("Should be able to get specific segments with repeating requiredSegment", (done) => { - client.get(`${endpoint}/d518?requiredSegment=requiredSegmentVid-2&requiredSegment=requiredSegmentVid-3`) + const prefix = requiredSegmentVidHash.substring(0, 5); + client.get(`${endpoint}/${prefix}?requiredSegment=requiredSegmentsVid-2&requiredSegment=requiredSegmentsVid-3`) .then(res => { assert.strictEqual(res.status, 200); const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); @@ -387,9 +291,9 @@ describe("getSkipSegmentsByHash", () => { assert.strictEqual(data[0].segments.length, 2); const expected = [{ segments: [{ - UUID: "requiredSegmentVid-2" + UUID: "requiredSegmentsVid-2" }, { - UUID: "requiredSegmentVid-3" + UUID: "requiredSegmentsVid-3" }] }]; assert.ok(partialDeepEquals(data, expected)); @@ -565,7 +469,8 @@ describe("getSkipSegmentsByHash", () => { it("Should be able to get specific segments with partial requiredSegments", (done) => { const requiredSegment1 = "fbf0af454059733c8822f6a4ac8ec568e0787f8c0a5ee915dd5b05e0d7a9a388"; const requiredSegment2 = "7e1ebc5194551d2d0a606d64f675e5a14952e4576b2959f8c9d51e316c14f8da"; - client.get(`${endpoint}/17bf?requiredSegments=["${requiredSegment1.slice(0,8)}","${requiredSegment2.slice(0,8)}"]`) + const prefix = requiredSegmentHashVidHash.substring(0, 5); + client.get(`${endpoint}/${prefix}?requiredSegments=["${requiredSegment1.slice(0,8)}","${requiredSegment2.slice(0,8)}"]`) .then(res => { assert.strictEqual(res.status, 200); const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); @@ -586,7 +491,8 @@ describe("getSkipSegmentsByHash", () => { it("Should be able to get single segment with requiredSegments", (done) => { const requiredSegment1 = "fbf0af454059733c8822f6a4ac8ec568e0787f8c0a5ee915dd5b05e0d7a9a388"; - client.get(`${endpoint}/17bf?requiredSegment=${requiredSegment1}`) + const prefix = requiredSegmentHashVidHash.substring(0, 5); + client.get(`${endpoint}/${prefix}?requiredSegment=${requiredSegment1}`) .then(res => { assert.strictEqual(res.status, 200); const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); From 28a457ca29cb724a22062a9225d575e25f0e9e36 Mon Sep 17 00:00:00 2001 From: Michael C Date: Thu, 21 Dec 2023 17:21:49 -0500 Subject: [PATCH 30/31] test checkpoint --- test/cases/getSkipSegmentsByHash.ts | 43 ++--------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/test/cases/getSkipSegmentsByHash.ts b/test/cases/getSkipSegmentsByHash.ts index 20b06198..334bdf0d 100644 --- a/test/cases/getSkipSegmentsByHash.ts +++ b/test/cases/getSkipSegmentsByHash.ts @@ -237,48 +237,9 @@ describe("getSkipSegmentsByHash", () => { assert.strictEqual(retreive.data[0].segments.length, 1); }); - it("Should be able to get multiple categories with repeating parameters", (done) => { - client.get(`${endpoint}/${hashedVideoIDs[0].hashedVideoID.substring(0,7)}?&category=sponsor&category=intro`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - const expected = [{ - segments: [{ - segment: [1, 10], - category: "sponsor", - UUID: "getSegmentsByHash-01", - }, { - segment: [20, 30], - category: "intro", - UUID: "getSegmentsByHash-03", - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); - }); - - it("Should be able to get specific segments with requiredSegments", (done) => { + it("Should be able to get specific segments with requiredSegments", () => { const prefix = requiredSegmentVidHash.substring(0, 5); - client.get(`${endpoint}/${prefix}?requiredSegments=["requiredSegmentsVid-2","requiredSegmentsVid-3"]`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - const expected = [{ - segments: [{ - UUID: "requiredSegmentsVid-2" - }, { - UUID: "requiredSegmentsVid-3" - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data[0].segments.length, 2); - done(); - }) - .catch(err => done(err)); + return assertSegmentsEqual(prefix, ["requiredSegmentsVid-2", "requiredSegmentsVid-3"], { params: { requiredSegments: `["requiredSegmentsVid-2","requiredSegmentsVid-3"]` } }); }); it("Should be able to get specific segments with repeating requiredSegment", (done) => { From 94cd59ebb3d70c8d639d2c9d9e1385444b1d299d Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 26 Dec 2023 05:28:31 -0500 Subject: [PATCH 31/31] finish getSkipSegmentsByHash test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit merry christmas from japan ❤️ --- test/cases/getSkipSegmentsByHash.ts | 307 ++++++++++------------------ 1 file changed, 104 insertions(+), 203 deletions(-) diff --git a/test/cases/getSkipSegmentsByHash.ts b/test/cases/getSkipSegmentsByHash.ts index 334bdf0d..f8323e72 100644 --- a/test/cases/getSkipSegmentsByHash.ts +++ b/test/cases/getSkipSegmentsByHash.ts @@ -1,5 +1,5 @@ import { db } from "../../src/databases/databases"; -import { partialDeepEquals, arrayPartialDeepEquals } from "../utils/partialDeepEquals"; +import { partialDeepEquals } from "../utils/partialDeepEquals"; import { getHash } from "../../src/utils/getHash"; import { ImportMock, } from "ts-mock-imports"; import * as YouTubeAPIModule from "../../src/utils/youtubeApi"; @@ -42,10 +42,6 @@ const defaultResponseSegment = { description: "" }; -interface segmentInsertSegmentParams extends insertSegmentParams { - segment: [number, number] | number[], -} - describe("getSkipSegmentsByHash", () => { const endpoint = "/api/skipSegments"; const videoIDs = multiGenProxy("video", "getSegmentsByHash"); @@ -60,16 +56,8 @@ describe("getSkipSegmentsByHash", () => { return result; } }); - console.log(hashedVideoIDs[0].videoID) - console.log(hashedVideoIDs[0].hashedVideoID) - console.log(hashedVideoIDs[0].hashPrefix) - console.log(`${hashedVideoIDs[0].hashPrefix}noMatchHash`) const requiredSegmentVidHash = getHash("requiredSegmentsVid", 1); - const requiredSegmentHashVidHash = getHash("requiredSegmentsHashVid", 1); - const differentCategoryVidHash = "7fac44d1ee3257ec7f18953e2b5f991828de6854ad57193d1027c530981a89c0"; - const nonMusicOverlapVidHash = "306151f778f9bfd19872b3ccfc83cbab37c4f370717436bfd85e0a624cd8ba3c"; - const fullCategoryVidHash = "278fa987eebfe07ae3a4a60cf0663989ad874dd0c1f0430831d63c2001567e6f"; const insertSegments: insertSegmentParams[] = [ // videoID 0 { videoID: hashedVideoIDs[0].videoID, startTime: 1, endTime: 10, UUID: "getSegmentsByHash-01" }, @@ -160,6 +148,16 @@ describe("getSkipSegmentsByHash", () => { assert.ok(partialDeepEquals(data, expectedArray, true)); }; + const assertSegmentsArray = async(hashPrefix: string, expectedArray: { segments: any[]}[][], axiosConfig: AxiosRequestConfig) => { + // fetch segments + const res = await client.get(`${endpoint}/${hashPrefix}`, axiosConfig); + assert.strictEqual(res.status, 200); + const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); + const arrayMatch = expectedArray.some(exp => partialDeepEquals(data, exp)); + assert.ok(arrayMatch); + return assert.strictEqual(data[0].segments.length, expectedArray[0][0].segments.length); + }; + before(async () => { for (const segment of insertSegments) { await insertSegment(db, segment); @@ -242,95 +240,64 @@ describe("getSkipSegmentsByHash", () => { return assertSegmentsEqual(prefix, ["requiredSegmentsVid-2", "requiredSegmentsVid-3"], { params: { requiredSegments: `["requiredSegmentsVid-2","requiredSegmentsVid-3"]` } }); }); - it("Should be able to get specific segments with repeating requiredSegment", (done) => { + it("Should be able to get specific segments with repeating requiredSegment", () => { const prefix = requiredSegmentVidHash.substring(0, 5); - client.get(`${endpoint}/${prefix}?requiredSegment=requiredSegmentsVid-2&requiredSegment=requiredSegmentsVid-3`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - assert.strictEqual(data[0].segments.length, 2); - const expected = [{ - segments: [{ - UUID: "requiredSegmentsVid-2" - }, { - UUID: "requiredSegmentsVid-3" - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - done(); - }) - .catch(err => done(err)); + const requiredSegments = ["requiredSegmentsVid-2","requiredSegmentsVid-3"]; + return assertSegmentsEqual(prefix, requiredSegments, { params: { requiredSegment: requiredSegments } }); }); - it("Should be able to get overlapping chapter segments if very different", (done) => { - client.get(`${endpoint}/7258?category=chapter&actionType=chapter`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - const expected = [{ - segments: [{ - UUID: "chapterVid-hash-1", - description: "Chapter 1" - }, { - UUID: "chapterVid-hash-2", - description: "Chapter 2" - }] - }]; - const expected2 = [{ - segments: [{ - UUID: "chapterVid-hash-1", - description: "Chapter 1" - }, { - UUID: "chapterVid-hash-3", - description: "Chapter 3" - }] - }]; - - assert.ok(partialDeepEquals(data, expected, false) || partialDeepEquals(data, expected2)); - assert.strictEqual(data[0].segments.length, 2); - done(); - }) - .catch(err => done(err)); + it("Should be able to get overlapping chapter segments if very different", () => { + const expectedArray = [ + [{ + segments: [{ + UUID: "chapterVid-hash-1", + description: "Chapter 1" + }, { + UUID: "chapterVid-hash-2", + description: "Chapter 2" + }] + }], + [{ + segments: [{ + UUID: "chapterVid-hash-1", + description: "Chapter 1" + }, { + UUID: "chapterVid-hash-3", + description: "Chapter 3" + }] + }] + ]; + return assertSegmentsArray("7258", expectedArray, { params: { category: "chapter", actionType: "chapter" } }); }); - it("Should be able to get mute segment with small skip segment in middle", (done) => { - client.get(`${endpoint}/6613?actionType=skip&actionType=mute`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - const expected = [{ - segments: [{ - UUID: "longMuteVid-hash-3", - actionType: "mute" - }, { - UUID: "longMuteVid-hash-2", - actionType: "skip" - }, { - UUID: "longMuteVid-hash-1", - actionType: "skip" - }] - }]; - const expected2 = [{ - segments: [{ - UUID: "longMuteVid-hash-4", - actionType: "mute" - }, { - UUID: "longMuteVid-hash-2", - actionType: "skip" - }, { - UUID: "longMuteVid-hash-1", - actionType: "skip" - }] - }]; - - assert.ok(arrayPartialDeepEquals(data, expected) || arrayPartialDeepEquals(data, expected2)); - assert.strictEqual(data[0].segments.length, 3); - done(); - }) - .catch(err => done(err)); + it("Should be able to get mute segment with small skip segment in middle", () => { + const expectedArray = [ + [{ + segments: [{ + UUID: "longMuteVid-hash-3", + actionType: "mute" + }, { + UUID: "longMuteVid-hash-2", + actionType: "skip" + }, { + UUID: "longMuteVid-hash-1", + actionType: "skip" + }] + }], + [{ + segments: [{ + UUID: "longMuteVid-hash-4", + actionType: "mute" + }, { + UUID: "longMuteVid-hash-2", + actionType: "skip" + }, { + UUID: "longMuteVid-hash-1", + actionType: "skip" + }] + }] + ]; + return assertSegmentsArray("6613", expectedArray, { params: { actionType: ["skip", "mute"] } }); }); // This behavior was causing unintended consequence, uncommend when a solution is found @@ -353,121 +320,55 @@ describe("getSkipSegmentsByHash", () => { // .catch(err => done(err)); // }); - it("Should be able to get overlapping segments where one is non music and one is other", (done) => { - client.get(`${endpoint}/3061?categories=["sponsor","music_offtopic"]`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - const expected = [{ - segments: [{ - category: "sponsor" - }, { - category: "music_offtopic" - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data[0].segments.length, 2); - done(); - }) - .catch(err => done(err)); + it("Should be able to get overlapping segments where one is non music and one is other", () => { + return assertSegmentsEqual("3061", ["nonMusicOverlapVid-1", "nonMusicOverlapVid-2"], { params: { categories: `["sponsor","music_offtopic"]` } }); }); - it("Should be able to get mute segment with small skip segment in middle (2)", (done) => { - client.get(`${endpoint}/ab0c?actionType=skip&actionType=mute`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - const expected = [{ - segments: [{ - UUID: "longMuteVid-2-hash-1", - actionType: "mute" - }, { - UUID: "longMuteVid-2-hash-2", - actionType: "skip" - }] - }]; - const expected2 = [{ - segments: [{ - UUID: "longMuteVid-2-hash-3", - actionType: "mute" - }, { - UUID: "longMuteVid-2-hash-2", - actionType: "skip" - }] - }]; - const expected3 = [{ - segments: [{ - UUID: "longMuteVid-2-hash-4", - actionType: "mute" - }, { - UUID: "longMuteVid-2-hash-2", - actionType: "skip" - }] - }]; - - assert.ok(partialDeepEquals(data, expected, false) || partialDeepEquals(data, expected2) || partialDeepEquals(data, expected3)); - assert.strictEqual(data[0].segments.length, 2); - done(); - }) - .catch(err => done(err)); + it("Should be able to get mute segment with small skip segment in middle (2)", () => { + const expectedArrays = [ + [{ + segments: [{ + UUID: "longMuteVid-2-hash-1", + actionType: "mute" + }, { + UUID: "longMuteVid-2-hash-2", + actionType: "skip" + }] + }], + [{ + segments: [{ + UUID: "longMuteVid-2-hash-3", + actionType: "mute" + }, { + UUID: "longMuteVid-2-hash-2", + actionType: "skip" + }] + }], + [{ + segments: [{ + UUID: "longMuteVid-2-hash-4", + actionType: "mute" + }, { + UUID: "longMuteVid-2-hash-2", + actionType: "skip" + }] + }] + ]; + return assertSegmentsArray("ab0c", expectedArrays, { params: { actionType: ["skip", "mute"] } }); }); - it("Should only return one segment when fetching full video segments", (done) => { - client.get(`${endpoint}/278f`, { params: { category: ["sponsor", "selfpromo"], actionType: "full" } }) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - assert.strictEqual(data[0].segments.length, 1); - assert.strictEqual(data[0].segments[0].category, "selfpromo"); - done(); - }) - .catch(err => done(err)); + it("Should only return one segment when fetching full video segments", () => { + return assertSegmentsEqual("278f", ["fullCategoryVid-2"], { params: { category: ["sponsor", "selfpromo"], actionType: "full" } }); }); - it("Should be able to get specific segments with partial requiredSegments", (done) => { + it("Should be able to get specific segments with partial requiredSegments", () => { const requiredSegment1 = "fbf0af454059733c8822f6a4ac8ec568e0787f8c0a5ee915dd5b05e0d7a9a388"; const requiredSegment2 = "7e1ebc5194551d2d0a606d64f675e5a14952e4576b2959f8c9d51e316c14f8da"; - const prefix = requiredSegmentHashVidHash.substring(0, 5); - client.get(`${endpoint}/${prefix}?requiredSegments=["${requiredSegment1.slice(0,8)}","${requiredSegment2.slice(0,8)}"]`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - const expected = [{ - segments: [{ - UUID: requiredSegment1 - }, { - UUID: requiredSegment2 - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data[0].segments.length, 2); - done(); - }) - .catch(err => done(err)); + return assertSegmentsEqual("32ef", [requiredSegment1, requiredSegment2], { params: { requiredSegments: `["${requiredSegment1.slice(0,8)}","${requiredSegment2.slice(0,8)}"]` } }); }); - it("Should be able to get single segment with requiredSegments", (done) => { - const requiredSegment1 = "fbf0af454059733c8822f6a4ac8ec568e0787f8c0a5ee915dd5b05e0d7a9a388"; - const prefix = requiredSegmentHashVidHash.substring(0, 5); - client.get(`${endpoint}/${prefix}?requiredSegment=${requiredSegment1}`) - .then(res => { - assert.strictEqual(res.status, 200); - const data = (res.data as Array).sort((a, b) => a.videoID.localeCompare(b.videoID)); - assert.strictEqual(data.length, 1); - const expected = [{ - segments: [{ - UUID: requiredSegment1 - }] - }]; - assert.ok(partialDeepEquals(data, expected)); - assert.strictEqual(data[0].segments.length, 1); - done(); - }) - .catch(err => done(err)); + it("Should be able to get single segment with requiredSegments", () => { + const requiredSegment = "fbf0af454059733c8822f6a4ac8ec568e0787f8c0a5ee915dd5b05e0d7a9a388"; + return assertSegmentsEqual("32ef", [requiredSegment], { params: { requiredSegment: requiredSegment } }); }); - });