Merge branch 'master' of https://github.com/ajayyy/SponsorBlockServer into fullVideoLabels

This commit is contained in:
Michael C
2022-09-30 19:21:54 -04:00
108 changed files with 5844 additions and 4299 deletions

68
test/cases/addFeatures.ts Normal file
View File

@@ -0,0 +1,68 @@
import assert from "assert";
import { db } from "../../src/databases/databases";
import { Feature, HashedUserID } from "../../src/types/user.model";
import { hasFeature } from "../../src/utils/features";
import { getHash } from "../../src/utils/getHash";
import { client } from "../utils/httpClient";
const endpoint = "/api/feature";
const postAddFeatures = (userID: string, adminUserID: string, feature: Feature, enabled: string) => client({
method: "POST",
url: endpoint,
data: {
userID,
feature,
enabled,
adminUserID
}
});
const privateVipUserID = "VIPUser-addFeatures";
const vipUserID = getHash(privateVipUserID);
const hashedUserID1 = "user1-addFeatures" as HashedUserID;
const hashedUserID2 = "user2-addFeatures" as HashedUserID;
const hashedUserID3 = "user3-addFeatures" as HashedUserID;
const validFeatures = [Feature.ChapterSubmitter];
describe("addFeatures", () => {
before(() => {
const userFeatureQuery = `INSERT INTO "userFeatures" ("userID", "feature", "issuerUserID", "timeSubmitted") VALUES(?, ?, ?, ?)`;
return Promise.all([
db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [vipUserID]),
db.prepare("run", userFeatureQuery, [hashedUserID2, Feature.ChapterSubmitter, "some-user", 0]),
db.prepare("run", userFeatureQuery, [hashedUserID3, Feature.ChapterSubmitter, "some-user", 0])
]);
});
it("can add features", async () => {
for (const feature of validFeatures) {
const result = await postAddFeatures(hashedUserID1, privateVipUserID, feature, "true");
assert.strictEqual(result.status, 200);
assert.strictEqual(await hasFeature(hashedUserID1, feature), true);
}
});
it("can remove features", async () => {
const feature = Feature.ChapterSubmitter;
const result = await postAddFeatures(hashedUserID2, privateVipUserID, feature, "false");
assert.strictEqual(result.status, 200);
assert.strictEqual(await hasFeature(hashedUserID2, feature), false);
});
it("can update features", async () => {
const feature = Feature.ChapterSubmitter;
const result = await postAddFeatures(hashedUserID3, privateVipUserID, feature, "true");
assert.strictEqual(result.status, 200);
assert.strictEqual(await hasFeature(hashedUserID3, feature), true);
});
});

View File

@@ -19,9 +19,9 @@ if (db instanceof Postgres) {
await db.prepare("run", query, [chapterNamesVid1, 70, 75, 2, 0, "chapterNamesVid-2", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "A different one"]);
await db.prepare("run", query, [chapterNamesVid1, 71, 76, 2, 0, "chapterNamesVid-3", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "Something else"]);
await db.prepare("run", `INSERT INTO "videoInfo" ("videoID", "channelID", "title", "published", "genreUrl")
SELECT ?, ?, ?, ?, ?`, [
chapterNamesVid1, chapterChannelID, "", 0, ""
await db.prepare("run", `INSERT INTO "videoInfo" ("videoID", "channelID", "title", "published")
SELECT ?, ?, ?, ?`, [
chapterNamesVid1, chapterChannelID, "", 0
]);
});

View File

@@ -0,0 +1,31 @@
import { config } from "../../src/config";
import { getHashCache } from "../../src/utils/getHashCache";
import { shaHashKey } from "../../src/utils/redisKeys";
import { getHash } from "../../src/utils/getHash";
import redis from "../../src/utils/redis";
import crypto from "crypto";
import assert from "assert";
import { setTimeout } from "timers/promises";
const genRandom = (bytes=8) => crypto.pseudoRandomBytes(bytes).toString("hex");
const rand1Hash = genRandom(24);
const rand1Hash_Key = getHash(rand1Hash, 1);
const rand1Hash_Result = getHash(rand1Hash);
describe("getHashCache test", function() {
before(function() {
if (!config.redis?.enabled) this.skip();
});
it("Should set hashKey and be able to retreive", (done) => {
const redisKey = shaHashKey(rand1Hash_Key);
getHashCache(rand1Hash)
.then(() => setTimeout(50)) // add timeout for redis to complete async
.then(() => redis.get(redisKey))
.then(result => {
assert.strictEqual(result, rand1Hash_Result);
done();
})
.catch(err => done(err === undefined ? "no set value" : err));
}).timeout(5000);
});

View File

@@ -31,7 +31,7 @@ describe("getLockReason", () => {
});
after(async () => {
const deleteUserNameQuery = 'DELETE FROM "userNames" WHERE "userID" = ? AND "userName" = ? LIMIT 1';
const deleteUserNameQuery = 'DELETE FROM "userNames" WHERE "userID" = ? AND "userName" = ?';
await db.prepare("run", deleteUserNameQuery, [vipUserID1, vipUserName1]);
await db.prepare("run", deleteUserNameQuery, [vipUserID2, vipUserName2]);
});

View File

@@ -23,6 +23,8 @@ describe("getSkipSegments", () => {
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"]);
@@ -447,4 +449,42 @@ describe("getSkipSegments", () => {
})
.catch(err => done(err));
});
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 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));
});
});

View File

@@ -150,7 +150,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/fdaf`, { params: { categories: `["sponsor","intro"]` } })
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).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);
@@ -163,15 +163,15 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/fdaf`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
const expected = [{
segments: [{
category: "sponsor",
UUID: "getSegmentsByHash-01",
UUID: "getSegmentsByHash-01"
}]
}, {
segments: [{
category: "sponsor",
category: "sponsor"
}]
}];
assert.strictEqual(data.length, 2);
@@ -187,7 +187,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/fdaf`, { params: { actionType: "skip" } })
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).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);
@@ -211,7 +211,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/fdaf?actionType=skip&actionType=mute`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).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);
@@ -237,7 +237,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/fdaf?actionTypes=["skip","mute"]`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 2);
const expected = [{
segments: [{
@@ -261,7 +261,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/fdaf`, { params: { service: "PeerTube" } })
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 1);
const expected = [{
segments: [{
@@ -279,7 +279,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/c962`, { params: { category: "poi_highlight", actionType: "poi" } })
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).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");
@@ -293,7 +293,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/c962`, { params: { category: "poi_highlight" } })
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).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");
@@ -317,7 +317,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/${getHash(testID, 1).substring(0, 3)}`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 1);
const expected = [{
segments: [{
@@ -337,7 +337,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/fdaff4?&category=sponsor&category=intro`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 1);
const expected = [{
segments: [{
@@ -360,7 +360,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/d518?requiredSegments=["requiredSegmentVid-2","requiredSegmentVid-3"]`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 1);
const expected = [{
segments: [{
@@ -380,7 +380,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/d518?requiredSegment=requiredSegmentVid-2&requiredSegment=requiredSegmentVid-3`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 1);
assert.strictEqual(data[0].segments.length, 2);
const expected = [{
@@ -400,7 +400,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/7258?category=chapter&actionType=chapter`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 1);
const expected = [{
segments: [{
@@ -432,7 +432,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/6613?actionType=skip&actionType=mute`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 1);
const expected = [{
segments: [{
@@ -490,7 +490,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/3061?categories=["sponsor","music_offtopic"]`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 1);
const expected = [{
segments: [{
@@ -510,7 +510,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/ab0c?actionType=skip&actionType=mute`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 1);
const expected = [{
segments: [{
@@ -551,7 +551,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/278f`, { params: { category: ["sponsor", "selfpromo"], actionType: "full" } })
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).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");
@@ -566,7 +566,7 @@ describe("getSkipSegmentsByHash", () => {
client.get(`${endpoint}/17bf?requiredSegments=["${requiredSegment1.slice(0,8)}","${requiredSegment2.slice(0,8)}"]`)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
const data = (res.data as Array<any>).sort((a, b) => a.videoID.localeCompare(b.videoID));
assert.strictEqual(data.length, 1);
const expected = [{
segments: [{

View File

@@ -110,4 +110,16 @@ describe("getStatus", () => {
})
.catch(err => done(err));
});
it("Should be able to get redis latency", function (done) {
if (!config.redis?.enabled) this.skip();
client.get(endpoint)
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
assert.ok(data.redisProcessTime >= 0);
done();
})
.catch(err => done(err));
});
});

View File

@@ -10,16 +10,17 @@ describe("getUserInfo", () => {
const insertUserNameQuery = 'INSERT INTO "userNames" ("userID", "userName") VALUES(?, ?)';
await db.prepare("run", insertUserNameQuery, [getHash("getuserinfo_user_01"), "Username user 01"]);
const sponsorTimesQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "shadowHidden") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
await db.prepare("run", sponsorTimesQuery, ["getUserInfo0", 1, 11, 2, "uuid000001", getHash("getuserinfo_user_01"), 1, 10, "sponsor", 0]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo0", 1, 11, 2, "uuid000002", getHash("getuserinfo_user_01"), 2, 10, "sponsor", 0]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo1", 1, 11, -1, "uuid000003", getHash("getuserinfo_user_01"), 3, 10, "sponsor", 0]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo1", 1, 11, -2, "uuid000004", getHash("getuserinfo_user_01"), 4, 10, "sponsor", 1]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo2", 1, 11, -5, "uuid000005", getHash("getuserinfo_user_01"), 5, 10, "sponsor", 1]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo0", 1, 11, 2, "uuid000007", getHash("getuserinfo_user_02"), 7, 10, "sponsor", 1]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo0", 1, 11, 2, "uuid000008", getHash("getuserinfo_user_02"), 8, 10, "sponsor", 1]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo0", 0, 36000, 2,"uuid000009", getHash("getuserinfo_user_03"), 8, 10, "sponsor", 0]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo3", 1, 11, 2, "uuid000006", getHash("getuserinfo_user_02"), 6, 10, "sponsor", 0]);
const sponsorTimesQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "actionType", "shadowHidden") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
await db.prepare("run", sponsorTimesQuery, ["getUserInfo0", 1, 11, 2, "uuid000001", getHash("getuserinfo_user_01"), 1, 10, "sponsor", "skip", 0]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo0", 1, 11, 2, "uuid000002", getHash("getuserinfo_user_01"), 2, 10, "sponsor", "skip", 0]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo1", 1, 11, -1, "uuid000003", getHash("getuserinfo_user_01"), 3, 10, "sponsor", "skip", 0]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo1", 1, 11, -2, "uuid000004", getHash("getuserinfo_user_01"), 4, 10, "sponsor", "skip", 1]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo2", 1, 11, -5, "uuid000005", getHash("getuserinfo_user_01"), 5, 10, "sponsor", "skip", 1]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo0", 1, 11, 2, "uuid000007", getHash("getuserinfo_user_02"), 7, 10, "sponsor", "skip", 1]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo0", 1, 11, 2, "uuid000008", getHash("getuserinfo_user_02"), 8, 10, "sponsor", "skip", 1]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo0", 0, 36000, 2,"uuid000009", getHash("getuserinfo_user_03"), 8, 10, "sponsor", "skip", 0]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo3", 1, 11, 2, "uuid000006", getHash("getuserinfo_user_02"), 6, 10, "sponsor", "skip", 0]);
await db.prepare("run", sponsorTimesQuery, ["getUserInfo4", 1, 11, 2, "uuid000010", getHash("getuserinfo_user_04"), 9, 10, "chapter", "chapter", 0]);
const insertWarningQuery = 'INSERT INTO warnings ("userID", "issueTime", "issuerUserID", "enabled", "reason") VALUES (?, ?, ?, ?, ?)';
@@ -64,7 +65,7 @@ describe("getUserInfo", () => {
ignoredViewCount: 20,
segmentCount: 3,
ignoredSegmentCount: 2,
reputation: -2,
reputation: -1.5,
lastSegmentID: "uuid000005",
vip: false,
warnings: 0,
@@ -307,4 +308,28 @@ describe("getUserInfo", () => {
})
.catch(err => done(err));
});
it("Should ignore chapters for saved time calculations", (done) => {
client.get(endpoint, { params: { userID: "getuserinfo_user_04" } })
.then(res => {
assert.strictEqual(res.status, 200);
const expected = {
userName: "f187933817e7b0211a3f6f7d542a63ca9cc289d6cc8a8a79669d69a313671ccf",
userID: "f187933817e7b0211a3f6f7d542a63ca9cc289d6cc8a8a79669d69a313671ccf",
minutesSaved: 0,
viewCount: 10,
ignoredViewCount: 0,
segmentCount: 1,
ignoredSegmentCount: 0,
reputation: 0,
lastSegmentID: "uuid000010",
vip: false,
warnings: 0,
warningReason: ""
};
assert.deepStrictEqual(res.data, expected);
done();
})
.catch(err => done(err));
});
});

View File

@@ -22,8 +22,7 @@ describe("getUserStats", () => {
await db.prepare("run", sponsorTimesQuery, ["getuserstats1", 0, 60, -2, "skip", "getuserstatsuuid9", getHash("getuserstats_user_02"), 8, 2, "sponsor", 0]);
await db.prepare("run", sponsorTimesQuery, ["getuserstats1", 0, 60, 0, "skip", "getuserstatsuuid10", getHash("getuserstats_user_01"), 8, 2, "filler", 0]);
await db.prepare("run", sponsorTimesQuery, ["getuserstats1", 0, 0, 0, "full", "getuserstatsuuid11", getHash("getuserstats_user_01"), 8, 2, "exclusive_access", 0]);
await db.prepare("run", sponsorTimesQuery, ["getuserstats1", 0, 60, 0, "chapter", "getuserstatsuuid12", getHash("getuserstats_user_01"), 9, 2, "chapter", 0]);
});
it("Should be able to get a 400 (No userID parameter)", (done) => {
@@ -52,17 +51,19 @@ describe("getUserStats", () => {
music_offtopic: 1,
poi_highlight: 1,
filler: 1,
exclusive_access: 1
exclusive_access: 1,
chapter: 1,
},
actionTypeCount: {
mute: 0,
skip: 8,
full: 1,
poi: 1
poi: 1,
chapter: 1,
},
overallStats: {
minutesSaved: 30,
segmentCount: 10
segmentCount: 11
}
};
assert.ok(partialDeepEquals(res.data, expected));

View File

@@ -0,0 +1,49 @@
import { config } from "../../src/config";
import assert from "assert";
import { YouTubeAPI } from "../../src/utils/youtubeApi";
import * as innerTube from "../../src/utils/innerTubeAPI";
import { partialDeepEquals } from "../utils/partialDeepEquals";
import { getVideoDetails } from "../../src/utils/getVideoDetails";
const videoID = "BaW_jenozKc";
const expectedInnerTube = { // partial type of innerTubeVideoDetails
videoId: videoID,
title: "youtube-dl test video \"'/\\ä↭𝕐",
lengthSeconds: "10",
channelId: "UCLqxVugv74EIW3VWh2NOa3Q",
isOwnerViewing: false,
isCrawlable: true,
allowRatings: true,
author: "Philipp Hagemeister",
isPrivate: false,
isUnpluggedCorpus: false,
isLiveContent: false
};
const currentViews = 49816;
describe("innertube API test", function() {
it("should be able to get innerTube details", async () => {
const result = await innerTube.getPlayerData(videoID, true);
assert.ok(partialDeepEquals(result, expectedInnerTube));
});
it("Should have more views than current", async () => {
const result = await innerTube.getPlayerData(videoID, true);
assert.ok(Number(result.viewCount) >= currentViews);
});
it("Should have equivalent response from NewLeaf", async function () {
if (!config.newLeafURLs || config.newLeafURLs.length <= 0 || config.newLeafURLs[0] == "placeholder") this.skip();
const itResponse = await innerTube.getPlayerData(videoID, true);
const newLeafResponse = await YouTubeAPI.listVideos(videoID, true);
// validate videoID
assert.strictEqual(itResponse.videoId, videoID);
assert.strictEqual(newLeafResponse.data?.videoId, videoID);
// validate description
assert.strictEqual(itResponse.shortDescription, newLeafResponse.data?.description);
// validate authorId
assert.strictEqual(itResponse.channelId, newLeafResponse.data?.authorId);
});
it("Should return data from generic endpoint", async function () {
const videoDetail = await getVideoDetails(videoID);
assert.ok(videoDetail);
});
});

View File

@@ -54,6 +54,7 @@ describe("lockCategoriesRecords", () => {
await db.prepare("run", insertLockCategoryQuery, [lockVIPUserHash, "delete-record-1", "mute", "sponsor", "reason-5", "YouTube"]);
await db.prepare("run", insertLockCategoryQuery, [lockVIPUserHash, "delete-record-1", "skip", "intro", "reason-5", "YouTube"]);
await db.prepare("run", insertLockCategoryQuery, [lockVIPUserHash, "delete-record-1", "mute", "intro", "reason-5", "YouTube"]);
await db.prepare("run", insertLockCategoryQuery, [lockVIPUserHash, "delete-record-poi", "poi", "poi_highlight", "reason-6", "YouTube"]);
});
it("Should update the database version when starting the application", async () => {
@@ -519,4 +520,44 @@ describe("lockCategoriesRecords", () => {
})
.catch(err => done(err));
});
it("should be able to delete poi type category by type poi", (done) => {
const videoID = "delete-record-poi";
const json = {
videoID,
userID: lockVIPUser,
categories: [
"poi_highlight",
],
actionTypes: ["poi"]
};
client.delete(endpoint, { data: json })
.then(async res => {
assert.strictEqual(res.status, 200);
const result = await checkLockCategories(videoID);
assert.strictEqual(result.length, 0);
done();
})
.catch(err => done(err));
});
it("should be able to delete poi type category by type poi", (done) => {
const videoID = "delete-record-poi";
const json = {
videoID,
userID: lockVIPUser,
categories: [
"poi_highlight",
],
actionTypes: ["poi"]
};
client.delete(endpoint, { data: json })
.then(async res => {
assert.strictEqual(res.status, 200);
const result = await checkLockCategories(videoID);
assert.strictEqual(result.length, 0);
done();
})
.catch(err => done(err));
});
});

View File

@@ -44,8 +44,17 @@ describe("postVideoSponsorTime (Old submission method)", () => {
.catch(err => done(err));
});
it("Should return 400 for missing params", (done) => {
client.post(endpoint, { params: { startTime: 1, endTime: 10, userID } })
it("Should return 400 for missing video", (done) => {
client.get(endpoint, { params: { startTime: 1, endTime: 10, userID } })
.then(res => {
assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
});
it("Should return 400 for missing userID", (done) => {
client.get(endpoint, { params: { videoID: videoID1, startTime: 1, endTime: 10 } })
.then(res => {
assert.strictEqual(res.status, 400);
done();

View File

@@ -7,6 +7,7 @@ import * as YouTubeAPIModule from "../../src/utils/youtubeApi";
import { YouTubeApiMock } from "../youtubeMock";
import assert from "assert";
import { client } from "../utils/httpClient";
import { Feature } from "../../src/types/user.model";
const mockManager = ImportMock.mockStaticClass(YouTubeAPIModule, "YouTubeAPI");
const sinonStub = mockManager.mock("listVideos");
@@ -15,6 +16,7 @@ sinonStub.callsFake(YouTubeApiMock.listVideos);
describe("postSkipSegments", () => {
// Constant and helpers
const submitUserOne = `PostSkipUser1${".".repeat(18)}`;
const submitUserOneHash = getHash(submitUserOne);
const submitUserTwo = `PostSkipUser2${".".repeat(18)}`;
const submitUserTwoHash = getHash(submitUserTwo);
const submitUserThree = `PostSkipUser3${".".repeat(18)}`;
@@ -30,7 +32,6 @@ describe("postSkipSegments", () => {
const banUser01 = "ban-user01-loremipsumdolorsitametconsectetur";
const banUser01Hash = getHash(banUser01);
const submitUserOneHash = getHash(submitUserOne);
const submitVIPuser = `VIPPostSkipUser${".".repeat(16)}`;
const warnVideoID = "postSkip2";
const badInputVideoID = "dQw4w9WgXcQ";
@@ -66,6 +67,15 @@ describe("postSkipSegments", () => {
db.prepare("run", insertSponsorTimeQuery, ["full_video_duration_segment", 0, 0, 0, "full-video-duration-uuid-0", submitUserTwoHash, 0, 0, "sponsor", "full", 123, 0, "full_video_duration_segment"]);
db.prepare("run", insertSponsorTimeQuery, ["full_video_duration_segment", 25, 30, 0, "full-video-duration-uuid-1", submitUserTwoHash, 0, 0, "sponsor", "skip", 123, 0, "full_video_duration_segment"]);
const reputationVideoID = "post_reputation_video";
db.prepare("run", insertSponsorTimeQuery, [reputationVideoID, 1, 11, 2,"post_reputation-5-uuid-0", submitUserOneHash, 1606240000000, 50, "sponsor", "skip", 0, 0, reputationVideoID]);
db.prepare("run", insertSponsorTimeQuery, [reputationVideoID, 1, 11, 2,"post_reputation-5-uuid-1", submitUserOneHash, 1606240000000, 50, "sponsor", "skip", 0, 0, reputationVideoID]);
db.prepare("run", insertSponsorTimeQuery, [reputationVideoID, 1, 11, 2,"post_reputation-5-uuid-2", submitUserOneHash, 1606240000000, 50, "sponsor", "skip", 0, 0, reputationVideoID]);
db.prepare("run", insertSponsorTimeQuery, [reputationVideoID, 1, 11, 2,"post_reputation-5-uuid-3", submitUserOneHash, 1606240000000, 50, "sponsor", "skip", 0, 0, reputationVideoID]);
db.prepare("run", insertSponsorTimeQuery, [reputationVideoID, 1, 11, 2,"post_reputation-5-uuid-4", submitUserOneHash, 1606240000000, 50, "sponsor", "skip", 0, 0, reputationVideoID]);
db.prepare("run", insertSponsorTimeQuery, [reputationVideoID, 1, 11, 0,"post_reputation-5-uuid-6", submitUserOneHash, 1606240000000, 50, "sponsor", "skip", 0, 0, reputationVideoID]);
db.prepare("run", insertSponsorTimeQuery, [reputationVideoID, 1, 11, 0,"post_reputation-5-uuid-7", submitUserOneHash, 1606240000000, 50, "sponsor", "skip", 0, 0, reputationVideoID]);
const now = Date.now();
const warnVip01Hash = getHash("warn-vip01-qwertyuiopasdfghjklzxcvbnm");
const reason01 = "Reason01";
@@ -83,7 +93,6 @@ describe("postSkipSegments", () => {
db.prepare("run", insertWarningQuery, [warnUser01Hash, warnVip01Hash, 1, reason01, (now - 3601000)]);
// User 2
db.prepare("run", insertWarningQuery, [warnUser02Hash, warnVip01Hash, 1, reason02, now]);
db.prepare("run", insertWarningQuery, [warnUser02Hash, warnVip01Hash, 1, reason02, now]);
db.prepare("run", insertWarningQuery, [warnUser02Hash, warnVip01Hash, 1, reason02, (now - (warningExpireTime + 1000))]);
db.prepare("run", insertWarningQuery, [warnUser02Hash, warnVip01Hash, 1, reason02, (now - (warningExpireTime + 2000))]);
// User 3
@@ -102,6 +111,9 @@ describe("postSkipSegments", () => {
// ban user
db.prepare("run", `INSERT INTO "shadowBannedUsers" ("userID") VALUES(?)`, [banUser01Hash]);
// user feature
db.prepare("run", `INSERT INTO "userFeatures" ("userID", "feature", "issuerUserID", "timeSubmitted") VALUES(?, ?, ?, ?)`, [submitUserTwoHash, Feature.ChapterSubmitter, "some-user", 0]);
});
it("Should be able to submit a single time (Params method)", (done) => {
@@ -129,7 +141,6 @@ describe("postSkipSegments", () => {
title: "Example Title",
channelID: "ExampleChannel",
published: 123,
genreUrl: ""
};
assert.ok(partialDeepEquals(videoInfo, expectedVideoInfo));
@@ -189,7 +200,7 @@ describe("postSkipSegments", () => {
.catch(err => done(err));
});
it("Should be able to submit a single chapter (JSON method)", (done) => {
it("Should be able to submit a single chapter due to reputation (JSON method)", (done) => {
const videoID = "postSkipChapter1";
postSkipSegmentJSON({
userID: submitUserOne,
@@ -217,6 +228,34 @@ describe("postSkipSegments", () => {
.catch(err => done(err));
});
it("Should be able to submit a single chapter due to user feature (JSON method)", (done) => {
const videoID = "postSkipChapter2";
postSkipSegmentJSON({
userID: submitUserTwo,
videoID,
segments: [{
segment: [0, 10],
category: "chapter",
actionType: "chapter",
description: "This is a chapter"
}],
})
.then(async res => {
assert.strictEqual(res.status, 200);
const row = await queryDatabaseChapter(videoID);
const expected = {
startTime: 0,
endTime: 10,
category: "chapter",
actionType: "chapter",
description: "This is a chapter"
};
assert.ok(partialDeepEquals(row, expected));
done();
})
.catch(err => done(err));
});
it("Should not be able to submit an music_offtopic with mute action type (JSON method)", (done) => {
const videoID = "postSkip4";
postSkipSegmentJSON({
@@ -237,8 +276,27 @@ describe("postSkipSegments", () => {
.catch(err => done(err));
});
it("Should not be able to submit a chapter without permission (JSON method)", (done) => {
const videoID = "postSkipChapter3";
postSkipSegmentJSON({
userID: submitUserThree,
videoID,
segments: [{
segment: [0, 10],
category: "chapter",
actionType: "chapter",
description: "This is a chapter"
}],
})
.then(res => {
assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
});
it("Should not be able to submit a chapter with skip action type (JSON method)", (done) => {
const videoID = "postSkipChapter2";
const videoID = "postSkipChapter4";
postSkipSegmentJSON({
userID: submitUserOne,
videoID,
@@ -258,7 +316,7 @@ describe("postSkipSegments", () => {
});
it("Should not be able to submit a sponsor with a description (JSON method)", (done) => {
const videoID = "postSkipChapter3";
const videoID = "postSkipChapter5";
postSkipSegmentJSON({
userID: submitUserOne,
videoID,
@@ -1220,7 +1278,7 @@ describe("postSkipSegments", () => {
});
it("Should return 400 if videoID is empty", (done) => {
const videoID = null as string;
const videoID = null as unknown as string;
postSkipSegmentParam({
videoID,
startTime: 1,

View File

@@ -9,6 +9,8 @@ describe("postWarning", () => {
const endpoint = "/api/warnUser";
const getWarning = (userID: string) => db.prepare("get", `SELECT "userID", "issueTime", "issuerUserID", enabled, "reason" FROM warnings WHERE "userID" = ?`, [userID]);
const warnedUser = getHash("warning-0");
before(async () => {
await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES (?)`, [getHash("warning-vip")]);
});
@@ -16,7 +18,7 @@ describe("postWarning", () => {
it("Should be able to create warning if vip (exp 200)", (done) => {
const json = {
issuerUserID: "warning-vip",
userID: "warning-0",
userID: warnedUser,
reason: "warning-reason-0"
};
client.post(endpoint, json)
@@ -37,7 +39,7 @@ describe("postWarning", () => {
it("Should be not be able to create a duplicate warning if vip", (done) => {
const json = {
issuerUserID: "warning-vip",
userID: "warning-0",
userID: warnedUser,
};
client.post(endpoint, json)
@@ -57,7 +59,7 @@ describe("postWarning", () => {
it("Should be able to remove warning if vip", (done) => {
const json = {
issuerUserID: "warning-vip",
userID: "warning-0",
userID: warnedUser,
enabled: false
};
@@ -100,7 +102,7 @@ describe("postWarning", () => {
it("Should re-enable disabled warning", (done) => {
const json = {
issuerUserID: "warning-vip",
userID: "warning-0",
userID: warnedUser,
enabled: true
};
@@ -116,4 +118,41 @@ describe("postWarning", () => {
})
.catch(err => done(err));
});
it("Should be able to remove your own warning", (done) => {
const json = {
userID: "warning-0",
enabled: false
};
client.post(endpoint, json)
.then(async res => {
assert.strictEqual(res.status, 200);
const data = await getWarning(warnedUser);
const expected = {
enabled: 0
};
assert.ok(partialDeepEquals(data, expected));
done();
})
.catch(err => done(err));
});
it("Should be able to add your own warning", (done) => {
const json = {
userID: "warning-0"
};
client.post(endpoint, json)
.then(async res => {
assert.strictEqual(res.status, 403);
const data = await getWarning(warnedUser);
const expected = {
enabled: 0
};
assert.ok(partialDeepEquals(data, expected));
done();
})
.catch(err => done(err));
});
});

View File

@@ -1,118 +0,0 @@
import { db } from "../../../src/databases/databases";
import { getHash } from "../../../src/utils/getHash";
import assert from "assert";
import { client } from "../../utils/httpClient";
import { AxiosResponse } from "axios";
import { partialDeepEquals, arrayPartialDeepEquals } from "../../utils/partialDeepEquals";
const endpoint = "/api/ratings/rate";
const getRating = (hash: string, params?: unknown): Promise<AxiosResponse> => client.get(`${endpoint}/${hash}`, { params });
const getBulkRating = (hashes: string[], params?: any): Promise<AxiosResponse> => client.get(endpoint, { params: { ...params, prefix: hashes } });
const videoOneID = "some-likes-and-dislikes";
const videoOneIDHash = getHash(videoOneID, 1);
const videoOnePartialHash = videoOneIDHash.substr(0, 4);
const videoTwoID = "some-likes-and-dislikes-2";
const videoTwoIDHash = getHash(videoTwoID, 1);
const videoTwoPartialHash = videoTwoIDHash.substr(0, 4);
describe("getRating", () => {
before(async () => {
const insertUserNameQuery = 'INSERT INTO "ratings" ("videoID", "service", "type", "count", "hashedVideoID") VALUES (?, ?, ?, ?, ?)';
await db.prepare("run", insertUserNameQuery, [videoOneID, "YouTube", 0, 5, videoOneIDHash]);
await db.prepare("run", insertUserNameQuery, [videoOneID, "YouTube", 1, 10, videoOneIDHash]);
await db.prepare("run", insertUserNameQuery, [videoTwoID, "YouTube", 0, 20, videoTwoIDHash]);
await db.prepare("run", insertUserNameQuery, [videoTwoID, "YouTube", 1, 30, videoTwoIDHash]);
});
it("Should be able to get dislikes and likes by default", (done) => {
getRating(videoOnePartialHash)
.then(res => {
assert.strictEqual(res.status, 200);
const expected = [{
type: 0,
count: 5,
}, {
type: 1,
count: 10,
}];
assert.ok(partialDeepEquals(res.data, expected));
done();
})
.catch(err => done(err));
});
it("Should be able to filter for only dislikes", (done) => {
getRating(videoOnePartialHash, { type: 0 })
.then(res => {
assert.strictEqual(res.status, 200);
const expected = [{
type: 0,
count: 5,
}];
assert.ok(partialDeepEquals(res.data, expected));
done();
})
.catch(err => done(err));
});
/*
This test will fail if tests are already ran with redis.
*/
it("Should be able to bulk fetch", (done) => {
getBulkRating([videoOnePartialHash, videoTwoPartialHash])
.then(res => {
assert.strictEqual(res.status, 200);
const expected = [{
type: 0,
count: 20,
hash: videoTwoIDHash,
},
{
type: 1,
count: 30,
hash: videoTwoIDHash,
}, {
type: 0,
count: 5,
hash: videoOneIDHash,
}, {
type: 1,
count: 10,
hash: videoOneIDHash,
}];
assert.ok(arrayPartialDeepEquals(res.data, expected));
done();
})
.catch(err => done(err));
});
it("Should return 400 for invalid hash", (done) => {
getRating("a")
.then(res => {
assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
});
it("Should return 404 for nonexitent type", (done) => {
getRating(videoOnePartialHash, { type: 100 })
.then(res => {
assert.strictEqual(res.status, 404);
done();
})
.catch(err => done(err));
});
it("Should return 404 for nonexistent videoID", (done) => {
getRating("aaaa")
.then(res => {
assert.strictEqual(res.status, 404);
done();
})
.catch(err => done(err));
});
});

View File

@@ -1,51 +0,0 @@
import { db } from "../../../src/databases/databases";
import { getHash } from "../../../src/utils/getHash";
import assert from "assert";
import { client } from "../../utils/httpClient";
const VIPUser = "clearCacheVIP";
const regularUser = "regular-user";
const endpoint = "/api/ratings/clearCache";
const postClearCache = (userID: string, videoID: string) => client({ method: "post", url: endpoint, params: { userID, videoID } });
describe("ratings postClearCache", () => {
before(async () => {
await db.prepare("run", `INSERT INTO "vipUsers" ("userID") VALUES ('${getHash(VIPUser)}')`);
});
it("Should be able to clear cache amy video", (done) => {
postClearCache(VIPUser, "dne-video")
.then(res => {
assert.strictEqual(res.status, 200);
done();
})
.catch(err => done(err));
});
it("Should get 403 as non-vip", (done) => {
postClearCache(regularUser, "clear-test")
.then(res => {
assert.strictEqual(res.status, 403);
done();
})
.catch(err => done(err));
});
it("Should give 400 with missing videoID", (done) => {
client.post(endpoint, { params: { userID: VIPUser } })
.then(res => {
assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
});
it("Should give 400 with missing userID", (done) => {
client.post(endpoint, { params: { videoID: "clear-test" } })
.then(res => {
assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
});
});

View File

@@ -1,118 +0,0 @@
import { db } from "../../../src/databases/databases";
import { getHash } from "../../../src/utils/getHash";
import assert from "assert";
import { client } from "../../utils/httpClient";
import { AxiosResponse } from "axios";
import { partialDeepEquals } from "../../utils/partialDeepEquals";
const endpoint = "/api/ratings/rate/";
const postRating = (body: unknown): Promise<AxiosResponse> => client.post(endpoint, body);
const queryDatabase = (videoID: string) => db.prepare("all", `SELECT * FROM "ratings" WHERE "videoID" = ?`, [videoID]);
const videoIDOne = "normal-video";
const videoIDTwo = "multiple-rates";
const ratingUserID = "rating-testman";
describe("postRating", () => {
before(async () => {
const insertUserNameQuery = 'INSERT INTO "ratings" ("videoID", "service", "type", "count", "hashedVideoID") VALUES (?, ?, ?, ?, ?)';
await db.prepare("run", insertUserNameQuery, [videoIDTwo, "YouTube", 0, 3, getHash(videoIDTwo, 1)]);
});
it("Should be able to vote on a video", (done) => {
const videoID = videoIDOne;
postRating({
userID: ratingUserID,
videoID,
type: 0
})
.then(async res => {
assert.strictEqual(res.status, 200);
const expected = [{
hashedVideoID: getHash(videoID, 1),
videoID,
type: 0,
count: 1,
service: "YouTube"
}];
assert.ok(partialDeepEquals(await queryDatabase(videoID), expected));
done();
})
.catch(err => done(err));
});
it("Should be able to undo a vote on a video", (done) => {
const videoID = videoIDOne;
postRating({
userID: ratingUserID,
videoID,
type: 0,
enabled: false
})
.then(async res => {
assert.strictEqual(res.status, 200);
const expected = [{
type: 0,
count: 0
}];
assert.ok(partialDeepEquals(await queryDatabase(videoID), expected));
done();
})
.catch(err => done(err));
});
it("Should be able to vote after someone else on a video", (done) => {
const videoID = videoIDTwo;
postRating({
userID: ratingUserID,
videoID,
type: 0
})
.then(async res => {
assert.strictEqual(res.status, 200);
const expected = [{
type: 0,
count: 4
}];
assert.ok(partialDeepEquals(await queryDatabase(videoID), expected));
done();
})
.catch(err => done(err));
});
it("Should be able to vote a different type than existing votes on a video", (done) => {
const videoID = videoIDTwo;
postRating({
userID: ratingUserID,
videoID,
type: 1
})
.then(async res => {
assert.strictEqual(res.status, 200);
const expected = [{
type: 0,
count: 4
}, {
type: 1,
count: 1
}];
assert.ok(partialDeepEquals(await queryDatabase(videoID), expected));
done();
})
.catch(err => done(err));
});
it("Should not be able to vote with nonexistent type", (done) => {
const videoID = videoIDOne;
postRating({
userID: ratingUserID,
videoID,
type: 100
})
.then(res => {
assert.strictEqual(res.status, 400);
done();
})
.catch(err => done(err));
});
});

View File

@@ -8,6 +8,8 @@ const genRandom = (bytes=8) => crypto.pseudoRandomBytes(bytes).toString("hex");
const randKey1 = genRandom();
const randValue1 = genRandom();
const randKey2 = genRandom(16);
const randKey3 = genRandom();
const randValue3 = genRandom();
describe("redis test", function() {
before(async function() {
@@ -19,13 +21,41 @@ describe("redis test", function() {
.then(res => {
assert.strictEqual(res, randValue1);
done();
}).catch(err => assert.fail(err));
}).catch(err => done(err));
});
it("Should not be able to get not stored value", (done) => {
redis.get(randKey2)
.then(res => {
if (res) assert.fail("Value should not be found");
if (res) done("Value should not be found");
done();
}).catch(err => assert.fail(err));
}).catch(err => done(err));
});
it("Should be able to delete stored value", (done) => {
redis.del(randKey1)
.then(() => {
redis.get(randKey1)
.then(res => {
assert.strictEqual(res, null);
done();
}).catch(err => done(err));
}).catch(err => done(err));
});
it("Should be able to set expiring value", (done) => {
redis.setEx(randKey3, 8400, randValue3)
.then(() => {
redis.get(randKey3)
.then(res => {
assert.strictEqual(res, randValue3);
done();
}).catch(err => done(err));
}).catch(err => done(err));
});
it("Should continue when undefined value is fetched", (done) => {
const undefkey = `undefined.${genRandom()}`;
redis.get(undefkey)
.then(result => {
assert.ok(!result); // result should be falsy
done();
});
});
});

View File

@@ -10,6 +10,8 @@ describe("reputation", () => {
const userHashLowSubmissions = getHash(userIDLowSubmissions);
const userIDHighDownvotes = "reputation-highdownvotes" as UserID;
const userHashHighDownvotes = getHash(userIDHighDownvotes);
const userIDLowNonSelfDownvotes = "reputation-lownonselfdownvotes" as UserID;
const userHashLowNonSelfDownvotes = getHash(userIDLowNonSelfDownvotes);
const userIDHighNonSelfDownvotes = "reputation-highnonselfdownvotes" as UserID;
const userHashHighNonSelfDownvotes = getHash(userIDHighNonSelfDownvotes);
const userIDNewSubmissions = "reputation-newsubmissions" as UserID;
@@ -45,15 +47,28 @@ describe("reputation", () => {
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-uuid-7", userHashHighDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
// First video is considered a normal downvote, second is considered a self-downvote (ie. they didn't resubmit to fix their downvote)
await db.prepare("run", sponsorTimesInsertQuery, [`${videoID}A`, 1, 11, 2, 0, "reputation-1-1-uuid-0", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [`${videoID}A`, 1, 11, 2, 0, "reputation-1-1-uuid-0", userHashLowNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
// Different category, same video
await db.prepare("run", sponsorTimesInsertQuery, [`${videoID}A`, 1, 11, -2, 0, "reputation-1-1-uuid-1", userHashHighNonSelfDownvotes, 1606240000000, 50, "intro", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-uuid-2", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-uuid-3", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-uuid-4", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, -1, 0, "reputation-1-1-uuid-5", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-uuid-6", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-uuid-7", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [`${videoID}A`, 1, 11, -2, 0, "reputation-1-1-uuid-1", userHashLowNonSelfDownvotes, 1606240000000, 50, "intro", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-uuid-2", userHashLowNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-uuid-3", userHashLowNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-uuid-4", userHashLowNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, -1, 0, "reputation-1-1-uuid-5", userHashLowNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-uuid-6", userHashLowNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-uuid-7", userHashLowNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
// First videos is considered a normal downvote, last is considered a self-downvote (ie. they didn't resubmit to fix their downvote)
await db.prepare("run", sponsorTimesInsertQuery, [`${videoID}A`, 1, 11, 2, 0, "reputation-1-1-1-uuid-0", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
// Different category, same video
await db.prepare("run", sponsorTimesInsertQuery, [`${videoID}A`, 1, 11, -2, 0, "reputation-1-1-1-uuid-1", userHashHighNonSelfDownvotes, 1606240000000, 50, "intro", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [`${videoID}B`, 1, 11, -2, 0, "reputation-1-1-1-uuid-1-b", userHashHighNonSelfDownvotes, 1606240000000, 50, "intro", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [`${videoID}C`, 1, 11, -2, 0, "reputation-1-1-1-uuid-1-c", userHashHighNonSelfDownvotes, 1606240000000, 50, "intro", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-1-uuid-2", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-1-uuid-3", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-1-uuid-4", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, -1, 0, "reputation-1-1-1-uuid-5", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-1-uuid-6", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 0, 0, "reputation-1-1-1-uuid-7", userHashHighNonSelfDownvotes, 1606240000000, 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 2, 0, "reputation-2-uuid-0", userHashNewSubmissions, Date.now(), 50, "sponsor", 0, 0]);
await db.prepare("run", sponsorTimesInsertQuery, [videoID, 1, 11, 2, 0, "reputation-2-uuid-1", userHashNewSubmissions, Date.now(), 50, "sponsor", 0, 0]);
@@ -141,10 +156,10 @@ describe("reputation", () => {
};
const data = await getReputation(getHash(userIDHighDownvotes));
assert.strictEqual(data, calculateReputationFromMetrics(metrics));
assert.strictEqual(data, -2.125);
assert.strictEqual(data, -1.7500000000000002);
});
it("user with high non self downvote ratio", async () => {
it("user with low non self downvote ratio", async () => {
const metrics = {
totalSubmissions: 8,
downvotedSubmissions: 2,
@@ -155,9 +170,14 @@ describe("reputation", () => {
oldUpvotedSubmissions: 1,
mostUpvotedInLockedVideoSum: 0
};
const data = await getReputation(userHashHighNonSelfDownvotes);
const data = await getReputation(userHashLowNonSelfDownvotes);
assert.strictEqual(data, calculateReputationFromMetrics(metrics));
assert.strictEqual(data, -1.6428571428571428);
assert.strictEqual(data, 0);
});
it("user with high non self downvote ratio", async () => {
const data = await getReputation(userHashHighNonSelfDownvotes);
assert.strictEqual(data, -2.5);
});
it("user with mostly new submissions", async () => {

View File

@@ -6,14 +6,13 @@ import { client } from "../utils/httpClient";
import { db, privateDB } from "../../src/databases/databases";
import redis from "../../src/utils/redis";
import assert from "assert";
import { Logger } from "../../src/utils/logger";
// helpers
const getSegment = (UUID: string) => db.prepare("get", `SELECT "votes", "locked", "category" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]);
const permVIP1 = "tempVipPermOne";
const permVIP1 = "tempVip_permaVIPOne";
const publicPermVIP1 = getHash(permVIP1) as HashedUserID;
const permVIP2 = "tempVipPermOne";
const permVIP2 = "tempVip_permaVIPTwo";
const publicPermVIP2 = getHash(permVIP2) as HashedUserID;
const tempVIPOne = "tempVipTempOne";
@@ -51,15 +50,8 @@ const postVoteCategory = (userID: string, UUID: string, category: string) => cli
category
}
});
const checkUserVIP = async (publicID: HashedUserID) => {
try {
const reply = await redis.get(tempVIPKey(publicID));
return reply;
} catch (e) {
Logger.error(e as string);
return false;
}
};
const checkUserVIP = async (publicID: HashedUserID): Promise<string> =>
await redis.get(tempVIPKey(publicID));
describe("tempVIP test", function() {
before(async function() {
@@ -152,7 +144,7 @@ describe("tempVIP test", function() {
.catch(err => done(err));
});
it("Should be able to remove tempVIP prematurely", (done) => {
addTempVIP("false", permVIP1, publicTempVIPOne, null)
addTempVIP("false", permVIP1, publicTempVIPOne)
.then(async res => {
assert.strictEqual(res.status, 200);
const vip = await checkUserVIP(publicTempVIPOne);

View File

@@ -59,6 +59,8 @@ describe("voteOnSponsorTime", () => {
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
@@ -67,6 +69,8 @@ describe("voteOnSponsorTime", () => {
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]);
const insertWarningQuery = 'INSERT INTO "warnings" ("userID", "issueTime", "issuerUserID", "enabled") VALUES(?, ?, ?, ?)';
await db.prepare("run", insertWarningQuery, [warnUser01Hash, now, warnVip01Hash, 1]);
@@ -74,7 +78,6 @@ describe("voteOnSponsorTime", () => {
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, warnVip01Hash, 1]);
await db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 1000)), warnVip01Hash, 1]);
await db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 2000)), warnVip01Hash, 1]);
@@ -223,6 +226,30 @@ describe("voteOnSponsorTime", () => {
.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")
@@ -265,6 +292,18 @@ describe("voteOnSponsorTime", () => {
.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")
@@ -419,6 +458,32 @@ describe("voteOnSponsorTime", () => {
.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) => {
const UUID = "invalid-uuid";
postVoteCategory("randomID3", UUID, "intro")
@@ -628,4 +693,18 @@ describe("voteOnSponsorTime", () => {
done();
});
});
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));
});
});
});