
- Production-ready MCP server for Name Cheap API integration - Domain management (registration, renewal, availability checking) - DNS management (records, nameserver configuration) - SSL certificate management and monitoring - Account information and balance checking - Smart identifier resolution for improved UX - Comprehensive error handling with specific exception types - 80%+ test coverage with unit, integration, and MCP tests - CLI and MCP server interfaces - FastMCP 2.10.5+ implementation with full MCP spec compliance 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
143 lines
4.9 KiB
Python
143 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Comprehensive test runner for the MCP Name Cheap server."""
|
|
|
|
import sys
|
|
import subprocess
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
|
|
def run_command(cmd: list[str], description: str) -> bool:
|
|
"""Run a command and return success status."""
|
|
print(f"\n{'='*60}")
|
|
print(f"Running: {description}")
|
|
print(f"Command: {' '.join(cmd)}")
|
|
print('='*60)
|
|
|
|
try:
|
|
result = subprocess.run(cmd, check=True, cwd=Path(__file__).parent)
|
|
print(f"✓ {description} passed")
|
|
return True
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"✗ {description} failed with exit code {e.returncode}")
|
|
return False
|
|
except FileNotFoundError:
|
|
print(f"✗ Command not found: {cmd[0]}")
|
|
return False
|
|
|
|
|
|
def main():
|
|
"""Main test runner."""
|
|
parser = argparse.ArgumentParser(description="Run MCP Name Cheap tests")
|
|
parser.add_argument("--unit", action="store_true", help="Run only unit tests")
|
|
parser.add_argument("--integration", action="store_true", help="Run only integration tests")
|
|
parser.add_argument("--mcp", action="store_true", help="Run only MCP tests")
|
|
parser.add_argument("--slow", action="store_true", help="Include slow tests")
|
|
parser.add_argument("--coverage", action="store_true", help="Run with coverage")
|
|
parser.add_argument("--lint", action="store_true", help="Run linting only")
|
|
parser.add_argument("--format", action="store_true", help="Run formatting only")
|
|
parser.add_argument("--typecheck", action="store_true", help="Run type checking only")
|
|
parser.add_argument("--all", action="store_true", help="Run all checks and tests")
|
|
parser.add_argument("--fix", action="store_true", help="Auto-fix formatting issues")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if not any([args.unit, args.integration, args.mcp, args.lint, args.format,
|
|
args.typecheck, args.all]):
|
|
# Default: run all tests but not linting/formatting
|
|
args.unit = args.integration = args.mcp = True
|
|
|
|
success = True
|
|
|
|
# Code formatting
|
|
if args.format or args.all:
|
|
if args.fix:
|
|
success &= run_command(
|
|
["python", "-m", "ruff", "format", "src/", "tests/", "run_tests.py"],
|
|
"Code formatting (ruff) - fixing"
|
|
)
|
|
success &= run_command(
|
|
["python", "-m", "ruff", "check", "--fix", "src/", "tests/"],
|
|
"Code linting (ruff) - fixing"
|
|
)
|
|
success &= run_command(
|
|
["python", "-m", "black", "src/", "tests/", "run_tests.py"],
|
|
"Code formatting (black) - fixing"
|
|
)
|
|
success &= run_command(
|
|
["python", "-m", "isort", "src/", "tests/", "run_tests.py"],
|
|
"Import sorting (isort) - fixing"
|
|
)
|
|
else:
|
|
success &= run_command(
|
|
["python", "-m", "black", "--check", "src/", "tests/", "run_tests.py"],
|
|
"Code formatting (black)"
|
|
)
|
|
success &= run_command(
|
|
["python", "-m", "isort", "--check-only", "src/", "tests/", "run_tests.py"],
|
|
"Import sorting (isort)"
|
|
)
|
|
|
|
# Linting with Ruff
|
|
if args.lint or args.all:
|
|
success &= run_command(
|
|
["python", "-m", "ruff", "check", "src/", "tests/"],
|
|
"Code linting (ruff)"
|
|
)
|
|
success &= run_command(
|
|
["python", "-m", "ruff", "format", "--check", "src/", "tests/"],
|
|
"Code formatting check (ruff)"
|
|
)
|
|
|
|
# Type checking
|
|
if args.typecheck or args.all:
|
|
success &= run_command(
|
|
["python", "-m", "mypy", "src/mcp_namecheap/"],
|
|
"Type checking (mypy)"
|
|
)
|
|
|
|
# Test execution
|
|
pytest_cmd = ["python", "-m", "pytest"]
|
|
|
|
if args.coverage:
|
|
pytest_cmd.extend(["--cov=src/mcp_namecheap", "--cov-report=term-missing", "--cov-report=html"])
|
|
|
|
# Add verbosity
|
|
pytest_cmd.extend(["-v", "--tb=short"])
|
|
|
|
# Test selection
|
|
markers = []
|
|
if args.unit:
|
|
markers.append("unit")
|
|
if args.integration:
|
|
markers.append("integration")
|
|
if args.mcp:
|
|
markers.append("mcp")
|
|
|
|
if markers:
|
|
marker_expr = " or ".join(markers)
|
|
pytest_cmd.extend(["-m", marker_expr])
|
|
|
|
if not args.slow:
|
|
if markers:
|
|
pytest_cmd.extend(["-m", f"({marker_expr}) and not slow"])
|
|
else:
|
|
pytest_cmd.extend(["-m", "not slow"])
|
|
|
|
if any([args.unit, args.integration, args.mcp, args.all]) or not any([args.lint, args.format, args.typecheck]):
|
|
success &= run_command(pytest_cmd, "Running tests")
|
|
|
|
# Summary
|
|
print(f"\n{'='*60}")
|
|
if success:
|
|
print("🎉 All checks passed!")
|
|
print("="*60)
|
|
sys.exit(0)
|
|
else:
|
|
print("💥 Some checks failed!")
|
|
print("="*60)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |