Skip to content

Latest commit

Β 

History

History
739 lines (586 loc) Β· 17.3 KB

File metadata and controls

739 lines (586 loc) Β· 17.3 KB

Testing Guide

Comprehensive guide to the test suite, testing strategies, and best practices for the MCP WordPress Server.

πŸ§ͺ Test Infrastructure

Current Test Status βœ…

  • Main Test Suite: 512/512 passed (100%) with Vitest
  • Security Tests: 40/40 passed (100%)
  • Performance Tests: 8/8 passed (100%)
  • CI/CD Pipeline: Fully functional with Vitest integration
  • Code Coverage: 96.17% line coverage maintained

Test Architecture

tests/
β”œβ”€β”€ unit/                    # Unit tests (17 tests)
β”‚   β”œβ”€β”€ security-utils.test.js
β”‚   β”œβ”€β”€ cache-manager.test.js
β”‚   └── helper-functions.test.js
β”œβ”€β”€ integration/             # Integration tests (54 tests)
β”‚   β”œβ”€β”€ wordpress-api.test.js
β”‚   β”œβ”€β”€ multi-site.test.js
β”‚   └── auth-methods.test.js
β”œβ”€β”€ security/                # Security tests (40 tests)
β”‚   β”œβ”€β”€ input-validation.test.js
β”‚   β”œβ”€β”€ auth-security.test.js
β”‚   β”œβ”€β”€ penetration.test.js
β”‚   └── vulnerability.test.js
β”œβ”€β”€ performance/             # Performance tests (8 tests)
β”‚   β”œβ”€β”€ benchmarks.test.js
β”‚   β”œβ”€β”€ regression.test.js
β”‚   └── load-testing.test.js
β”œβ”€β”€ cache/                   # Cache tests (37 tests)
β”‚   β”œβ”€β”€ cache-functionality.test.js
β”‚   β”œβ”€β”€ cache-invalidation.test.js
β”‚   └── cache-performance.test.js
β”œβ”€β”€ property/                # Property-based tests (12 tests)
β”‚   β”œβ”€β”€ data-structure.test.js
β”‚   └── edge-cases.test.js
β”œβ”€β”€ config/                  # Configuration tests (27 tests)
β”‚   β”œβ”€β”€ schema-validation.test.js
β”‚   β”œβ”€β”€ multi-site-config.test.js
β”‚   └── environment-config.test.js
β”œβ”€β”€ contracts/               # Contract tests (mock)
β”‚   β”œβ”€β”€ pact-testing.test.js
β”‚   └── api-contracts.test.js
β”œβ”€β”€ typescript-build.test.js # TypeScript build tests (21 tests)
β”œβ”€β”€ auth-headers-fix.test.js # Authentication header tests
β”œβ”€β”€ config-loading.test.js   # Configuration loading tests
β”œβ”€β”€ tool-validation.test.js  # Tool validation tests
β”œβ”€β”€ env-loading.test.js      # Environment loading tests (7 tests)
└── upload-timeout.test.js   # Upload timeout tests (12 tests)

πŸš€ Running Tests

Quick Commands

# Run all tests
npm test

# Run specific test suites
npm run test:unit           # Unit tests only
npm run test:integration    # Integration tests only
npm run test:security       # Security tests only
npm run test:performance    # Performance tests only
npm run test:cache          # Cache tests only
npm run test:config         # Configuration tests only
npm run test:property       # Property-based tests only

# Development commands
npm run test:watch          # Watch mode
npm run test:coverage       # Generate coverage report
npm run test:fast           # Quick validation

Advanced Test Commands

# TypeScript and build tests
npm run test:typescript     # TypeScript compilation tests
npm run test:tools          # Tool functionality tests
npm run test:mcp            # MCP protocol tests

# Multi-site and authentication
npm run test:multisite      # Multi-site configuration tests
npm run test:auth           # Authentication method tests

# Contract testing
npm run test:contracts      # Mock contract tests
npm run test:contracts:live # Live WordPress contract tests

# Docker test environment
npm run test:with-env       # Tests with Docker WordPress
./scripts/start-test-env.sh # Start test environment

πŸ”§ Test Configuration

Vitest Configuration

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'node',
    include: ['tests/**/*.test.js'],
    coverage: {
      include: ['src/**/*.ts'],
      exclude: ['src/**/*.d.ts', 'src/**/*.test.ts'],
  coverageThreshold: {
    global: {
      branches: 50,
      functions: 50,
      lines: 50,
      statements: 50,
    },
  },
  setupFilesAfterEnv: ["<rootDir>/tests/setup.js"],
  testTimeout: 30000,
};

Test Environment Setup

// tests/setup.js
const { performance } = require("perf_hooks");

// Global test utilities
global.testUtils = {
  createMockClient: () => ({
    /* mock client */
  }),
  generateTestData: (schema) => ({
    /* generated data */
  }),
  measurePerformance: (fn) => {
    const start = performance.now();
    const result = fn();
    const duration = performance.now() - start;
    return { result, duration };
  },
};

// Setup test environment
beforeAll(() => {
  process.env.NODE_ENV = "test";
  process.env.DEBUG = "false";
});

afterAll(() => {
  // Cleanup test environment
});

πŸ“ Test Categories

Unit Tests

Purpose: Test individual components in isolation

Coverage:

  • Security utilities and helper functions
  • Cache manager functionality
  • Input validation logic
  • Error handling mechanisms

Example:

// tests/unit/security-utils.test.js
describe("SecurityUtils", () => {
  test("should sanitize input correctly", () => {
    const input = '<script>alert("xss")</script>';
    const sanitized = SecurityUtils.sanitizeInput(input);
    expect(sanitized).not.toContain("<script>");
  });

  test("should validate email format", () => {
    expect(SecurityUtils.isValidEmail("test@example.com")).toBe(true);
    expect(SecurityUtils.isValidEmail("invalid-email")).toBe(false);
  });
});

Integration Tests

Purpose: Test multi-component interactions with real WordPress instances

Coverage:

  • WordPress API connectivity
  • Multi-site configuration
  • Authentication method validation
  • End-to-end tool functionality

Example:

// tests/integration/wordpress-api.test.js
describe("WordPress API Integration", () => {
  let client;

  beforeAll(async () => {
    client = new WordPressClient({
      siteUrl: process.env.WORDPRESS_TEST_URL,
      username: "admin",
      appPassword: "test test test test test test",
    });
  });

  test("should create and retrieve post", async () => {
    const post = await client.posts.create({
      title: "Test Post",
      content: "Test content",
      status: "publish",
    });

    expect(post.id).toBeDefined();
    expect(post.title).toBe("Test Post");

    const retrieved = await client.posts.get({ id: post.id });
    expect(retrieved.title).toBe("Test Post");
  });
});

Security Tests

Purpose: Validate security measures and vulnerability protection

Coverage:

  • Input validation and sanitization
  • Authentication security
  • Authorization checks
  • XSS and SQL injection prevention
  • Rate limiting and DoS protection

Example:

// tests/security/input-validation.test.js
describe("Input Validation Security", () => {
  test("should reject malicious script injection", () => {
    const maliciousInput = {
      title: '<script>alert("xss")</script>',
      content: '"><script>alert("xss")</script>',
    };

    expect(() => {
      createPostSchema.parse(maliciousInput);
    }).toThrow("Invalid input");
  });

  test("should prevent SQL injection in search", () => {
    const sqlInjection = "'; DROP TABLE posts; --";
    expect(() => {
      searchSchema.parse({ query: sqlInjection });
    }).toThrow("Invalid characters");
  });
});

Performance Tests

Purpose: Ensure performance standards and detect regressions

Coverage:

  • Response time benchmarks
  • Memory usage monitoring
  • Cache performance validation
  • Load testing scenarios

Example:

// tests/performance/benchmarks.test.js
describe("Performance Benchmarks", () => {
  test("should handle post creation under 500ms", async () => {
    const start = performance.now();

    await client.posts.create({
      title: "Performance Test",
      content: "Test content",
    });

    const duration = performance.now() - start;
    expect(duration).toBeLessThan(500);
  });

  test("should maintain cache hit rate above 70%", async () => {
    // Warm cache
    await client.posts.list();
    await client.posts.list();

    const stats = await client.cache.getStats();
    expect(stats.hitRate).toBeGreaterThan(0.7);
  });
});

Cache Tests

Purpose: Validate caching functionality and performance

Coverage:

  • Cache hit and miss scenarios
  • Cache invalidation logic
  • Multi-site cache isolation
  • Cache performance optimization

Example:

// tests/cache/cache-functionality.test.js
describe("Cache Functionality", () => {
  test("should cache GET requests", async () => {
    const client = new CachedWordPressClient(config);

    // First request - cache miss
    const start1 = performance.now();
    const posts1 = await client.posts.list();
    const duration1 = performance.now() - start1;

    // Second request - cache hit
    const start2 = performance.now();
    const posts2 = await client.posts.list();
    const duration2 = performance.now() - start2;

    expect(posts1).toEqual(posts2);
    expect(duration2).toBeLessThan(duration1 * 0.1); // 90% faster
  });

  test("should invalidate cache on updates", async () => {
    const client = new CachedWordPressClient(config);

    // Cache posts list
    await client.posts.list();

    // Create new post
    await client.posts.create({
      title: "New Post",
      content: "Content",
    });

    // List should be refreshed
    const posts = await client.posts.list();
    expect(posts.some((p) => p.title === "New Post")).toBe(true);
  });
});

Property-Based Tests

Purpose: Test with generated data to find edge cases

Coverage:

  • Data structure validation
  • Edge case discovery
  • Input boundary testing
  • Randomized testing scenarios

Example:

// tests/property/data-structure.test.js
const fc = require("fast-check");

describe("Property-Based Testing", () => {
  test("should handle any valid post data", () => {
    fc.assert(
      fc.property(
        fc.record({
          title: fc.string({ minLength: 1, maxLength: 200 }),
          content: fc.string(),
          status: fc.constantFrom("draft", "publish", "private"),
        }),
        async (postData) => {
          const result = await client.posts.create(postData);
          expect(result.title).toBe(postData.title);
          expect(result.status).toBe(postData.status);
        },
      ),
    );
  });
});

Configuration Tests

Purpose: Validate configuration loading and validation

Coverage:

  • Multi-site configuration validation
  • Environment variable loading
  • Schema validation with Zod
  • Configuration error handling

Example:

// tests/config/schema-validation.test.js
describe("Configuration Schema Validation", () => {
  test("should validate multi-site configuration", () => {
    const validConfig = {
      sites: [
        {
          id: "site1",
          name: "Test Site",
          config: {
            WORDPRESS_SITE_URL: "https://example.com",
            WORDPRESS_USERNAME: "admin",
            WORDPRESS_APP_PASSWORD: "test test test test test test",
          },
        },
      ],
    };

    expect(() => {
      multiSiteConfigSchema.parse(validConfig);
    }).not.toThrow();
  });

  test("should reject invalid site URLs", () => {
    const invalidConfig = {
      sites: [
        {
          id: "site1",
          name: "Test Site",
          config: {
            WORDPRESS_SITE_URL: "invalid-url",
            WORDPRESS_USERNAME: "admin",
            WORDPRESS_APP_PASSWORD: "password",
          },
        },
      ],
    };

    expect(() => {
      multiSiteConfigSchema.parse(invalidConfig);
    }).toThrow("Invalid URL");
  });
});

🐳 Docker Test Environment

Docker Environment Setup

# Start complete test environment
./scripts/start-test-env.sh

# This creates:
# - WordPress instance on http://localhost:8081
# - MySQL database
# - Pre-configured admin user
# - Application password setup

Docker Compose Configuration

# docker-compose.test.yml
version: "3.8"
services:
  wordpress:
    image: wordpress:latest
    ports:
      - "8081:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
    depends_on:
      - db
    volumes:
      - ./tests/wordpress-config.php:/var/www/html/wp-config.php

  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

Test Environment Usage

# Run tests with Docker environment
npm run test:with-env

# Run specific tests against Docker
WORDPRESS_TEST_URL=http://localhost:8081 npm test

# Clean up test environment
docker-compose -f docker-compose.test.yml down -v

πŸ“Š Test Reporting

Coverage Reports

# Generate coverage report
npm run test:coverage

# Coverage report locations:
# - coverage/lcov-report/index.html (HTML report)
# - coverage/lcov.info (LCOV format)
# - coverage/coverage-final.json (JSON format)

Performance Metrics

# Performance test reporting
npm run test:performance

# Generates:
# - Performance benchmarks
# - Regression detection
# - Memory usage analysis
# - Response time metrics

πŸ”„ Continuous Integration

GitHub Actions Workflow

# .github/workflows/ci.yml
name: CI/CD Pipeline

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20]
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - name: Install dependencies
        run: npm ci
      - name: Run tests
        run: npm test
      - name: Run security tests
        run: npm run test:security
      - name: Run performance tests
        run: npm run test:performance
      - name: Upload coverage
        uses: codecov/codecov-action@v3

Test Quality Gates

Pre-Commit Hooks:

  • ESLint validation
  • TypeScript type checking
  • Quick test validation
  • Security scanning

Pre-Push Hooks:

  • Full test suite execution
  • Security audit
  • Performance regression tests
  • Coverage threshold validation

🧩 Writing Tests

Test Structure

// Standard test structure
describe("Component Name", () => {
  // Setup
  beforeAll(() => {
    // One-time setup
  });

  beforeEach(() => {
    // Per-test setup
  });

  // Test cases
  describe("method name", () => {
    test("should do something specific", () => {
      // Arrange
      const input = "test data";

      // Act
      const result = component.method(input);

      // Assert
      expect(result).toBe("expected output");
    });
  });

  // Cleanup
  afterEach(() => {
    // Per-test cleanup
  });

  afterAll(() => {
    // One-time cleanup
  });
});

Test Best Practices

  1. Descriptive Test Names: Use clear, specific test descriptions
  2. Arrange-Act-Assert Pattern: Structure tests consistently
  3. Mock External Dependencies: Isolate units under test
  4. Test Edge Cases: Include boundary conditions and error scenarios
  5. Performance Aware: Include performance assertions where relevant
  6. Security Focused: Test security-related functionality thoroughly

Mock Utilities

// tests/utils/mocks.js
export const mockWordPressClient = {
  posts: {
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
    get: vi.fn(),
    list: vi.fn(),
  },
  auth: {
    authenticate: vi.fn(),
    getAuthHeaders: vi.fn(),
  },
};

export const mockConfig = {
  siteUrl: "https://test.example.com",
  username: "testuser",
  appPassword: "test test test test test test",
};

πŸ“ˆ Test Metrics

Key Performance Indicators

  • Test Coverage: 95%+ maintained
  • Test Execution Time: < 5 minutes for full suite
  • Test Reliability: 100% pass rate in CI/CD
  • Security Test Coverage: 100% of security features tested
  • Performance Regression: < 20% performance degradation threshold

Test Health Dashboard

# Check test health
npm run test:health

# Outputs:
# - Test execution time trends
# - Coverage percentage by component
# - Flaky test detection
# - Performance regression alerts

πŸ” Debugging Tests

Debug Mode

# Run tests with debug output
DEBUG=true npm test

# Debug specific test file
DEBUG=true npm test -- tests/integration/wordpress-api.test.js

# Debug with Node.js inspector (Vitest)
node --inspect-brk ./node_modules/.bin/vitest run --no-coverage

Test Debugging Tools

// Debug utility functions
const debug = require("debug")("test:debug");

describe("Debug Example", () => {
  test("should debug test execution", () => {
    debug("Starting test execution");

    const result = someFunction();
    debug("Function result:", result);

    expect(result).toBeDefined();
  });
});

πŸ“š Further Reading


Need help with testing? This comprehensive testing infrastructure ensures code quality, security, and performance. All tests are designed to be fast, reliable, and maintainable.