diff --git a/apps/studio/src/server/modules/folder/__tests__/folder.router.test.ts b/apps/studio/src/server/modules/folder/__tests__/folder.router.test.ts new file mode 100644 index 000000000..54796eff0 --- /dev/null +++ b/apps/studio/src/server/modules/folder/__tests__/folder.router.test.ts @@ -0,0 +1,218 @@ +import { TRPCError } from "@trpc/server" +import { + applyAuthedSession, + applySession, + createMockRequest, +} from "tests/integration/helpers/iron-session" +import { + setupFolder, + setupPageResource, + setupSite, +} from "tests/integration/helpers/seed" + +import { createCallerFactory } from "~/server/trpc" +import { db } from "../../database" +import { folderRouter } from "../folder.router" + +const createCaller = createCallerFactory(folderRouter) + +describe("folder.router", () => { + let caller: ReturnType + let unauthedCaller: ReturnType + + beforeAll(async () => { + const session = await applyAuthedSession() + caller = createCaller(createMockRequest(session)) + const unauthedSession = applySession() + unauthedCaller = createCaller(createMockRequest(unauthedSession)) + }) + + describe("create", () => { + it("should throw 401 if not logged in", async () => { + // Act + const result = unauthedCaller.create({ + folderTitle: "test folder", + siteId: 1, + permalink: "test-folder", + }) + + // Assert + await expect(result).rejects.toThrowError( + new TRPCError({ code: "UNAUTHORIZED" }), + ) + }) + + it("should throw 409 if permalink already exists", async () => { + // Arrange + const duplicatePermalink = "duplicate-permalink" + const { site } = await setupFolder({ permalink: duplicatePermalink }) + + // Act + const result = caller.create({ + folderTitle: "test folder", + siteId: site.id, + permalink: duplicatePermalink, + }) + + // Assert + await expect(result).rejects.toThrowError( + new TRPCError({ + code: "CONFLICT", + message: "A resource with the same permalink already exists", + }), + ) + }) + + it("should throw 404 if `siteId` does not exist", async () => { + // Arrange + const invalidSiteId = 999 + const { site } = await setupSite() + expect(site.id).not.toEqual(invalidSiteId) + + // Act + const result = caller.create({ + folderTitle: "test folder", + siteId: invalidSiteId, + permalink: "test-folder", + }) + + // Assert + await expect(result).rejects.toThrowError( + new TRPCError({ + code: "NOT_FOUND", + message: "Site does not exist", + }), + ) + }) + + it("should throw 404 if `parentFolderId` does not exist", async () => { + // Arrange + const { site } = await setupSite() + + // Act + const result = caller.create({ + folderTitle: "test folder", + siteId: site.id, + permalink: "test-folder", + parentFolderId: 999, + }) + + // Assert + await expect(result).rejects.toThrowError( + new TRPCError({ + code: "NOT_FOUND", + message: "Parent folder does not exist", + }), + ) + }) + + it("should throw 400 if `parentFolderId` is not a folder", async () => { + // Arrange + const { site, page } = await setupPageResource({ + resourceType: "Page", + }) + + // Act + const result = caller.create({ + folderTitle: "test folder", + siteId: site.id, + permalink: "test-folder", + parentFolderId: Number(page.id), + }) + + // Assert + await expect(result).rejects.toThrowError( + new TRPCError({ + code: "BAD_REQUEST", + message: "Resource ID does not point to a folder", + }), + ) + }) + + it("should create a folder even with duplicate permalink if `siteId` is different", async () => { + // Arrange + const duplicatePermalink = "duplicate-permalink" + const { site: _firstSite } = await setupFolder({ + permalink: duplicatePermalink, + }) + const { site: secondSite } = await setupSite() + + // Act + const result = await caller.create({ + folderTitle: "test folder", + siteId: secondSite.id, + permalink: duplicatePermalink, + }) + + // Assert + const actualFolder = await getFolderWithPermalink({ + siteId: secondSite.id, + permalink: duplicatePermalink, + }) + expect(result).toEqual({ folderId: actualFolder.id }) + }) + + it("should create a folder", async () => { + // Arrange + const permalinkToUse = "test-folder-999" + const { site } = await setupSite() + // Act + const result = await caller.create({ + folderTitle: "test folder 999", + siteId: site.id, + permalink: permalinkToUse, + }) + + // Assert + const actualFolder = await getFolderWithPermalink({ + permalink: permalinkToUse, + siteId: site.id, + }) + expect(result).toEqual({ folderId: actualFolder.id }) + }) + + it("should create a nested folder if `parentFolderId` is provided", async () => { + // Arrange + const permalinkToUse = "test-folder-777" + const { folder: parentFolder, site } = await setupFolder() + + // Act + const result = await caller.create({ + folderTitle: "test folder", + siteId: site.id, + permalink: permalinkToUse, + parentFolderId: Number(parentFolder.id), + }) + + // Assert + const actualFolder = await getFolderWithPermalink({ + permalink: permalinkToUse, + siteId: site.id, + }) + expect(actualFolder.parentId).toEqual(parentFolder.id) + expect(result).toEqual({ folderId: actualFolder.id }) + }) + + // TODO: Add tests when permissions are implemented + it.skip("should throw 403 if user does not have write access to the site", async () => {}) + + it.skip("should throw 403 if user does not have write access to the parent folder", async () => {}) + }) +}) + +// Test util functions +const getFolderWithPermalink = ({ + siteId, + permalink, +}: { + siteId: number + permalink: string +}) => { + return db + .selectFrom("Resource") + .where("type", "=", "Folder") + .where("siteId", "=", siteId) + .where("permalink", "=", permalink) + .selectAll() + .executeTakeFirstOrThrow() +}