UNPKG

nats-micro

Version:

NATS micro compatible extra-lightweight microservice library

261 lines (204 loc) 7.25 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { expect } from 'chai'; import { zodToJsonSchema } from 'zod-to-json-schema'; import { Microservice, method, microservice, z, MicroserviceSchema, BrokerResponse, Request, Response, } from '../../src/index.js'; import { broker } from '../common.js'; const testRequestSchema = z.object({ field1: z.string().optional(), field2: z.number({ description: 'a number' }).min(15), }); type TestRequest = z.infer<typeof testRequestSchema>; const testRequestJsonSchema = zodToJsonSchema(testRequestSchema); const testResponseSchema = z.object({ success: z.boolean(), }); type TestResponse = z.infer<typeof testResponseSchema>; const testResponseJsonSchema = zodToJsonSchema(testResponseSchema); const voidJsonSchema = zodToJsonSchema(z.void()); describe('type schemas', function () { this.afterEach(function () { broker.offAll(); }); it('request+response typed method', async function () { @microservice() class Test { @method({ request: testRequestSchema, response: testResponseSchema, }) public test(req: Request<TestRequest>, res: Response<TestResponse>): void { res.send({ success: req.data.field1 === 'user', }); } } const service = await Microservice.createFromClass(broker, new Test()); const reqSchema = service.discovery.getMethodJsonSchema('test', 'request'); const resSchema = service.discovery.getMethodJsonSchema('test', 'response'); expect(reqSchema).to.eql(testRequestJsonSchema); expect(resSchema).to.eql(testResponseJsonSchema); }); it('request+response typed async method', async function () { @microservice() class Test { @method({ request: testRequestSchema, response: testResponseSchema, }) public test(req: Request<TestRequest>, res: Response<TestResponse>): void { res.send({ success: req.data.field1 === 'user', }); } } const service = await Microservice.createFromClass(broker, new Test()); const reqSchema = service.discovery.getMethodJsonSchema('test', 'request'); const resSchema = service.discovery.getMethodJsonSchema('test', 'response'); expect(reqSchema).to.eql(testRequestJsonSchema); expect(resSchema).to.eql(testResponseJsonSchema); }); it('request-only typed method', async function () { @microservice() class Test { @method({ request: testRequestSchema, }) public test(_req: Request<TestRequest>, res: Response<void>): void { res.sendNoResponse(); } } const service = await Microservice.createFromClass(broker, new Test()); const reqSchema = service.discovery.getMethodJsonSchema('test', 'request'); const resSchema = service.discovery.getMethodJsonSchema('test', 'response'); expect(reqSchema).to.eql(testRequestJsonSchema); expect(resSchema).to.eql(voidJsonSchema); }); it('non-typed method', async function () { @microservice() class Test { @method() public test(_req: Request<void>, res: Response<void>): void { res.sendNoResponse(); } } const service = await Microservice.createFromClass(broker, new Test()); const reqSchema = service.discovery.getMethodJsonSchema('test', 'request'); const resSchema = service.discovery.getMethodJsonSchema('test', 'response'); expect(reqSchema).to.eql(voidJsonSchema); expect(resSchema).to.eql(voidJsonSchema); }); it('getting schemas via broker', async function () { @microservice() class Test { @method({ request: testRequestSchema, response: testResponseSchema, }) public test(req: Request<TestRequest>, res: Response<TestResponse>): void { res.send({ success: req.data.field1 === 'user', }); } } await Microservice.createFromClass(broker, new Test()); const data: BrokerResponse<MicroserviceSchema | undefined> = await broker.request('$SRV.SCHEMA', ''); expect(data.data).to.exist; const methodSchema = data.data!.endpoints.find((ep) => ep.name === 'test'); expect(methodSchema).to.exist; expect(methodSchema!.schema.request).to.eql(testRequestJsonSchema); expect(methodSchema!.schema.response).to.eql(testResponseJsonSchema); }); describe('request validation', function () { it('non-json', async function () { @microservice() class Test { @method({ request: testRequestSchema, response: testResponseSchema, }) public test(req: Request<TestRequest>, res: Response<TestResponse>): void { res.send({ success: req.data.field1 === 'user', }); } } await Microservice.createFromClass(broker, new Test()); await expect(broker.request('test.test', '')).to.be.rejectedWith(/request/i); }); it('invalid type', async function () { @microservice() class Test { @method({ request: testRequestSchema, response: testResponseSchema, }) public test(req: Request<TestRequest>, res: Response<TestResponse>): void { res.send({ success: req.data.field1 === 'user', }); } } await Microservice.createFromClass(broker, new Test()); await expect(broker.request('test.test', { name: 'no name' })).to.be.rejectedWith(/request/i); }); }); describe('response validation', function () { it('invalid type', async function () { @microservice() class Test { @method({ request: z.void(), response: testResponseSchema, }) public test(_req: Request<void>, res: Response<TestResponse>): void { res.send( { hello: 'invalid', } as any, ); } } await Microservice.createFromClass(broker, new Test()); await expect(broker.request('test.test', '')).to.be.rejectedWith(/response/i); }); it('invalid empty response', async function () { @microservice() class Test { @method({ request: z.void(), response: testResponseSchema, }) public test(_req: Request<void>, res: Response<TestResponse>): void { res.send(undefined as any); } } await Microservice.createFromClass(broker, new Test()); await expect(broker.request('test.test', '')).to.be.rejectedWith(/response/i); }); // eslint-disable-next-line mocha/no-identical-title it('non-empty response', async function () { @microservice() class Test { @method({ request: z.void(), response: z.void(), }) public test(_req: Request<void>, res: Response<void>): void { res.send( { hello: 'invalid', } as any, ); } } await Microservice.createFromClass(broker, new Test()); await expect(broker.request('test.test', { name: 'no name' })).to.be.rejectedWith(/response/i); }); }); });