Skip to main content
Glama
transaction-trace-logger.spec.ts17.1 kB
import { AuditEventType, AuditSeverity } from '../../../src/audit/types'; import { TransactionTraceLogger } from '../../../src/audit/transaction-trace-logger'; describe('TransactionTraceLogger exportTraces filters', () => { let logger: TransactionTraceLogger; beforeEach(() => { logger = new TransactionTraceLogger(); // @ts-ignore logger.logger = { error: jest.fn(), info: jest.fn(), debug: jest.fn() }; // @ts-ignore logger.auditService = { getAllEvents: jest.fn(() => []), logEvent: jest.fn(() => 'event-id'), generateCorrelationId: jest.fn(() => 'corr-id') }; }); it('should filter by status', async () => { jest.spyOn(logger, 'getTraces').mockReturnValue([ { status: 'completed', startTime: 1, endTime: 2, transactionId: 'id' } ]); const result = await logger.exportTraces({ status: 'failed' }); expect(result).toEqual([]); }); it('should filter by startTime', async () => { jest.spyOn(logger, 'getTraces').mockReturnValue([ { status: 'completed', startTime: 1, endTime: 2, transactionId: 'id' } ]); const result = await logger.exportTraces({ startTime: 10 }); expect(result).toEqual([]); }); it('should filter by endTime', async () => { jest.spyOn(logger, 'getTraces').mockReturnValue([ { status: 'completed', startTime: 1, endTime: 20, transactionId: 'id' } ]); const result = await logger.exportTraces({ endTime: 10 }); expect(result).toEqual([]); }); it('should filter by transactionId', async () => { jest.spyOn(logger, 'getTraces').mockReturnValue([ { status: 'completed', startTime: 1, endTime: 2, transactionId: 'id1' } ]); const result = await logger.exportTraces({ transactionId: 'id2' }); expect(result).toEqual([]); }); it('should call generateCorrelationId if correlationId is not provided', () => { const mockGenerateCorrelationId = jest.fn(() => 'mock-corr-id'); // @ts-ignore logger.auditService = { generateCorrelationId: mockGenerateCorrelationId, logEvent: jest.fn(() => 'event-id') }; // Mock logger to avoid errors // @ts-ignore logger.logger = { info: jest.fn() }; const transactionId = 'tx-1'; logger.startTrace(transactionId); // No pass correlationId expect(mockGenerateCorrelationId).toHaveBeenCalled(); }); it('should throw if transaction trace does not exist in addStep', () => { // No add any trace to activeTraces, so the get will return undefined expect(() => { logger.addStep('nonexistent-tx', 'step', 'component', {}); }).toThrow('Transaction trace nonexistent-tx not found'); }); it('should throw if transaction trace does not exist in completeStep', () => { expect(() => { logger.completeStep('nonexistent-tx', 'step1'); }).toThrow('Transaction trace nonexistent-tx not found'); }); it('should throw if step does not exist in completeStep', () => { // Prepare a valid trace but without steps // @ts-ignore logger.activeTraces.set('tx1', { steps: [] }); expect(() => { logger.completeStep('tx1', 'missing-step'); }).toThrow('Step missing-step not found in transaction tx1'); }); it('should complete step as completed when no error', () => { // Prepare a valid trace with a valid step const step = { stepId: 'step1', startTime: 1, status: 'started' }; // @ts-ignore logger.activeTraces.set('tx2', { steps: [step], correlationId: 'corr', }); // Mock logger and auditService // @ts-ignore logger.logger = { debug: jest.fn() }; // @ts-ignore logger.auditService = { logEvent: jest.fn() }; logger.completeStep('tx2', 'step1', 'output'); expect(step.status).toBe('completed'); }); it('should complete step as failed when error is provided', () => { const step = { stepId: 'step2', startTime: 1, status: 'started' }; // @ts-ignore logger.activeTraces.set('tx3', { steps: [step], correlationId: 'corr', }); // @ts-ignore logger.logger = { debug: jest.fn() }; // @ts-ignore logger.auditService = { logEvent: jest.fn() }; const error = new Error('fail'); logger.completeStep('tx3', 'step2', 'output', error); expect(step.status).toBe('failed'); }); it('should throw if transaction trace does not exist in completeTrace', () => { expect(() => { logger.completeTrace('nonexistent-tx', 'completed', 'summary'); }).toThrow('Transaction trace nonexistent-tx not found'); }); it('should return a transaction trace if it exists', () => { // @ts-ignore logger.activeTraces.set('tx1', { id: 'tx1' }); expect(logger.getTransactionTrace('tx1')).toBeDefined(); }); it('should return undefined if transaction trace does not exist', () => { expect(logger.getTransactionTrace('nope')).toBeUndefined(); }); it('should filter transaction events by correlationId and type', () => { // @ts-ignore logger.auditService = { getAllEvents: jest.fn(() => [ { id: '1', context: { correlationId: 'corr1', timestamp: 0, source: 'source' }, type: AuditEventType.TRANSACTION_INITIATED, severity: AuditSeverity.LOW, message: '', createdAt: 0 }, { id: '2', context: { correlationId: 'corr2', timestamp: 0, source: 'source' }, type: AuditEventType.TRANSACTION_COMPLETED, severity: AuditSeverity.LOW, message: '', createdAt: 0 }, ]) }; const events = logger.getTransactionEvents('corr1'); expect(events.length).toBe(1); expect(events[0].type).toBe(AuditEventType.TRANSACTION_INITIATED); }); it('should return null if transaction trace does not exist in getTransactionSummary', () => { expect(logger.getTransactionSummary('nope')).toBeNull(); }); it('should return a summary for an existing transaction trace', () => { const trace = { correlationId: 'corr1', status: 'completed', startTime: 1, endTime: 2, steps: [ { status: 'completed' }, { status: 'failed' } ], metadata: {} }; // @ts-ignore logger.activeTraces.set('tx2', trace); // Mock getTransactionEvents jest.spyOn(logger, 'getTransactionEvents').mockReturnValue([{ type: 'TRANSACTION_INITIATED' }]); const summary = logger.getTransactionSummary('tx2'); expect(summary.completedSteps).toBe(1); expect(summary.failedSteps).toBe(1); expect(summary.totalEvents).toBe(1); expect(summary.transactionId).toBe('tx2'); }); it('should return all active traces', () => { // @ts-ignore logger.activeTraces.set('tx1', {}); expect(logger.getActiveTraces().length).toBeGreaterThan(0); }); it('getTransactionEvents should handle events with missing context or type', () => { // @ts-ignore logger.auditService.getAllEvents = jest.fn(() => [ { context: undefined, type: 'transaction_initiated' }, { context: { correlationId: 'corr1' }, type: undefined }, { context: { correlationId: 'corr1' }, type: 'not_transaction' } ]); expect(logger.getTransactionEvents('corr1')).toEqual([]); }); it('getTransactionSummary should return totalDuration as undefined if endTime is missing', () => { // @ts-ignore logger.activeTraces.set('tx1', { correlationId: 'corr1', status: 'completed', startTime: 1, steps: [], metadata: {} }); jest.spyOn(logger, 'getTransactionEvents').mockReturnValue([]); const summary = logger.getTransactionSummary('tx1'); expect(summary.totalDuration).toBeUndefined(); }); it('getTransactionSummary should handle empty steps and events', () => { // @ts-ignore logger.activeTraces.set('tx1', { correlationId: 'corr1', status: 'completed', startTime: 1, endTime: 2, steps: [], metadata: {} }); jest.spyOn(logger, 'getTransactionEvents').mockReturnValue([]); const summary = logger.getTransactionSummary('tx1'); expect(summary.completedSteps).toBe(0); expect(summary.failedSteps).toBe(0); expect(summary.totalEvents).toBe(0); }); it('logTransactionFailure should call logger and auditService', () => { const error = new Error('fail'); // @ts-ignore const spy = jest.spyOn(logger.logger, 'error'); const eventId = logger.logTransactionFailure('tx1', error, { foo: 'bar' }, 'corr1'); expect(eventId).toBe('event-id'); // @ts-ignore expect(logger.auditService.logEvent).toHaveBeenCalled(); expect(spy).toHaveBeenCalled(); }); it('getTraces should skip events with missing transactionId', () => { // @ts-ignore logger.auditService.getAllEvents = jest.fn(() => [ { type: 'transaction_completed', context: {}, data: {} } ]); expect(logger.getTraces()).toEqual([]); }); it('getTraces should skip events with wrong type', () => { // @ts-ignore logger.auditService.getAllEvents = jest.fn(() => [ { type: 'not_transaction', context: { transactionId: 'id' }, data: {} } ]); expect(logger.getTraces()).toEqual([]); }); it('getTransactionEvents should skip events where type is not a string', () => { // @ts-ignore logger.auditService.getAllEvents = jest.fn(() => [ { context: { correlationId: 'corr1' }, type: undefined }, { context: { correlationId: 'corr1' }, type: null }, { context: { correlationId: 'corr1' }, type: 123 }, { context: { correlationId: 'corr1' }, type: {} }, { context: { correlationId: 'corr1' }, type: ['transaction_initiated'] } ]); expect(logger.getTransactionEvents('corr1')).toEqual([]); }); it('completeTrace should use HIGH severity for failed status', () => { // @ts-ignore logger.activeTraces.set('tx-failed', { correlationId: 'corr', status: 'processing', startTime: 1, steps: [], metadata: {} }); // @ts-ignore logger.logger = { info: jest.fn() }; // @ts-ignore logger.auditService = { logEvent: jest.fn(() => 'event-id') }; logger.completeTrace('tx-failed', 'failed', 'summary'); // @ts-ignore expect(logger.auditService.logEvent).toHaveBeenCalledWith( expect.anything(), expect.anything(), 'high', // AuditSeverity.HIGH expect.anything(), expect.anything() ); }); it('completeTrace should use MEDIUM severity for cancelled status', () => { // @ts-ignore logger.activeTraces.set('tx-cancelled', { correlationId: 'corr', status: 'processing', startTime: 1, steps: [], metadata: {} }); // @ts-ignore logger.logger = { info: jest.fn() }; // @ts-ignore logger.auditService = { logEvent: jest.fn(() => 'event-id') }; logger.completeTrace('tx-cancelled', 'cancelled', 'summary'); // @ts-ignore expect(logger.auditService.logEvent).toHaveBeenCalledWith( expect.anything(), expect.anything(), 'medium', // AuditSeverity.MEDIUM expect.anything(), expect.anything() ); }); it('completeTrace should use LOW severity for completed status', () => { // @ts-ignore logger.activeTraces.set('tx-completed', { correlationId: 'corr', status: 'processing', startTime: 1, steps: [], metadata: {} }); // @ts-ignore logger.logger = { info: jest.fn() }; // @ts-ignore logger.auditService = { logEvent: jest.fn(() => 'event-id') }; logger.completeTrace('tx-completed', 'completed', 'summary'); // @ts-ignore expect(logger.auditService.logEvent).toHaveBeenCalledWith( expect.anything(), expect.anything(), 'low', // AuditSeverity.LOW expect.anything(), expect.anything() ); }); it('completeTrace should use LOW severity for unknown status', () => { // @ts-ignore logger.activeTraces.set('tx-unknown', { correlationId: 'corr', status: 'processing', startTime: 1, steps: [], metadata: {} }); // @ts-ignore logger.logger = { info: jest.fn() }; // @ts-ignore logger.auditService = { logEvent: jest.fn(() => 'event-id') }; logger.completeTrace('tx-unknown', 'unknown' as any, 'summary'); // @ts-ignore expect(logger.auditService.logEvent).toHaveBeenCalledWith( expect.anything(), expect.anything(), 'low', // AuditSeverity.LOW expect.anything(), expect.anything() ); }); it('completeTrace should use TRANSACTION_COMPLETED event type for completed status', () => { // @ts-ignore logger.activeTraces.set('tx-completed', { correlationId: 'corr', status: 'processing', startTime: 1, steps: [], metadata: {} }); // @ts-ignore logger.logger = { info: jest.fn() }; // @ts-ignore logger.auditService = { logEvent: jest.fn(() => 'event-id') }; logger.completeTrace('tx-completed', 'completed', 'summary'); // @ts-ignore expect(logger.auditService.logEvent).toHaveBeenCalledWith( 'transaction_completed', // AuditEventType.TRANSACTION_COMPLETED expect.anything(), expect.anything(), expect.anything(), expect.anything() ); }); it('completeTrace should use TRANSACTION_FAILED event type for failed status', () => { // @ts-ignore logger.activeTraces.set('tx-failed', { correlationId: 'corr', status: 'processing', startTime: 1, steps: [], metadata: {} }); // @ts-ignore logger.logger = { info: jest.fn() }; // @ts-ignore logger.auditService = { logEvent: jest.fn(() => 'event-id') }; logger.completeTrace('tx-failed', 'failed', 'summary'); // @ts-ignore expect(logger.auditService.logEvent).toHaveBeenCalledWith( 'transaction_failed', // AuditEventType.TRANSACTION_FAILED expect.anything(), expect.anything(), expect.anything(), expect.anything() ); }); it('completeTrace should use TRANSACTION_FAILED event type for cancelled status', () => { // @ts-ignore logger.activeTraces.set('tx-cancelled', { correlationId: 'corr', status: 'processing', startTime: 1, steps: [], metadata: {} }); // @ts-ignore logger.logger = { info: jest.fn() }; // @ts-ignore logger.auditService = { logEvent: jest.fn(() => 'event-id') }; logger.completeTrace('tx-cancelled', 'cancelled', 'summary'); // @ts-ignore expect(logger.auditService.logEvent).toHaveBeenCalledWith( 'transaction_failed', // AuditEventType.TRANSACTION_FAILED expect.anything(), expect.anything(), expect.anything(), expect.anything() ); }); it('completeTrace should use TRANSACTION_TRACE event type for unknown status', () => { // @ts-ignore logger.activeTraces.set('tx-unknown', { correlationId: 'corr', status: 'processing', startTime: 1, steps: [], metadata: {} }); // @ts-ignore logger.logger = { info: jest.fn() }; // @ts-ignore logger.auditService = { logEvent: jest.fn(() => 'event-id') }; logger.completeTrace('tx-unknown', 'unknown' as any, 'summary'); // @ts-ignore expect(logger.auditService.logEvent).toHaveBeenCalledWith( 'transaction_trace', // AuditEventType.TRANSACTION_TRACE expect.anything(), expect.anything(), expect.anything(), expect.anything() ); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/evilpixi/pixi-midnight-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server