Compare commits

...

2 Commits

Author SHA1 Message Date
7f6a0f36e9 feat(api): add page tests
Some checks failed
Build and Push Docker Image / tests (push) Successful in 1m9s
Build and Push Docker Image / build (push) Failing after 1m15s
2024-11-12 18:51:30 +01:00
377942fe3c feat(api): add access-token tests 2024-11-12 17:33:20 +01:00
10 changed files with 348 additions and 23 deletions

View File

@ -0,0 +1,151 @@
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,
UserOutput,
} from '@boring.tools/schema'
import type { z } from '@hono/zod-openapi'
import { eq } from 'drizzle-orm'
import { fetch } from '../utils/testing/fetch'
describe('AccessToken', () => {
let testUser: z.infer<typeof UserOutput>
let testAccessToken: z.infer<typeof AccessTokenOutput>
let createdAccessToken: z.infer<typeof AccessTokenOutput>
let testChangelog: z.infer<typeof ChangelogOutput>
beforeAll(async () => {
const createdUser = await db
.insert(user)
.values({ email: 'access_token@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<typeof AccessTokenOutput>
testUser = createdUser[0] as z.infer<typeof UserOutput>
})
afterAll(async () => {
await db.delete(user).where(eq(user.email, 'access_token@test.local'))
})
describe('Create', () => {
test('Success', async () => {
const payload: z.infer<typeof AccessTokenCreateInput> = {
name: 'Test Token',
}
const res = await fetch(
{
path: '/v1/access-token',
method: 'POST',
body: payload,
},
testAccessToken.token as string,
)
const json = (await res.json()) as z.infer<typeof AccessTokenOutput>
createdAccessToken = json
expect(res.status).toBe(201)
})
})
// describe('By Id', () => {
// test('Success', async () => {
// const res = await fetch(
// {
// path: `/v1/changelog/${testChangelog.id}`,
// method: 'GET',
// },
// testAccessToken.token as string,
// )
// expect(res.status).toBe(200)
// })
// test('Not Found', async () => {
// const res = await fetch(
// {
// path: '/v1/changelog/635f4aa7-79fc-4d6b-af7d-6731999cc8bb',
// method: 'GET',
// },
// testAccessToken.token as string,
// )
// expect(res.status).toBe(404)
// })
// test('Invalid Id', async () => {
// const res = await fetch(
// {
// path: '/v1/changelog/some',
// method: 'GET',
// },
// testAccessToken.token as string,
// )
// expect(res.status).toBe(400)
// const json = (await res.json()) as { success: boolean }
// expect(json.success).toBeFalse()
// })
// })
describe('List', () => {
test('Success', async () => {
const res = await fetch(
{
path: '/v1/access-token',
method: 'GET',
},
testAccessToken.token as string,
)
expect(res.status).toBe(200)
const json = (await res.json()) as z.infer<typeof AccessTokenListOutput>
// Check if token is redacted
expect(json[0].token).toHaveLength(10)
expect(json).toHaveLength(2)
})
})
describe('Remove', () => {
test('Success', async () => {
const res = await fetch(
{
path: `/v1/access-token/${createdAccessToken.id}`,
method: 'DELETE',
},
testAccessToken.token as string,
)
expect(res.status).toBe(200)
})
test('Not found', async () => {
const res = await fetch(
{
path: `/v1/access-token/${createdAccessToken.id}`,
method: 'DELETE',
},
testAccessToken.token as string,
)
expect(res.status).toBe(404)
})
})
})

View File

@ -29,7 +29,7 @@ export const registerAccessTokenDelete = (api: typeof accessTokenApi) => {
const id = c.req.param('id')
const userId = await verifyAuthentication(c)
const result = await db
const [result] = await db
.delete(access_token)
.where(and(eq(access_token.userId, userId), eq(access_token.id, id)))
.returning()

View File

@ -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)
})
}

View File

@ -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()

View File

@ -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<typeof UserOutput>
let testAccessToken: z.infer<typeof AccessTokenOutput>
let createdAccessToken: z.infer<typeof AccessTokenOutput>
let testPage: z.infer<typeof PageOutput>
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<typeof AccessTokenOutput>
testUser = createdUser[0] as z.infer<typeof UserOutput>
})
afterAll(async () => {
await db.delete(user).where(eq(user.email, 'page@test.local'))
})
describe('Create', () => {
test('Success', async () => {
const payload: z.infer<typeof PageCreateInput> = {
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<typeof PageOutput>
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<typeof PageUpdateInput> = {
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<typeof PageListOutput>
// 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)
})
})
})

View File

@ -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)
})
}

BIN
bun.lockb

Binary file not shown.

View File

@ -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')

View File

@ -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()),

View File

@ -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({