UNPKG

meld

Version:

Meld: A template language for LLM prompts

194 lines (178 loc) 4.82 kB
/** * Basic Error Test Utilities (Phase 1) * * Simple utilities for testing error handling in the Meld language interpreter. * This is a minimal implementation for the initial release. */ import { MeldError } from '@core/errors/MeldError.js'; import { ErrorSeverity } from '@core/errors/ErrorSeverity.js'; /** * Options for creating a test environment */ export interface ErrorTestOptions { strict?: boolean; logger?: any; } /** * Create options for strict mode testing */ export function createStrictModeOptions(options: Partial<ErrorTestOptions> = {}): ErrorTestOptions { return { strict: true, ...options }; } /** * Create options for permissive mode testing */ export function createPermissiveModeOptions(options: Partial<ErrorTestOptions> = {}): ErrorTestOptions { return { strict: false, ...options }; } /** * Verify that an error has the expected severity */ export function expectErrorSeverity(error: MeldError, expectedSeverity: ErrorSeverity): void { if (error.severity !== expectedSeverity) { throw new Error( `Error severity mismatch. Expected: ${ErrorSeverity[expectedSeverity]}, ` + `Actual: ${ErrorSeverity[error.severity]}` ); } } /** * Verify that a function throws an error with the expected type and severity */ export async function expectThrowsWithSeverity<T extends Error>( fn: () => Promise<any> | any, errorType: new (...args: any[]) => T, expectedSeverity: ErrorSeverity ): Promise<void> { try { await fn(); throw new Error(`Expected function to throw ${errorType.name} but it did not throw`); } catch (error) { if (!(error instanceof errorType)) { throw new Error( `Expected error to be instance of ${errorType.name} but got ${error.constructor.name}` ); } if (error instanceof MeldError) { expectErrorSeverity(error, expectedSeverity); } else { throw new Error( `Expected error to be instance of MeldError but got ${error.constructor.name}` ); } } } /** * Create a mock logger for testing */ export function createMockLogger() { return { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn(), reset() { this.error.mockReset(); this.warn.mockReset(); this.info.mockReset(); this.debug.mockReset(); } }; } /** * Mock console for CLI testing */ export function mockConsole() { const originalConsole = { log: console.log, error: console.error, warn: console.warn, info: console.info }; const mockFns = { log: jest.fn(), error: jest.fn(), warn: jest.fn(), info: jest.fn() }; // Replace console methods with mocks console.log = mockFns.log; console.error = mockFns.error; console.warn = mockFns.warn; console.info = mockFns.info; // Return mock functions and a restore function return { ...mockFns, restore() { console.log = originalConsole.log; console.error = originalConsole.error; console.warn = originalConsole.warn; console.info = originalConsole.info; } }; } /** * Mock process.exit for CLI testing */ export function mockProcessExit() { const originalExit = process.exit; const mockExit = jest.fn(); // @ts-ignore - We're intentionally mocking this process.exit = mockExit; // Return the mock function and a restore function return Object.assign(mockExit, { restore() { process.exit = originalExit; } }); } /** * Example usage: * * ```typescript * // Test a fatal error * it('should throw fatal error for invalid syntax', async () => { * await expectThrowsWithSeverity( * () => service.process('@invalid', createStrictModeOptions()), * MeldSyntaxError, * ErrorSeverity.Fatal * ); * }); * * // Test a recoverable error * it('should handle recoverable errors differently in strict and permissive modes', async () => { * // Strict mode * await expect( * service.process('@text x = #{undefined}', createStrictModeOptions()) * ).rejects.toThrow(MeldResolutionError); * * // Permissive mode * const result = await service.process('@text x = #{undefined}', createPermissiveModeOptions()); * expect(result).toBeDefined(); * }); * * // Test CLI error handling * it('should display error message and exit with code 1', async () => { * const consoleMock = mockConsole(); * const exitMock = mockProcessExit(); * * try { * await cli.run(['--strict', 'non-existent.meld']); * * expect(consoleMock.error).toHaveBeenCalledWith( * expect.stringContaining('File not found') * ); * expect(exitMock).toHaveBeenCalledWith(1); * } finally { * consoleMock.restore(); * exitMock.restore(); * } * }); * ``` */