generate-conftest.sh 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #!/bin/bash
  2. # Generate a pytest conftest.py with optional async/db/api fixture blocks.
  3. #
  4. # Usage: generate-conftest.sh [--async] [--db] [--api]
  5. # Input: optional flags selecting fixture blocks; writes ./tests/conftest.py
  6. # Output: the generated conftest.py file plus a one-line summary on stdout
  7. # Stderr: none (the overwrite confirmation prompt is read from the tty)
  8. # Exit: 0 ok (also --help, and when an existing file is kept at the prompt)
  9. #
  10. # Examples:
  11. # generate-conftest.sh
  12. # generate-conftest.sh --async --db
  13. # generate-conftest.sh --api
  14. set -e
  15. # --help / -h: print usage to stdout and exit 0 before any side effects (so the
  16. # prompt never fires and no file is touched). Behavior-preserving for all other
  17. # invocations — only -h/--help short-circuits here.
  18. for _arg in "$@"; do
  19. case "$_arg" in
  20. -h|--help)
  21. cat <<'EOF'
  22. Usage: generate-conftest.sh [--async] [--db] [--api]
  23. Generate ./tests/conftest.py with optional fixture blocks. If a conftest.py
  24. already exists, it prompts for confirmation (default: keep the existing file).
  25. Options:
  26. --async include async fixtures (event_loop, async_client)
  27. --db include database fixtures (db_engine, db_session)
  28. --api include API test fixtures (app, client, authenticated_client)
  29. Examples:
  30. generate-conftest.sh
  31. generate-conftest.sh --async --db
  32. generate-conftest.sh --api
  33. EOF
  34. exit 0
  35. ;;
  36. esac
  37. done
  38. OUTPUT="tests/conftest.py"
  39. # Check if tests directory exists
  40. if [[ ! -d "tests" ]]; then
  41. echo "Creating tests directory..."
  42. mkdir -p tests
  43. fi
  44. # Check if conftest.py already exists
  45. if [[ -f "$OUTPUT" ]]; then
  46. read -p "conftest.py already exists. Overwrite? [y/N] " -n 1 -r
  47. echo
  48. if [[ ! $REPLY =~ ^[Yy]$ ]]; then
  49. exit 0
  50. fi
  51. fi
  52. # Parse arguments
  53. ASYNC=""
  54. DB=""
  55. API=""
  56. while [[ $# -gt 0 ]]; do
  57. case $1 in
  58. --async)
  59. ASYNC=1
  60. shift
  61. ;;
  62. --db)
  63. DB=1
  64. shift
  65. ;;
  66. --api)
  67. API=1
  68. shift
  69. ;;
  70. *)
  71. shift
  72. ;;
  73. esac
  74. done
  75. # Generate conftest.py
  76. cat > "$OUTPUT" << 'HEADER'
  77. """
  78. Pytest configuration and fixtures.
  79. Generated by generate-conftest.sh
  80. """
  81. import pytest
  82. HEADER
  83. # Add async imports if needed
  84. if [[ -n "$ASYNC" ]]; then
  85. cat >> "$OUTPUT" << 'ASYNC_IMPORTS'
  86. import asyncio
  87. ASYNC_IMPORTS
  88. fi
  89. # Add database imports if needed
  90. if [[ -n "$DB" ]]; then
  91. cat >> "$OUTPUT" << 'DB_IMPORTS'
  92. from sqlalchemy import create_engine
  93. from sqlalchemy.orm import sessionmaker
  94. DB_IMPORTS
  95. fi
  96. # Add API imports if needed
  97. if [[ -n "$API" ]]; then
  98. cat >> "$OUTPUT" << 'API_IMPORTS'
  99. from fastapi.testclient import TestClient
  100. # or: from flask.testing import FlaskClient
  101. API_IMPORTS
  102. fi
  103. # Add base fixtures
  104. cat >> "$OUTPUT" << 'BASE_FIXTURES'
  105. # ============================================================
  106. # Test Configuration
  107. # ============================================================
  108. def pytest_configure(config):
  109. """Register custom markers."""
  110. config.addinivalue_line("markers", "slow: marks tests as slow")
  111. config.addinivalue_line("markers", "integration: marks integration tests")
  112. config.addinivalue_line("markers", "e2e: marks end-to-end tests")
  113. def pytest_collection_modifyitems(config, items):
  114. """Skip slow tests unless --slow flag is provided."""
  115. if not config.getoption("--slow", default=False):
  116. skip_slow = pytest.mark.skip(reason="use --slow to run")
  117. for item in items:
  118. if "slow" in item.keywords:
  119. item.add_marker(skip_slow)
  120. def pytest_addoption(parser):
  121. """Add custom CLI options."""
  122. parser.addoption(
  123. "--slow",
  124. action="store_true",
  125. default=False,
  126. help="run slow tests"
  127. )
  128. # ============================================================
  129. # Common Fixtures
  130. # ============================================================
  131. @pytest.fixture
  132. def sample_data():
  133. """Provide sample test data."""
  134. return {
  135. "id": 1,
  136. "name": "Test",
  137. "active": True,
  138. }
  139. @pytest.fixture
  140. def temp_file(tmp_path):
  141. """Create a temporary file for testing."""
  142. file_path = tmp_path / "test_file.txt"
  143. file_path.write_text("test content")
  144. return file_path
  145. BASE_FIXTURES
  146. # Add async fixtures if requested
  147. if [[ -n "$ASYNC" ]]; then
  148. cat >> "$OUTPUT" << 'ASYNC_FIXTURES'
  149. # ============================================================
  150. # Async Fixtures
  151. # ============================================================
  152. @pytest.fixture(scope="session")
  153. def event_loop():
  154. """Create event loop for async tests."""
  155. loop = asyncio.new_event_loop()
  156. yield loop
  157. loop.close()
  158. @pytest.fixture
  159. async def async_client():
  160. """Async HTTP client fixture."""
  161. import aiohttp
  162. async with aiohttp.ClientSession() as session:
  163. yield session
  164. ASYNC_FIXTURES
  165. fi
  166. # Add database fixtures if requested
  167. if [[ -n "$DB" ]]; then
  168. cat >> "$OUTPUT" << 'DB_FIXTURES'
  169. # ============================================================
  170. # Database Fixtures
  171. # ============================================================
  172. @pytest.fixture(scope="session")
  173. def db_engine():
  174. """Create test database engine."""
  175. engine = create_engine("sqlite:///:memory:")
  176. # Create tables here
  177. yield engine
  178. engine.dispose()
  179. @pytest.fixture
  180. def db_session(db_engine):
  181. """Create database session with transaction rollback."""
  182. Session = sessionmaker(bind=db_engine)
  183. session = Session()
  184. yield session
  185. session.rollback()
  186. session.close()
  187. DB_FIXTURES
  188. fi
  189. # Add API fixtures if requested
  190. if [[ -n "$API" ]]; then
  191. cat >> "$OUTPUT" << 'API_FIXTURES'
  192. # ============================================================
  193. # API Fixtures
  194. # ============================================================
  195. @pytest.fixture
  196. def app():
  197. """Create test application."""
  198. from myapp import create_app
  199. app = create_app(testing=True)
  200. return app
  201. @pytest.fixture
  202. def client(app):
  203. """Create test client."""
  204. return TestClient(app)
  205. @pytest.fixture
  206. def authenticated_client(client):
  207. """Create authenticated test client."""
  208. # Add authentication logic here
  209. client.headers["Authorization"] = "Bearer test-token"
  210. return client
  211. API_FIXTURES
  212. fi
  213. echo "Generated $OUTPUT"
  214. echo ""
  215. echo "Options used:"
  216. [[ -n "$ASYNC" ]] && echo " --async: Async fixtures included"
  217. [[ -n "$DB" ]] && echo " --db: Database fixtures included"
  218. [[ -n "$API" ]] && echo " --api: API fixtures included"
  219. exit 0