From 666674327f009e9b1013218fc384f193b64c6997 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sun, 14 Dec 2025 22:39:18 -0800 Subject: Adds unit tests --- tst/server_request_response.test.ts | 110 ++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 tst/server_request_response.test.ts (limited to 'tst/server_request_response.test.ts') diff --git a/tst/server_request_response.test.ts b/tst/server_request_response.test.ts new file mode 100644 index 0000000..99e74d8 --- /dev/null +++ b/tst/server_request_response.test.ts @@ -0,0 +1,110 @@ +import { + Either, + JsonResponse, + PenguenoRequest, + PenguenoResponse, + getResponseMetrics, + MetricValueTag, + type BaseRequest, + type ServerTrace, +} from '../lib/index'; +import { CollectingTrace, TestTraceable } from './test_utils'; + +const makeBaseRequest = (overrides: Partial = {}): BaseRequest => ({ + url: 'https://example.com/hello?x=1', + method: 'GET', + header: () => ({}), + formData: async () => new FormData(), + json: async () => ({}), + text: async () => '', + param: () => undefined, + query: () => ({}), + queries: () => ({}), + ...overrides, +}); + +describe('server/request + server/response', () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(new Date('2020-01-01T00:00:00.000Z')); + }); + + afterEach(() => { + jest.useRealTimers(); + jest.restoreAllMocks(); + }); + + test('PenguenoRequest.from creates response headers', () => { + jest.spyOn(globalThis.crypto, 'randomUUID').mockReturnValue('00000000-0000-0000-0000-000000000000'); + jest.spyOn(Math, 'random').mockReturnValue(0); + + const baseReq = makeBaseRequest(); + const trace = new CollectingTrace(); + const req = PenguenoRequest.from(TestTraceable.of(baseReq, trace)); + + jest.setSystemTime(new Date('2020-01-01T00:00:01.000Z')); + const headers = req.get().getResponseHeaders(); + + expect(headers).toMatchObject({ + RequestId: '00000000-0000-0000-0000-000000000000', + Hai: 'hewwo :D', + }); + expect(headers.DeltaUnix).toBe('1000'); + expect(headers.RequestReceivedUnix).toBe('1577836800000'); + expect(headers.RequestHandleUnix).toBe('1577836801000'); + }); + + test('PenguenoResponse sets headers and emits metrics', () => { + jest.spyOn(globalThis.crypto, 'randomUUID').mockReturnValue('00000000-0000-0000-0000-000000000000'); + jest.spyOn(Math, 'random').mockReturnValue(0); + + const trace = new CollectingTrace(); + const req = PenguenoRequest.from(TestTraceable.of(makeBaseRequest(), trace)); + + jest.setSystemTime(new Date('2020-01-01T00:00:01.000Z')); + const res = new PenguenoResponse(req, 'hi', { status: 200, headers: { X: 'Y' } }); + + expect(res.status).toBe(200); + expect(res.statusText).toBe('OK'); + expect(res.headers['Content-Type']).toBe('text/plain; charset=utf-8'); + expect(res.headers.X).toBe('Y'); + + const lastTrace = trace.events[trace.events.length - 1]; + expect(lastTrace).toBeDefined(); + + const metricValues = lastTrace[lastTrace.length - 1] as any[]; + expect(Array.isArray(metricValues)).toBe(true); + expect( + metricValues.some((m) => m._tag === MetricValueTag && m.name === 'response.2xx.count' && m.value === 1), + ).toBe(true); + }); + + test('JsonResponse formats Either bodies', () => { + jest.spyOn(globalThis.crypto, 'randomUUID').mockReturnValue('00000000-0000-0000-0000-000000000000'); + + const trace = new CollectingTrace(); + const req = PenguenoRequest.from(TestTraceable.of(makeBaseRequest(), trace)); + + const ok = new JsonResponse(req, Either.right('yay'), { status: 200, headers: {} }); + expect(ok.headers['Content-Type']).toBe('application/json; charset=utf-8'); + expect(ok.body()).toBe('{"ok":"yay"}'); + + const err = new JsonResponse(req, Either.left('nope'), { status: 400, headers: {} }); + expect(err.body()).toBe('{"error":"nope"}'); + }); + + test('getResponseMetrics returns one active bucket', () => { + jest.setSystemTime(new Date('2020-01-01T00:00:00.000Z')); + const metrics = getResponseMetrics(201, 12); + + expect(metrics).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'response.2xx.count', value: 1 }), + expect.objectContaining({ name: 'response.2xx.time', value: 12 }), + ]), + ); + + const inactive = metrics.filter((m) => m.value === 0); + expect(inactive).toHaveLength(5); + }); +}); -- cgit v1.2.3-70-g09d2