Files
json-server/src/service.test.ts
2023-12-30 01:32:39 +01:00

305 lines
6.9 KiB
TypeScript

import assert from 'node:assert/strict'
import test from 'node:test'
import { Low, Memory } from 'lowdb'
import { ParsedUrlQuery } from 'querystring'
import { Data, Item, PaginatedItems, Service } from './service.js'
const defaultData = { posts: [] }
const adapter = new Memory<Data>()
const db = new Low<Data>(adapter, defaultData)
const service = new Service(db)
const POSTS = 'posts'
const COMMENTS = 'comments'
const UNKNOWN_RESOURCE = 'xxx'
const UNKNOWN_ID = 'xxx'
const post1 = {
id: '1',
title: 'a',
views: 100,
author: { name: 'foo' },
tags: ['foo', 'bar'],
}
const post2 = {
id: '2',
title: 'b',
views: 200,
author: { name: 'bar' },
tags: ['bar'],
}
const post3 = {
id: '3',
title: 'c',
views: 300,
author: { name: 'baz' },
tags: ['foo'],
}
const comment1 = { id: '1', title: 'a', postId: '1' }
const items = 3
function reset() {
db.data = structuredClone({
posts: [post1, post2, post3],
comments: [comment1],
})
}
type Test = {
data?: Data
name: string
params?: ParsedUrlQuery
res: Item | Item[] | PaginatedItems | undefined
error?: Error
}
await test('findById', () => {
reset()
assert.deepEqual(service.findById(POSTS, '1', {}), db.data?.[POSTS]?.[0])
assert.equal(service.findById(POSTS, UNKNOWN_ID, {}), undefined)
assert.deepEqual(service.findById(POSTS, '1', { _include: ['comments'] }), {
...post1,
comments: [comment1],
})
assert.deepEqual(service.findById(COMMENTS, '1', { _include: ['post'] }), {
...comment1,
post: post1,
})
assert.equal(service.findById(UNKNOWN_RESOURCE, '1', {}), undefined)
})
await test('find', async (t) => {
const arr: Test[] = [
{
name: POSTS,
res: [post1, post2, post3],
},
{
name: POSTS,
params: { id: post1.id },
res: [post1],
},
{
name: POSTS,
params: { id: UNKNOWN_ID },
res: [],
},
{
name: POSTS,
params: { views: post1.views.toString() },
res: [post1],
},
{
name: POSTS,
params: { 'author.name': post1.author.name },
res: [post1],
},
{
name: POSTS,
params: { 'tags[0]': 'foo' },
res: [post1, post3],
},
{
name: POSTS,
params: { id: UNKNOWN_ID, views: post1.views.toString() },
res: [],
},
{
name: POSTS,
params: { views_ne: post1.views.toString() },
res: [post2, post3],
},
{
name: POSTS,
params: { views_lt: (post1.views + 1).toString() },
res: [post1],
},
{
name: POSTS,
params: { views_lt: post1.views.toString() },
res: [],
},
{
name: POSTS,
params: { views_lte: post1.views.toString() },
res: [post1],
},
{
name: POSTS,
params: { views_gt: post1.views.toString() },
res: [post2, post3],
},
{
name: POSTS,
params: { views_gt: (post1.views - 1).toString() },
res: [post1, post2, post3],
},
{
name: POSTS,
params: { views_gte: post1.views.toString() },
res: [post1, post2, post3],
},
{
data: { posts: [post3, post1, post2] },
name: POSTS,
params: { _sort: 'views' },
res: [post1, post2, post3],
},
{
data: { posts: [post3, post1, post2] },
name: POSTS,
params: { _sort: '-views' },
res: [post3, post2, post1],
},
{
data: { posts: [post3, post1, post2] },
name: POSTS,
params: { _sort: '-views,id' },
res: [post3, post2, post1],
},
{
name: POSTS,
params: { _start: '0', _end: '2' },
res: [post1, post2],
},
{
name: POSTS,
params: { _start: '1', _end: '3' },
res: [post2, post3],
},
{
name: POSTS,
params: { _start: '0', _limit: '2' },
res: [post1, post2],
},
{
name: POSTS,
params: { _start: '1', _limit: '2' },
res: [post2, post3],
},
{
name: POSTS,
params: { _page: '1', _per_page: '2' },
res: {
first: 1,
last: 2,
prev: null,
next: 2,
pages: 2,
items,
data: [post1, post2],
},
},
{
name: POSTS,
params: { _page: '2', _per_page: '2' },
res: {
first: 1,
last: 2,
prev: 1,
next: null,
pages: 2,
items,
data: [post3],
},
},
{
name: POSTS,
params: { _page: '3', _per_page: '2' },
res: {
first: 1,
last: 2,
prev: 1,
next: null,
pages: 2,
items,
data: [post3],
},
},
{
name: POSTS,
params: { _include: ['comments'] },
res: [
{ ...post1, comments: [comment1] },
{ ...post2, comments: [] },
{ ...post3, comments: [] },
],
},
{
name: COMMENTS,
params: { _include: ['post'] },
res: [{ ...comment1, post: post1 }],
},
{
name: UNKNOWN_RESOURCE,
res: undefined,
},
]
for (const tc of arr) {
await t.test(`${tc.name} ${JSON.stringify(tc.params)}`, () => {
if (tc.data) {
db.data = tc.data
} else {
reset()
}
assert.deepEqual(service.find(tc.name, tc.params), tc.res)
})
}
})
await test('create', async () => {
reset()
const post = { title: 'new post' }
const res = await service.create(POSTS, post)
assert.equal(res?.['title'], post.title)
assert.equal(typeof res?.['id'], 'string', 'id should be a string')
assert.equal(await service.create(UNKNOWN_RESOURCE, post), undefined)
})
await test('update', async () => {
reset()
const post = { id: 'xxx', title: 'updated post' }
const res = await service.update(POSTS, post1.id, post)
assert.equal(res?.['id'], post1.id, 'id should not change')
assert.equal(res?.['title'], post.title)
assert.equal(
await service.update(UNKNOWN_RESOURCE, post1.id, post),
undefined,
)
assert.equal(await service.update(POSTS, UNKNOWN_ID, post), undefined)
})
await test('patch', async () => {
reset()
const post = { id: 'xxx', title: 'updated post' }
const res = await service.patch(POSTS, post1.id, post)
assert.notEqual(res, undefined)
assert.equal(res?.['id'], post1.id)
assert.equal(res?.['title'], post.title)
assert.equal(await service.patch(UNKNOWN_RESOURCE, post1.id, post), undefined)
assert.equal(await service.patch(POSTS, UNKNOWN_ID, post), undefined)
})
await test('destroy', async () => {
reset()
let prevLength = db.data?.[POSTS]?.length || 0
await service.destroy(POSTS, post1.id)
assert.equal(db.data?.[POSTS]?.length, prevLength - 1)
assert.deepEqual(db.data?.[COMMENTS], [{ ...comment1, postId: null }])
reset()
prevLength = db.data?.[POSTS]?.length || 0
await service.destroy(POSTS, post1.id, [COMMENTS])
assert.equal(db.data[POSTS].length, prevLength - 1)
assert.equal(db.data[COMMENTS].length, 0)
assert.equal(await service.destroy(UNKNOWN_RESOURCE, post1.id), undefined)
assert.equal(await service.destroy(POSTS, UNKNOWN_ID), undefined)
})