Skip to content

Commit 5c1d636

Browse files
committed
chore: add tests
1 parent 360a3ac commit 5c1d636

File tree

2 files changed

+279
-9
lines changed

2 files changed

+279
-9
lines changed

apps/studio/src/server/modules/folder/__tests__/folder.router.test.ts

Lines changed: 259 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
} from "tests/integration/helpers/seed"
1717

1818
import { createCallerFactory } from "~/server/trpc"
19-
import { AuditLogEvent, db, ResourceType } from "../../database"
19+
import { AuditLogEvent, db, ResourceState, ResourceType } from "../../database"
2020
import { folderRouter } from "../folder.router"
2121

2222
const createCaller = createCallerFactory(folderRouter)
@@ -737,6 +737,198 @@ describe("folder.router", async () => {
737737
expect(auditLogs?.eventType).toEqual(AuditLogEvent.ResourceUpdate)
738738
})
739739
})
740+
describe("listChildPages", () => {
741+
it("should throw 401 if not logged in", async () => {
742+
// Arrange
743+
const { folder, site } = await setupFolder()
744+
const { page: indexPage } = await setupPageResource({
745+
parentId: folder.id,
746+
siteId: site.id,
747+
resourceType: "IndexPage",
748+
})
749+
await createChildPages({
750+
parentId: folder.id,
751+
siteId: site.id,
752+
numPages: 3,
753+
numFolders: 5,
754+
})
755+
756+
// Act
757+
const result = unauthedCaller.listChildPages({
758+
siteId: String(site.id),
759+
indexPageId: indexPage.id,
760+
})
761+
762+
// Assert
763+
await expect(result).rejects.toThrowError(
764+
new TRPCError({ code: "UNAUTHORIZED" }),
765+
)
766+
await expect(
767+
db.selectFrom("AuditLog").selectAll().execute(),
768+
).resolves.toHaveLength(0)
769+
})
770+
771+
it("should return an empty array if `siteId` does not exist", async () => {
772+
// Arrange
773+
const invalidSiteId = 999
774+
const { site, folder } = await setupFolder()
775+
await setupAdminPermissions({
776+
userId: session.userId,
777+
siteId: site.id,
778+
})
779+
expect(site.id).not.toEqual(invalidSiteId)
780+
const { page: indexPage } = await setupPageResource({
781+
parentId: folder.id,
782+
siteId: site.id,
783+
resourceType: "IndexPage",
784+
})
785+
await createChildPages({
786+
parentId: folder.id,
787+
siteId: site.id,
788+
numPages: 3,
789+
numFolders: 5,
790+
})
791+
792+
// Act
793+
const result = await caller.listChildPages({
794+
siteId: String(site.id),
795+
indexPageId: indexPage.id,
796+
})
797+
798+
// Assert
799+
expect(result.childPages).toEqual([])
800+
})
801+
802+
it("should throw 403 if user does not have access to the site", async () => {
803+
// Arrange
804+
const { site, folder } = await setupFolder()
805+
const { page: indexPage } = await setupPageResource({
806+
parentId: folder.id,
807+
siteId: site.id,
808+
resourceType: "IndexPage",
809+
})
810+
811+
// Act
812+
const result = caller.listChildPages({
813+
siteId: String(site.id),
814+
indexPageId: indexPage.id,
815+
})
816+
817+
// Assert
818+
await expect(result).rejects.toThrowError(
819+
new TRPCError({
820+
code: "NOT_FOUND",
821+
message:
822+
"You do not have sufficient permissions to perform this action",
823+
}),
824+
)
825+
await expect(
826+
db.selectFrom("AuditLog").selectAll().execute(),
827+
).resolves.toHaveLength(0)
828+
})
829+
830+
it("should throw 404 if the page specified by `indexPageId` is not an `IndexPage`", async () => {
831+
// Arrange
832+
const { site, folder } = await setupFolder()
833+
await setupAdminPermissions({ siteId: site.id, userId: session.userId })
834+
const { page } = await setupPageResource({
835+
resourceType: "Page",
836+
parentId: folder.id,
837+
})
838+
839+
// Act
840+
841+
const result = caller.listChildPages({
842+
siteId: String(site.id),
843+
indexPageId: page.id,
844+
})
845+
846+
// Assert
847+
await expect(result).rejects.toThrowError(
848+
new TRPCError({
849+
code: "NOT_FOUND",
850+
message: "No index page with the specified id could be found",
851+
}),
852+
)
853+
await expect(
854+
db.selectFrom("AuditLog").selectAll().execute(),
855+
).resolves.toHaveLength(0)
856+
})
857+
858+
it("should throw 404 if the `indexPageId` does not exist", async () => {
859+
// Arrange
860+
const { site } = await setupFolder()
861+
await setupAdminPermissions({ siteId: site.id, userId: session.userId })
862+
863+
// Act
864+
const result = caller.listChildPages({
865+
siteId: String(site.id),
866+
indexPageId: "1234",
867+
})
868+
869+
// Assert
870+
await expect(result).rejects.toThrowError(
871+
new TRPCError({
872+
code: "NOT_FOUND",
873+
message: "No index page with the specified id could be found",
874+
}),
875+
)
876+
await expect(
877+
db.selectFrom("AuditLog").selectAll().execute(),
878+
).resolves.toHaveLength(0)
879+
})
880+
881+
it("should return only the published pages of the parent folder", async () => {
882+
// Arrange
883+
const { site, folder } = await setupFolder()
884+
await setupAdminPermissions({ siteId: site.id, userId: session.userId })
885+
const { page: indexPage } = await setupPageResource({
886+
parentId: folder.id,
887+
siteId: site.id,
888+
resourceType: "IndexPage",
889+
})
890+
const { pages, folders } = await createChildPages({
891+
parentId: folder.id,
892+
siteId: site.id,
893+
numPages: 3,
894+
numFolders: 4,
895+
state: "Published",
896+
userId: session.userId,
897+
})
898+
899+
// NOTE: Not `published`
900+
await createChildPages({
901+
parentId: folder.id,
902+
siteId: site.id,
903+
numPages: 3,
904+
numFolders: 4,
905+
})
906+
907+
// Act
908+
const result = await caller.listChildPages({
909+
siteId: String(site.id),
910+
indexPageId: indexPage.id,
911+
})
912+
913+
// Assert
914+
expect(result.childPages).toHaveLength(7)
915+
const indexPagesOfFolders = await db
916+
.selectFrom("Resource")
917+
.where(
918+
"parentId",
919+
"in",
920+
folders.map(({ id }) => id),
921+
)
922+
.where("type", "=", "IndexPage")
923+
.select("id")
924+
.execute()
925+
const indexPagesId = indexPagesOfFolders.map(({ id }) => id)
926+
const pagesId = pages.map(({ id }) => id)
927+
expect(result.childPages.map(({ id }) => id).toSorted()).toStrictEqual(
928+
[...pagesId, ...indexPagesId].toSorted(),
929+
)
930+
})
931+
})
740932
})
741933

742934
// Test util functions
@@ -755,3 +947,69 @@ const getFolderWithPermalink = ({
755947
.selectAll()
756948
.executeTakeFirstOrThrow()
757949
}
950+
951+
const createChildPages = async ({
952+
parentId,
953+
siteId,
954+
numPages,
955+
numFolders,
956+
state = ResourceState.Draft,
957+
userId,
958+
}: {
959+
parentId: string
960+
siteId: number
961+
numPages: number
962+
numFolders: number
963+
state?: ResourceState
964+
userId?: string
965+
}) => {
966+
if (state === ResourceState.Published && !userId) {
967+
throw new Error(
968+
"Precondition failed for `createChildPages`: a valid `userId` is required in order to publish a resource",
969+
)
970+
}
971+
972+
const pages = await Promise.all(
973+
Array.from({ length: numPages })
974+
.fill(null)
975+
.map(async () => {
976+
const permalink = crypto.randomUUID()
977+
const { page } = await setupPageResource({
978+
resourceType: "Page",
979+
siteId,
980+
parentId,
981+
state,
982+
permalink,
983+
userId,
984+
})
985+
return page
986+
}),
987+
)
988+
989+
const folders = await Promise.all(
990+
Array.from({ length: numFolders })
991+
.fill(null)
992+
.map(async () => {
993+
const { folder } = await setupFolder({
994+
siteId,
995+
parentId,
996+
permalink: crypto.randomUUID(),
997+
state: ResourceState.Published,
998+
})
999+
1000+
const permalink = crypto.randomUUID()
1001+
await setupPageResource({
1002+
resourceType: "IndexPage",
1003+
siteId,
1004+
parentId: folder.id,
1005+
state,
1006+
permalink,
1007+
userId,
1008+
})
1009+
1010+
return folder
1011+
}),
1012+
)
1013+
1014+
return { pages, folders }
1015+
}

apps/studio/src/server/modules/folder/folder.router.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -312,20 +312,32 @@ export const folderRouter = router({
312312
listChildPages: protectedProcedure
313313
.input(listChildPagesSchema)
314314
.query(async ({ ctx, input: { indexPageId, siteId } }) => {
315+
// NOTE: The `resourceId` passed here is the id of the index page
316+
// of the folder, not the actual folder itself
317+
const { parentId, type } = await db
318+
.selectFrom("Resource")
319+
.where("id", "=", indexPageId)
320+
.select(["parentId", "type"])
321+
.executeTakeFirstOrThrow(
322+
() =>
323+
new TRPCError({
324+
code: "NOT_FOUND",
325+
message: "No index page with the specified id could be found",
326+
}),
327+
)
328+
315329
await validateUserPermissionsForResource({
316330
siteId: Number(siteId),
317331
action: "read",
318332
userId: ctx.user.id,
319-
resourceId: indexPageId,
320333
})
321334

322-
// NOTE: The `resourceId` passed here is the id of the index page
323-
// of the folder, not the actual folder itself
324-
const { parentId } = await db
325-
.selectFrom("Resource")
326-
.where("id", "=", indexPageId)
327-
.select(["parentId"])
328-
.executeTakeFirstOrThrow()
335+
if (type !== ResourceType.IndexPage) {
336+
throw new TRPCError({
337+
code: "NOT_FOUND",
338+
message: "No index page with the specified id could be found",
339+
})
340+
}
329341

330342
// NOTE: This is not a general `resource.list`
331343
// but reimplemented here because it makes certain assumptions about what should be shown

0 commit comments

Comments
 (0)