193 lines
5.6 KiB
TypeScript
193 lines
5.6 KiB
TypeScript
import { ExecutionContext, CallHandler } from '@nestjs/common';
|
|
import { of, throwError } from 'rxjs';
|
|
import { AuditLogInterceptor } from '../../../src/shared/interceptors/audit-log.interceptor';
|
|
|
|
describe('AuditLogInterceptor', () => {
|
|
let interceptor: AuditLogInterceptor;
|
|
|
|
const createMockContext = (
|
|
method: string = 'POST',
|
|
url: string = '/backup-share/store',
|
|
body: any = {},
|
|
sourceService: string = 'identity-service',
|
|
sourceIp: string = '127.0.0.1',
|
|
): ExecutionContext => {
|
|
return {
|
|
switchToHttp: () => ({
|
|
getRequest: () => ({
|
|
method,
|
|
url,
|
|
body,
|
|
sourceService,
|
|
sourceIp,
|
|
}),
|
|
}),
|
|
} as ExecutionContext;
|
|
};
|
|
|
|
const createMockCallHandler = (response: any = {}): CallHandler => ({
|
|
handle: () => of(response),
|
|
});
|
|
|
|
const createErrorCallHandler = (error: Error): CallHandler => ({
|
|
handle: () => throwError(() => error),
|
|
});
|
|
|
|
beforeEach(() => {
|
|
interceptor = new AuditLogInterceptor();
|
|
});
|
|
|
|
describe('intercept', () => {
|
|
it('should allow request to proceed', (done) => {
|
|
const context = createMockContext();
|
|
const handler = createMockCallHandler({ success: true });
|
|
|
|
interceptor.intercept(context, handler).subscribe({
|
|
next: (response) => {
|
|
expect(response).toEqual({ success: true });
|
|
done();
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should not modify the response', (done) => {
|
|
const context = createMockContext();
|
|
const originalResponse = { shareId: '123', success: true };
|
|
const handler = createMockCallHandler(originalResponse);
|
|
|
|
interceptor.intercept(context, handler).subscribe({
|
|
next: (response) => {
|
|
expect(response).toEqual(originalResponse);
|
|
done();
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should propagate errors', (done) => {
|
|
const context = createMockContext();
|
|
const error = new Error('Test error');
|
|
const handler = createErrorCallHandler(error);
|
|
|
|
interceptor.intercept(context, handler).subscribe({
|
|
error: (err) => {
|
|
expect(err).toBe(error);
|
|
done();
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should sanitize sensitive fields in body', () => {
|
|
const sensitiveBody = {
|
|
userId: '12345',
|
|
encryptedShareData: 'secret-data',
|
|
recoveryToken: 'secret-token',
|
|
password: 'secret-password',
|
|
publicKey: '02abc...',
|
|
};
|
|
|
|
// Access the private method through prototype
|
|
const sanitized = (interceptor as any).sanitizeBody(sensitiveBody);
|
|
|
|
expect(sanitized.userId).toBe('12345');
|
|
expect(sanitized.publicKey).toBe('02abc...');
|
|
expect(sanitized.encryptedShareData).toBe('[REDACTED]');
|
|
expect(sanitized.recoveryToken).toBe('[REDACTED]');
|
|
expect(sanitized.password).toBe('[REDACTED]');
|
|
});
|
|
|
|
it('should handle null body', () => {
|
|
const sanitized = (interceptor as any).sanitizeBody(null);
|
|
expect(sanitized).toBeNull();
|
|
});
|
|
|
|
it('should handle undefined body', () => {
|
|
const sanitized = (interceptor as any).sanitizeBody(undefined);
|
|
expect(sanitized).toBeUndefined();
|
|
});
|
|
|
|
it('should handle empty body', () => {
|
|
const sanitized = (interceptor as any).sanitizeBody({});
|
|
expect(sanitized).toEqual({});
|
|
});
|
|
|
|
it('should not modify original body object', () => {
|
|
const originalBody = {
|
|
userId: '12345',
|
|
encryptedShareData: 'secret-data',
|
|
};
|
|
const bodyCopy = { ...originalBody };
|
|
|
|
(interceptor as any).sanitizeBody(originalBody);
|
|
|
|
expect(originalBody).toEqual(bodyCopy);
|
|
});
|
|
});
|
|
|
|
describe('logging behavior', () => {
|
|
it('should handle different HTTP methods', (done) => {
|
|
const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
|
|
|
|
let completed = 0;
|
|
methods.forEach((method) => {
|
|
const context = createMockContext(method, '/test');
|
|
const handler = createMockCallHandler();
|
|
|
|
interceptor.intercept(context, handler).subscribe({
|
|
complete: () => {
|
|
completed++;
|
|
if (completed === methods.length) {
|
|
done();
|
|
}
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should handle different URL paths', (done) => {
|
|
const paths = [
|
|
'/backup-share/store',
|
|
'/backup-share/retrieve',
|
|
'/backup-share/revoke',
|
|
'/health',
|
|
];
|
|
|
|
let completed = 0;
|
|
paths.forEach((url) => {
|
|
const context = createMockContext('GET', url);
|
|
const handler = createMockCallHandler();
|
|
|
|
interceptor.intercept(context, handler).subscribe({
|
|
complete: () => {
|
|
completed++;
|
|
if (completed === paths.length) {
|
|
done();
|
|
}
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should handle unknown source service', (done) => {
|
|
const context = createMockContext('POST', '/test', {}, undefined, '127.0.0.1');
|
|
const handler = createMockCallHandler();
|
|
|
|
interceptor.intercept(context, handler).subscribe({
|
|
complete: () => {
|
|
done();
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should handle unknown source IP', (done) => {
|
|
const context = createMockContext('POST', '/test', {}, 'identity-service', undefined);
|
|
const handler = createMockCallHandler();
|
|
|
|
interceptor.intercept(context, handler).subscribe({
|
|
complete: () => {
|
|
done();
|
|
},
|
|
});
|
|
});
|
|
});
|
|
});
|