Spaces:
Running
Running
| """The Hugging Face Space deploy configuration. | |
| A deploy variable is code that only ever executes on the platform, so a mistake | |
| in one is invisible until the Space refuses to boot — three minutes after the | |
| push, with a message that names the field but not the file. Everything checkable | |
| without credentials is checked here instead. | |
| """ | |
| from __future__ import annotations | |
| import importlib.util | |
| from pathlib import Path | |
| from types import ModuleType | |
| import pytest | |
| from app.core.settings import Settings | |
| REPO_ROOT = Path(__file__).resolve().parents[3] | |
| def _load_deploy_script() -> ModuleType: | |
| """Import the script by path: `scripts/` is a directory of CLIs, not a package.""" | |
| path = REPO_ROOT / "scripts" / "deploy_space.py" | |
| spec = importlib.util.spec_from_file_location("deploy_space", path) | |
| assert spec is not None and spec.loader is not None | |
| module = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(module) | |
| return module | |
| deploy = _load_deploy_script() | |
| class TestSpaceVariables: | |
| def test_they_build_a_real_settings_object(self) -> None: | |
| """Every deployed variable must be one `Settings` actually accepts. | |
| `ENVIRONMENT: prod` instead of `production` is the archetype: pydantic | |
| rejects it at import, so the container dies before serving a request and | |
| the platform reports it as a crashed build. | |
| """ | |
| settings = Settings(**{key.lower(): value for key, value in deploy.SPACE_VARIABLES.items()}) | |
| assert settings.environment == "production" | |
| assert settings.mcp_transport == "stdio" | |
| def test_production_is_declared(self) -> None: | |
| """On `local` the API mints an approval token into a file for the console. | |
| A Space shares no filesystem with the console, so that handshake cannot | |
| work there — and trusting a file would let anything able to write to the | |
| image supply the credential. `production` disables it and requires an | |
| explicit APPROVAL_TOKEN. | |
| """ | |
| assert deploy.SPACE_VARIABLES["ENVIRONMENT"] == "production" | |
| def test_cors_is_never_a_wildcard(self) -> None: | |
| """These endpoints take a bearer token; an open origin list undoes that.""" | |
| origins = deploy.SPACE_VARIABLES["CORS_ALLOW_ORIGINS"] | |
| assert "*" not in origins | |
| assert origins.startswith("https://") | |
| def test_no_secret_is_committed_as_a_variable(self) -> None: | |
| """Space variables are public. The day one holds a password, so is the password.""" | |
| secretish = ("KEY", "TOKEN", "PASSWORD", "SECRET", "DATABASE_URL") | |
| leaked = [name for name in deploy.SPACE_VARIABLES if any(s in name for s in secretish)] | |
| assert leaked == [] | |
| class TestFrontmatter: | |
| def test_the_committed_config_passes_its_own_check(self) -> None: | |
| config = (REPO_ROOT / ".hf-space.yml").read_text(encoding="utf-8") | |
| assert deploy.frontmatter_problems(config) == [] | |
| def test_a_missing_sdk_is_caught(self) -> None: | |
| problems = deploy.frontmatter_problems("title: AlphaBrief\napp_port: 7860\n") | |
| assert any("sdk: docker" in problem for problem in problems) | |
| def test_a_wrong_port_is_caught(self) -> None: | |
| """Spaces routes to the declared port; a mismatch is a silent 504.""" | |
| problems = deploy.frontmatter_problems("sdk: docker\napp_port: 8000\n") | |
| assert any("app_port" in problem for problem in problems) | |
| def test_an_over_long_description_is_caught_before_the_upload(self) -> None: | |
| """The Hub enforces this in a pre-receive hook — after the whole push.""" | |
| long = "x" * (deploy.SHORT_DESCRIPTION_MAX + 1) | |
| problems = deploy.frontmatter_problems( | |
| f'sdk: docker\napp_port: 7860\nshort_description: "{long}"\n' | |
| ) | |
| assert any("short_description" in problem for problem in problems) | |
| def test_a_description_containing_a_colon_is_measured_whole(self) -> None: | |
| """Splitting on every colon would truncate at the first and under-measure.""" | |
| problems = deploy.frontmatter_problems( | |
| 'sdk: docker\napp_port: 7860\nshort_description: "a: b"\n' | |
| ) | |
| assert problems == [] | |
| class TestPreflight: | |
| def test_the_repository_as_committed_is_deployable(self) -> None: | |
| assert deploy.preflight() == [] | |
| def test_the_dockerfile_builds_from_the_repository_root(self) -> None: | |
| """Spaces builds ./Dockerfile with the repo root as context, not apps/api. | |
| A COPY of a path relative to apps/api resolves against the root and fails | |
| with a bare "file not found" naming a path that does exist. | |
| """ | |
| content = (REPO_ROOT / "apps" / "api" / "Dockerfile").read_text(encoding="utf-8") | |
| assert "COPY apps/api/" in content | |
| def test_the_generated_root_dockerfile_is_gitignored(self) -> None: | |
| """It is written onto the deploy commit only; on main it would be a duplicate.""" | |
| ignored = (REPO_ROOT / ".gitignore").read_text(encoding="utf-8") | |
| assert "/Dockerfile" in ignored | |
| class TestPushFailureAdvice: | |
| def test_the_remote_reason_selects_the_advice(self, stderr: str, expected: str) -> None: | |
| """Guessing sends you to the tokens page while the real fault is elsewhere.""" | |
| assert expected in deploy.explain_push_failure(stderr) | |
| def test_an_unrecognised_failure_does_not_invent_a_cause(self) -> None: | |
| advice = deploy.explain_push_failure("remote: something entirely new") | |
| assert "above" in advice | |