Files
study_work/packages/backend/src/audit/operation-log.interceptor.spec.ts
T

112 lines
3.2 KiB
TypeScript

import { CallHandler, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { lastValueFrom, of, throwError } from 'rxjs';
import { AuditService } from './audit.service';
import { OperationLogInterceptor } from './operation-log.interceptor';
function createContext(request: Record<string, unknown>) {
return {
getHandler: () => function handler() {},
getClass: () => class Controller {},
switchToHttp: () => ({
getRequest: () => request,
}),
} as unknown as ExecutionContext;
}
describe('OperationLogInterceptor', () => {
it('writes a success operation log with masked request body', async () => {
const write = jest.fn();
const reflector = {
getAllAndOverride: jest.fn().mockReturnValue({
action: 'admin.users.create',
resourceType: 'user',
resourceIdFromResult: 'id',
metadataFromBody: ['name', 'password'],
}),
} as unknown as Reflector;
const interceptor = new OperationLogInterceptor(
{ write } as unknown as AuditService,
reflector,
);
const request = {
user: { id: 'actor-1', name: 'root' },
params: {},
query: {},
body: { name: 'demo', password: '123456' },
headers: { 'user-agent': 'vitest' },
ip: '127.0.0.1',
};
const next = {
handle: () => of({ id: 'user-1' }),
} as CallHandler;
await expect(lastValueFrom(interceptor.intercept(createContext(request), next))).resolves.toEqual({
id: 'user-1',
});
expect(write).toHaveBeenCalledWith(
expect.objectContaining({
actorId: 'actor-1',
actorUsername: 'root',
action: 'admin.users.create',
resourceType: 'user',
resourceId: 'user-1',
ipAddress: '127.0.0.1',
metadata: expect.objectContaining({
status: 'success',
request: expect.objectContaining({
body: { name: 'demo', password: '******' },
}),
}),
}),
);
});
it('writes a failed operation log and rethrows the error', async () => {
const write = jest.fn();
const reflector = {
getAllAndOverride: jest.fn().mockReturnValue({
action: 'admin.users.update',
resourceType: 'user',
resourceIdParam: 'id',
}),
} as unknown as Reflector;
const interceptor = new OperationLogInterceptor(
{ write } as unknown as AuditService,
reflector,
);
const error = new Error('保存失败');
const request = {
user: { id: 'actor-1', name: 'root' },
params: { id: 'user-1' },
query: {},
body: {},
headers: {},
ip: '127.0.0.1',
};
const next = {
handle: () => throwError(() => error),
} as CallHandler;
await expect(
lastValueFrom(interceptor.intercept(createContext(request), next)),
).rejects.toThrow('保存失败');
expect(write).toHaveBeenCalledWith(
expect.objectContaining({
action: 'admin.users.update',
resourceId: 'user-1',
metadata: expect.objectContaining({
status: 'failed',
error: {
name: 'Error',
message: '保存失败',
},
}),
}),
);
});
});