@ahmedhegazee/nestjs-telescope
Version:
Advanced observability and monitoring solution for NestJS applications with ML-powered analytics, enterprise features, and production-ready scaling
448 lines • 18.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const testing_1 = require("@nestjs/testing");
const rxjs_1 = require("rxjs");
const request_watcher_interceptor_1 = require("./request-watcher.interceptor");
const request_watcher_service_1 = require("./request-watcher.service");
describe('RequestWatcherInterceptor', () => {
let interceptor;
let requestWatcherService;
beforeEach(async () => {
const mockRequestWatcherService = {
shouldSampleRequest: jest.fn().mockReturnValue(true),
getExcludedPaths: jest.fn().mockReturnValue(['/health', '/metrics']),
shouldMaskBody: jest.fn().mockReturnValue(false),
shouldLogSuccessfulResponseBodies: jest.fn().mockReturnValue(false),
trackRequest: jest.fn(),
};
const module = await testing_1.Test.createTestingModule({
providers: [
request_watcher_interceptor_1.RequestWatcherInterceptor,
{
provide: request_watcher_service_1.RequestWatcherService,
useValue: mockRequestWatcherService,
},
],
}).compile();
interceptor = module.get(request_watcher_interceptor_1.RequestWatcherInterceptor);
requestWatcherService = module.get(request_watcher_service_1.RequestWatcherService);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('intercept', () => {
it('should track successful request', (done) => {
const mockRequest = {
method: 'GET',
originalUrl: '/api/users',
path: '/api/users',
headers: {
'user-agent': 'test-agent',
'x-forwarded-for': '192.168.1.1'
},
query: { page: 1 },
body: null,
cookies: {},
connection: { remoteAddress: '127.0.0.1' },
ip: '127.0.0.1'
};
const mockResponse = {
statusCode: 200,
getHeaders: jest.fn().mockReturnValue({
'content-type': 'application/json',
'content-length': '100'
}),
get: jest.fn().mockReturnValue('100')
};
const mockContext = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
getResponse: jest.fn().mockReturnValue(mockResponse)
})
};
const mockCallHandler = {
handle: jest.fn().mockReturnValue((0, rxjs_1.of)({ users: [] }))
};
interceptor.intercept(mockContext, mockCallHandler).subscribe({
next: (result) => {
expect(result).toEqual({ users: [] });
expect(requestWatcherService.trackRequest).toHaveBeenCalledWith(expect.objectContaining({
method: 'GET',
url: '/api/users',
ip: '127.0.0.1',
userAgent: 'test-agent',
query: { page: 1 }
}), expect.objectContaining({
statusCode: 200,
duration: expect.any(Number)
}), null);
done();
},
error: done
});
});
it('should track error request', (done) => {
const mockRequest = {
method: 'POST',
originalUrl: '/api/users',
path: '/api/users',
headers: { 'user-agent': 'test-agent' },
query: {},
body: { name: 'test' },
cookies: {},
connection: { remoteAddress: '127.0.0.1' },
ip: '127.0.0.1'
};
const mockResponse = {
statusCode: 500,
getHeaders: jest.fn().mockReturnValue({
'content-type': 'application/json'
}),
get: jest.fn().mockReturnValue(null)
};
const mockContext = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
getResponse: jest.fn().mockReturnValue(mockResponse)
})
};
const error = new Error('Database connection failed');
const mockCallHandler = {
handle: jest.fn().mockReturnValue((0, rxjs_1.throwError)(() => error))
};
interceptor.intercept(mockContext, mockCallHandler).subscribe({
next: () => done.fail('Should have thrown error'),
error: (err) => {
expect(err).toBe(error);
expect(requestWatcherService.trackRequest).toHaveBeenCalledWith(expect.objectContaining({
method: 'POST',
url: '/api/users',
body: { name: 'test' }
}), expect.objectContaining({
statusCode: 500
}), error);
done();
}
});
});
it('should skip tracking for excluded paths', (done) => {
const mockRequest = {
method: 'GET',
originalUrl: '/health',
path: '/health',
headers: {},
query: {},
body: null,
cookies: {},
connection: { remoteAddress: '127.0.0.1' },
ip: '127.0.0.1'
};
const mockResponse = {
statusCode: 200,
getHeaders: jest.fn().mockReturnValue({}),
get: jest.fn().mockReturnValue(null)
};
const mockContext = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
getResponse: jest.fn().mockReturnValue(mockResponse)
})
};
const mockCallHandler = {
handle: jest.fn().mockReturnValue((0, rxjs_1.of)('OK'))
};
interceptor.intercept(mockContext, mockCallHandler).subscribe({
next: (result) => {
expect(result).toBe('OK');
expect(requestWatcherService.trackRequest).not.toHaveBeenCalled();
done();
},
error: done
});
});
it('should skip tracking when sampling returns false', (done) => {
requestWatcherService.shouldSampleRequest.mockReturnValue(false);
const mockRequest = {
method: 'GET',
originalUrl: '/api/test',
path: '/api/test',
headers: {},
query: {},
body: null,
cookies: {},
connection: { remoteAddress: '127.0.0.1' },
ip: '127.0.0.1'
};
const mockResponse = {
statusCode: 200,
getHeaders: jest.fn().mockReturnValue({}),
get: jest.fn().mockReturnValue(null)
};
const mockContext = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
getResponse: jest.fn().mockReturnValue(mockResponse)
})
};
const mockCallHandler = {
handle: jest.fn().mockReturnValue((0, rxjs_1.of)('OK'))
};
interceptor.intercept(mockContext, mockCallHandler).subscribe({
next: (result) => {
expect(result).toBe('OK');
expect(requestWatcherService.trackRequest).not.toHaveBeenCalled();
done();
},
error: done
});
});
});
describe('sanitization', () => {
it('should sanitize sensitive headers', (done) => {
const mockRequest = {
method: 'GET',
originalUrl: '/api/users',
path: '/api/users',
headers: {
'authorization': 'Bearer token123',
'cookie': 'session=abc123',
'x-api-key': 'secret-key',
'user-agent': 'test-agent'
},
query: {},
body: null,
cookies: {},
connection: { remoteAddress: '127.0.0.1' },
ip: '127.0.0.1'
};
const mockResponse = {
statusCode: 200,
getHeaders: jest.fn().mockReturnValue({}),
get: jest.fn().mockReturnValue(null)
};
const mockContext = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
getResponse: jest.fn().mockReturnValue(mockResponse)
})
};
const mockCallHandler = {
handle: jest.fn().mockReturnValue((0, rxjs_1.of)({}))
};
interceptor.intercept(mockContext, mockCallHandler).subscribe({
next: () => {
expect(requestWatcherService.trackRequest).toHaveBeenCalledWith(expect.objectContaining({
headers: expect.objectContaining({
'authorization': '[REDACTED]',
'cookie': '[REDACTED]',
'x-api-key': '[REDACTED]',
'user-agent': 'test-agent'
})
}), expect.any(Object), null);
done();
},
error: done
});
});
it('should sanitize sensitive query parameters', (done) => {
const mockRequest = {
method: 'GET',
originalUrl: '/api/users?token=secret123&page=1',
path: '/api/users',
headers: {},
query: {
token: 'secret123',
page: '1',
password: 'hidden'
},
body: null,
cookies: {},
connection: { remoteAddress: '127.0.0.1' },
ip: '127.0.0.1'
};
const mockResponse = {
statusCode: 200,
getHeaders: jest.fn().mockReturnValue({}),
get: jest.fn().mockReturnValue(null)
};
const mockContext = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
getResponse: jest.fn().mockReturnValue(mockResponse)
})
};
const mockCallHandler = {
handle: jest.fn().mockReturnValue((0, rxjs_1.of)({}))
};
interceptor.intercept(mockContext, mockCallHandler).subscribe({
next: () => {
expect(requestWatcherService.trackRequest).toHaveBeenCalledWith(expect.objectContaining({
query: expect.objectContaining({
token: '[REDACTED]',
page: '1',
password: '[REDACTED]'
})
}), expect.any(Object), null);
done();
},
error: done
});
});
it('should sanitize sensitive request body', (done) => {
const mockRequest = {
method: 'POST',
originalUrl: '/api/login',
path: '/api/login',
headers: {},
query: {},
body: {
username: 'test@example.com',
password: 'secret123',
rememberMe: true
},
cookies: {},
connection: { remoteAddress: '127.0.0.1' },
ip: '127.0.0.1'
};
const mockResponse = {
statusCode: 200,
getHeaders: jest.fn().mockReturnValue({}),
get: jest.fn().mockReturnValue(null)
};
const mockContext = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
getResponse: jest.fn().mockReturnValue(mockResponse)
})
};
const mockCallHandler = {
handle: jest.fn().mockReturnValue((0, rxjs_1.of)({ token: 'jwt-token' }))
};
interceptor.intercept(mockContext, mockCallHandler).subscribe({
next: () => {
expect(requestWatcherService.trackRequest).toHaveBeenCalledWith(expect.objectContaining({
body: expect.objectContaining({
username: 'test@example.com',
password: '[REDACTED]',
rememberMe: true
})
}), expect.any(Object), null);
done();
},
error: done
});
});
});
describe('session and user tracking', () => {
it('should extract session ID from cookie', (done) => {
const mockRequest = {
method: 'GET',
originalUrl: '/api/users',
path: '/api/users',
headers: {},
query: {},
body: null,
cookies: { sessionId: 'session-123' },
connection: { remoteAddress: '127.0.0.1' },
ip: '127.0.0.1'
};
const mockResponse = {
statusCode: 200,
getHeaders: jest.fn().mockReturnValue({}),
get: jest.fn().mockReturnValue(null)
};
const mockContext = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
getResponse: jest.fn().mockReturnValue(mockResponse)
})
};
const mockCallHandler = {
handle: jest.fn().mockReturnValue((0, rxjs_1.of)({}))
};
interceptor.intercept(mockContext, mockCallHandler).subscribe({
next: () => {
expect(requestWatcherService.trackRequest).toHaveBeenCalledWith(expect.objectContaining({
sessionId: 'session-123'
}), expect.any(Object), null);
done();
},
error: done
});
});
it('should extract user ID from request context', (done) => {
const mockRequest = {
method: 'GET',
originalUrl: '/api/users',
path: '/api/users',
headers: {},
query: {},
body: null,
cookies: {},
connection: { remoteAddress: '127.0.0.1' },
ip: '127.0.0.1',
user: { id: 'user-123', name: 'Test User' }
};
const mockResponse = {
statusCode: 200,
getHeaders: jest.fn().mockReturnValue({}),
get: jest.fn().mockReturnValue(null)
};
const mockContext = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
getResponse: jest.fn().mockReturnValue(mockResponse)
})
};
const mockCallHandler = {
handle: jest.fn().mockReturnValue((0, rxjs_1.of)({}))
};
interceptor.intercept(mockContext, mockCallHandler).subscribe({
next: () => {
expect(requestWatcherService.trackRequest).toHaveBeenCalledWith(expect.objectContaining({
userId: 'user-123'
}), expect.any(Object), null);
done();
},
error: done
});
});
it('should generate trace ID when not provided', (done) => {
const mockRequest = {
method: 'GET',
originalUrl: '/api/users',
path: '/api/users',
headers: {},
query: {},
body: null,
cookies: {},
connection: { remoteAddress: '127.0.0.1' },
ip: '127.0.0.1'
};
const mockResponse = {
statusCode: 200,
getHeaders: jest.fn().mockReturnValue({}),
get: jest.fn().mockReturnValue(null)
};
const mockContext = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
getResponse: jest.fn().mockReturnValue(mockResponse)
})
};
const mockCallHandler = {
handle: jest.fn().mockReturnValue((0, rxjs_1.of)({}))
};
interceptor.intercept(mockContext, mockCallHandler).subscribe({
next: () => {
expect(requestWatcherService.trackRequest).toHaveBeenCalledWith(expect.objectContaining({
traceId: expect.stringMatching(/^trace_/)
}), expect.any(Object), null);
done();
},
error: done
});
});
});
});
//# sourceMappingURL=request-watcher.interceptor.spec.js.map