diff --git a/server/prisma/migrations/20220911155944_cascade_note_delete_to_embeds/migration.sql b/server/prisma/migrations/20220911155944_cascade_note_delete_to_embeds/migration.sql new file mode 100644 index 0000000..0ff3465 --- /dev/null +++ b/server/prisma/migrations/20220911155944_cascade_note_delete_to_embeds/migration.sql @@ -0,0 +1,17 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_EncryptedEmbed" ( + "id" TEXT NOT NULL PRIMARY KEY, + "note_id" TEXT NOT NULL, + "embed_id" TEXT NOT NULL, + "ciphertext" BLOB NOT NULL, + "hmac" TEXT NOT NULL, + "size_bytes" INTEGER NOT NULL, + CONSTRAINT "EncryptedEmbed_note_id_fkey" FOREIGN KEY ("note_id") REFERENCES "EncryptedNote" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); +INSERT INTO "new_EncryptedEmbed" ("ciphertext", "embed_id", "hmac", "id", "note_id", "size_bytes") SELECT "ciphertext", "embed_id", "hmac", "id", "note_id", "size_bytes" FROM "EncryptedEmbed"; +DROP TABLE "EncryptedEmbed"; +ALTER TABLE "new_EncryptedEmbed" RENAME TO "EncryptedEmbed"; +CREATE UNIQUE INDEX "EncryptedEmbed_note_id_embed_id_key" ON "EncryptedEmbed"("note_id", "embed_id"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index 772b450..c71f926 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -28,7 +28,7 @@ model EncryptedEmbed { ciphertext Bytes hmac String size_bytes Int - note EncryptedNote @relation(fields: [note_id], references: [id]) + note EncryptedNote @relation(fields: [note_id], references: [id], onDelete: Cascade) @@unique([note_id, embed_id], name: "noteId_embedId") } diff --git a/server/src/app.integration.test.ts b/server/src/app.integration.test.ts index 465fda8..71eadf8 100644 --- a/server/src/app.integration.test.ts +++ b/server/src/app.integration.test.ts @@ -4,6 +4,8 @@ import { describe, it, expect } from "vitest"; import prisma from "./db/client"; import { deleteExpiredNotes } from "./tasks/deleteExpiredNotes"; import { EventType } from "./logging/EventLogger"; +import { createNote } from "./db/note.dao"; +import { EncryptedNote } from "@prisma/client"; // const testNote with base64 ciphertext and hmac const testNote = { @@ -273,6 +275,44 @@ describe("Clean expired notes", () => { testNote.ciphertext.length + testNote.hmac.length ); }); + + it("removes notes with embeds", async () => { + // insert a note with embeds and with expiry date in the past using prisma + const note = { + ...testNote, + expire_time: new Date(0), + } as EncryptedNote; + const embeds = [ + { + embed_id: "EMBED_ID", + ciphertext: Buffer.from("sample_ciphertext").toString("base64"), + hmac: Buffer.from("sample_hmac").toString("base64"), + }, + ]; + const { id } = await createNote(note, embeds); + + // make request for note and check that response is 200 + const res = await supertest(app).get(`/api/note/${id}`); + expect(res.statusCode).toBe(200); + const embedRes = await supertest(app).get( + `/api/note/${id}/embeds/EMBED_ID` + ); + expect(embedRes.statusCode).toBe(200); + + // run cleanup + const nDeleted = await deleteExpiredNotes(); + expect(nDeleted).toBeGreaterThan(0); + + // if the note is added to the expire filter, it returns 410 + const res2 = await supertest(app).get(`/api/note/${id}`); + expect(res2.statusCode).toBe(410); + + // check that the embed is not found + const embedRes2 = await supertest(app).get( + `/api/note/${id}/embeds/EMBED_ID` + ); + expect(embedRes2.statusCode).toBe(404); + }); }); function expectCodeOrThrowResponse(res: supertest.Response, expected: number) {