Skip to content

Commit

Permalink
test: add tests for updatePageBlob
Browse files Browse the repository at this point in the history
  • Loading branch information
karrui committed Oct 2, 2024
1 parent 8c20584 commit 8409d36
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 18 deletions.
158 changes: 154 additions & 4 deletions apps/studio/src/server/modules/page/__tests__/page.router.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { IsomerSchema } from "@opengovsg/isomer-components"
import type { z } from "zod"
import { TRPCError } from "@trpc/server"
import { omit, pick } from "lodash"
Expand All @@ -9,17 +10,18 @@ import {
} from "tests/integration/helpers/iron-session"
import { setupFolder, setupPageResource } from "tests/integration/helpers/seed"

import type { reorderBlobSchema } from "~/schemas/page"
import type { reorderBlobSchema, updatePageBlobSchema } from "~/schemas/page"
import { createCallerFactory } from "~/server/trpc"
import { db } from "../../database"
import { pageRouter } from "../page.router"

const createCaller = createCallerFactory(pageRouter)

describe("page.router", () => {
describe("page.router", async () => {
let caller: ReturnType<typeof createCaller>
beforeAll(async () => {
const session = await applyAuthedSession()
const session = await applyAuthedSession()

beforeAll(() => {
caller = createCaller(createMockRequest(session))
})

Expand Down Expand Up @@ -418,4 +420,152 @@ describe("page.router", () => {
expect(result).toEqual(expectedBlocks)
})
})

describe("updatePageBlob", () => {
const NEW_PAGE_BLOCKS: IsomerSchema["content"] = [
{
type: "prose",
content: [
{
type: "paragraph",
content: [
{
type: "text",
text: "This is the new block",
},
],
},
],
},
{
type: "callout",
content: {
type: "prose",
content: [
{
type: "paragraph",
content: [
{
type: "text",
text: "Test Callout content",
},
],
},
],
},
},
]

type Page = Awaited<ReturnType<typeof setupPageResource>>["page"]
let pageToUpdate: Page
type UpdatePageOutput = z.output<typeof updatePageBlobSchema>
const createPageUpdateArgs = (page: Page) => {
return {
pageId: Number(page.id),
siteId: page.siteId,
content: JSON.stringify({
content: NEW_PAGE_BLOCKS,
layout: "content",
page: pick(page, ["title", "permalink"]),
version: "0.1.0",
} as UpdatePageOutput["content"]),
}
}

beforeEach(async () => {
const { page } = await setupPageResource({ resourceType: "Page" })
pageToUpdate = page
})

it("should throw 401 if not logged in", async () => {
const unauthedSession = applySession()
const unauthedCaller = createCaller(createMockRequest(unauthedSession))
const pageUpdateArgs = createPageUpdateArgs(pageToUpdate)
const result = unauthedCaller.updatePageBlob(pageUpdateArgs)

await expect(result).rejects.toThrowError(
new TRPCError({ code: "UNAUTHORIZED" }),
)
})

it("should return 404 if page does not exist", async () => {
// Arrange
const pageUpdateArgs = createPageUpdateArgs(pageToUpdate)

// Act
const result = caller.updatePageBlob({
...pageUpdateArgs,
pageId: 999999, // should not exist
})

// Assert
await expect(result).rejects.toThrowError(
new TRPCError({
code: "NOT_FOUND",
}),
)
})

it("should return 422 if content is not valid", async () => {
// Arrange
const pageUpdateArgs = createPageUpdateArgs(pageToUpdate)

// Act
const result = caller.updatePageBlob({
...pageUpdateArgs,
content: "do not match the shape",
})

// Assert
await expect(result).rejects.toThrowError("Invalid page content")
})

it("should update draft page blob if args are valid and has current draft", async () => {
// Arrange
const pageUpdateArgs = createPageUpdateArgs(pageToUpdate)

// Act
const result = await caller.updatePageBlob(pageUpdateArgs)

// Assert
const actual = await db
.selectFrom("Blob")
.where("id", "=", pageToUpdate.draftBlobId)
.select("content")
.executeTakeFirstOrThrow()
expect(actual.content).toEqual(result.content)
})

it.only("should create draft page blob if args are valid and without current draft", async () => {
// Arrange
const { page: publishedPageToUpdate } = await setupPageResource({
resourceType: "Page",
state: "Published",
userId: session.userId,
})
expect(publishedPageToUpdate.publishedVersionId).not.toBeNull()
expect(publishedPageToUpdate.draftBlobId).toBeNull()
const pageUpdateArgs = createPageUpdateArgs(publishedPageToUpdate)

// Act
const result = await caller.updatePageBlob(pageUpdateArgs)

// Assert
const actual = await db
.selectFrom("Blob")
.innerJoin("Resource", "Resource.draftBlobId", "Blob.id")
.where("Resource.id", "=", publishedPageToUpdate.id)
.select([
"Blob.content",
"Resource.publishedVersionId",
"Resource.draftBlobId",
])
.executeTakeFirstOrThrow()
expect(actual).toMatchObject({
content: result.content,
publishedVersionId: publishedPageToUpdate.publishedVersionId,
draftBlobId: expect.any(String),
})
})
})
})
20 changes: 9 additions & 11 deletions apps/studio/src/server/modules/resource/resource.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { SelectExpression } from "kysely"
import type { UnwrapTagged } from "type-fest"
import { TRPCError } from "@trpc/server"
import { type DB } from "~prisma/generated/generatedTypes"

import type { Resource, SafeKysely } from "../database"
Expand Down Expand Up @@ -164,25 +163,24 @@ export const updatePageById = (

export const updateBlobById = async (
db: SafeKysely,
props: {
{
pageId,
content,
siteId,
}: {
pageId: number
content: UnwrapTagged<PrismaJson.BlobJsonContent>
siteId: number
},
) => {
const { pageId: id, content } = props
const page = await db
.selectFrom("Resource")
.where("Resource.id", "=", String(id))
.where("siteId", "=", props.siteId)
.where("Resource.id", "=", String(pageId))
.where("siteId", "=", siteId)
// NOTE: We update the draft first
// Main should only be updated at build
.select("draftBlobId")
.executeTakeFirst()

if (!page) {
throw new TRPCError({ code: "NOT_FOUND" })
}
.executeTakeFirstOrThrow()

if (!page.draftBlobId) {
// NOTE: no draft for this yet, need to create a new one
Expand All @@ -193,7 +191,7 @@ export const updateBlobById = async (
.executeTakeFirstOrThrow()
await db
.updateTable("Resource")
.where("id", "=", String(id))
.where("id", "=", String(pageId))
.set({ draftBlobId: newBlob.id })
.execute()
}
Expand Down
33 changes: 30 additions & 3 deletions apps/studio/tests/integration/helpers/seed/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { db, jsonb } from "~server/db"
import { nanoid } from "nanoid"

import type { ResourceType } from "~server/db"
import type { ResourceState, ResourceType } from "~server/db"

export const setupSite = async (siteId?: number, fetch?: boolean) => {
if (siteId !== undefined && fetch) {
Expand Down Expand Up @@ -170,15 +170,19 @@ export const setupPageResource = async ({
siteId: siteIdProp,
blobId: blobIdProp,
resourceType,
state = "Draft",
userId,
}: {
siteId?: number
blobId?: string
resourceType: ResourceType
state?: ResourceState
userId?: string
}) => {
const { site, navbar, footer } = await setupSite(siteIdProp)
const blob = await setupBlob(blobIdProp)

const page = await db
let page = await db
.insertInto("Resource")
.values({
title: "test page",
Expand All @@ -188,11 +192,34 @@ export const setupPageResource = async ({
publishedVersionId: null,
draftBlobId: blob.id,
type: resourceType,
state: "Draft",
state,
})
.returningAll()
.executeTakeFirstOrThrow()

if (state === "Published" && userId) {
const version = await db
.insertInto("Version")
.values({
versionNum: 1,
resourceId: page.id,
blobId: blob.id,
publishedBy: userId,
})
.returning("id")
.executeTakeFirstOrThrow()

page = await db
.updateTable("Resource")
.where("id", "=", page.id)
.set({
publishedVersionId: version.id,
draftBlobId: null,
})
.returningAll()
.executeTakeFirstOrThrow()
}

return {
site,
navbar,
Expand Down

0 comments on commit 8409d36

Please sign in to comment.