FastAPI-fastkit 1.2.1__py3-none-any.whl → 1.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_fastkit/__init__.py +1 -1
- fastapi_fastkit/backend/inspection/__init__.py +26 -0
- fastapi_fastkit/backend/inspection/checks.py +349 -0
- fastapi_fastkit/backend/inspection/consistency.py +125 -0
- fastapi_fastkit/backend/inspection/context.py +67 -0
- fastapi_fastkit/backend/inspection/core.py +347 -0
- fastapi_fastkit/backend/inspection/docker.py +185 -0
- fastapi_fastkit/backend/inspection/freshness.py +145 -0
- fastapi_fastkit/backend/inspection/fsutils.py +139 -0
- fastapi_fastkit/backend/inspection/lint.py +89 -0
- fastapi_fastkit/backend/inspection/report.py +58 -0
- fastapi_fastkit/backend/inspection/smoke.py +327 -0
- fastapi_fastkit/backend/inspection/strategies.py +336 -0
- fastapi_fastkit/backend/inspector.py +18 -1279
- fastapi_fastkit/backend/interactive/__init__.py +8 -0
- fastapi_fastkit/backend/interactive/config_builder.py +66 -65
- fastapi_fastkit/backend/interactive/prompts.py +205 -5
- fastapi_fastkit/backend/interactive/selectors.py +28 -2
- fastapi_fastkit/backend/main.py +956 -95
- fastapi_fastkit/backend/package_managers/base.py +172 -0
- fastapi_fastkit/backend/package_managers/factory.py +6 -5
- fastapi_fastkit/backend/package_managers/pdm_manager.py +32 -85
- fastapi_fastkit/backend/package_managers/pip_manager.py +31 -85
- fastapi_fastkit/backend/package_managers/poetry_manager.py +73 -113
- fastapi_fastkit/backend/package_managers/uv_manager.py +46 -115
- fastapi_fastkit/backend/project_builder/__init__.py +10 -0
- fastapi_fastkit/backend/project_builder/config_generator.py +813 -450
- fastapi_fastkit/backend/project_builder/config_schema.py +384 -0
- fastapi_fastkit/backend/project_builder/dependency_collector.py +105 -66
- fastapi_fastkit/backend/project_builder/preset_layout.py +429 -0
- fastapi_fastkit/backend/scaffolder.py +578 -0
- fastapi_fastkit/backend/transducer.py +54 -22
- fastapi_fastkit/cli.py +452 -284
- fastapi_fastkit/core/settings.py +248 -43
- fastapi_fastkit/fastapi_project_template/README.md +88 -23
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/README.md-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/pyproject.toml-tpl +25 -23
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/requirements.txt-tpl +39 -36
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/api/api.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/api/routes/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/crud/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/main.py-tpl +9 -1
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/schemas/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/tests/conftest.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/.env-tpl +21 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/.gitignore-tpl +33 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/Dockerfile-tpl +18 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/README.md-tpl +210 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/alembic.ini-tpl +119 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/pyproject.toml-tpl +86 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/requirements.txt-tpl +23 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/format.sh-tpl +5 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/lint.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/run-server.sh-tpl +8 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/test.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/README-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/env.py-tpl +51 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/script.py.mako-tpl +26 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/versions/0001_initial_auth_schema.py-tpl +62 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/deps.py-tpl +91 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/health.py-tpl +11 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/router.py-tpl +17 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/core/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/core/config.py-tpl +64 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/core/security.py-tpl +100 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/db/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/db/session.py-tpl +36 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/models.py-tpl +25 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/repository.py-tpl +41 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/router.py-tpl +82 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/schemas.py-tpl +14 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/service.py-tpl +65 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/models.py-tpl +35 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/repository.py-tpl +33 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/router.py-tpl +71 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/schemas.py-tpl +29 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/service.py-tpl +55 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/main.py-tpl +69 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/template-config.yml-tpl +16 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/conftest.py-tpl +73 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/test_auth.py-tpl +201 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/test_health.py-tpl +15 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/test_users.py-tpl +77 -0
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/pyproject.toml-tpl +25 -23
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/requirements.txt-tpl +39 -36
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/api/api.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/api/routes/items.py-tpl +3 -3
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/crud/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/helper/pagination.py-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/main.py-tpl +9 -0
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/schemas/base.py-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/schemas/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/tests/conftest.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-default/pyproject.toml-tpl +22 -20
- fastapi_fastkit/fastapi_project_template/fastapi-default/requirements.txt-tpl +37 -34
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/api/api.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/api/routes/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/crud/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/main.py-tpl +9 -1
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/schemas/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-default/tests/conftest.py-tpl +2 -3
- fastapi_fastkit/fastapi_project_template/fastapi-default/tests/test_items.py-tpl +0 -2
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/Dockerfile-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/README.md-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/pyproject.toml-tpl +25 -23
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/requirements.txt-tpl +41 -36
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/api/api.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/api/routes/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/crud/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/main.py-tpl +9 -1
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/schemas/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/tests/conftest.py-tpl +2 -3
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/tests/test_items.py-tpl +0 -2
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/.env-tpl +2 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/.gitignore-tpl +31 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/README.md-tpl +128 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/pyproject.toml-tpl +69 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/requirements.txt-tpl +37 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/scripts/format.sh-tpl +5 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/scripts/lint.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/scripts/run-server.sh-tpl +8 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/scripts/test.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/api/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/api/health.py-tpl +11 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/api/router.py-tpl +15 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/core/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/core/config.py-tpl +49 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/db/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/db/memory.py-tpl +49 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/__init__.py-tpl +9 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/models.py-tpl +17 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/repository.py-tpl +47 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/router.py-tpl +58 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/schemas.py-tpl +23 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/service.py-tpl +51 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/main.py-tpl +28 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/tests/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/tests/conftest.py-tpl +23 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/tests/test_health.py-tpl +11 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/tests/test_items.py-tpl +77 -0
- fastapi_fastkit/fastapi_project_template/fastapi-empty/README.md-tpl +2 -2
- fastapi_fastkit/fastapi_project_template/fastapi-empty/pyproject.toml-tpl +18 -16
- fastapi_fastkit/fastapi_project_template/fastapi-empty/requirements.txt-tpl +36 -10
- fastapi_fastkit/fastapi_project_template/fastapi-empty/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-empty/src/main.py-tpl +3 -1
- fastapi_fastkit/fastapi_project_template/fastapi-empty/tests/conftest.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-empty/tests/test_main.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/.env-tpl +15 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/.gitignore-tpl +31 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/Dockerfile-tpl +18 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/README.md-tpl +193 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/pyproject.toml-tpl +76 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/requirements.txt-tpl +15 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/chat.sh-tpl +10 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/format.sh-tpl +5 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/lint.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/run-server.sh-tpl +8 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/test.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/chat.py-tpl +62 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/deps.py-tpl +23 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/health.py-tpl +11 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/router.py-tpl +13 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/core/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/core/config.py-tpl +67 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/agent.py-tpl +136 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/client.py-tpl +30 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/tools.py-tpl +131 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/main.py-tpl +53 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/memory/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/memory/base.py-tpl +27 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/memory/in_memory.py-tpl +32 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/schemas/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/schemas/chat.py-tpl +46 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/template-config.yml-tpl +16 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/conftest.py-tpl +158 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_chat.py-tpl +160 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_health.py-tpl +12 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_memory.py-tpl +38 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_tools.py-tpl +37 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/README.md-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/pyproject.toml-tpl +29 -26
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/requirements.txt-tpl +68 -39
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/api.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/routes/auth.py-tpl +4 -5
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/routes/items.py-tpl +3 -4
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/auth/dependencies.py-tpl +4 -5
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/core/config.py-tpl +4 -5
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/main.py-tpl +4 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/{mcp → mcp_server}/router.py-tpl +3 -5
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/schemas/items.py-tpl +6 -7
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/conftest.py-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_auth.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_items.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_mcp.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/Dockerfile-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/pyproject.toml-tpl +26 -24
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/requirements.txt-tpl +44 -41
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/env.py-tpl +1 -3
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/versions/bedcdc35b64a_first_alembic.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/api.py-tpl +2 -0
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/routes/items.py-tpl +2 -4
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/core/config.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/core/db.py-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/crud/items.py-tpl +4 -6
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/main.py-tpl +9 -0
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/schemas/items.py-tpl +4 -5
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/tests/conftest.py-tpl +1 -5
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/tests/test_items.py-tpl +0 -2
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/.env-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/.gitignore-tpl +30 -0
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/pyproject.toml-tpl +7 -5
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/requirements.txt-tpl +26 -6
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/src/main.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/tests/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/tests/conftest.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/.env-tpl +7 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/.gitignore-tpl +35 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/Dockerfile-tpl +18 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/README.md-tpl +166 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/alembic.ini-tpl +44 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/pyproject.toml-tpl +82 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/requirements.txt-tpl +21 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/format.sh-tpl +5 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/lint.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/migrate.sh-tpl +7 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/run-server.sh-tpl +8 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/test.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/README-tpl +9 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/env.py-tpl +72 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/script.py.mako-tpl +27 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/versions/0001_create_items_table.py-tpl +38 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/api/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/api/health.py-tpl +11 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/api/router.py-tpl +15 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/core/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/core/config.py-tpl +62 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/crud/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/crud/base.py-tpl +62 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/crud/pagination.py-tpl +21 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/db/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/db/base.py-tpl +12 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/db/session.py-tpl +50 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/crud.py-tpl +26 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/models.py-tpl +70 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/router.py-tpl +69 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/main.py-tpl +43 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/template-config.yml-tpl +16 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/conftest.py-tpl +60 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/test_crud_base.py-tpl +43 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/test_health.py-tpl +12 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/test_items.py-tpl +110 -0
- fastapi_fastkit/fastapi_project_template/modules/api/routes/new_route.py-tpl +2 -2
- fastapi_fastkit/fragments/README.md +90 -0
- fastapi_fastkit/fragments/auth/fastapi_users.py.j2 +8 -0
- fastapi_fastkit/fragments/auth/jwt.py.j2 +44 -0
- fastapi_fastkit/fragments/auth/oauth2.py.j2 +105 -0
- fastapi_fastkit/fragments/auth/session.py.j2 +67 -0
- fastapi_fastkit/fragments/db/mongodb.py.j2 +19 -0
- fastapi_fastkit/fragments/db/redis.py.j2 +26 -0
- fastapi_fastkit/fragments/db/sqlalchemy.py.j2 +45 -0
- fastapi_fastkit/fragments/docker/docker_compose.j2 +84 -0
- fastapi_fastkit/fragments/docker/dockerfile.j2 +19 -0
- fastapi_fastkit/fragments/features/cache.py.j2 +26 -0
- fastapi_fastkit/fragments/features/pagination.py.j2 +31 -0
- fastapi_fastkit/fragments/features/tasks_celery.py.j2 +50 -0
- fastapi_fastkit/fragments/features/tasks_dramatiq.py.j2 +59 -0
- fastapi_fastkit/fragments/features/websocket.py.j2 +66 -0
- fastapi_fastkit/fragments/header.j2 +4 -0
- fastapi_fastkit/fragments/logging/structured.py.j2 +88 -0
- fastapi_fastkit/fragments/main/imports/async_tasks.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/caching.j2 +5 -0
- fastapi_fastkit/fragments/main/imports/fastapi_users.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/jwt.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/loguru.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/mongodb.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/oauth2.j2 +4 -0
- fastapi_fastkit/fragments/main/imports/opentelemetry.j2 +6 -0
- fastapi_fastkit/fragments/main/imports/pagination.j2 +3 -0
- fastapi_fastkit/fragments/main/imports/prometheus.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/rate_limiting.j2 +3 -0
- fastapi_fastkit/fragments/main/imports/session_auth.j2 +4 -0
- fastapi_fastkit/fragments/main/imports/sqlalchemy.j2 +2 -0
- fastapi_fastkit/fragments/main/imports/structured_logging.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/websocket.j2 +1 -0
- fastapi_fastkit/fragments/main/lifespan/caching.j2 +7 -0
- fastapi_fastkit/fragments/main/lifespan/mongodb.j2 +2 -0
- fastapi_fastkit/fragments/main/lifespan/prometheus.j2 +2 -0
- fastapi_fastkit/fragments/main/lifespan/sqlalchemy.j2 +2 -0
- fastapi_fastkit/fragments/main/setup/async_tasks.j2 +1 -0
- fastapi_fastkit/fragments/main/setup/caching.j2 +1 -0
- fastapi_fastkit/fragments/main/setup/cors.j2 +8 -0
- fastapi_fastkit/fragments/main/setup/oauth2.j2 +3 -0
- fastapi_fastkit/fragments/main/setup/opentelemetry.j2 +19 -0
- fastapi_fastkit/fragments/main/setup/pagination.j2 +2 -0
- fastapi_fastkit/fragments/main/setup/rate_limiting.j2 +3 -0
- fastapi_fastkit/fragments/main/setup/session_auth.j2 +6 -0
- fastapi_fastkit/fragments/main/setup/structured_logging.j2 +2 -0
- fastapi_fastkit/fragments/main/setup/websocket.j2 +1 -0
- fastapi_fastkit/fragments/main.py.j2 +57 -0
- fastapi_fastkit/fragments/migrations/alembic_ini.j2 +42 -0
- fastapi_fastkit/fragments/migrations/env.py.j2 +76 -0
- fastapi_fastkit/fragments/migrations/initial_revision.py.j2 +27 -0
- fastapi_fastkit/fragments/migrations/migrate_sh.j2 +34 -0
- fastapi_fastkit/fragments/migrations/script.py.mako.j2 +26 -0
- fastapi_fastkit/fragments/tasks/celery_worker.py.j2 +49 -0
- fastapi_fastkit/fragments/tasks/dramatiq_worker.py.j2 +41 -0
- fastapi_fastkit/fragments/test/factories.py.j2 +68 -0
- fastapi_fastkit/fragments/test/pytest_ini.j2 +16 -0
- fastapi_fastkit/fragments/test/test_factories.py.j2 +27 -0
- fastapi_fastkit/fragments/test/test_main.py.j2 +46 -0
- fastapi_fastkit/fragments/tooling/devcontainer.j2 +26 -0
- fastapi_fastkit/fragments/tooling/github_actions.j2 +37 -0
- fastapi_fastkit/fragments/tooling/makefile.j2 +40 -0
- fastapi_fastkit/fragments/tooling/pre_commit.j2 +36 -0
- fastapi_fastkit/fragments/tooling/ruff_toml.j2 +16 -0
- fastapi_fastkit/utils/config_file.py +263 -0
- fastapi_fastkit/utils/main.py +111 -6
- {fastapi_fastkit-1.2.1.dist-info → fastapi_fastkit-1.4.0.dist-info}/METADATA +33 -6
- fastapi_fastkit-1.4.0.dist-info/RECORD +484 -0
- {fastapi_fastkit-1.2.1.dist-info → fastapi_fastkit-1.4.0.dist-info}/WHEEL +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/setup.py-tpl +0 -25
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/setup.py-tpl +0 -27
- fastapi_fastkit/fastapi_project_template/fastapi-default/setup.py-tpl +0 -22
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/setup.py-tpl +0 -22
- fastapi_fastkit/fastapi_project_template/fastapi-empty/setup.py-tpl +0 -22
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/setup.py-tpl +0 -51
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/setup.py-tpl +0 -25
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/setup.py-tpl +0 -17
- fastapi_fastkit-1.2.1.dist-info/RECORD +0 -256
- /fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/{mcp → mcp_server}/__init__.py-tpl +0 -0
- {fastapi_fastkit-1.2.1.dist-info → fastapi_fastkit-1.4.0.dist-info}/entry_points.txt +0 -0
- {fastapi_fastkit-1.2.1.dist-info → fastapi_fastkit-1.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Test strategies for template inspection.
|
|
3
|
+
#
|
|
4
|
+
# All strategies share one base: prepare an environment, pick between the
|
|
5
|
+
# template's own ``scripts/test.sh`` and a direct pytest invocation, then turn
|
|
6
|
+
# a non-zero return code into an inspection error. The concrete strategies
|
|
7
|
+
# only supply what actually differs - the environment and the command.
|
|
8
|
+
#
|
|
9
|
+
# @author bnbong
|
|
10
|
+
# --------------------------------------------------------------------------
|
|
11
|
+
import os
|
|
12
|
+
import subprocess
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
from typing import Any, Dict, List
|
|
15
|
+
|
|
16
|
+
from fastapi_fastkit.backend.main import create_venv, install_dependencies_with_manager
|
|
17
|
+
from fastapi_fastkit.utils.logging import debug_log
|
|
18
|
+
|
|
19
|
+
from .context import InspectionContext
|
|
20
|
+
from .docker import DockerCompose
|
|
21
|
+
from .fsutils import fix_all_script_line_endings, fix_script_line_endings
|
|
22
|
+
|
|
23
|
+
TEST_TIMEOUT = 300
|
|
24
|
+
DEFAULT_TEST_COMMAND = "pytest tests/ -v"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def detect_package_manager(project_dir: str) -> str:
|
|
28
|
+
"""Pick the package manager that fits the project's dependency files."""
|
|
29
|
+
if os.path.exists(os.path.join(project_dir, "pyproject.toml")):
|
|
30
|
+
return "uv"
|
|
31
|
+
return "pip"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def prepare_environment(ctx: InspectionContext) -> bool:
|
|
35
|
+
"""Create the inspection venv and install the project's dependencies.
|
|
36
|
+
|
|
37
|
+
Idempotent: a context that already carries a ``venv_path`` is left alone,
|
|
38
|
+
so the smoke test can reuse whatever a test strategy already built.
|
|
39
|
+
"""
|
|
40
|
+
if ctx.venv_path:
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
venv_path = create_venv(ctx.temp_dir)
|
|
45
|
+
except Exception as e:
|
|
46
|
+
ctx.add_error(f"Failed to create virtual environment: {e}")
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
package_manager = detect_package_manager(ctx.temp_dir)
|
|
50
|
+
debug_log(f"Using package manager: {package_manager}", "info")
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
install_dependencies_with_manager(ctx.temp_dir, venv_path, package_manager)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
ctx.add_error(f"Failed to install dependencies: {str(e)}")
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
ctx.venv_path = venv_path
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class TestStrategy(ABC):
|
|
63
|
+
"""Base class for the ways a template's test suite can be executed."""
|
|
64
|
+
|
|
65
|
+
name = "base"
|
|
66
|
+
|
|
67
|
+
def __init__(self, ctx: InspectionContext):
|
|
68
|
+
self.ctx = ctx
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def config(self) -> Dict[str, Any]:
|
|
72
|
+
"""The template's configuration mapping (empty when absent)."""
|
|
73
|
+
return self.ctx.template_config or {}
|
|
74
|
+
|
|
75
|
+
@abstractmethod
|
|
76
|
+
def run(self) -> bool:
|
|
77
|
+
"""Execute the template's tests, recording errors on the context."""
|
|
78
|
+
|
|
79
|
+
def _report_failure(self, result: subprocess.CompletedProcess[str]) -> None:
|
|
80
|
+
"""Turn a failed test process into a single descriptive error entry."""
|
|
81
|
+
message = f"{self.name} tests failed with return code {result.returncode}\n"
|
|
82
|
+
if result.stderr:
|
|
83
|
+
message += f"STDERR:\n{result.stderr}\n"
|
|
84
|
+
if result.stdout:
|
|
85
|
+
message += f"STDOUT:\n{result.stdout}\n"
|
|
86
|
+
self.ctx.add_error(message)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class LocalVenvStrategy(TestStrategy):
|
|
90
|
+
"""Run the tests in a virtual environment created next to the project."""
|
|
91
|
+
|
|
92
|
+
name = "Standard"
|
|
93
|
+
|
|
94
|
+
def environment(self) -> Dict[str, str]:
|
|
95
|
+
"""Environment variables for the test process."""
|
|
96
|
+
return os.environ.copy()
|
|
97
|
+
|
|
98
|
+
def test_command(self) -> List[str]:
|
|
99
|
+
"""The pytest invocation used when the template ships no test script."""
|
|
100
|
+
return DEFAULT_TEST_COMMAND.split()
|
|
101
|
+
|
|
102
|
+
def on_success(self) -> None:
|
|
103
|
+
"""Hook for strategies that want to annotate a successful run."""
|
|
104
|
+
|
|
105
|
+
def run(self) -> bool:
|
|
106
|
+
if not prepare_environment(self.ctx):
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
venv_path = self.ctx.venv_path
|
|
110
|
+
assert venv_path is not None # prepare_environment guarantees this
|
|
111
|
+
|
|
112
|
+
env = self.environment()
|
|
113
|
+
bin_dir = "Scripts" if os.name == "nt" else "bin"
|
|
114
|
+
separator = ";" if os.name == "nt" else ":"
|
|
115
|
+
env["PATH"] = (
|
|
116
|
+
f"{os.path.join(venv_path, bin_dir)}{separator}{env.get('PATH', '')}"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
test_script_path = self.ctx.temp_path("scripts", "test.sh")
|
|
120
|
+
try:
|
|
121
|
+
if os.path.exists(test_script_path):
|
|
122
|
+
debug_log("Found scripts/test.sh, using template test script", "info")
|
|
123
|
+
result = self._run_test_script(test_script_path, env)
|
|
124
|
+
else:
|
|
125
|
+
debug_log("No scripts/test.sh found, running pytest directly", "info")
|
|
126
|
+
result = self._run_pytest(env)
|
|
127
|
+
except subprocess.TimeoutExpired:
|
|
128
|
+
self.ctx.add_error(f"{self.name} tests timed out after 5 minutes")
|
|
129
|
+
return False
|
|
130
|
+
except OSError as e:
|
|
131
|
+
self.ctx.add_error(f"Error running {self.name.lower()} tests: {e}")
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
if result.returncode != 0:
|
|
135
|
+
self._report_failure(result)
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
debug_log(f"{self.name} tests passed successfully", "info")
|
|
139
|
+
self.on_success()
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
def _run_test_script(
|
|
143
|
+
self, test_script_path: str, env: Dict[str, str]
|
|
144
|
+
) -> subprocess.CompletedProcess[str]:
|
|
145
|
+
"""Run the template's own test script."""
|
|
146
|
+
fix_script_line_endings(test_script_path)
|
|
147
|
+
os.chmod(test_script_path, 0o755)
|
|
148
|
+
return subprocess.run(
|
|
149
|
+
[test_script_path],
|
|
150
|
+
cwd=self.ctx.temp_dir,
|
|
151
|
+
capture_output=True,
|
|
152
|
+
text=True,
|
|
153
|
+
env=env,
|
|
154
|
+
timeout=TEST_TIMEOUT,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def _run_pytest(self, env: Dict[str, str]) -> subprocess.CompletedProcess[str]:
|
|
158
|
+
"""Run pytest with the strategy's command inside the inspection venv."""
|
|
159
|
+
python_executable = self.ctx.python_executable()
|
|
160
|
+
assert python_executable is not None
|
|
161
|
+
return subprocess.run(
|
|
162
|
+
[python_executable, "-m", *self.test_command()],
|
|
163
|
+
cwd=self.ctx.temp_dir,
|
|
164
|
+
capture_output=True,
|
|
165
|
+
text=True,
|
|
166
|
+
env=env,
|
|
167
|
+
timeout=TEST_TIMEOUT,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class StandardStrategy(LocalVenvStrategy):
|
|
172
|
+
"""Plain virtual environment run - the default for most templates."""
|
|
173
|
+
|
|
174
|
+
name = "Standard"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class FallbackStrategy(LocalVenvStrategy):
|
|
178
|
+
"""Docker-free substitute run (e.g. SQLite in place of PostgreSQL)."""
|
|
179
|
+
|
|
180
|
+
name = "Fallback"
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def fallback_config(self) -> Dict[str, Any]:
|
|
184
|
+
"""The ``fallback_testing`` block of the template configuration."""
|
|
185
|
+
block = self.config.get("fallback_testing", {})
|
|
186
|
+
return block if isinstance(block, dict) else {}
|
|
187
|
+
|
|
188
|
+
def environment(self) -> Dict[str, str]:
|
|
189
|
+
env = os.environ.copy()
|
|
190
|
+
database_url = self.fallback_config.get("database_url", "sqlite:///:memory:")
|
|
191
|
+
env["DATABASE_URL"] = database_url
|
|
192
|
+
env["SQLALCHEMY_DATABASE_URI"] = database_url
|
|
193
|
+
return env
|
|
194
|
+
|
|
195
|
+
def test_command(self) -> List[str]:
|
|
196
|
+
command = self.fallback_config.get("test_command", DEFAULT_TEST_COMMAND)
|
|
197
|
+
return str(command).split()
|
|
198
|
+
|
|
199
|
+
def on_success(self) -> None:
|
|
200
|
+
self.ctx.add_warning(
|
|
201
|
+
"Tests passed using fallback strategy (SQLite instead of PostgreSQL)"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class DockerStrategy(TestStrategy):
|
|
206
|
+
"""Run the tests inside the template's own Docker Compose stack."""
|
|
207
|
+
|
|
208
|
+
name = "Docker"
|
|
209
|
+
|
|
210
|
+
def _testing_config(self) -> Dict[str, Any]:
|
|
211
|
+
block = self.config.get("testing", {})
|
|
212
|
+
return block if isinstance(block, dict) else {}
|
|
213
|
+
|
|
214
|
+
def _setup_test_environment(self) -> None:
|
|
215
|
+
"""Materialise a .env file from ``test_env_defaults`` and fix scripts."""
|
|
216
|
+
env_defaults = self.config.get("test_env_defaults", {})
|
|
217
|
+
env_file_path = self.ctx.temp_path(".env")
|
|
218
|
+
|
|
219
|
+
existing_vars: Dict[str, str] = {}
|
|
220
|
+
if os.path.exists(env_file_path):
|
|
221
|
+
debug_log(f".env file already exists at {env_file_path}", "info")
|
|
222
|
+
try:
|
|
223
|
+
with open(env_file_path, encoding="utf-8") as f:
|
|
224
|
+
for line in f:
|
|
225
|
+
line = line.strip()
|
|
226
|
+
if line and "=" in line and not line.startswith("#"):
|
|
227
|
+
key, value = line.split("=", 1)
|
|
228
|
+
existing_vars[key] = value
|
|
229
|
+
except (OSError, UnicodeDecodeError) as e:
|
|
230
|
+
debug_log(f"Error reading existing .env file: {e}", "warning")
|
|
231
|
+
|
|
232
|
+
final_vars = {**env_defaults, **existing_vars}
|
|
233
|
+
with open(env_file_path, "w", encoding="utf-8") as f:
|
|
234
|
+
for key, value in final_vars.items():
|
|
235
|
+
f.write(f"{key}={value}\n")
|
|
236
|
+
|
|
237
|
+
debug_log(
|
|
238
|
+
f"Set up environment file: {env_file_path} "
|
|
239
|
+
f"with variables: {list(final_vars.keys())}",
|
|
240
|
+
"info",
|
|
241
|
+
)
|
|
242
|
+
fix_all_script_line_endings(self.ctx.temp_dir)
|
|
243
|
+
|
|
244
|
+
def run(self) -> bool:
|
|
245
|
+
if not DockerCompose.is_available():
|
|
246
|
+
debug_log("Docker not available, trying fallback strategy", "warning")
|
|
247
|
+
self.ctx.add_warning(
|
|
248
|
+
"Docker not available, using fallback testing strategy"
|
|
249
|
+
)
|
|
250
|
+
return select_fallback_strategy(self.ctx).run()
|
|
251
|
+
|
|
252
|
+
testing_config = self._testing_config()
|
|
253
|
+
compose = DockerCompose(
|
|
254
|
+
self.ctx.temp_dir,
|
|
255
|
+
str(testing_config.get("compose_file", "docker-compose.yml")),
|
|
256
|
+
)
|
|
257
|
+
timeout = int(testing_config.get("health_check_timeout", 120))
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
self._setup_test_environment()
|
|
261
|
+
|
|
262
|
+
if not compose.containers_running():
|
|
263
|
+
debug_log("Starting Docker Compose services for testing", "info")
|
|
264
|
+
result = compose.up(timeout)
|
|
265
|
+
if result.returncode != 0:
|
|
266
|
+
self.ctx.add_error(
|
|
267
|
+
f"Failed to start Docker services: {result.stderr}"
|
|
268
|
+
)
|
|
269
|
+
return False
|
|
270
|
+
compose.wait_until_healthy(timeout)
|
|
271
|
+
else:
|
|
272
|
+
debug_log(
|
|
273
|
+
"Docker Compose services already running, skipping startup", "info"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
verification_error = compose.verify_services_running()
|
|
277
|
+
if verification_error is not None:
|
|
278
|
+
self.ctx.add_error(verification_error)
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
return self._run_tests(compose)
|
|
282
|
+
except subprocess.TimeoutExpired:
|
|
283
|
+
self.ctx.add_error("Docker Compose setup timed out")
|
|
284
|
+
return False
|
|
285
|
+
except OSError as e:
|
|
286
|
+
self.ctx.add_error(f"Unexpected error during Docker testing: {e}")
|
|
287
|
+
return False
|
|
288
|
+
finally:
|
|
289
|
+
compose.cleanup()
|
|
290
|
+
|
|
291
|
+
def _run_tests(self, compose: DockerCompose) -> bool:
|
|
292
|
+
"""Execute the suite inside the app container and grade the result."""
|
|
293
|
+
test_script_path = self.ctx.temp_path("scripts", "test.sh")
|
|
294
|
+
use_test_script = os.path.exists(test_script_path)
|
|
295
|
+
if use_test_script:
|
|
296
|
+
fix_script_line_endings(test_script_path)
|
|
297
|
+
|
|
298
|
+
result = compose.exec_tests(use_test_script)
|
|
299
|
+
if result is None:
|
|
300
|
+
self.ctx.add_error("Docker tests timed out")
|
|
301
|
+
return False
|
|
302
|
+
|
|
303
|
+
if result.returncode != 0:
|
|
304
|
+
self._report_failure(result)
|
|
305
|
+
return False
|
|
306
|
+
|
|
307
|
+
debug_log("Docker tests passed successfully", "info")
|
|
308
|
+
return True
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def select_fallback_strategy(ctx: InspectionContext) -> TestStrategy:
|
|
312
|
+
"""Pick the fallback strategy, or the standard one when none is configured."""
|
|
313
|
+
config = ctx.template_config or {}
|
|
314
|
+
if "fallback_testing" not in config:
|
|
315
|
+
debug_log("No fallback strategy configured, using standard strategy", "info")
|
|
316
|
+
return StandardStrategy(ctx)
|
|
317
|
+
return FallbackStrategy(ctx)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def select_strategy(ctx: InspectionContext) -> TestStrategy:
|
|
321
|
+
"""Pick the strategy the template's configuration asks for."""
|
|
322
|
+
config = ctx.template_config or {}
|
|
323
|
+
if config.get("requires_docker", False):
|
|
324
|
+
return DockerStrategy(ctx)
|
|
325
|
+
return StandardStrategy(ctx)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def run_template_tests(ctx: InspectionContext) -> bool:
|
|
329
|
+
"""Run the template's test suite with the appropriate strategy."""
|
|
330
|
+
if not ctx.options.run_template_tests:
|
|
331
|
+
debug_log("Template test execution disabled", "info")
|
|
332
|
+
return prepare_environment(ctx)
|
|
333
|
+
|
|
334
|
+
strategy = select_strategy(ctx)
|
|
335
|
+
debug_log(f"Running tests with {strategy.name} strategy", "info")
|
|
336
|
+
return strategy.run()
|