UNPKG

@ordojs/core

Version:

Core compiler and runtime for OrdoJS framework

290 lines 9.17 kB
/** * @fileoverview Server Function Mocking Utilities */ /** * Utilities for mocking server functions in tests */ export class ServerMockUtils { mocks = new Map(); originalFetch; rpcCallHistory = []; constructor() { this.setupFetchMock(); } /** * Mock a server function */ mockServerFunction(functionName, implementation, config = {}) { // Ensure function name is in config const configWithName = { ...config, functionName }; const mockFn = this.createMockFunction(implementation, configWithName); const mockServerFunction = { name: functionName, mock: mockFn, originalImplementation: implementation, callHistory: [] }; this.mocks.set(functionName, mockServerFunction); return mockServerFunction; } /** * Create a mock function with configuration */ createMockFunction(implementation, config) { let callCount = 0; const functionName = config.functionName || 'unknown'; return async (...args) => { callCount++; const timestamp = Date.now(); // Check call count limit if (config.callCount && callCount > config.callCount) { throw new Error(`Mock function called more than ${config.callCount} times`); } // Add delay if specified if (config.delay) { await new Promise(resolve => setTimeout(resolve, config.delay)); } // Simulate failure if configured if (config.shouldFail) { const error = new Error(config.failureMessage || 'Mock server function failed'); this.recordCall(functionName, args, timestamp, undefined, error); throw error; } try { const result = await implementation(...args); this.recordCall(functionName, args, timestamp, result); return result; } catch (error) { this.recordCall(functionName, args, timestamp, undefined, error); throw error; } }; } /** * Record RPC call for testing */ recordCall(functionName, args, timestamp, result, error) { this.rpcCallHistory.push({ functionName, args, timestamp, result, error }); // Also record in the specific mock's history const mock = this.mocks.get(functionName); if (mock) { mock.callHistory.push({ args, timestamp, result, error }); } } /** * Set up fetch mock to intercept RPC calls */ setupFetchMock() { if (typeof global !== 'undefined' && global.fetch) { this.originalFetch = global.fetch; } const mockFetch = async (url, options) => { const urlString = url.toString(); // Check if this is an OrdoJS RPC call const rpcMatch = urlString.match(/\/ordojs-rpc\/(.+)$/); if (rpcMatch && options?.method === 'POST') { const functionName = rpcMatch[1]; const mock = this.mocks.get(functionName); if (mock) { try { const body = options.body ? JSON.parse(options.body) : []; const result = await mock.mock(...body); return new Response(JSON.stringify(result), { status: 200, headers: { 'Content-Type': 'application/json' } }); } catch (error) { return new Response(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } } } // Fall back to original fetch if not an RPC call or no mock found if (this.originalFetch) { return this.originalFetch(url, options); } throw new Error('Fetch not available and no mock found'); }; global.fetch = mockFetch; } /** * Get mock for a specific function */ getMock(functionName) { return this.mocks.get(functionName); } /** * Check if a function was called */ wasCalled(functionName) { const mock = this.mocks.get(functionName); return mock ? mock.callHistory.length > 0 : false; } /** * Get call count for a function */ getCallCount(functionName) { const mock = this.mocks.get(functionName); return mock ? mock.callHistory.length : 0; } /** * Get call arguments for a specific call */ getCallArgs(functionName, callIndex = 0) { const mock = this.mocks.get(functionName); return mock?.callHistory[callIndex]?.args; } /** * Get last call arguments */ getLastCallArgs(functionName) { const mock = this.mocks.get(functionName); if (!mock || mock.callHistory.length === 0) return undefined; return mock.callHistory[mock.callHistory.length - 1].args; } /** * Get all call history for a function */ getCallHistory(functionName) { const mock = this.mocks.get(functionName); return mock ? [...mock.callHistory] : []; } /** * Get complete RPC call history */ getRPCCallHistory() { return [...this.rpcCallHistory]; } /** * Clear mock for a specific function */ clearMock(functionName) { const mock = this.mocks.get(functionName); if (mock) { mock.callHistory = []; } } /** * Clear all mocks */ clearAllMocks() { for (const mock of this.mocks.values()) { mock.callHistory = []; } this.rpcCallHistory = []; } /** * Remove mock for a specific function */ removeMock(functionName) { this.mocks.delete(functionName); } /** * Remove all mocks */ removeAllMocks() { this.mocks.clear(); this.rpcCallHistory = []; } /** * Restore original fetch */ restoreFetch() { if (this.originalFetch) { global.fetch = this.originalFetch; this.originalFetch = undefined; } } /** * Create a mock server function with specific behavior */ createMockWithBehavior(config) { return this.mockServerFunction(config.functionName, config.implementation, config); } /** * Verify function was called with specific arguments */ verifyCalledWith(functionName, expectedArgs) { const mock = this.mocks.get(functionName); if (!mock) return false; return mock.callHistory.some(call => this.deepEqual(call.args, expectedArgs)); } /** * Verify function was called exactly n times */ verifyCallCount(functionName, expectedCount) { return this.getCallCount(functionName) === expectedCount; } /** * Verify function was never called */ verifyNeverCalled(functionName) { return this.getCallCount(functionName) === 0; } /** * Deep equality check for arguments */ deepEqual(a, b) { if (a === b) return true; if (a == null || b == null) return a === b; if (typeof a !== typeof b) return false; if (typeof a === 'object') { if (Array.isArray(a) !== Array.isArray(b)) return false; if (Array.isArray(a)) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (!this.deepEqual(a[i], b[i])) return false; } return true; } const keysA = Object.keys(a); const keysB = Object.keys(b); if (keysA.length !== keysB.length) return false; for (const key of keysA) { if (!keysB.includes(key)) return false; if (!this.deepEqual(a[key], b[key])) return false; } return true; } return false; } /** * Create a spy that tracks calls without changing behavior */ spyOnServerFunction(functionName, originalImplementation) { return this.mockServerFunction(functionName, originalImplementation); } /** * Mock multiple server functions at once */ mockMultipleServerFunctions(configs) { return configs.map(config => this.createMockWithBehavior(config)); } } //# sourceMappingURL=server-mock.js.map