UNPKG

@prism-engineer/router

Version:

Type-safe Express.js router with automatic client generation

152 lines 7.14 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const vitest_1 = require("vitest"); const supertest_1 = __importDefault(require("supertest")); const typebox_1 = require("@sinclair/typebox"); const createApiRoute_1 = require("../../createApiRoute"); const router_1 = require("../../router"); (0, vitest_1.describe)('Response Header Validation', () => { let router; let app; (0, vitest_1.beforeEach)(() => { router = (0, router_1.createRouter)(); app = router.app; }); (0, vitest_1.it)('should validate response headers match the schema', async () => { const responseHeaderSchema = typebox_1.Type.Object({ 'x-rate-limit': typebox_1.Type.String({ pattern: '^\\d+$' }), 'x-rate-limit-remaining': typebox_1.Type.String({ pattern: '^\\d+$' }), 'x-rate-limit-reset': typebox_1.Type.String(), 'cache-control': typebox_1.Type.Optional(typebox_1.Type.String()), 'x-custom-header': typebox_1.Type.String({ minLength: 5 }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/with-headers', method: 'GET', response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ message: typebox_1.Type.String() }), headers: responseHeaderSchema } }, handler: async (_req) => { return { status: 200, body: { message: 'Success' }, headers: { 'x-rate-limit': '1000', 'x-rate-limit-remaining': '999', 'x-rate-limit-reset': '2023-01-01T00:00:00Z', 'cache-control': 'no-cache', 'x-custom-header': 'valid-value' } }; } }); router.registerRoute(route); const response = await (0, supertest_1.default)(app) .get('/api/with-headers') .expect(200); (0, vitest_1.expect)(response.headers['x-rate-limit']).toBe('1000'); (0, vitest_1.expect)(response.headers['x-rate-limit-remaining']).toBe('999'); (0, vitest_1.expect)(response.headers['x-rate-limit-reset']).toBe('2023-01-01T00:00:00Z'); (0, vitest_1.expect)(response.headers['cache-control']).toBe('no-cache'); (0, vitest_1.expect)(response.headers['x-custom-header']).toBe('valid-value'); }); (0, vitest_1.it)('should handle missing optional response headers', async () => { const responseHeaderSchema = typebox_1.Type.Object({ 'x-required-header': typebox_1.Type.String(), 'x-optional-header': typebox_1.Type.Optional(typebox_1.Type.String()), 'x-version': typebox_1.Type.Union([typebox_1.Type.Literal('v1'), typebox_1.Type.Literal('v2')]) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/partial-headers', method: 'GET', response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ data: typebox_1.Type.String() }), headers: responseHeaderSchema } }, handler: async (_req) => { return { status: 200, body: { data: 'test' }, headers: { 'x-required-header': 'present', 'x-version': 'v2' // x-optional-header is intentionally omitted } }; } }); router.registerRoute(route); const response = await (0, supertest_1.default)(app) .get('/api/partial-headers') .expect(200); (0, vitest_1.expect)(response.headers['x-required-header']).toBe('present'); (0, vitest_1.expect)(response.headers['x-version']).toBe('v2'); (0, vitest_1.expect)(response.headers['x-optional-header']).toBeUndefined(); }); (0, vitest_1.it)('should validate response headers with complex constraints', async () => { const complexHeaderSchema = typebox_1.Type.Object({ 'content-range': typebox_1.Type.String({ pattern: '^bytes \\d+-\\d+/\\d+$' }), 'x-api-version': typebox_1.Type.Union([typebox_1.Type.Literal('v1'), typebox_1.Type.Literal('v2'), typebox_1.Type.Literal('v3')]), 'x-request-id': typebox_1.Type.String({ format: 'uuid' }), 'expires': typebox_1.Type.String(), 'x-total-count': typebox_1.Type.String({ pattern: '^\\d+$' }), 'link': typebox_1.Type.Optional(typebox_1.Type.String()) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/paginated', method: 'GET', response: { 206: { contentType: 'application/json', body: typebox_1.Type.Array(typebox_1.Type.Object({ id: typebox_1.Type.Number(), name: typebox_1.Type.String() })), headers: complexHeaderSchema } }, handler: async (_req) => { return { status: 206, body: [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' } ], headers: { 'content-range': 'bytes 0-99/200', 'x-api-version': 'v2', 'x-request-id': '123e4567-e89b-12d3-a456-426614174000', 'expires': 'Thu, 01 Dec 2023 16:00:00 GMT', 'x-total-count': '200', 'link': '<https://api.example.com/items?page=2>; rel="next"' } }; } }); router.registerRoute(route); const response = await (0, supertest_1.default)(app) .get('/api/paginated') .expect(206); (0, vitest_1.expect)(response.headers['content-range']).toBe('bytes 0-99/200'); (0, vitest_1.expect)(response.headers['x-api-version']).toBe('v2'); (0, vitest_1.expect)(response.headers['x-request-id']).toBe('123e4567-e89b-12d3-a456-426614174000'); (0, vitest_1.expect)(response.headers['expires']).toBe('Thu, 01 Dec 2023 16:00:00 GMT'); (0, vitest_1.expect)(response.headers['x-total-count']).toBe('200'); (0, vitest_1.expect)(response.headers['link']).toBe('<https://api.example.com/items?page=2>; rel="next"'); }); }); //# sourceMappingURL=response-header-validation.test.js.map