nestjs-request-deduplication
Version:
[](https://www.npmjs.com/package/nestjs-request-deduplication) [](https://gith
216 lines (215 loc) • 10.9 kB
JavaScript
"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);
}));
});
});