Testing

Overview

SupaScan includes comprehensive testing strategies for blockchain indexing, ClickHouse database operations, API endpoints, and real-time data processing. Tests ensure data accuracy, system reliability, and performance under high load.

Test Structure

tests/
├── unit/                 # Unit tests for core components
│   ├── indexing/        # Block parsing, transaction processing
│   ├── database/        # ClickHouse operations, queries
│   └── api/            # API endpoints, validation
├── integration/         # End-to-end workflows
│   ├── indexing-flow/   # Full indexing pipeline
│   └── api-integration/ # API + database integration
└── e2e/                # Complete system tests
    ├── webhook-delivery/ # Notification testing
    └── performance/     # Load and stress tests

Running Tests

# All tests
npm test

# Unit tests only
npm run test:unit

# Integration tests
npm run test:integration

# Performance tests
npm run test:performance

# Coverage report
npm run test:coverage

Core Test Categories

1. Blockchain Indexing Tests

describe('Solana Indexing', () => {
  it('should parse transaction correctly', async () => {
    const mockTx = {
      signature: '5J7X8C9D...',
      slot: 123456789,
      blockTime: 1640995200,
      transaction: { /* Solana transaction data */ }
    };
    
    const result = await parseTransaction(mockTx);
    
    expect(result.signature).toBe('5J7X8C9D...');
    expect(result.tokenTransfers).toHaveLength(2);
    expect(result.dexSwaps).toBeDefined();
  });
  
  it('should detect DEX swaps accurately', async () => {
    const swapData = await detectDEXSwap(mockSwapTransaction);
    
    expect(swapData.protocol).toBe('raydium');
    expect(swapData.tokenIn).toBe('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
    expect(swapData.amountIn).toBeGreaterThan(0);
  });
});

2. ClickHouse Database Tests

describe('ClickHouse Operations', () => {
  it('should insert transaction data efficiently', async () => {
    const txData = generateMockTransaction();
    
    await insertTransaction(txData);
    
    const result = await queryClickHouse(`
      SELECT * FROM transactions 
      WHERE signature = '${txData.signature}'
    `);
    
    expect(result.rows).toHaveLength(1);
    expect(result.rows[0].success).toBe(1);
  });
  
  it('should handle large batch inserts', async () => {
    const batch = generateTransactionBatch(10000);
    
    const startTime = Date.now();
    await batchInsertTransactions(batch);
    const duration = Date.now() - startTime;
    
    expect(duration).toBeLessThan(5000); // < 5 seconds
  });
});

3. API Endpoint Tests

describe('API Endpoints', () => {
  it('should return transaction data via REST API', async () => {
    const response = await request(app)
      .get('/api/v1/transactions/5J7X8C9D...')
      .set('Authorization', `Bearer ${validApiKey}`)
      .expect(200);
    
    expect(response.body.signature).toBe('5J7X8C9D...');
    expect(response.body.tokenTransfers).toBeDefined();
  });
  
  it('should execute SQL queries via API', async () => {
    const response = await request(app)
      .post('/api/v1/sql')
      .set('Authorization', `Bearer ${validApiKey}`)
      .send({
        query: 'SELECT token_mint, SUM(volume_usd) FROM token_swaps WHERE date = today() GROUP BY token_mint ORDER BY SUM(volume_usd) DESC LIMIT 10'
      })
      .expect(200);
    
    expect(response.body.data).toHaveLength(10);
    expect(response.body.data[0].volume_usd).toBeGreaterThan(0);
  });
});

4. Webhook & Notification Tests

describe('Webhook Delivery', () => {
  it('should deliver token creation alerts', async () => {
    const webhook = {
      url: 'https://test-webhook.com/alerts',
      filters: { minLiquidity: 10000 }
    };
    
    await createWebhook(webhook);
    
    // Trigger token creation
    const newToken = await createTestToken({ liquidity: 15000 });
    
    // Verify webhook was called
    expect(mockWebhookServer.receivedCalls).toHaveLength(1);
    expect(mockWebhookServer.lastCall.body.token.mint).toBe(newToken.mint);
  });
});

Test Configuration

Jest Setup

// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  testTimeout: 30000, // 30s for blockchain tests
  setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
  collectCoverageFrom: [
    'src/**/*.ts',
    '!src/**/*.d.ts'
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    }
  }
};

Test Environment Setup

// tests/setup.ts
import { mockSolanaRPC } from './mocks/solana';
import { mockClickHouse } from './mocks/clickhouse';

// Mock external services
jest.mock('../src/services/solana', () => mockSolanaRPC);
jest.mock('../src/services/clickhouse', () => mockClickHouse);

// Test data setup
beforeEach(async () => {
  await seedTestData();
});

afterEach(async () => {
  await cleanupTestData();
});

Performance Testing

Load Testing

describe('Performance Tests', () => {
  it('should handle 1000 concurrent API requests', async () => {
    const requests = Array(1000).fill(null).map(() => 
      request(app)
        .get('/api/v1/tokens/top')
        .set('Authorization', `Bearer ${validApiKey}`)
    );
    
    const startTime = Date.now();
    const responses = await Promise.all(requests);
    const duration = Date.now() - startTime;
    
    expect(responses.every(r => r.status === 200)).toBe(true);
    expect(duration).toBeLessThan(10000); // < 10 seconds
  });
  
  it('should index blocks in real-time', async () => {
    const blocks = await generateMockBlocks(100);
    
    const startTime = Date.now();
    for (const block of blocks) {
      await processBlock(block);
    }
    const duration = Date.now() - startTime;
    
    expect(duration).toBeLessThan(5000); // < 5 seconds for 100 blocks
  });
});

Security Testing

describe('Security Tests', () => {
  it('should prevent SQL injection', async () => {
    const maliciousQuery = "'; DROP TABLE transactions; --";
    
    const response = await request(app)
      .post('/api/v1/sql')
      .set('Authorization', `Bearer ${validApiKey}`)
      .send({ query: maliciousQuery })
      .expect(400);
    
    expect(response.body.error).toContain('Invalid query');
  });
  
  it('should validate API key permissions', async () => {
    const response = await request(app)
      .get('/api/v1/admin/users')
      .set('Authorization', `Bearer ${freeTierApiKey}`)
      .expect(403);
    
    expect(response.body.error).toBe('Insufficient permissions');
  });
});

Test Best Practices

1. Data Accuracy

  • Verify blockchain data integrity
  • Test edge cases in transaction parsing
  • Validate ClickHouse query results

2. Performance

  • Test indexing speed under load
  • Monitor memory usage during batch operations
  • Ensure API response times < 200ms

3. Reliability

  • Test webhook delivery reliability
  • Verify error handling and recovery
  • Test system behavior during Solana RPC outages

4. Security

  • Test input validation and sanitization
  • Verify API key authentication
  • Test rate limiting and abuse prevention

Continuous Integration

# .github/workflows/test.yml
name: SupaScan Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      clickhouse:
        image: clickhouse/clickhouse-server:latest
        ports:
          - 8123:8123
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test
    
    - name: Run performance tests
      run: npm run test:performance