Skip to main content
Glama
index.spec.ts32 kB
// Mock dependencies - must be at the very top jest.mock('@midnight-ntwrk/wallet'); jest.mock('@midnight-ntwrk/ledger'); jest.mock('@midnight-ntwrk/midnight-js-network-id'); jest.mock('../../../src/logger/index'); jest.mock('../../../src/config.js'); jest.mock('../../../src/utils/seed-manager.js'); jest.mock('../../../src/wallet/wallet'); jest.mock('../../../src/mcp/index'); jest.mock('../../../src/controllers/wallet.controller'); jest.mock('../../../src/utils/file-manager'); jest.mock('../../../src/integrations/marketplace/api.js', () => require('../__mocks__/marketplace-api.ts')); import { of, Subscription } from 'rxjs'; import { describe, it, beforeAll, afterAll, beforeEach, afterEach, jest, expect } from '@jest/globals'; import { WalletController } from '../../../src/controllers/wallet.controller'; import { WalletServiceMCP, WalletServiceError, WalletServiceErrorType } from '../../../src/mcp/index'; import { NetworkId } from '@midnight-ntwrk/midnight-js-network-id'; import { WalletServiceMCP as MockWalletServiceMCP } from '../__mocks__/mcp'; // Mock console.error to capture error messages const originalConsoleError = console.error; const mockConsoleError = jest.fn(); // Create a mock instance at the top level const mockWalletService = new MockWalletServiceMCP(); let someObservable: any; let subscription: Subscription; let intervalId: NodeJS.Timeout; beforeEach(() => { intervalId = setInterval(() => { }, 1000); someObservable = of(1); subscription = someObservable.subscribe(); }); afterEach(() => { clearInterval(intervalId); subscription.unsubscribe(); }); describe('Server setup', () => { let mockController: any; beforeAll(() => { mockController = { getStatus: jest.fn((_req, res: any) => res.json({ status: 'ok' })), getAddress: jest.fn((_req, res: any) => res.json({ address: '0x123' })), getBalance: jest.fn((_req, res: any) => res.json({ balance: 100 })), sendFunds: jest.fn((_req, res: any) => res.json({ txId: 'abc123' })), verifyTransaction: jest.fn((_req, res: any) => res.json({ valid: true })), getTransactionStatus: jest.fn((_req, res: any) => res.json({ status: 'confirmed' })), getTransactions: jest.fn((_req, res: any) => res.json([])), getPendingTransactions: jest.fn((_req, res: any) => res.json([])), getWalletConfig: jest.fn((_req, res: any) => res.json({ config: true })), healthCheck: jest.fn((_req, res: any) => res.json({ health: 'ok' })) }; // Mock WalletController constructor (WalletController as jest.Mock).mockImplementation(() => mockController); }); it('WalletController can be instantiated', () => { const controller = new WalletController(mockWalletService); expect(controller).toBeDefined(); }); it('WalletServiceMCP can be instantiated', () => { const service = new WalletServiceMCP(NetworkId.TestNet, 'test-seed', 'test-wallet', { indexer: 'https://test-indexer.com', indexerWS: 'wss://test-indexer.com/ws', node: 'https://test-node.com', proofServer: 'http://test-proof.com' }); expect(service).toBeDefined(); }); it('WalletServiceMCP can be instantiated without external config', () => { const service = new WalletServiceMCP(NetworkId.TestNet, 'test-seed', 'test-wallet'); expect(service).toBeDefined(); }); it('Mock controller methods work correctly', () => { const mockReq = {}; const mockRes = { json: jest.fn() }; mockController.getStatus(mockReq, mockRes); expect(mockRes.json).toHaveBeenCalledWith({ status: 'ok' }); mockController.getAddress(mockReq, mockRes); expect(mockRes.json).toHaveBeenCalledWith({ address: '0x123' }); mockController.healthCheck(mockReq, mockRes); expect(mockRes.json).toHaveBeenCalledWith({ health: 'ok' }); }); it('Mock wallet service methods work correctly', () => { expect(mockWalletService.isReady()).toBe(true); expect(mockWalletService.getAddress()).toBe('test-address'); expect(mockWalletService.getBalance()).toEqual({ balance: '1000', pendingBalance: '0' }); }); }); describe('WalletServiceMCP Constructor', () => { it('should initialize with network ID when provided', () => { const service = new WalletServiceMCP(NetworkId.TestNet, 'test-seed', 'test-wallet'); expect(service).toBeDefined(); }); it('should initialize with default config when no external config provided', () => { const service = new WalletServiceMCP(NetworkId.TestNet, 'test-seed', 'test-wallet'); expect(service).toBeDefined(); }); it('should initialize with custom config when provided', () => { const customConfig = { indexer: 'https://custom-indexer.com', indexerWS: 'wss://custom-indexer.com/ws', node: 'https://custom-node.com', proofServer: 'http://custom-proof.com' }; const service = new WalletServiceMCP(NetworkId.TestNet, 'test-seed', 'test-wallet', customConfig); expect(service).toBeDefined(); }); }); describe('WalletServiceMCP - Wallet Ready State', () => { beforeEach(() => { // Ensure wallet starts in ready state, then set to not ready mockWalletService.setWalletReady(true); mockWalletService.setWalletReady(false); }); afterEach(() => { // Reset wallet to ready state mockWalletService.setWalletReady(true); }); it('getAddress should throw when wallet is not ready', () => { expect(() => mockWalletService.getAddress()).toThrow(WalletServiceError); }); it('getBalance should throw when wallet is not ready', () => { expect(() => mockWalletService.getBalance()).toThrow(WalletServiceError); }); it('sendFunds should throw when wallet is not ready', async () => { try { await mockWalletService.sendFunds('addr', '100'); fail('Expected sendFunds to throw WalletServiceError'); } catch (error) { expect(error).toBeInstanceOf(WalletServiceError); } }); it('sendFundsAndWait should throw when wallet is not ready', async () => { try { await mockWalletService.sendFundsAndWait('addr', '100'); fail('Expected sendFundsAndWait to throw WalletServiceError'); } catch (error) { expect(error).toBeInstanceOf(WalletServiceError); } }); it('getTransactionStatus should throw when wallet is not ready', () => { expect(() => mockWalletService.getTransactionStatus('tx-id')).toThrow(WalletServiceError); }); it('getTransactions should throw when wallet is not ready', () => { expect(() => mockWalletService.getTransactions()).toThrow(WalletServiceError); }); it('getPendingTransactions should throw when wallet is not ready', () => { expect(() => mockWalletService.getPendingTransactions()).toThrow(WalletServiceError); }); it('confirmTransactionHasBeenReceived should throw when wallet is not ready', () => { expect(() => mockWalletService.confirmTransactionHasBeenReceived('identifier')).toThrow(WalletServiceError); }); it('getWalletStatus should reflect wallet ready state', () => { const status = mockWalletService.getWalletStatus(); expect(status.ready).toBe(false); }); }); describe('WalletServiceMCP - Error Handling', () => { beforeEach(() => { // Ensure wallet is in ready state for error handling tests mockWalletService.setWalletReady(true); }); afterEach(() => { // Ensure wallet is in ready state after each test mockWalletService.setWalletReady(true); }); it('getAddress should handle wallet errors gracefully', () => { mockWalletService.getAddress.mockImplementation(() => { throw new WalletServiceError(WalletServiceErrorType.WALLET_NOT_READY, 'Error accessing wallet address'); }); expect(() => mockWalletService.getAddress()).toThrow(WalletServiceError); }); it('getBalance should handle wallet errors gracefully', () => { mockWalletService.getBalance.mockImplementation(() => { throw new WalletServiceError(WalletServiceErrorType.WALLET_NOT_READY, 'Error accessing wallet balance'); }); expect(() => mockWalletService.getBalance()).toThrow(WalletServiceError); }); it('sendFunds should handle wallet errors gracefully', async () => { mockWalletService.sendFunds.mockImplementation(async () => { throw new WalletServiceError(WalletServiceErrorType.TX_SUBMISSION_FAILED, 'Failed to submit transaction'); }); try { await mockWalletService.sendFunds('addr', '100'); fail('Expected sendFunds to throw WalletServiceError'); } catch (error) { expect(error).toBeInstanceOf(WalletServiceError); } }); it('sendFundsAndWait should handle wallet errors gracefully', async () => { mockWalletService.sendFundsAndWait.mockImplementation(async () => { throw new WalletServiceError(WalletServiceErrorType.TX_SUBMISSION_FAILED, 'Failed to submit transaction'); }); try { await mockWalletService.sendFundsAndWait('addr', '100'); fail('Expected sendFundsAndWait to throw WalletServiceError'); } catch (error) { expect(error).toBeInstanceOf(WalletServiceError); } }); it('getTransactionStatus should handle wallet errors gracefully', () => { mockWalletService.getTransactionStatus.mockImplementation(() => { throw new WalletServiceError(WalletServiceErrorType.TX_NOT_FOUND, 'Transaction not found'); }); expect(() => mockWalletService.getTransactionStatus('tx-id')).toThrow(WalletServiceError); }); it('getTransactions should handle wallet errors gracefully', () => { mockWalletService.getTransactions.mockImplementation(() => { throw new WalletServiceError(WalletServiceErrorType.WALLET_NOT_READY, 'Failed to get transactions'); }); expect(() => mockWalletService.getTransactions()).toThrow(WalletServiceError); }); it('getPendingTransactions should handle wallet errors gracefully', () => { mockWalletService.getPendingTransactions.mockImplementation(() => { throw new WalletServiceError(WalletServiceErrorType.WALLET_NOT_READY, 'Failed to get pending transactions'); }); expect(() => mockWalletService.getPendingTransactions()).toThrow(WalletServiceError); }); it('confirmTransactionHasBeenReceived should handle wallet errors gracefully', () => { mockWalletService.confirmTransactionHasBeenReceived.mockImplementation(() => { throw new WalletServiceError(WalletServiceErrorType.IDENTIFIER_VERIFICATION_FAILED, 'Failed to verify transaction'); }); expect(() => mockWalletService.confirmTransactionHasBeenReceived('identifier')).toThrow(WalletServiceError); }); it('getWalletStatus should handle wallet errors gracefully', () => { mockWalletService.getWalletStatus.mockImplementation(() => { throw new WalletServiceError(WalletServiceErrorType.WALLET_NOT_READY, 'Failed to retrieve wallet status'); }); expect(() => mockWalletService.getWalletStatus()).toThrow(WalletServiceError); }); }); describe('WalletServiceMCP - Method Coverage', () => { let freshMockWalletService: any; beforeEach(() => { // Create a fresh mock instance for each test freshMockWalletService = new MockWalletServiceMCP(); freshMockWalletService.setWalletReady(true); }); it('should call close method successfully', async () => { await expect(freshMockWalletService.close()).resolves.toBeUndefined(); expect(freshMockWalletService.close).toHaveBeenCalled(); }); it('should handle close method errors gracefully', async () => { freshMockWalletService.close.mockImplementation(async () => { throw new Error('Close error'); }); await expect(freshMockWalletService.close()).rejects.toThrow('Close error'); }); it('should get wallet status successfully', () => { const status = freshMockWalletService.getWalletStatus(); expect(status).toBeDefined(); expect(status.ready).toBe(true); expect(status.address).toBe('test-address'); }); it('should get wallet config successfully', () => { const config = freshMockWalletService.getWalletConfig(); expect(config).toBeDefined(); expect(config.indexer).toBe('https://test-indexer.com'); }); it('should confirm transaction has been received successfully', () => { const result = freshMockWalletService.confirmTransactionHasBeenReceived('test-identifier'); expect(result).toBeDefined(); expect(result.exists).toBe(true); }); it('should send funds and wait successfully', async () => { const result = await freshMockWalletService.sendFundsAndWait('addr', '100'); expect(result).toBeDefined(); expect(result.txIdentifier).toBe('test-tx'); expect(result.amount).toBe('100'); }); it('should get transaction status successfully', () => { const status = freshMockWalletService.getTransactionStatus('tx-id'); expect(status).toBeDefined(); expect(status.transaction).toBeDefined(); expect(status.blockchainStatus).toBeDefined(); }); it('should get transactions successfully', () => { const transactions = freshMockWalletService.getTransactions(); expect(Array.isArray(transactions)).toBe(true); }); it('should get pending transactions successfully', () => { const pendingTransactions = freshMockWalletService.getPendingTransactions(); expect(Array.isArray(pendingTransactions)).toBe(true); }); }); describe('WalletServiceError Class', () => { it('should create WalletServiceError instance', () => { const error = new WalletServiceError(WalletServiceErrorType.WALLET_NOT_READY, 'Test error message'); expect(error).toBeInstanceOf(WalletServiceError); }); it('should create WalletServiceError with different error types', () => { const error = new WalletServiceError(WalletServiceErrorType.INSUFFICIENT_FUNDS, 'Insufficient funds'); expect(error).toBeInstanceOf(WalletServiceError); }); }); describe('Event Parsing: hasReceivedTransactionByIdentifier', () => { it('returns exists=true when identifier is found in transaction history', () => { // Set up mock state (mockWalletService as any).walletState = { transactionHistory: [ { identifiers: ['abc123'], deltas: { native: 1000000n } } ], syncProgress: { synced: true, lag: { applyGap: 0n, sourceGap: 0n } } }; (mockWalletService as any).ready = true; (mockWalletService as any).wallet = mockWalletService; // Mock the method to check the actual identifier mockWalletService.hasReceivedTransactionByIdentifier.mockImplementation((identifier: string) => { const history = (mockWalletService as any).walletState?.transactionHistory; if (history && Array.isArray(history)) { for (const tx of history) { if (tx.identifiers && tx.identifiers.includes(identifier)) { return { exists: true, syncStatus: { syncedIndices: '10', lag: { applyGap: '0', sourceGap: '0' }, isFullySynced: true }, transactionAmount: '1' }; } } } return { exists: false, syncStatus: { syncedIndices: '10', lag: { applyGap: '0', sourceGap: '0' }, isFullySynced: true }, transactionAmount: '0' }; }); const result = mockWalletService.hasReceivedTransactionByIdentifier('abc123'); expect(result.exists).toBe(true); expect(result.transactionAmount).toBe('1'); }); it('returns exists=false when identifier is not found', () => { // Set up mock state (mockWalletService as any).walletState = { transactionHistory: [ { identifiers: ['def456'], deltas: { native: 1000000n } } ], syncProgress: { synced: true, lag: { applyGap: 0n, sourceGap: 0n } } }; (mockWalletService as any).ready = true; (mockWalletService as any).wallet = mockWalletService; // Mock the method to check the actual identifier mockWalletService.hasReceivedTransactionByIdentifier.mockImplementation((identifier: string) => { const history = (mockWalletService as any).walletState?.transactionHistory; if (history && Array.isArray(history)) { for (const tx of history) { if (tx.identifiers && tx.identifiers.includes(identifier)) { return { exists: true, syncStatus: { syncedIndices: '10', lag: { applyGap: '0', sourceGap: '0' }, isFullySynced: true }, transactionAmount: '1' }; } } } return { exists: false, syncStatus: { syncedIndices: '10', lag: { applyGap: '0', sourceGap: '0' }, isFullySynced: true }, transactionAmount: '0' }; }); const result = mockWalletService.hasReceivedTransactionByIdentifier('abc123'); expect(result.exists).toBe(false); expect(result.transactionAmount).toBe('0'); }); it('handles missing transactionHistory gracefully', () => { (mockWalletService as any).walletState = {}; (mockWalletService as any).ready = true; (mockWalletService as any).wallet = mockWalletService; // Mock the method to handle missing history mockWalletService.hasReceivedTransactionByIdentifier.mockImplementation((identifier: string) => { const history = (mockWalletService as any).walletState?.transactionHistory; if (history && Array.isArray(history)) { for (const tx of history) { if (tx.identifiers && tx.identifiers.includes(identifier)) { return { exists: true, syncStatus: { syncedIndices: '10', lag: { applyGap: '0', sourceGap: '0' }, isFullySynced: true }, transactionAmount: '1' }; } } } return { exists: false, syncStatus: { syncedIndices: '10', lag: { applyGap: '0', sourceGap: '0' }, isFullySynced: true }, transactionAmount: '0' }; }); const result = mockWalletService.hasReceivedTransactionByIdentifier('abc123'); expect(result.exists).toBe(false); expect(result.transactionAmount).toBe('0'); }); }); describe('Transaction Submission', () => { beforeEach(() => { // Redefine sendFunds to use the mocked transferTransaction, proveTransaction, and submitTransaction mockWalletService.sendFunds = jest.fn(async (to: string, amount: string) => { // Simulate the real sequence of calls const txRecipe = await mockWalletService.transferTransaction(to, amount); const provenTx = await mockWalletService.proveTransaction(txRecipe); const submittedTx = await mockWalletService.submitTransaction(provenTx); return submittedTx; }); }); it('throws if transferTransaction fails', async () => { mockWalletService.transferTransaction.mockRejectedValue(new Error('fail')); await expect(mockWalletService.sendFunds('addr2', '0.5')).rejects.toThrow('fail'); }); it('throws if proveTransaction fails', async () => { mockWalletService.transferTransaction.mockResolvedValue('tx-recipe'); mockWalletService.proveTransaction.mockRejectedValue(new Error('fail')); await expect(mockWalletService.sendFunds('addr2', '0.5')).rejects.toThrow('fail'); }); it('throws if submitTransaction fails', async () => { mockWalletService.transferTransaction.mockResolvedValue('tx-recipe'); mockWalletService.proveTransaction.mockResolvedValue('proven-tx'); mockWalletService.submitTransaction.mockRejectedValue(new Error('fail')); await expect(mockWalletService.sendFunds('addr2', '0.5')).rejects.toThrow('fail'); }); it('handles errors during transaction submission', async () => { mockWalletService.transferTransaction.mockResolvedValue('tx-recipe'); mockWalletService.proveTransaction.mockResolvedValue('proven-tx'); mockWalletService.submitTransaction.mockRejectedValue(new Error('Submission failed')); await expect(mockWalletService.sendFunds('addr2', '0.5')).rejects.toThrow('Submission failed'); }); }); describe('Transaction Status and Error Handling', () => { it('should throw an error if transaction is not found', () => { mockWalletService.getTransactionStatus.mockImplementation(() => { throw new WalletServiceError(WalletServiceErrorType.TX_NOT_FOUND, 'Transaction with ID nonexistent not found'); }); expect(() => mockWalletService.getTransactionStatus('nonexistent')).toThrow(WalletServiceError); }); it('should handle getTransactionStatus when wallet throws WalletServiceError', () => { mockWalletService.getTransactionStatus.mockImplementation(() => { throw new WalletServiceError(WalletServiceErrorType.TX_NOT_FOUND, 'Transaction not found'); }); expect(() => mockWalletService.getTransactionStatus('tx-id')).toThrow(WalletServiceError); }); it('should handle getTransactionStatus when wallet throws regular error', () => { mockWalletService.getTransactionStatus.mockImplementation(() => { throw new Error('Database connection failed'); }); expect(() => mockWalletService.getTransactionStatus('tx-id')).toThrow(Error); }); }); describe('Environment Variable Handling', () => { const originalEnv = process.env; beforeEach(() => { jest.resetModules(); process.env = { ...originalEnv }; }); afterEach(() => { process.env = originalEnv; }); it('should use default agent ID when AGENT_ID is not set', () => { delete process.env.AGENT_ID; const service = new WalletServiceMCP(NetworkId.TestNet, 'test-seed', 'test-wallet'); expect(service).toBeDefined(); }); it('should use custom agent ID when AGENT_ID is set', () => { process.env.AGENT_ID = 'custom-agent'; const service = new WalletServiceMCP(NetworkId.TestNet, 'test-seed', 'test-wallet'); expect(service).toBeDefined(); }); }); describe('Server Startup', () => { let mockServer: any; let mockCreateServer: jest.Mock; beforeEach(() => { jest.clearAllMocks(); console.error = mockConsoleError; // Create mock server mockServer = { start: jest.fn(), stop: jest.fn() }; // Create mock createServer function mockCreateServer = jest.fn().mockReturnValue(mockServer); // Mock the module before importing jest.doMock('../../../src/stdio-server.js', () => ({ createServer: mockCreateServer })); }); afterEach(() => { console.error = originalConsoleError; // Restore original process.argv process.argv = ['node', 'test.js']; jest.resetModules(); }); describe('Server Creation', () => { it('should create server successfully', async () => { const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); expect(mockCreateServer).toHaveBeenCalled(); expect(server).toBe(mockServer); }); it('should handle server creation errors', async () => { const creationError = new Error('Failed to create server'); mockCreateServer.mockImplementation(() => { throw creationError; }); const { createServer } = await import('../../../src/stdio-server.js'); expect(() => createServer()).toThrow('Failed to create server'); }); }); describe('Server Startup', () => { it('should start server successfully', async () => { mockServer.start.mockResolvedValue(undefined); const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); await server.start(); expect(mockServer.start).toHaveBeenCalled(); }); it('should handle server startup errors', async () => { const startupError = new Error('Failed to start server'); mockServer.start.mockRejectedValue(startupError); const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); await expect(server.start()).rejects.toThrow('Failed to start server'); expect(mockServer.start).toHaveBeenCalled(); }); it('should handle network connection errors', async () => { const networkError = new Error('Connection refused'); mockServer.start.mockRejectedValue(networkError); const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); await expect(server.start()).rejects.toThrow('Connection refused'); }); it('should handle timeout errors', async () => { const timeoutError = new Error('Connection timeout'); mockServer.start.mockRejectedValue(timeoutError); const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); await expect(server.start()).rejects.toThrow('Connection timeout'); }); }); describe('Server Shutdown', () => { it('should stop server successfully', async () => { mockServer.stop.mockResolvedValue(undefined); const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); await server.stop(); expect(mockServer.stop).toHaveBeenCalled(); }); it('should handle server shutdown errors gracefully', async () => { const shutdownError = new Error('Failed to stop server'); // Mock the server's stop method to simulate the actual implementation // where errors are caught and logged but not re-thrown mockServer.stop.mockImplementation(async () => { try { // Simulate the actual stop logic that might fail throw shutdownError; } catch (error) { // Simulate the error logging that happens in the real implementation console.error("Error stopping server:", error); // Don't re-throw the error } }); const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); // The stop method should catch errors and not re-throw them await expect(server.stop()).resolves.toBeUndefined(); expect(mockServer.stop).toHaveBeenCalled(); expect(mockConsoleError).toHaveBeenCalledWith("Error stopping server:", shutdownError); }); }); describe('Main Module Execution', () => { let originalProcessExit: any; let mockProcessExit: jest.Mock; beforeEach(() => { originalProcessExit = process.exit; mockProcessExit = jest.fn(); process.exit = mockProcessExit as any; }); afterEach(() => { process.exit = originalProcessExit; }); it('should auto-start server when run as main module', async () => { // Mock import.meta.url to simulate main module execution const originalImportMeta = (global as any).import; (global as any).import = { meta: { url: `file://${process.argv[1]}` } }; // Simulate successful startup mockServer.start.mockResolvedValue(undefined); // Execute the main module logic const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); await server.start(); expect(mockCreateServer).toHaveBeenCalled(); expect(mockServer.start).toHaveBeenCalled(); expect(mockProcessExit).not.toHaveBeenCalled(); // Restore original import (global as any).import = originalImportMeta; }); it('should exit with code 1 on startup failure when run as main module', async () => { // Mock import.meta.url to simulate main module execution const originalImportMeta = (global as any).import; (global as any).import = { meta: { url: `file://${process.argv[1]}` } }; // Mock the main module execution with error const startupError = new Error('Failed to start server'); mockServer.start.mockRejectedValue(startupError); // Execute the main module logic const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); try { await server.start(); } catch (error) { console.error("Failed to start server:", error); process.exit(1); } expect(mockCreateServer).toHaveBeenCalled(); expect(mockServer.start).toHaveBeenCalled(); expect(mockConsoleError).toHaveBeenCalledWith("Failed to start server:", startupError); expect(mockProcessExit).toHaveBeenCalledWith(1); // Restore original import (global as any).import = originalImportMeta; }); it('should not auto-start server when not run as main module', async () => { // Mock import.meta.url to simulate non-main module execution const originalImportMeta = (global as any).import; (global as any).import = { meta: { url: 'file://some-other-file.js' } }; // The server should not be created automatically expect(mockCreateServer).not.toHaveBeenCalled(); // Restore original import (global as any).import = originalImportMeta; }); }); describe('Error Handling', () => { it('should handle configuration errors', async () => { const configError = new Error('Invalid configuration'); mockCreateServer.mockImplementation(() => { throw configError; }); const { createServer } = await import('../../../src/stdio-server.js'); expect(() => createServer()).toThrow('Invalid configuration'); }); it('should handle seed loading errors', async () => { const seedError = new Error('Seed file not found'); mockCreateServer.mockImplementation(() => { throw seedError; }); const { createServer } = await import('../../../src/stdio-server.js'); expect(() => createServer()).toThrow('Seed file not found'); }); it('should handle wallet initialization errors', async () => { const walletError = new Error('Failed to initialize wallet'); mockCreateServer.mockImplementation(() => { throw walletError; }); const { createServer } = await import('../../../src/stdio-server.js'); expect(() => createServer()).toThrow('Failed to initialize wallet'); }); it('should handle transport connection errors', async () => { const transportError = new Error('Failed to connect transport'); mockServer.start.mockRejectedValue(transportError); const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); await expect(server.start()).rejects.toThrow('Failed to connect transport'); }); }); describe('Integration Tests', () => { it('should handle complete server lifecycle', async () => { mockServer.start.mockResolvedValue(undefined); mockServer.stop.mockResolvedValue(undefined); const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); // Start server await server.start(); expect(mockServer.start).toHaveBeenCalled(); // Stop server await server.stop(); expect(mockServer.stop).toHaveBeenCalled(); }); it('should handle server restart scenario', async () => { mockServer.start.mockResolvedValue(undefined); mockServer.stop.mockResolvedValue(undefined); const { createServer } = await import('../../../src/stdio-server.js'); const server = createServer(); // First start await server.start(); expect(mockServer.start).toHaveBeenCalledTimes(1); // Stop await server.stop(); expect(mockServer.stop).toHaveBeenCalledTimes(1); // Second start await server.start(); expect(mockServer.start).toHaveBeenCalledTimes(2); }); }); });

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