Testing Guide

Comprehensive testing strategies and validation procedures for Neo N3 MCP Server

๐Ÿงช Testing Overview

Neo N3 MCP Server maintains 90%+ test coverage through a comprehensive testing strategy that includes unit tests, integration tests, security validation, and performance testing.

๐Ÿ“Š Test Coverage Stats
  • 395 Tests: Comprehensive test suite coverage
  • 90%+ Coverage: High code coverage across all modules
  • 34 Tools Tested: Every tool has dedicated test cases
  • 9 Resources Tested: All resources validated
  • Security Tests: Extensive security validation

๐Ÿ”ง Running Tests

Development Testing

NPM Scripts
# Run all tests
npm test

# Run tests with coverage
npm run test:coverage

# Run tests in watch mode
npm run test:watch

# Run specific test suite
npm run test:unit
npm run test:integration
npm run test:security

# Run tests with detailed output
npm run test:verbose

# Run performance tests
npm run test:performance

Docker Testing

Docker Test Environment
# Build test image
docker build -t neo-n3-mcp:test --target test .

# Run tests in container
docker run --rm neo-n3-mcp:test npm test

# Run with test network
docker-compose -f docker-compose.test.yml up --abort-on-container-exit

# Interactive test environment
docker run -it --rm neo-n3-mcp:test bash

๐ŸŽฏ Unit Testing

Tool Testing Framework

Example: Wallet Tool Tests
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals'
import { WalletService } from '../src/services/wallet-service'
import { MockNeoRpcClient } from './mocks/neo-rpc-client'

describe('WalletService', () => {
  let walletService: WalletService
  let mockRpcClient: MockNeoRpcClient

  beforeEach(() => {
    mockRpcClient = new MockNeoRpcClient()
    walletService = new WalletService(mockRpcClient)
  })

  afterEach(() => {
    jest.clearAllMocks()
  })

  describe('createWallet', () => {
    it('should create a new wallet with valid password', async () => {
      const result = await walletService.createWallet('secure-password-123')
      
      expect(result).toHaveProperty('address')
      expect(result).toHaveProperty('publicKey')
      expect(result.address).toMatch(/^N[A-Za-z0-9]{33}$/)
      expect(result.encrypted).toBe(true)
    })

    it('should reject weak passwords', async () => {
      await expect(walletService.createWallet('123'))
        .rejects.toThrow('Password too weak')
    })

    it('should handle network errors gracefully', async () => {
      mockRpcClient.setNetworkError(true)
      
      await expect(walletService.createWallet('secure-password-123'))
        .rejects.toThrow('Network connection failed')
    })
  })

  describe('getBalance', () => {
    it('should return balance for valid address', async () => {
      const address = 'NZNos2WqTbu5oCgyfss9kUJgBXJqhuYAaj'
      mockRpcClient.setBalance(address, { NEO: '100', GAS: '50.5' })
      
      const balance = await walletService.getBalance(address)
      
      expect(balance.NEO).toBe('100')
      expect(balance.GAS).toBe('50.5')
    })

    it('should validate address format', async () => {
      await expect(walletService.getBalance('invalid-address'))
        .rejects.toThrow('Invalid address format')
    })
  })
})

Contract Testing

Famous Contracts Tests
describe('ContractService', () => {
  describe('Famous Contracts', () => {
    it('should list all supported contracts', async () => {
      const contracts = await contractService.listFamousContracts()
      
      expect(contracts).toHaveLength(6)
      expect(contracts.map(c => c.name)).toEqual([
        'NeoFS', 'NeoBurger', 'Flamingo', 
        'NeoCompound', 'GrandShare', 'GhostMarket'
      ])
    })

    it('should get contract information', async () => {
      const contract = await contractService.getFamousContract('NeoFS')
      
      expect(contract).toHaveProperty('hash')
      expect(contract).toHaveProperty('name', 'NeoFS')
      expect(contract).toHaveProperty('description')
      expect(contract.methods).toBeInstanceOf(Array)
    })

    it('should invoke read-only contract methods', async () => {
      const result = await contractService.invokeRead(
        'NeoFS', 
        'totalSupply', 
        []
      )
      
      expect(result).toHaveProperty('type', 'Integer')
      expect(result).toHaveProperty('value')
    })
  })
})

๐Ÿ”— Integration Testing

End-to-End MCP Protocol Testing

MCP Protocol Integration
describe('MCP Protocol Integration', () => {
  let mcpServer: MCPServer

  beforeAll(async () => {
    mcpServer = new MCPServer()
    await mcpServer.initialize()
  })

  afterAll(async () => {
    await mcpServer.shutdown()
  })

  describe('Tools', () => {
    it('should list all available tools', async () => {
      const tools = await mcpServer.listTools()
      
      expect(tools).toHaveLength(34)
      expect(tools.every(tool => tool.name && tool.description)).toBe(true)
    })

    it('should execute blockchain tools', async () => {
      const result = await mcpServer.callTool('get_blockchain_info', {})
      
      expect(result.isError).toBe(false)
      expect(result.content).toHaveProperty('network')
      expect(result.content).toHaveProperty('blockHeight')
    })

    it('should handle tool execution errors', async () => {
      const result = await mcpServer.callTool('get_balance', {
        address: 'invalid-address'
      })
      
      expect(result.isError).toBe(true)
      expect(result.content).toContain('Invalid address format')
    })
  })

  describe('Resources', () => {
    it('should list all available resources', async () => {
      const resources = await mcpServer.listResources()
      
      expect(resources).toHaveLength(9)
      expect(resources.every(resource => resource.uri && resource.name)).toBe(true)
    })

    it('should read network status resource', async () => {
      const content = await mcpServer.readResource('neo://network/status')
      
      expect(content).toHaveProperty('mimeType', 'application/json')
      expect(content.text).toBeTruthy()
      
      const data = JSON.parse(content.text)
      expect(data).toHaveProperty('network')
      expect(data).toHaveProperty('status')
    })
  })
})

Network Integration Tests

Neo N3 Network Integration
describe('Neo N3 Network Integration', () => {
  describe('Testnet Integration', () => {
    it('should connect to testnet', async () => {
      const neoService = new NeoService('testnet')
      await neoService.initialize()
      
      const info = await neoService.getBlockchainInfo()
      expect(info.network).toBe('testnet')
      expect(info.blockHeight).toBeGreaterThan(0)
    })

    it('should retrieve real block data', async () => {
      const block = await neoService.getBlock(1)
      
      expect(block).toHaveProperty('hash')
      expect(block).toHaveProperty('index', 1)
      expect(block).toHaveProperty('previousblockhash')
      expect(block.tx).toBeInstanceOf(Array)
    })

    it('should handle network timeouts', async () => {
      const neoService = new NeoService('testnet', {
        timeout: 1 // Very short timeout
      })
      
      await expect(neoService.getBlockchainInfo())
        .rejects.toThrow(/timeout/i)
    })
  })

  describe('Famous Contracts Integration', () => {
    it('should interact with real NeoFS contract', async () => {
      const result = await contractService.invokeRead(
        'NeoFS',
        'symbol',
        []
      )
      
      expect(result.value).toBe('FS')
    })

    it('should retrieve Flamingo token information', async () => {
      const result = await contractService.invokeRead(
        'Flamingo',
        'totalSupply',
        []
      )
      
      expect(parseInt(result.value)).toBeGreaterThan(0)
    })
  })
})

๐Ÿ›ก๏ธ Security Testing

Input Validation Tests

Security Validation
describe('Security Validation', () => {
  describe('Input Sanitization', () => {
    const maliciousInputs = [
      '',
      'javascript:alert("xss")',
      '../../etc/passwd',
      'DROP TABLE users;',
      '${process.env.SECRET}',
      '\x00\x01\x02',
      'A'.repeat(10000) // Large input
    ]

    maliciousInputs.forEach(input => {
      it(`should sanitize malicious input: ${input.substring(0, 20)}...`, async () => {
        await expect(mcpServer.callTool('get_balance', {
          address: input
        })).rejects.toThrow(/Invalid address format|Input validation failed/)
      })
    })
  })

  describe('Rate Limiting', () => {
    it('should enforce rate limits', async () => {
      const promises = Array(150).fill(0).map(() => 
        mcpServer.callTool('get_blockchain_info', {})
      )
      
      const results = await Promise.allSettled(promises)
      const rejected = results.filter(r => r.status === 'rejected')
      
      expect(rejected.length).toBeGreaterThan(0)
      expect(rejected.some(r => 
        r.reason.message.includes('Rate limit exceeded')
      )).toBe(true)
    })
  })

  describe('Confirmation Requirements', () => {
    it('should require confirmation for write operations', async () => {
      await expect(mcpServer.callTool('transfer_assets', {
        fromWIF: 'test-key',
        toAddress: 'test-address',
        asset: 'GAS',
        amount: '1',
        confirm: false // Missing confirmation
      })).rejects.toThrow('Confirmation required')
    })
  })

  describe('Private Key Security', () => {
    it('should never log private keys', async () => {
      const consoleSpy = jest.spyOn(console, 'log')
      const errorSpy = jest.spyOn(console, 'error')
      
      try {
        await mcpServer.callTool('import_wallet', {
          key: 'KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr',
          password: 'test'
        })
      } catch (e) {
        // Expected to fail in test environment
      }
      
      const allLogs = [
        ...consoleSpy.mock.calls.flat(),
        ...errorSpy.mock.calls.flat()
      ].join(' ')
      
      expect(allLogs).not.toContain('KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr')
      
      consoleSpy.mockRestore()
      errorSpy.mockRestore()
    })
  })
})

Network Security Tests

Network Isolation
describe('Network Security', () => {
  it('should isolate mainnet and testnet operations', async () => {
    const mainnetService = new NeoService('mainnet')
    const testnetService = new NeoService('testnet')
    
    const mainnetInfo = await mainnetService.getBlockchainInfo()
    const testnetInfo = await testnetService.getBlockchainInfo()
    
    expect(mainnetInfo.network).toBe('mainnet')
    expect(testnetInfo.network).toBe('testnet')
    expect(mainnetInfo.magic).not.toBe(testnetInfo.magic)
  })

  it('should validate RPC endpoint certificates', async () => {
    const neoService = new NeoService('mainnet', {
      rpcUrl: 'https://invalid-certificate.example.com'
    })
    
    await expect(neoService.getBlockchainInfo())
      .rejects.toThrow(/certificate|SSL|TLS/)
  })
})

โšก Performance Testing

Load Testing

Performance Benchmarks
describe('Performance Tests', () => {
  describe('Throughput', () => {
    it('should handle 100 concurrent requests', async () => {
      const startTime = Date.now()
      
      const promises = Array(100).fill(0).map((_, i) => 
        mcpServer.callTool('get_blockchain_info', {})
      )
      
      const results = await Promise.all(promises)
      const endTime = Date.now()
      
      const duration = endTime - startTime
      const requestsPerSecond = 100 / (duration / 1000)
      
      expect(results.every(r => !r.isError)).toBe(true)
      expect(requestsPerSecond).toBeGreaterThan(10) // At least 10 RPS
      expect(duration).toBeLessThan(30000) // Under 30 seconds
    })
  })

  describe('Response Times', () => {
    const tools = [
      'get_blockchain_info',
      'get_network_mode',
      'list_famous_contracts'
    ]

    tools.forEach(toolName => {
      it(`should respond to ${toolName} within 2 seconds`, async () => {
        const startTime = Date.now()
        
        const result = await mcpServer.callTool(toolName, {})
        
        const duration = Date.now() - startTime
        
        expect(result.isError).toBe(false)
        expect(duration).toBeLessThan(2000) // Under 2 seconds
      })
    })
  })

  describe('Memory Usage', () => {
    it('should not leak memory under load', async () => {
      const initialMemory = process.memoryUsage().heapUsed
      
      // Perform many operations
      for (let i = 0; i < 1000; i++) {
        await mcpServer.callTool('get_blockchain_info', {})
        
        if (i % 100 === 0) {
          global.gc && global.gc() // Force garbage collection if available
        }
      }
      
      const finalMemory = process.memoryUsage().heapUsed
      const memoryIncrease = finalMemory - initialMemory
      
      // Memory should not increase by more than 50MB
      expect(memoryIncrease).toBeLessThan(50 * 1024 * 1024)
    })
  })
})

Cache Performance

Cache Efficiency Tests
describe('Cache Performance', () => {
  it('should improve response times with caching', async () => {
    // Clear cache
    await cacheService.clear()
    
    // First request (cache miss)
    const start1 = Date.now()
    await mcpServer.callTool('get_blockchain_info', {})
    const time1 = Date.now() - start1
    
    // Second request (cache hit)
    const start2 = Date.now()
    await mcpServer.callTool('get_blockchain_info', {})
    const time2 = Date.now() - start2
    
    // Cache hit should be significantly faster
    expect(time2).toBeLessThan(time1 * 0.5)
  })

  it('should maintain high cache hit rate', async () => {
    const operations = Array(100).fill(0).map(() => 
      mcpServer.callTool('get_blockchain_info', {})
    )
    
    await Promise.all(operations)
    
    const cacheStats = await cacheService.getStats()
    expect(cacheStats.hitRate).toBeGreaterThan(0.8) // 80% hit rate
  })
})

๐Ÿค– Automated Testing

CI/CD Testing Pipeline

GitHub Actions Test Workflow
name: Test Suite

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20]
    
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    
    - run: npm ci
    - run: npm run test:unit
    - run: npm run test:coverage
    
    - uses: codecov/codecov-action@v3
      with:
        file: ./coverage/lcov.info

  integration-tests:
    runs-on: ubuntu-latest
    services:
      redis:
        image: redis
        ports:
          - 6379:6379
    
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: 18
        cache: 'npm'
    
    - run: npm ci
    - run: npm run test:integration
      env:
        NEO_NETWORK: testnet
        REDIS_URL: redis://localhost:6379

  security-tests:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: 18
        cache: 'npm'
    
    - run: npm ci
    - run: npm run test:security
    - run: npm audit --audit-level moderate

  performance-tests:
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: 18
        cache: 'npm'
    
    - run: npm ci
    - run: npm run test:performance
    
    - name: Performance regression check
      run: |
        node scripts/check-performance-regression.js

Quality Gates

โœ… Quality Requirements
  • Test Coverage: Minimum 90% line coverage
  • Security Tests: All security tests must pass
  • Performance: No regression in key metrics
  • Integration: All Neo N3 integrations working
  • Code Quality: ESLint and TypeScript checks

๐Ÿ“‹ Testing Checklist

Pre-Release Testing

๐Ÿ” Manual Testing Checklist
  • โœ… All 34 tools execute successfully
  • โœ… All 9 resources return valid data
  • โœ… Famous contracts integration working
  • โœ… Wallet operations secure and functional
  • โœ… Network switching (mainnet/testnet) works
  • โœ… Error handling and validation proper
  • โœ… Performance within acceptable ranges
  • โœ… Security measures functioning
  • โœ… Documentation examples verified
  • โœ… Docker deployment tested

Test Data Management

Test Environment Setup
# Setup test environment
npm run test:setup

# Generate test wallets
npm run test:generate-wallets

# Setup test contracts
npm run test:deploy-contracts

# Cleanup test data
npm run test:cleanup

# Reset test environment
npm run test:reset

Next Steps