UNPKG

nestjs-google-pubsub-microservice

Version:
351 lines 14.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const chai_1 = require("chai"); const sinon = require("sinon"); const gc_pubsub_server_1 = require("./gc-pubsub.server"); const constants_1 = require("@nestjs/microservices/constants"); const base_rpc_context_1 = require("@nestjs/microservices/ctx-host/base-rpc.context"); const gc_pubsub_constants_1 = require("./gc-pubsub.constants"); const common_1 = require("@nestjs/common"); describe('GCPubSubServer', () => { let server; let pubsub; let topicMock; let subscriptionMock; let createClient; let sandbox; const objectToMap = (obj) => new Map(Object.keys(obj).map((key) => [key, obj[key]])); const createMockMessage = (data, attributes = {}) => ({ ackId: 'id', publishTime: new Date(), attributes, id: 'id', received: 0, deliveryAttempt: 1, ack: () => { }, modAck: () => { }, nack: () => { }, data: Buffer.from(typeof data === 'string' ? data : JSON.stringify(data)), }); const simulateMessageEvent = async (server, message) => { const onCall = subscriptionMock.on .getCalls() .find((call) => call.args[0] === 'message'); (0, chai_1.expect)(onCall, 'No message handler registered').to.exist; const messageHandler = onCall.args[1]; await messageHandler(message); }; afterEach(() => { sandbox.restore(); }); describe('constructor', () => { describe('when the scopedEnvKey is defined', () => { it('should set the scopedEnvKey on topics and subscriptions', () => { const scopedEnvKey = 'my-key'; server = getInstance({ scopedEnvKey }); (0, chai_1.expect)(server['topicName']).to.eq(`${scopedEnvKey}default_topic`); (0, chai_1.expect)(server['subscriptionName']).to.eq(`${scopedEnvKey}default_subscription`); }); }); }); describe('listen', () => { describe('when is check existence is true', () => { beforeEach(async () => { server = getInstance({}); await server.listen(() => { }); }); it('should call "createClient"', () => { (0, chai_1.expect)(createClient.called).to.be.true; }); it('should call "client.topic" once', async () => { (0, chai_1.expect)(pubsub.topic.called).to.be.true; }); it('should call "topic.create" once', async () => { (0, chai_1.expect)(topicMock.create.called).to.be.true; }); it('should call "topic.subscription" once', async () => { (0, chai_1.expect)(topicMock.subscription.called).to.be.true; }); it('should call "subscription.create" once', async () => { (0, chai_1.expect)(subscriptionMock.create.called).to.be.true; }); it('should call "subscription.on" twice', async () => { (0, chai_1.expect)(subscriptionMock.on.callCount).to.eq(2); }); }); describe('when is check existence is false', () => { beforeEach(async () => { server = getInstance({ init: false, checkExistence: false, }); await server.listen(() => { }); }); it('should call "createClient"', () => { (0, chai_1.expect)(createClient.called).to.be.true; }); it('should call "client.topic" once', async () => { (0, chai_1.expect)(pubsub.topic.called).to.be.true; }); it('should not call "topic.exists" once', async () => { (0, chai_1.expect)(topicMock.exists.called).to.be.false; }); it('should call "topic.subscription" once', async () => { (0, chai_1.expect)(topicMock.subscription.called).to.be.true; }); it('should not call "subscription.exists" once', async () => { (0, chai_1.expect)(subscriptionMock.exists.called).to.be.false; }); it('should call "subscription.on" twice', async () => { (0, chai_1.expect)(subscriptionMock.on.callCount).to.eq(2); }); }); describe('when subscriptionFilter is provided', () => { const filter = 'attributes.type = "order"'; beforeEach(async () => { server = getInstance({ subscriptionFilter: filter }); await server.listen(() => { }); }); it('should call "pubsub.createSubscription" with the filter', () => { (0, chai_1.expect)(pubsub.createSubscription.called).to.be.true; const args = pubsub.createSubscription.getCall(0).args; (0, chai_1.expect)(args[2]).to.deep.include({ filter }); }); it('should not call "subscription.create"', () => { (0, chai_1.expect)(subscriptionMock.create.called).to.be.false; }); }); describe('when subscriptionFilter is not provided', () => { beforeEach(async () => { server = getInstance({}); await server.listen(() => { }); }); it('should call "subscription.create" without filter', () => { (0, chai_1.expect)(subscriptionMock.create.called).to.be.true; }); it('should not call "pubsub.createSubscription"', () => { (0, chai_1.expect)(pubsub.createSubscription.called).to.be.false; }); }); }); describe('close', () => { beforeEach(async () => { server = getInstance({}); await server.listen(() => { }); await server.close(); }); it('should call "subscription.close"', function () { (0, chai_1.expect)(subscriptionMock.close.called).to.be.true; }); it('should close() pubsub', () => { (0, chai_1.expect)(pubsub.close.called).to.be.true; }); }); describe('handleMessage', () => { const msg = { pattern: 'test', data: 'tests', id: '3', }; beforeEach(async () => { server = getInstance({}); await server.listen(() => { }); }); it('should send NO_MESSAGE_HANDLER error if key does not exists in handlers object', async () => { await server.handleMessage({ ackId: 'id', publishTime: new Date(), attributes: { replyTo: 'replyTo', }, id: 'id', received: 0, deliveryAttempt: 1, ack: () => { }, modAck: () => { }, nack: () => { }, data: Buffer.from(JSON.stringify(msg)), }); (0, chai_1.expect)(topicMock.publishMessage.calledWith({ json: { id: msg.id, status: 'error', err: constants_1.NO_MESSAGE_HANDLER, }, attributes: { id: msg.id, }, })).to.be.true; }); it('should call handler if exists in handlers object', async () => { const handler = sinon.spy(); server.messageHandlers = objectToMap({ [msg.pattern]: handler, }); await server.handleMessage({ ackId: 'id', publishTime: new Date(), attributes: {}, id: 'id', received: 0, deliveryAttempt: 1, ack: () => { }, modAck: () => { }, nack: () => { }, data: Buffer.from(JSON.stringify(msg)), }); (0, chai_1.expect)(handler.calledOnce).to.be.true; }); it('should return undefined when data is not in JSON format', async () => { const result = await server.handleMessage({ ackId: 'id', publishTime: new Date(), attributes: {}, id: 'id', received: 0, deliveryAttempt: 1, ack: () => { }, modAck: () => { }, nack: () => { }, data: Buffer.from('text'), }); (0, chai_1.expect)(result).to.be.undefined; }); }); describe('message acknowledgment behavior', () => { const msg = { pattern: 'test', data: 'tests', id: '3', }; it('should auto acknowledge message when noAck is true and message handling succeeded', async () => { server = getInstance({ noAck: true }); await server.listen(() => { }); const message = createMockMessage(msg); const ackSpy = sinon.spy(message, 'ack'); await simulateMessageEvent(server, message); (0, chai_1.expect)(ackSpy.calledOnce).to.be.true; }); it('should auto negative acknowledge message when noAck is true and message handling failed', async () => { server = getInstance({ noAck: true }); await server.listen(() => { }); const message = createMockMessage(msg); const nackSpy = sinon.spy(message, 'nack'); server.messageHandlers = objectToMap({ [msg.pattern]: sinon.stub().rejects(new Error('Handler failed')), }); await simulateMessageEvent(server, message); (0, chai_1.expect)(nackSpy.calledOnce).to.be.true; }); it('should not auto acknowledge message when noAck is false and message handling succeeded', async () => { server = getInstance({ noAck: false }); await server.listen(() => { }); const message = createMockMessage(msg); const ackSpy = sinon.spy(message, 'ack'); await simulateMessageEvent(server, message); (0, chai_1.expect)(ackSpy.called).to.be.false; }); it('should not auto negative acknowledge message when noAck is false and message handling failed', async () => { server = getInstance({ noAck: false }); await server.listen(() => { }); const message = createMockMessage(msg); const nackSpy = sinon.spy(message, 'nack'); server.messageHandlers = objectToMap({ [msg.pattern]: sinon.stub().rejects(new Error('Handler failed')), }); await simulateMessageEvent(server, message); (0, chai_1.expect)(nackSpy.called).to.be.false; }); }); describe('sendMessage', () => { beforeEach(async () => { server = getInstance({}); await server.listen(() => { }); }); it('should publish message to indicated topic', async () => { const message = { test: true }; const replyTo = 'test'; const correlationId = '0'; await server.sendMessage(message, replyTo, correlationId); (0, chai_1.expect)(topicMock.publishMessage.calledWith({ json: { ...message, id: correlationId }, attributes: { id: correlationId, }, })).to.be.true; }); describe('when scopedEnvKey is defined', () => { beforeEach(async () => { server = getInstance({ scopedEnvKey: 'my-key' }); await server.listen(() => { }); }); it('should set scopedEnvKey on replyTo', async () => { const message = { test: true }; const replyTo = 'test'; const correlationId = '0'; await server.sendMessage(message, replyTo, correlationId); (0, chai_1.expect)(Array.from(server['replyTopics'].values())).to.deep.eq(['test']); }); }); }); describe('handleEvent', () => { const channel = 'test'; const data = 'test'; beforeEach(async () => { server = getInstance({}); }); it('should call handler with expected arguments', () => { const handler = sandbox.spy(); server.messageHandlers = objectToMap({ [channel]: handler, }); server.handleEvent(channel, { pattern: '', data }, new base_rpc_context_1.BaseRpcContext([])); (0, chai_1.expect)(handler.calledWith(data)).to.be.true; }); }); describe('createIfNotExists', () => { it('should throw error', async () => { const create = sandbox.stub().rejects({ code: 7 }); try { await server['createIfNotExists'](create); } catch (error) { (0, chai_1.expect)(error).to.include({ code: 7 }); } (0, chai_1.expect)(create.called).to.be.true; }); it('should skip error', async () => { const create = sandbox.stub().rejects({ code: gc_pubsub_constants_1.ALREADY_EXISTS }); await server['createIfNotExists'](create); (0, chai_1.expect)(create.called).to.be.true; }); }); function getInstance(options) { const server = new gc_pubsub_server_1.GCPubSubServer(options); sandbox = sinon.createSandbox(); const logger = new common_1.Logger(); sinon.stub(logger, 'error'); server['logger'] = logger; subscriptionMock = { create: sandbox.stub().resolves(), close: sandbox.stub().callsFake((callback) => callback()), on: sandbox.stub().returnsThis(), exists: sandbox.stub().resolves([true]), }; topicMock = { create: sandbox.stub().resolves(), exists: sandbox.stub().resolves([true]), flush: sandbox.stub().callsFake((callback) => callback()), publishMessage: sandbox.stub().resolves(), subscription: sandbox.stub().returns(subscriptionMock), }; pubsub = { topic: sandbox.stub().returns(topicMock), close: sandbox.stub().callsFake((callback) => callback()), createSubscription: sandbox.stub().resolves([subscriptionMock]), }; createClient = sandbox.stub(server, 'createClient').returns(pubsub); return server; } }); //# sourceMappingURL=gc-pubsub.server.spec.js.map