diff --git a/src/databases/Sqlite.ts b/src/databases/Sqlite.ts index 7220b5c..a178f1f 100644 --- a/src/databases/Sqlite.ts +++ b/src/databases/Sqlite.ts @@ -102,7 +102,9 @@ export class Sqlite implements IDatabase { } private static processUpgradeQuery(query: string): string { - return query.replace(/^.*--!sqlite-ignore/gm, ""); + return query + .replace(/SERIAL PRIMARY KEY/gi, "INTEGER PRIMARY KEY AUTOINCREMENT") + .replace(/^.*--!sqlite-ignore/gm, ""); } highLoad() { diff --git a/src/routes/postBranding.ts b/src/routes/postBranding.ts index 178db6e..4c57616 100644 --- a/src/routes/postBranding.ts +++ b/src/routes/postBranding.ts @@ -183,51 +183,31 @@ export async function postBranding(req: Request, res: Response) { async function handleExistingVotes(type: BrandingType, videoID: VideoID, hashedUserID: HashedUserID, UUID: BrandingUUID, hashedIP: HashedIP, voteType: BrandingVoteType) { const table = type === BrandingType.Title ? `"titleVotes"` : `"thumbnailVotes"`; - const table2 = type === BrandingType.Title ? `"titles"` : `"thumbnails"`; - - const isUsersSubmission = (await db.prepare("get", `SELECT "userID" FROM ${table2} WHERE "UUID" = ?`, [UUID]))?.userID === hashedUserID; const idsDealtWith: BrandingUUID[] = []; // Either votes of the same type, or on the same submission (undo a downvote) const existingVotes = await privateDB.prepare("all", `SELECT "id", "UUID", "type" from ${table} where "videoID" = ? AND "userID" = ? AND ("type" = ? OR "UUID" = ?)`, [videoID, hashedUserID, voteType, UUID]) as ExistingVote[]; if (existingVotes.length > 0) { // Only one upvote per video - if (voteType === BrandingVoteType.Upvote) { - for (const existingVote of existingVotes) { - switch (existingVote.type) { - case BrandingVoteType.Upvote: - // Old case where there are duplicate rows in private db - if (!idsDealtWith.includes(existingVote.UUID)) { - idsDealtWith.push(existingVote.UUID); - await db.prepare("run", `UPDATE ${table} SET "votes" = "votes" - 1 WHERE "UUID" = ?`, [existingVote.UUID]); - } + for (const existingVote of existingVotes) { + // For downvotes, only undo for this specific submission (multiple downvotes on one submission not allowed) + if (voteType === BrandingVoteType.Downvote && existingVote.UUID !== UUID) continue; - await privateDB.prepare("run", `DELETE FROM ${table} WHERE "id" = ?`, [existingVote.id]); - break; - case BrandingVoteType.Downvote: { - // Undoing a downvote now that it is being upvoted - - // Only undo downvote if it is not their submission - if (!isUsersSubmission) { - await db.prepare("run", `UPDATE ${table} SET "downvotes" = "downvotes" - 1 WHERE "UUID" = ?`, [existingVote.UUID]); - } - - await privateDB.prepare("run", `DELETE FROM ${table} WHERE "id" = ?`, [existingVote.id]); - break; - } - } - } - } else if (isUsersSubmission) { - // Treat like upvoting another submission (undoing upvote) - let dealtWith = false; - for (const existingVote of existingVotes) { - if (existingVote.type === BrandingVoteType.Upvote && existingVote.UUID === UUID) { - if (!dealtWith) { - dealtWith = true; - await db.prepare("run", `UPDATE ${table} SET "votes" = "votes" - 1 WHERE "UUID" = ?`, [UUID]); + switch (existingVote.type) { + case BrandingVoteType.Upvote: + // Old case where there are duplicate rows in private db + if (!idsDealtWith.includes(existingVote.UUID)) { + idsDealtWith.push(existingVote.UUID); + await db.prepare("run", `UPDATE ${table} SET "votes" = "votes" - 1 WHERE "UUID" = ?`, [existingVote.UUID]); } await privateDB.prepare("run", `DELETE FROM ${table} WHERE "id" = ?`, [existingVote.id]); + break; + case BrandingVoteType.Downvote: { + await db.prepare("run", `UPDATE ${table} SET "downvotes" = "downvotes" - 1 WHERE "UUID" = ?`, [existingVote.UUID]); + + await privateDB.prepare("run", `DELETE FROM ${table} WHERE "id" = ?`, [existingVote.id]); + break; } } } diff --git a/test/cases/postBranding.ts b/test/cases/postBranding.ts index 7d3d985..9560ab6 100644 --- a/test/cases/postBranding.ts +++ b/test/cases/postBranding.ts @@ -373,6 +373,51 @@ describe("postBranding", () => { } }); + it("Double downvote title and thumbnail should only count once", async () => { + const videoID = "postBrand5"; + const title = { + title: "Some other title", + original: false + }; + const thumbnail = { + timestamp: 13.42, + original: false + }; + + const res = await postBranding({ + title, + thumbnail, + userID: userID6, + service: Service.YouTube, + videoID, + downvote: true + }); + + assert.strictEqual(res.status, 200); + const dbTitles = await queryTitleByVideo(videoID, true); + for (const dbTitle of dbTitles) { + if (dbTitle.title === title.title) { + const dbTitleVotes = await queryTitleVotesByUUID(dbTitle.UUID); + assert.strictEqual(dbTitleVotes.votes, 0); + assert.strictEqual(dbTitleVotes.downvotes, 1); + assert.strictEqual(dbTitleVotes.locked, 0); + assert.strictEqual(dbTitleVotes.shadowHidden, 0); + } + } + + const dbThumbnails = await queryThumbnailByVideo(videoID, true); + for (const dbThumbnail of dbThumbnails) { + if (dbThumbnail.timestamp === thumbnail.timestamp) { + const dbThumbnailVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID); + + assert.strictEqual(dbThumbnailVotes.votes, 0); + assert.strictEqual(dbThumbnailVotes.downvotes, 1); + assert.strictEqual(dbThumbnailVotes.locked, 0); + assert.strictEqual(dbThumbnailVotes.shadowHidden, 0); + } + } + }); + it("Downvote your own title and thumbnail", async () => { const videoID = "postBrand5"; const title = {