UNPKG

nestjs-request-deduplication

Version:

[![npm version](https://badge.fury.io/js/nestjs-request-deduplication.svg)](https://www.npmjs.com/package/nestjs-request-deduplication) [![CI](https://github.com/daniyel/nestjs-request-deduplication/actions/workflows/pr-checks.yml/badge.svg)](https://gith

216 lines (215 loc) 10.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const testing_1 = require("@nestjs/testing"); const request_deduplication_service_1 = require("./request-deduplication.service"); const constants_1 = require("../constants"); const storages_1 = require("../storages"); const interfaces_1 = require("../interfaces"); jest.mock('../storages/redis.adapter'); jest.mock('../storages/memcached.adapter'); jest.mock('../storages/memory.adapter'); describe('RequestDeduplicationService', () => { let service; let mockStorage; beforeEach(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const mockLogger = { error: jest.fn(), warn: jest.fn(), log: jest.fn(), debug: jest.fn(), verbose: jest.fn(), fatal: jest.fn(), setLogLevels: jest.fn(), localInstance: null, registerLocalInstanceRef: jest.fn(), }; mockStorage = { init: jest.fn().mockResolvedValue(undefined), get: jest.fn(), set: jest.fn().mockResolvedValue(undefined), delete: jest.fn().mockResolvedValue(undefined), logger: mockLogger, }; const module = yield testing_1.Test.createTestingModule({ providers: [ request_deduplication_service_1.RequestDeduplicationService, { provide: constants_1.REQUEST_DEDUPLICATION_MODULE_OPTIONS, useValue: { ttl: 1000, }, }, ], }).compile(); service = module.get(request_deduplication_service_1.RequestDeduplicationService); Object.defineProperty(request_deduplication_service_1.RequestDeduplicationService, 'storageAdapter', { value: mockStorage, writable: true, }); })); describe('processRequest', () => { it('should return true for first-time requests', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { mockStorage.get.mockResolvedValue(''); const result = yield service.processRequest('test-key', 'value', 1000); expect(result).toBe(true); expect(mockStorage.get).toHaveBeenCalledWith('test-key'); expect(mockStorage.set).toHaveBeenCalledWith('test-key', 'value', 1000); })); it('should return false for duplicate requests', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { mockStorage.get.mockResolvedValue('existing-value'); const result = yield service.processRequest('test-key', 'value', 1000); expect(result).toBe(false); expect(mockStorage.get).toHaveBeenCalledWith('test-key'); expect(mockStorage.set).not.toHaveBeenCalled(); })); it('should throw error when storage fails', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { mockStorage.get.mockRejectedValue(new Error('Storage error')); yield expect(service.processRequest('test-key', 'value', 1000)).rejects.toThrow('Storage error'); })); }); describe('deleteRequest', () => { it('should delete existing request', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { yield service.deleteRequest('test-key'); expect(mockStorage.delete).toHaveBeenCalledWith('test-key'); })); it('should handle delete errors gracefully', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { mockStorage.delete.mockRejectedValue(new Error('Delete error')); yield expect(service.deleteRequest('test-key')).resolves.not.toThrow(); })); }); describe('storage initialization', () => { it('should initialize memory storage by default', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { yield service.onModuleInit(); expect(service['storage']).toBeDefined(); })); it('should initialize redis storage when config provided', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const moduleWithRedis = yield testing_1.Test.createTestingModule({ providers: [ request_deduplication_service_1.RequestDeduplicationService, { provide: constants_1.REQUEST_DEDUPLICATION_MODULE_OPTIONS, useValue: { ttl: 1000, redisConfig: { host: 'localhost', port: 6379, }, }, }, ], }).compile(); const serviceWithRedis = moduleWithRedis.get(request_deduplication_service_1.RequestDeduplicationService); yield serviceWithRedis.onModuleInit(); expect(serviceWithRedis['storage']).toBeDefined(); })); it('should initialize memcached storage when config provided', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const moduleWithMemcached = yield testing_1.Test.createTestingModule({ providers: [ request_deduplication_service_1.RequestDeduplicationService, { provide: constants_1.REQUEST_DEDUPLICATION_MODULE_OPTIONS, useValue: { ttl: 1000, memcachedConfig: { servers: ['localhost:11211'], }, }, }, ], }).compile(); const serviceWithMemcached = moduleWithMemcached.get(request_deduplication_service_1.RequestDeduplicationService); yield serviceWithMemcached.onModuleInit(); expect(serviceWithMemcached['storage']).toBeDefined(); })); }); describe('storage adapter initialization', () => { beforeEach(() => { // Reset using proper type request_deduplication_service_1.RequestDeduplicationService.storageAdapter = undefined; jest.clearAllMocks(); }); it('should initialize Redis adapter when redisConfig is provided', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const moduleRef = yield testing_1.Test.createTestingModule({ providers: [ request_deduplication_service_1.RequestDeduplicationService, { provide: constants_1.REQUEST_DEDUPLICATION_MODULE_OPTIONS, useValue: { storage: interfaces_1.StorageType.REDIS, redisConfig: { url: 'redis://localhost:6379', }, }, }, ], }).compile(); const service = moduleRef.get(request_deduplication_service_1.RequestDeduplicationService); yield service.onModuleInit(); const adapter = request_deduplication_service_1.RequestDeduplicationService.storageAdapter; expect(storages_1.RedisAdapter).toHaveBeenCalledWith(expect.objectContaining({ redisConfig: expect.any(Object), })); expect(adapter.init).toHaveBeenCalled(); })); it('should initialize Memcached adapter when memcachedConfig is provided', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const moduleRef = yield testing_1.Test.createTestingModule({ providers: [ request_deduplication_service_1.RequestDeduplicationService, { provide: constants_1.REQUEST_DEDUPLICATION_MODULE_OPTIONS, useValue: { storage: interfaces_1.StorageType.MEMCACHED, memcachedConfig: { uri: 'localhost:11211', }, }, }, ], }).compile(); const service = moduleRef.get(request_deduplication_service_1.RequestDeduplicationService); yield service.onModuleInit(); const adapter = request_deduplication_service_1.RequestDeduplicationService.storageAdapter; expect(storages_1.MemcachedAdapter).toHaveBeenCalledWith(expect.objectContaining({ memcachedConfig: expect.any(Object), })); expect(adapter.init).toHaveBeenCalled(); })); it('should initialize Memory adapter when no specific config is provided', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const moduleRef = yield testing_1.Test.createTestingModule({ providers: [ request_deduplication_service_1.RequestDeduplicationService, { provide: constants_1.REQUEST_DEDUPLICATION_MODULE_OPTIONS, useValue: { storage: interfaces_1.StorageType.MEMORY, }, }, ], }).compile(); const service = moduleRef.get(request_deduplication_service_1.RequestDeduplicationService); yield service.onModuleInit(); const adapter = request_deduplication_service_1.RequestDeduplicationService.storageAdapter; expect(storages_1.MemoryAdapter).toHaveBeenCalled(); expect(adapter.init).toHaveBeenCalled(); })); it('should throw error if storage adapter initialization fails', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const initError = new Error('Failed to initialize storage'); storages_1.MemoryAdapter.prototype.init.mockRejectedValueOnce(initError); const moduleRef = yield testing_1.Test.createTestingModule({ providers: [ request_deduplication_service_1.RequestDeduplicationService, { provide: constants_1.REQUEST_DEDUPLICATION_MODULE_OPTIONS, useValue: { storage: interfaces_1.StorageType.MEMORY, }, }, ], }).compile(); const service = moduleRef.get(request_deduplication_service_1.RequestDeduplicationService); yield expect(service.onModuleInit()).rejects.toThrow(initError); })); }); });