Files
2025-11-29 18:29:30 +08:00

237 lines
5.7 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Run tests for Grey Haven applications with Doppler environment variables.
Supports Vitest (TypeScript) and pytest (Python) with markers for different
test types (unit, integration, e2e, benchmark).
Usage:
# Run all tests with test environment
python scripts/run_tests.py
# Run unit tests only
python scripts/run_tests.py --type unit
# Run integration and e2e tests
python scripts/run_tests.py --type integration --type e2e
# Run tests with coverage
python scripts/run_tests.py --coverage
# Run tests in watch mode (for development)
python scripts/run_tests.py --watch
# Run pytest instead of Vitest
python scripts/run_tests.py --backend pytest
# Run with specific Doppler environment
python scripts/run_tests.py --env ci
Always run with --help first to see all options.
"""
import argparse
import subprocess
import sys
from typing import List
def run_command(cmd: str, description: str) -> bool:
"""Run a shell command and return success status."""
print(f"\n{description}")
print(f" Command: {cmd}\n")
result = subprocess.run(cmd, shell=True)
return result.returncode == 0
def main():
parser = argparse.ArgumentParser(
description="Run tests with Doppler environment variables",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Run all Vitest tests
python scripts/run_tests.py
# Run unit tests only
python scripts/run_tests.py --type unit
# Run integration and e2e tests with coverage
python scripts/run_tests.py --type integration --type e2e --coverage
# Run pytest unit tests
python scripts/run_tests.py --backend pytest --type unit
# Run tests in watch mode
python scripts/run_tests.py --watch
Test Types (Markers):
unit - Unit tests (fast, isolated)
integration - Integration tests (database, external services)
e2e - End-to-end tests (full application flow)
benchmark - Performance benchmark tests
Backends:
vitest - Vitest (TypeScript/React) - default
pytest - pytest (Python/FastAPI)
Doppler Configuration:
Uses 'test' config by default.
Override with --env flag for CI environments.
"""
)
parser.add_argument(
"--backend",
default="vitest",
choices=["vitest", "pytest"],
help="Test backend to use (default: vitest)"
)
parser.add_argument(
"--type",
action="append",
choices=["unit", "integration", "e2e", "benchmark"],
help="Test type(s) to run (can be repeated). If not specified, runs all tests."
)
parser.add_argument(
"--coverage",
action="store_true",
help="Run with coverage reporting"
)
parser.add_argument(
"--watch",
action="store_true",
help="Run in watch mode (for development)"
)
parser.add_argument(
"--env",
default="test",
help="Doppler environment config to use (default: test)"
)
parser.add_argument(
"--verbose",
action="store_true",
help="Verbose output"
)
args = parser.parse_args()
backend = args.backend
env = args.env
test_types = args.type or []
print(f"\n{'=' * 70}")
print(f" Running {backend.upper()} Tests")
print(f" Environment: {env}")
if test_types:
print(f" Types: {', '.join(test_types)}")
print(f"{'=' * 70}")
# Construct test command based on backend
if backend == "vitest":
# Base Vitest command
cmd_parts = ["doppler", "run", "--config", env, "--", "vitest"]
# Add test types as grep patterns
if test_types:
# Vitest uses file patterns or test name patterns
# We'll use test name patterns matching our markers
patterns = "|".join(test_types)
cmd_parts.extend(["-t", f"({patterns})"])
# Add coverage flag
if args.coverage:
cmd_parts.append("--coverage")
# Add watch mode
if args.watch:
cmd_parts.append("--watch")
# Add verbose flag
if args.verbose:
cmd_parts.append("--reporter=verbose")
# Run mode (not watch)
if not args.watch:
cmd_parts.append("run")
elif backend == "pytest":
# Base pytest command
cmd_parts = ["doppler", "run", "--config", env, "--", "pytest"]
# Add test types as markers
if test_types:
markers = " or ".join(test_types)
cmd_parts.extend(["-m", markers])
# Add coverage flag
if args.coverage:
cmd_parts.extend([
"--cov=app",
"--cov-report=term-missing",
"--cov-report=html"
])
# Add verbose flag
if args.verbose:
cmd_parts.append("-vv")
# pytest doesn't have built-in watch mode
if args.watch:
print("\nWARNING: Warning: pytest doesn't support watch mode natively")
print(" Consider using pytest-watch: pip install pytest-watch")
cmd = " ".join(cmd_parts)
success = run_command(cmd, f"Running {backend} tests")
if not success:
print(f"\nERROR: Tests failed")
sys.exit(1)
# Coverage threshold check (if coverage was run)
if args.coverage and backend == "vitest":
print("\n→ Checking coverage thresholds...")
print(" Required: 80% (lines, functions, branches, statements)")
# Vitest coverage is configured in vitest.config.ts
# Thresholds are enforced automatically
print(" ✓ Coverage thresholds enforced by Vitest config")
elif args.coverage and backend == "pytest":
print("\n→ Checking coverage thresholds...")
print(" Required: 80% coverage")
# Check coverage with pytest-cov
coverage_cmd = f"doppler run --config {env} -- pytest --cov=app --cov-fail-under=80 -q"
coverage_success = run_command(coverage_cmd, "Validating coverage threshold")
if not coverage_success:
print("\nERROR: Coverage below 80% threshold")
print(" Add more tests to increase coverage")
sys.exit(1)
# Success!
print(f"\n{'=' * 70}")
print(f" SUCCESS: All tests passed!")
print(f"{'=' * 70}")
if args.coverage:
if backend == "vitest":
print("\n Coverage report: coverage/index.html")
else:
print("\n Coverage report: htmlcov/index.html")
print("\nNext steps:")
if not test_types:
print(" • All tests passed - ready to commit")
else:
print(f"{', '.join(test_types)} tests passed")
if not args.coverage:
print(" • Run with --coverage to check code coverage")
print(" • Deploy with: python scripts/deploy.py --env staging")
if __name__ == "__main__":
main()