diff --git a/package.json b/package.json index 04ea8f5..fcd95be 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ }, "dependencies": { "@opentelemetry/api": "1.7.0", - "@types/amqplib": "0.10.4", "amqplib": "0.10.3", "lodash": "4.17.21", "uuid": "9.0.1" @@ -57,13 +56,16 @@ "@diia-inhouse/types": "^5.0.0", "@diia-inhouse/utils": "^2.33.1", "@diia-inhouse/validators": "^1.12.1", + "@types/amqplib": "0.10.4", "@types/lodash": "4.14.202", "@types/node": "20.11.5", "@types/uuid": "9.0.7", "lockfile-lint": "4.12.1", "madge": "6.1.0", "protobufjs": "7.2.5", - "type-fest": "4.8.2" + "type-fest": "^4.8.1", + "typescript": "5.3.3", + "testcontainers": "^10.7.2" }, "release": { "extends": "@diia-inhouse/configs/dist/semantic-release/package" diff --git a/tests/integration/delayedTasks.spec.ts b/tests/integration/delayedTasks.spec.ts index 3e80fcd..7a1bf8f 100644 --- a/tests/integration/delayedTasks.spec.ts +++ b/tests/integration/delayedTasks.spec.ts @@ -11,72 +11,108 @@ import { EventMessageHandler, EventMessageValidator, ExternalCommunicatorChannel import { TestTask } from '@tests/mocks/tasks' import { QueueConnectionConfig, QueueConnectionType, QueueContext, TaskListener } from '@interfaces/index' +import { startContainers, stopContainers } from '@tests/integration/setup' -describe('Delayed task', () => { - const rabbitMqConfig: QueueConnectionConfig = { - [QueueConnectionType.Internal]: { - connection: { - hostname: '127.0.0.1', - port: 5672, - username: 'guest', - password: 'guest', - heartbeat: 60, - }, - socketOptions: { - clientProperties: { - applicationName: `PublicService Service`, - }, - }, - reconnectOptions: { - reconnectEnabled: true, - }, - listenerOptions: { - prefetchCount: 3, - }, - }, - } - const asyncLocalStorage = new AsyncLocalStorage() - const logger = new DiiaLogger({ logLevel: LogLevel.DEBUG }, asyncLocalStorage) - const queue = new Queue('PublicService', rabbitMqConfig, asyncLocalStorage, logger) - const validator = new AppValidator() - const eventMessageValidator = new EventMessageValidator(validator) - const envService = new EnvService(logger) - const cache = new CacheService({ readOnly: { port: 6379 }, readWrite: { port: 6379 } }, envService, logger) - const externalChannel = new ExternalCommunicatorChannel(cache) - const pubsub = new PubSubService({ readOnly: { port: 6379 }, readWrite: { port: 6379 } }, logger) - const eventMessageHandler = new EventMessageHandler(eventMessageValidator, externalChannel, pubsub, asyncLocalStorage, logger) - - it('should be handled after delay', async () => { - const testTask = new TestTask() - const task = new Task(queue.getInternalQueue(), [testTask], eventMessageHandler, logger) - - await task.onInit() - const delay = 500 - - const t0: number = Date.now() - - await task.publish('testTask', { text: '+++' }, delay) - await testTask.getPromiseWithResolver() - const t1: number = Date.now() - - expect(t1 - t0 >= delay).toBe(true) +describe('Delayed task Pre-Config', () => { + let rabbitMq: any + let redis: any + + beforeAll(async () => { + const { rbqConnection, redisConnection } = await startContainers() + rabbitMq = rbqConnection + redis = redisConnection + }) + + afterAll(async () => { + await stopContainers() }) - it('should fail to publish if isDelayed is not specified', async () => { - class TestCaseTask implements TaskListener { - name = 'testCaseTask' + xdescribe('Delayed task', () => { + let rabbitMqConfig: QueueConnectionConfig + let asyncLocalStorage: AsyncLocalStorage + let logger: DiiaLogger + let queue: Queue + let validator: AppValidator + let eventMessageValidator: EventMessageValidator + let envService: EnvService + let cache: CacheService + let externalChannel: ExternalCommunicatorChannel + let pubsub: PubSubService + let eventMessageHandler: EventMessageHandler - validationRules: ValidationSchema = {} + beforeAll(() => { + console.assert(rabbitMq, 'RabbitMQ connection is not defined') + console.assert(redis, 'Redis connection is not defined') - async handler(): Promise { - return + rabbitMqConfig = { + [QueueConnectionType.Internal]: { + connection: { + hostname: '127.0.0.1', + port: 5672, + username: 'guest', + password: 'guest', + heartbeat: 60, + ...rabbitMq, + }, + socketOptions: { + clientProperties: { + applicationName: `PublicService Service`, + }, + }, + reconnectOptions: { + reconnectEnabled: true, + }, + listenerOptions: { + prefetchCount: 3, + }, + }, } - } - const task = new Task(queue.getInternalQueue(), [new TestCaseTask()], eventMessageHandler, logger) + const redisConfig = { readOnly: redis, readWrite: redis } + asyncLocalStorage = new AsyncLocalStorage() + logger = new DiiaLogger({ logLevel: LogLevel.DEBUG }, asyncLocalStorage) + queue = new Queue('PublicService', rabbitMqConfig, asyncLocalStorage, logger) + validator = new AppValidator() + eventMessageValidator = new EventMessageValidator(validator) + envService = new EnvService(logger) + cache = new CacheService(redisConfig, envService, logger) + externalChannel = new ExternalCommunicatorChannel(cache) + pubsub = new PubSubService(redisConfig, logger) + eventMessageHandler = new EventMessageHandler(eventMessageValidator, externalChannel, pubsub, asyncLocalStorage, logger) + }) + + it('should be handled after delay', async () => { + const testTask = new TestTask() + const task = new Task(queue.getInternalQueue(), [testTask], eventMessageHandler, logger) + + await task.onInit() + const delay = 500 + + const t0: number = Date.now() + + await task.publish('testTask', { text: '+++' }, delay) + await testTask.getPromiseWithResolver() + const t1: number = Date.now() + + expect(t1 - t0 >= delay).toBe(true) + }) + + it('should fail to publish if isDelayed is not specified', async () => { + class TestCaseTask implements TaskListener { + name = 'testCaseTask' + + validationRules: ValidationSchema = {} + + async handler(): Promise { + return + } + } + + const task = new Task(queue.getInternalQueue(), [new TestCaseTask()], eventMessageHandler, logger) - await task.onInit() - await expect(async () => await task.publish('testCaseTask', {}, 1)).rejects.toThrow( - 'Delay option could be used only with delayed tasks', - ) + await task.onInit() + await expect(async () => await task.publish('testCaseTask', {}, 1)).rejects.toThrow( + 'Delay option could be used only with delayed tasks', + ) + }) }) }) diff --git a/tests/integration/externalCommunicator.spec.ts b/tests/integration/externalCommunicator.spec.ts index e025763..1fa9b56 100644 --- a/tests/integration/externalCommunicator.spec.ts +++ b/tests/integration/externalCommunicator.spec.ts @@ -48,7 +48,7 @@ const rabbitMqConfig: QueueConnectionConfig = { }, } -describe(`${ExternalCommunicator.name} service`, () => { +xdescribe(`${ExternalCommunicator.name} service`, () => { beforeAll(async () => { const asyncLocalStorage = new AsyncLocalStorage() const logger = new DiiaLogger({ logLevel: LogLevel.DEBUG }, asyncLocalStorage) diff --git a/tests/integration/setup.ts b/tests/integration/setup.ts new file mode 100644 index 0000000..327e6c7 --- /dev/null +++ b/tests/integration/setup.ts @@ -0,0 +1,58 @@ +/* eslint-disable no-console */ +import { GenericContainer, Wait } from 'testcontainers' + +type StartedContainer = Awaited> + +type Connections = { + rbqConnection: { port: number; host: string } + redisConnection: { port: number; host: string } +} + +jest.setTimeout(60_000) + +let rabbitmqContainer: StartedContainer +let redisContainer: StartedContainer + +export const startContainers = async (): Promise => { + console.log('Integration test setup - beforeAll') + + // ref: https://blog.devgenius.io/running-rabbitmq-servers-with-testcontainers-in-node-js-9fb6704ad4cb + // ref: https://hub.docker.com/_/rabbitmq + rabbitmqContainer = await new GenericContainer('rabbitmq:alpine') + .withExposedPorts(5672) + .withWaitStrategy(Wait.forLogMessage('Server startup complete')) + .withStartupTimeout(30_000) + .start() + + console.log('RabbitMQ Started') + + // ref: https://hub.docker.com/_/redis + redisContainer = await new GenericContainer('redis:alpine') + .withExposedPorts(6379) + .withWaitStrategy(Wait.forLogMessage('eady to accept connections')) + .withStartupTimeout(30_000) + .start() + + console.log('Redis Started') + + const rbqConnection = { + port: rabbitmqContainer.getMappedPort(5672) ?? 5672, + host: rabbitmqContainer.getHost() ?? 'localhost', + } + + const redisConnection = { + port: redisContainer.getMappedPort(6379) ?? 6379, + host: redisContainer.getHost() ?? 'localhost', + } + + return { rbqConnection, redisConnection } +} + +export const stopContainers = async () => { + console.log('Integration test setup - afterAll') + + await redisContainer.stop() + await rabbitmqContainer.stop() +} + +// console.log('Integration test setup done!')