created lots of tests
This commit is contained in:
parent
42baadae38
commit
54f37f9521
21 changed files with 4577 additions and 215 deletions
273
libs/core/di/test/operation-context.test.ts
Normal file
273
libs/core/di/test/operation-context.test.ts
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
import { describe, expect, it, beforeEach, mock } from 'bun:test';
|
||||
import { OperationContext } from '../src/operation-context';
|
||||
import type { OperationContextOptions } from '../src/operation-context';
|
||||
|
||||
describe('OperationContext', () => {
|
||||
const mockLogger = {
|
||||
info: mock(() => {}),
|
||||
error: mock(() => {}),
|
||||
warn: mock(() => {}),
|
||||
debug: mock(() => {}),
|
||||
trace: mock(() => {}),
|
||||
child: mock(() => mockLogger),
|
||||
};
|
||||
|
||||
const mockContainer = {
|
||||
resolve: mock((name: string) => ({ name })),
|
||||
resolveAsync: mock(async (name: string) => ({ name })),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset mocks
|
||||
Object.keys(mockLogger).forEach(key => {
|
||||
if (typeof mockLogger[key as keyof typeof mockLogger] === 'function') {
|
||||
(mockLogger as any)[key] = mock(() =>
|
||||
key === 'child' ? mockLogger : undefined
|
||||
);
|
||||
}
|
||||
});
|
||||
mockContainer.resolve = mock((name: string) => ({ name }));
|
||||
mockContainer.resolveAsync = mock(async (name: string) => ({ name }));
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should create context with required options', () => {
|
||||
const options: OperationContextOptions = {
|
||||
handlerName: 'test-handler',
|
||||
operationName: 'test-op',
|
||||
};
|
||||
|
||||
const context = new OperationContext(options);
|
||||
|
||||
expect(context).toBeDefined();
|
||||
expect(context.traceId).toBeDefined();
|
||||
expect(context.metadata).toEqual({});
|
||||
expect(context.logger).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create context with all options', () => {
|
||||
const options: OperationContextOptions = {
|
||||
handlerName: 'test-handler',
|
||||
operationName: 'test-op',
|
||||
parentLogger: mockLogger,
|
||||
container: mockContainer,
|
||||
metadata: { key: 'value' },
|
||||
traceId: 'custom-trace-id',
|
||||
};
|
||||
|
||||
const context = new OperationContext(options);
|
||||
|
||||
expect(context.traceId).toBe('custom-trace-id');
|
||||
expect(context.metadata).toEqual({ key: 'value' });
|
||||
expect(context.logger).toBe(mockLogger);
|
||||
});
|
||||
});
|
||||
|
||||
describe('static create', () => {
|
||||
it('should create context using static method', () => {
|
||||
const context = OperationContext.create('handler', 'operation', {
|
||||
metadata: { foo: 'bar' },
|
||||
});
|
||||
|
||||
expect(context).toBeDefined();
|
||||
expect(context.metadata).toEqual({ foo: 'bar' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('service resolution', () => {
|
||||
it('should resolve services from container', () => {
|
||||
const context = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
container: mockContainer,
|
||||
});
|
||||
|
||||
const service = context.resolve('myService');
|
||||
expect(service).toEqual({ name: 'myService' });
|
||||
expect(mockContainer.resolve).toHaveBeenCalledWith('myService');
|
||||
});
|
||||
|
||||
it('should resolve services asynchronously', async () => {
|
||||
const context = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
container: mockContainer,
|
||||
});
|
||||
|
||||
const service = await context.resolveAsync('myService');
|
||||
expect(service).toEqual({ name: 'myService' });
|
||||
expect(mockContainer.resolveAsync).toHaveBeenCalledWith('myService');
|
||||
});
|
||||
|
||||
it('should throw error when no container available', () => {
|
||||
const context = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
});
|
||||
|
||||
expect(() => context.resolve('service')).toThrow('No service container available');
|
||||
});
|
||||
|
||||
it('should throw error when no container available for async', async () => {
|
||||
const context = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
});
|
||||
|
||||
await expect(context.resolveAsync('service')).rejects.toThrow('No service container available');
|
||||
});
|
||||
});
|
||||
|
||||
describe('metadata', () => {
|
||||
it('should add metadata', () => {
|
||||
const context = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
});
|
||||
|
||||
context.addMetadata('userId', '12345');
|
||||
context.addMetadata('correlationId', 'corr-456');
|
||||
|
||||
expect(context.metadata.userId).toBe('12345');
|
||||
expect(context.metadata.correlationId).toBe('corr-456');
|
||||
});
|
||||
|
||||
it('should preserve initial metadata', () => {
|
||||
const context = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
metadata: { initial: 'value' },
|
||||
});
|
||||
|
||||
context.addMetadata('added', 'new-value');
|
||||
|
||||
expect(context.metadata.initial).toBe('value');
|
||||
expect(context.metadata.added).toBe('new-value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('execution time', () => {
|
||||
it('should track execution time', async () => {
|
||||
const context = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
});
|
||||
|
||||
// Wait a bit
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
|
||||
const executionTime = context.getExecutionTime();
|
||||
expect(executionTime).toBeGreaterThan(40);
|
||||
expect(executionTime).toBeLessThan(100);
|
||||
});
|
||||
});
|
||||
|
||||
describe('logging', () => {
|
||||
it('should log successful completion', () => {
|
||||
const context = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
parentLogger: mockLogger,
|
||||
});
|
||||
|
||||
context.logCompletion(true);
|
||||
|
||||
expect(mockLogger.info).toHaveBeenCalledWith(
|
||||
'Operation completed successfully',
|
||||
expect.objectContaining({
|
||||
executionTime: expect.any(Number),
|
||||
metadata: {},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should log failed completion with error', () => {
|
||||
const context = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
parentLogger: mockLogger,
|
||||
});
|
||||
|
||||
const error = new Error('Test error');
|
||||
context.logCompletion(false, error);
|
||||
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
'Operation failed',
|
||||
expect.objectContaining({
|
||||
executionTime: expect.any(Number),
|
||||
error: 'Test error',
|
||||
stack: expect.any(String),
|
||||
metadata: {},
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('child context', () => {
|
||||
it('should create child context', () => {
|
||||
const parent = new OperationContext({
|
||||
handlerName: 'parent',
|
||||
operationName: 'parent-op',
|
||||
parentLogger: mockLogger,
|
||||
container: mockContainer,
|
||||
traceId: 'parent-trace',
|
||||
metadata: { parentKey: 'parentValue' },
|
||||
});
|
||||
|
||||
const child = parent.createChild('child-op', { childKey: 'childValue' });
|
||||
|
||||
expect(child.traceId).toBe('parent-trace'); // Inherits trace ID
|
||||
expect(child.metadata).toEqual({
|
||||
parentKey: 'parentValue',
|
||||
childKey: 'childValue',
|
||||
});
|
||||
expect(child.logger).toBe(mockLogger); // Inherits logger
|
||||
});
|
||||
|
||||
it('should create child without additional metadata', () => {
|
||||
const parent = new OperationContext({
|
||||
handlerName: 'parent',
|
||||
operationName: 'parent-op',
|
||||
metadata: { key: 'value' },
|
||||
});
|
||||
|
||||
const child = parent.createChild('child-op');
|
||||
|
||||
expect(child.metadata).toEqual({ key: 'value' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('dispose', () => {
|
||||
it('should log completion on dispose', async () => {
|
||||
const context = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
parentLogger: mockLogger,
|
||||
});
|
||||
|
||||
await context.dispose();
|
||||
|
||||
expect(mockLogger.info).toHaveBeenCalledWith(
|
||||
'Operation completed successfully',
|
||||
expect.any(Object)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('trace ID generation', () => {
|
||||
it('should generate unique trace IDs', () => {
|
||||
const context1 = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
});
|
||||
|
||||
const context2 = new OperationContext({
|
||||
handlerName: 'test',
|
||||
operationName: 'test-op',
|
||||
});
|
||||
|
||||
expect(context1.traceId).not.toBe(context2.traceId);
|
||||
expect(context1.traceId).toMatch(/^\d+-[a-z0-9]+$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue