Files
arkvault/test/server/deletion.test.ts
2026-10-20 03:12:53 +09:00

93 lines
3.2 KiB
TypeScript

import { access } from "fs/promises";
import { beforeEach, describe, expect, it } from "vitest";
import { UploadRepo, db } from "#lib/server/db/index.ts";
import { processPendingFileDeletions } from "#lib/server/services/file-deletion.ts";
import { DirectoryService, FileService } from "#lib/server/services/index.ts";
import {
ciphertext,
createDirectory,
createFile,
createUser,
getQueuedDeletions,
newEntryParams,
resetDatabase,
} from "../helpers/database.ts";
import { createStoredFile } from "../helpers/storage.ts";
const exists = (path: string) =>
access(path).then(
() => true,
() => false,
);
beforeEach(resetDatabase);
describe("file deletion", () => {
it("deletes the stored file after the database row is deleted", async () => {
const { userId } = await createUser();
const path = await createStoredFile();
const fileId = await createFile(userId, "root", path);
await FileService.deleteFile(userId, fileId);
await processPendingFileDeletions();
expect(await exists(path)).toBe(false);
expect(await getQueuedDeletions()).toEqual([]);
});
it("keeps failed deletions in the queue", async () => {
const { userId } = await createUser();
const fileId = await createFile(userId, "root", "/proc/version"); // Can't be deleted
await FileService.deleteFile(userId, fileId);
await processPendingFileDeletions();
expect(await getQueuedDeletions()).toEqual(["/proc/version"]);
});
it("deletes directories recursively including pending uploads", async () => {
const { userId } = await createUser();
const rootId = await createDirectory(userId);
const childId = await createDirectory(userId, rootId);
const grandchildId = await createDirectory(userId, childId);
const otherId = await createDirectory(userId);
const paths = await Promise.all([1, 2, 3, 4].map(() => createStoredFile()));
const fileIds = [
await createFile(userId, rootId, paths[0]!),
await createFile(userId, grandchildId, paths[1]!),
];
const otherFileId = await createFile(userId, otherId, paths[2]!);
await UploadRepo.createFileUploadSession(db, {
...newEntryParams(userId, childId),
id: crypto.randomUUID(),
path: paths[3]!,
totalChunks: 1,
expiresAt: new Date(Date.now() + 60_000),
hskVersion: null,
contentType: "image/png",
encCreatedAt: null,
encLastModifiedAt: ciphertext(),
});
const { deletedFileIds } = await DirectoryService.deleteDirectory(userId, rootId);
expect(deletedFileIds.sort()).toEqual(fileIds.sort());
expect(await getQueuedDeletions()).toEqual([paths[0], paths[1], paths[3]].sort());
await expect(DirectoryService.getDirectory(userId, grandchildId)).rejects.toMatchObject({
code: "NOT_FOUND",
});
expect((await FileService.getFile(userId, otherFileId)).id).toBe(otherFileId);
});
it("rejects deleting another user's directory", async () => {
const { userId: ownerId } = await createUser();
const { userId: otherId } = await createUser();
const directoryId = await createDirectory(ownerId);
await expect(DirectoryService.deleteDirectory(otherId, directoryId)).rejects.toMatchObject({
code: "NOT_FOUND",
});
});
});