UNPKG

@unito/integration-sdk

Version:

Integration SDK

171 lines (170 loc) 7.72 kB
import assert from 'node:assert/strict'; import { afterEach, beforeEach, describe, it, mock } from 'node:test'; import { Handler, } 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 = [ ['', 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 = (() => { }); const updateItem = (() => { }); const deleteItem = (() => { }); const getCollection = (() => { }); const createItem = (() => { }); const paths = [ ['/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, request = {}) { let body = {}; let statusCode = 0; const response = { status: (receivedStatusCode) => ({ send: (receivedBody) => { body = receivedBody; statusCode = receivedStatusCode; }, }), statusCode: 0, locals: { credentials: { foo: 'bar' }, }, }; await handler(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.methods.get, true); assert.equal(routes[1].route.path, '/foo'); assert.equal(routes[1].route.methods.post, true); assert.equal(routes[2].route.path, '/foo/:bar'); assert.equal(routes[2].route.methods.get, true); assert.equal(routes[3].route.path, '/foo/:bar'); assert.equal(routes[3].route.methods.patch, true); assert.equal(routes[4].route.path, '/foo/:bar'); assert.equal(routes[4].route.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.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); }); }); });