Spaces:
Running
Running
File size: 5,791 Bytes
69e310f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | """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:
@pytest.mark.parametrize(
("stderr", "expected"),
[
("remote: YAML metadata is invalid", ".hf-space.yml"),
("remote: refusing binary files", "Xet"),
("fatal: Authentication failed", "WRITE token"),
],
)
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
|