UNPKG

@unito/integration-sdk

Version:

Integration SDK

213 lines (180 loc) 7.4 kB
import { Request, Response, NextFunction } from 'express'; import assert from 'node:assert/strict'; import { afterEach, beforeEach, describe, it, mock } from 'node:test'; import { Handler, HandlersInput, GetItemHandler, GetCollectionHandler, UpdateItemHandler, DeleteItemHandler, CreateItemHandler, } from '../src/handler.js'; import { BadRequestError } from '../src/httpErrors.js'; describe('Handler', () => { beforeEach(() => { // Disable debug logging to keep the test output clean. mock.method(global.console, 'debug', () => {}); }); afterEach(() => { mock.reset(); }); describe('constructor', () => { it('returns a Handler', () => { const itemHandler = new Handler('/', {}); assert.ok(itemHandler instanceof Handler); }); it('validates path', () => { const paths: [string, boolean][] = [ ['', false], ['invalid', false], ['/', true], ['/bar', true], ['/bar/', false], ['/foo/:bar', true], ['/foo/:bar/', false], ['/foo/:bar/spam/:baz', true], ['/foo/:bar/:spam', true], ]; for (const [path, valid] of paths) { if (valid) { assert.doesNotThrow(() => new Handler(path, {})); } else { assert.throws(() => new Handler(path, {})); } } }); it('validates configuration', () => { const getItem = (() => {}) as unknown as GetItemHandler; const updateItem = (() => {}) as unknown as UpdateItemHandler; const deleteItem = (() => {}) as unknown as DeleteItemHandler; const getCollection = (() => {}) as unknown as GetCollectionHandler; const createItem = (() => {}) as unknown as CreateItemHandler; const paths: [string, HandlersInput, boolean][] = [ ['/foo', { getItem }, true], ['/foo', { getCollection }, true], ['/foo', { getItem, getCollection }, false], ['/foo', { updateItem, createItem }, false], ['/foo', { deleteItem, createItem }, false], ['/foo/:bar', { getItem, getCollection }, true], ['/foo/:bar', { getItem, updateItem, deleteItem, createItem, getCollection }, true], ]; for (const [path, handlers, valid] of paths) { if (valid) { assert.doesNotThrow(() => new Handler(path, handlers)); } else { assert.throws(() => new Handler(path, handlers)); } } }); }); describe('generate', () => { async function executeHandler( handler: (req: Request, res: Response, callback: NextFunction) => Promise<void>, request: Record<string, unknown> = {}, ) { let body: Record<string, unknown> = {}; let statusCode = 0; const response = { status: (receivedStatusCode: number) => ({ send: (receivedBody: Record<string, unknown>) => { body = receivedBody; statusCode = receivedStatusCode; }, }), statusCode: 0, locals: { credentials: { foo: 'bar' }, }, } as unknown as Response; await handler(request as unknown as Request, response, () => {}); return { body, statusCode }; } it('returns a router', async () => { const handler = new Handler('/foo/:bar', { getCollection: () => Promise.resolve({ info: {}, data: [] }), getItem: () => Promise.resolve({ fields: {}, relations: [] }), createItem: () => Promise.resolve({ fields: {}, path: '/' }), updateItem: () => Promise.resolve({ fields: {}, relations: [] }), deleteItem: () => Promise.resolve(), }); const router = handler.generate(); const routes = router.stack; assert.equal(routes.length, 5); assert.equal(routes[0]!.route!.path, '/foo'); assert.equal((routes[0]!.route! as any).methods.get, true); assert.equal(routes[1]!.route!.path, '/foo'); assert.equal((routes[1]!.route! as any).methods.post, true); assert.equal(routes[2]!.route!.path, '/foo/:bar'); assert.equal((routes[2]!.route! as any).methods.get, true); assert.equal(routes[3]!.route!.path, '/foo/:bar'); assert.equal((routes[3]!.route! as any).methods.patch, true); assert.equal(routes[4]!.route!.path, '/foo/:bar'); assert.equal((routes[4]!.route! as any).methods.delete, true); // GetCollection. assert.deepEqual(await executeHandler(routes[0]!.route!.stack[0]!.handle), { body: { info: {}, data: [] }, statusCode: 200, }); // CreateItem. assert.deepEqual(await executeHandler(routes[1]!.route!.stack[0]!.handle, { body: { foo: 'bar' } }), { body: { fields: {}, path: '/' }, statusCode: 201, }); // GetItem. assert.deepEqual(await executeHandler(routes[2]!.route!.stack[0]!.handle), { body: { fields: {}, relations: [] }, statusCode: 200, }); // UpdateItem. assert.deepEqual(await executeHandler(routes[3]!.route!.stack[0]!.handle, { body: { foo: 'bar' } }), { body: { fields: {}, relations: [] }, statusCode: 200, }); // DeleteItem. assert.deepEqual(await executeHandler(routes[4]!.route!.stack[0]!.handle), { body: null, statusCode: 204, }); }); it('uses pathWithIdentifier as is when appropriate', async () => { const handler = new Handler('/foo/:bar/baz', { getCollection: () => Promise.resolve({ info: {}, data: [] }), }); const router = handler.generate(); const routes = router.stack; assert.equal(routes.length, 1); assert.equal(routes[0]!.route!.path, '/foo/:bar/baz'); assert.equal((routes[0]!.route as any).methods.get, true); // GetCollection. assert.deepEqual(await executeHandler(routes[0]!.route!.stack[0]!.handle), { body: { info: {}, data: [] }, statusCode: 200, }); }); it('undefined handlers', async () => { const handler = new Handler('/foo/:bar', {}); const router = handler.generate(); const routes = router.stack; assert.equal(routes.length, 0); }); it('returns a 400 for invalid request payloads', async () => { const handler = new Handler('/foo/:bar', { createItem: () => Promise.resolve({ fields: {}, path: '/' }), updateItem: () => Promise.resolve({ fields: {}, relations: [] }), }); const router = handler.generate(); const routes = router.stack; // CreateItemRequestPayload. const createHandler = routes[0]!.route!.stack[0]!.handle; await assert.doesNotReject(async () => await executeHandler(createHandler, { body: { foo: 'bar' } })); await assert.rejects(async () => await executeHandler(createHandler, { body: null }), BadRequestError); await assert.rejects(async () => await executeHandler(createHandler, { body: 'not json' }), BadRequestError); // UpdateItemRequestPayload. const updateHandler = routes[0]!.route!.stack[0]!.handle; await assert.doesNotReject(async () => await executeHandler(updateHandler, { body: { foo: 'bar' } })); await assert.rejects(async () => await executeHandler(updateHandler, { body: null }), BadRequestError); await assert.rejects(async () => await executeHandler(updateHandler, { body: 'not json' }), BadRequestError); }); }); });