Files
teaching-design/server/routes/books.test.ts
2026-06-16 09:37:50 -06:00

141 lines
4.5 KiB
TypeScript

import { beforeEach, describe, expect, it } from 'bun:test'
import type { Database } from 'bun:sqlite'
import { Hono } from 'hono'
import { createEmptyBook, createEmptyTeachingDesign } from '../../shared/domain/teachingDesign'
import { openDb } from '../db'
import { createBooksRouter } from './books'
describe('books routes', () => {
let app: Hono
let db: Database
beforeEach(() => {
db = openDb(':memory:')
app = new Hono().route('/api/books', createBooksRouter(db))
})
async function createViaApi(name: string): Promise<{ id: string }> {
const res = await app.request('/api/books', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name }),
})
return (await res.json()) as { id: string }
}
it('lists no books initially', async () => {
const res = await app.request('/api/books')
expect(res.status).toBe(200)
expect(await res.json()).toEqual([])
})
it('creates a book', async () => {
const res = await app.request('/api/books', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '示例整本' }),
})
expect(res.status).toBe(200)
const body = (await res.json()) as { name: string; data: { designs: unknown[] } }
expect(body.name).toBe('示例整本')
expect(body.data.designs).toEqual([])
})
it('returns 400 when creating without a name', async () => {
const res = await app.request('/api/books', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
})
expect(res.status).toBe(400)
})
it('gets a created book', async () => {
const created = await createViaApi('示例整本')
const res = await app.request(`/api/books/${created.id}`)
expect(res.status).toBe(200)
expect(((await res.json()) as { id: string }).id).toBe(created.id)
})
it('returns 404 for a missing book', async () => {
const res = await app.request('/api/books/missing')
expect(res.status).toBe(404)
})
it('saves book data without cover state', async () => {
const created = await createViaApi('示例整本')
const data = createEmptyBook()
data.designs.push(createEmptyTeachingDesign('1.md'))
const res = await app.request(`/api/books/${created.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data: { ...data, cover: { courseName: '旧课程', teacherName: '旧教师' } } }),
})
expect(res.status).toBe(200)
const fetched = await app.request(`/api/books/${created.id}`)
const body = (await fetched.json()) as { data: Record<string, unknown> }
expect(body.data).not.toHaveProperty('cover')
})
it('returns 404 when saving data for a missing book', async () => {
const res = await app.request('/api/books/missing', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data: createEmptyBook() }),
})
expect(res.status).toBe(404)
})
it('returns 400 when saving without data', async () => {
const created = await createViaApi('示例整本')
const res = await app.request(`/api/books/${created.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
})
expect(res.status).toBe(400)
})
it('renames a book', async () => {
const created = await createViaApi('旧名称')
const res = await app.request(`/api/books/${created.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '新名称' }),
})
expect(res.status).toBe(200)
expect(((await res.json()) as { name: string }).name).toBe('新名称')
})
it('returns 404 when renaming a missing book', async () => {
const res = await app.request('/api/books/missing', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '新名称' }),
})
expect(res.status).toBe(404)
})
it('deletes a book', async () => {
const created = await createViaApi('示例整本')
const res = await app.request(`/api/books/${created.id}`, { method: 'DELETE' })
expect(res.status).toBe(200)
expect(await res.json()).toEqual({ ok: true })
expect((await app.request(`/api/books/${created.id}`)).status).toBe(404)
})
it('returns 404 when deleting a missing book', async () => {
const res = await app.request('/api/books/missing', { method: 'DELETE' })
expect(res.status).toBe(404)
})
})