From 7f6a0f36e9823db609afba01ace56d8d394571a7 Mon Sep 17 00:00:00 2001 From: Lars Hampe Date: Tue, 12 Nov 2024 18:51:30 +0100 Subject: [PATCH] feat(api): add page tests --- apps/api/src/page/create.ts | 4 +- apps/api/src/page/delete.ts | 2 +- apps/api/src/page/page.test.ts | 171 +++++++++++++++++++++++++++++ apps/api/src/page/public.ts | 3 +- bun.lockb | Bin 562032 -> 562040 bytes packages/schema/src/page/base.ts | 4 +- packages/schema/src/page/create.ts | 4 +- packages/schema/src/page/public.ts | 30 ++--- 8 files changed, 196 insertions(+), 22 deletions(-) create mode 100644 apps/api/src/page/page.test.ts diff --git a/apps/api/src/page/create.ts b/apps/api/src/page/create.ts index 26c32fc..f4d13a6 100644 --- a/apps/api/src/page/create.ts +++ b/apps/api/src/page/create.ts @@ -20,7 +20,7 @@ const route = createRoute({ }, }, responses: { - 200: { + 201: { content: { 'application/json': { schema: PageOutput, @@ -62,6 +62,6 @@ export const registerPageCreate = (api: typeof pageApi) => { ) } - return c.json(PageOutput.parse(result), 200) + return c.json(PageOutput.parse(result), 201) }) } diff --git a/apps/api/src/page/delete.ts b/apps/api/src/page/delete.ts index 456a737..5aefeb4 100644 --- a/apps/api/src/page/delete.ts +++ b/apps/api/src/page/delete.ts @@ -35,7 +35,7 @@ export const registerPageDelete = (api: typeof pageApi) => { const userId = await verifyAuthentication(c) const { id } = c.req.valid('param') - const result = await db + const [result] = await db .delete(page) .where(and(eq(page.userId, userId), eq(page.id, id))) .returning() diff --git a/apps/api/src/page/page.test.ts b/apps/api/src/page/page.test.ts new file mode 100644 index 0000000..fc7fdc2 --- /dev/null +++ b/apps/api/src/page/page.test.ts @@ -0,0 +1,171 @@ +import { afterAll, beforeAll, describe, expect, test } from 'bun:test' +import { access_token, db, user } from '@boring.tools/database' +import type { + AccessTokenCreateInput, + AccessTokenListOutput, + AccessTokenOutput, + ChangelogCreateInput, + ChangelogCreateOutput, + ChangelogListOutput, + ChangelogOutput, + PageCreateInput, + PageListOutput, + PageOutput, + PageUpdateInput, + UserOutput, +} from '@boring.tools/schema' +import type { z } from '@hono/zod-openapi' +import { eq } from 'drizzle-orm' +import { fetch } from '../utils/testing/fetch' + +describe('Page', () => { + let testUser: z.infer + let testAccessToken: z.infer + let createdAccessToken: z.infer + let testPage: z.infer + + beforeAll(async () => { + const createdUser = await db + .insert(user) + .values({ email: 'page@test.local', providerId: 'test_000' }) + .returning() + const tAccessToken = await db + .insert(access_token) + .values({ + token: '1234567890', + userId: createdUser[0].id, + name: 'testtoken', + }) + .returning() + testAccessToken = tAccessToken[0] as z.infer + testUser = createdUser[0] as z.infer + }) + + afterAll(async () => { + await db.delete(user).where(eq(user.email, 'page@test.local')) + }) + + describe('Create', () => { + test('Success', async () => { + const payload: z.infer = { + title: 'Test Page', + changelogIds: [], + } + + const res = await fetch( + { + path: '/v1/page', + method: 'POST', + body: payload, + }, + testAccessToken.token as string, + ) + + const json = (await res.json()) as z.infer + testPage = json + expect(res.status).toBe(201) + }) + }) + + describe('By Id', () => { + test('Success', async () => { + const res = await fetch( + { + path: `/v1/page/${testPage.id}`, + method: 'GET', + }, + testAccessToken.token as string, + ) + + expect(res.status).toBe(200) + }) + + test('Not Found', async () => { + const res = await fetch( + { + path: '/v1/page/635f4aa7-79fc-4d6b-af7d-6731999cc8bb', + method: 'GET', + }, + testAccessToken.token as string, + ) + + expect(res.status).toBe(404) + }) + }) + + describe('Update', () => { + test('Success', async () => { + const update: z.infer = { + title: 'Test Update', + } + const res = await fetch( + { + path: `/v1/page/${testPage.id}`, + method: 'PUT', + body: update, + }, + testAccessToken.token as string, + ) + + expect(res.status).toBe(200) + }) + }) + + describe('Public', () => { + test('Success', async () => { + const res = await fetch( + { + path: `/v1/page/${testPage.id}/public`, + method: 'GET', + }, + testAccessToken.token as string, + ) + + expect(res.status).toBe(200) + }) + }) + + describe('List', () => { + test('Success', async () => { + const res = await fetch( + { + path: '/v1/page', + method: 'GET', + }, + testAccessToken.token as string, + ) + + expect(res.status).toBe(200) + + const json = (await res.json()) as z.infer + // Check if token is redacted + expect(json).toHaveLength(1) + }) + }) + + describe('Remove', () => { + test('Success', async () => { + const res = await fetch( + { + path: `/v1/page/${testPage.id}`, + method: 'DELETE', + }, + testAccessToken.token as string, + ) + + expect(res.status).toBe(200) + }) + + test('Not found', async () => { + const res = await fetch( + { + path: `/v1/page/${testPage.id}`, + method: 'DELETE', + }, + testAccessToken.token as string, + ) + + expect(res.status).toBe(404) + }) + }) +}) diff --git a/apps/api/src/page/public.ts b/apps/api/src/page/public.ts index 2aabd2d..22d5b1e 100644 --- a/apps/api/src/page/public.ts +++ b/apps/api/src/page/public.ts @@ -91,6 +91,7 @@ export const registerPagePublic = (api: typeof pageApi) => { } redis.set(id, JSON.stringify(mappedResult), { EX: 60 }) - return c.json(PagePublicOutput.parse(mappedResult), 200) + const asd = PagePublicOutput.parse(mappedResult) + return c.json(asd, 200) }) } diff --git a/bun.lockb b/bun.lockb index 6d066c9ed232654b536c608adb18e9f71e657eab..aa813c065820632fbccff89ec2cdf6f90e5a5394 100755 GIT binary patch delta 6087 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 delta 6010 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 NNcXaF{7SvO@*nP!zMTL7 diff --git a/packages/schema/src/page/base.ts b/packages/schema/src/page/base.ts index 0ad9e3c..929c9c9 100644 --- a/packages/schema/src/page/base.ts +++ b/packages/schema/src/page/base.ts @@ -7,8 +7,8 @@ export const PageOutput = z example: '', }), title: z.string(), - description: z.string().optional(), - icon: z.string(), + description: z.string().optional().nullable(), + icon: z.string().optional().nullable(), changelogs: z.array(ChangelogOutput).optional(), }) .openapi('Page') diff --git a/packages/schema/src/page/create.ts b/packages/schema/src/page/create.ts index 8db11f8..303c0d7 100644 --- a/packages/schema/src/page/create.ts +++ b/packages/schema/src/page/create.ts @@ -5,10 +5,10 @@ export const PageCreateInput = z title: z.string().min(3).openapi({ example: 'My page', }), - description: z.string().optional().openapi({ + description: z.string().optional().nullable().openapi({ example: '', }), - icon: z.string().optional().openapi({ + icon: z.string().optional().nullable().openapi({ example: 'base64...', }), changelogIds: z.array(z.string().uuid()), diff --git a/packages/schema/src/page/public.ts b/packages/schema/src/page/public.ts index 4d36b14..519ff5c 100644 --- a/packages/schema/src/page/public.ts +++ b/packages/schema/src/page/public.ts @@ -2,21 +2,23 @@ import { z } from '@hono/zod-openapi' export const PagePublicOutput = z.object({ title: z.string(), - description: z.string(), + description: z.string().nullable(), icon: z.string(), - changelogs: z.array( - z.object({ - title: z.string(), - description: z.string(), - versions: z.array( - z.object({ - markdown: z.string(), - version: z.string(), - releasedAt: z.date(), - }), - ), - }), - ), + changelogs: z + .array( + z.object({ + title: z.string(), + description: z.string(), + versions: z.array( + z.object({ + markdown: z.string(), + version: z.string(), + releasedAt: z.date(), + }), + ), + }), + ) + .optional(), }) export const PagePublicParams = z.object({