"""The v1 release contract, enforced. A release that misdescribes itself is a release nobody can support. These tests hold the version to one source of truth, hold the compatibility ranges to explicit answers, and hold production configuration to rules a development environment is allowed to break. """ from __future__ import annotations import json import re import tomllib from pathlib import Path import pytest from pydantic import SecretStr from modelforge_api import __version__ from modelforge_api.domain.release import ( CURRENT_AGENT_PROTOCOL_VERSION, MINIMUM_POSTGRES_MAJOR, MINIMUM_UPGRADE_SOURCE, PRODUCT_VERSION, RELEASE_CHANNEL, SUPPORTED_AGENT_PROTOCOL_VERSIONS, SUPPORTED_SCHEMA_REVISIONS, SUPPORTED_UPGRADE_SOURCE_SCHEMA_REVISIONS, TARGET_SCHEMA_REVISION, Compatibility, SemanticVersion, _canonicalize_packaged_version, agent_protocol_compatibility, build_identity, release_channel_for, schema_compatibility, upgrade_required, ) from modelforge_api.services.startup_validation import ( StartupFailureCode, StartupValidationError, enforce_startup, validate_settings, ) from modelforge_api.settings import Settings ROOT = Path(__file__).resolve().parents[2] def test_the_version_file_is_the_single_source_of_truth() -> None: declared = (ROOT / "VERSION").read_text("utf-8").strip() assert declared == PRODUCT_VERSION assert declared == __version__ @pytest.mark.parametrize( "manifest", ["backend/pyproject.toml", "node-agent/pyproject.toml", "runtime-worker/pyproject.toml"], ) def test_every_python_manifest_matches_the_version_file(manifest: str) -> None: data = tomllib.loads((ROOT / manifest).read_text("utf-8")) assert data["project"]["version"] == PRODUCT_VERSION, ( f"{manifest} declares {data['project']['version']!r}, VERSION says {PRODUCT_VERSION!r}" ) def test_the_console_manifest_matches_the_version_file() -> None: data = json.loads((ROOT / "frontend" / "package.json").read_text("utf-8")) assert data["version"] == PRODUCT_VERSION @pytest.mark.parametrize( "module", [ "node-agent/src/modelforge_node_agent/__init__.py", "runtime-worker/src/modelforge_runtime_worker/__init__.py", ], ) def test_every_component_module_matches_the_version_file(module: str) -> None: text = (ROOT / module).read_text("utf-8") match = re.search(r'__version__ = "([^"]+)"', text) assert match is not None, f"{module} declares no __version__" assert match.group(1) == PRODUCT_VERSION def test_the_release_version_is_the_explicit_rc_prerelease() -> None: parsed = SemanticVersion.parse(PRODUCT_VERSION) assert parsed.is_prerelease assert parsed.prerelease == "rc.1" assert RELEASE_CHANNEL == "rc" assert parsed.major >= 1, "a v1 release cannot carry a 0.x major" def test_release_channel_is_derived_from_a_semantic_version() -> None: assert release_channel_for("1.3.0") == "stable" assert release_channel_for("1.3.0-rc.2") == "rc" assert release_channel_for("1.3.0-rc.preview") == "prerelease" assert release_channel_for("1.3.0-preview.2") == "prerelease" @pytest.mark.parametrize( ("metadata_version", "expected"), [ ("1.3.0", "1.3.0"), ("1.3.0rc1", "1.3.0-rc.1"), ("1.3.0rc01", "1.3.0rc01"), ("1.3.0a1", "1.3.0a1"), ], ) def test_installed_package_metadata_only_canonicalizes_numbered_rc_semver( metadata_version: str, expected: str ) -> None: assert _canonicalize_packaged_version(metadata_version) == expected @pytest.mark.parametrize("bad", ["1.0", "1.0.0.0", "v1.0.0", "one.0.0", "1.0.0-", ""]) def test_a_malformed_version_is_refused(bad: str) -> None: with pytest.raises(ValueError): SemanticVersion.parse(bad) def test_the_target_schema_revision_is_supported() -> None: assert TARGET_SCHEMA_REVISION == "20260924_0039" assert TARGET_SCHEMA_REVISION in SUPPORTED_SCHEMA_REVISIONS assert schema_compatibility(TARGET_SCHEMA_REVISION) is Compatibility.COMPATIBLE assert "20260827_0021" in SUPPORTED_UPGRADE_SOURCE_SCHEMA_REVISIONS assert "20260828_0022" in SUPPORTED_UPGRADE_SOURCE_SCHEMA_REVISIONS assert "20260923_0037" in SUPPORTED_UPGRADE_SOURCE_SCHEMA_REVISIONS assert "20260827_0021" not in SUPPORTED_SCHEMA_REVISIONS assert "20260923_0037" not in SUPPORTED_SCHEMA_REVISIONS def test_the_rc_schema_has_one_explicit_direct_upgrade_contract() -> None: assert PRODUCT_VERSION == "1.3.0-rc.1" assert MINIMUM_UPGRADE_SOURCE == "v1.0.0" assert SUPPORTED_UPGRADE_SOURCE_SCHEMA_REVISIONS == ( "20260827_0021", "20260828_0022", "20260830_0023", "20260830_0024", "20260907_0025", "20260908_0026", "20260908_0027", "20260908_0028", "20260909_0029", "20260909_0030", "20260909_0031", "20260909_0032", "20260909_0033", "20260910_0034", "20260911_0035", "20260923_0036", "20260923_0037", "20260924_0038", "20260924_0039", ) def test_v1_1_documents_the_destructive_downgrade_boundary_truthfully() -> None: migration = ( ROOT / "backend" / "alembic" / "versions" / "20260828_0022_node_decommission.py" ).read_text("utf-8") upgrade_runbook = (ROOT / "docs" / "UPGRADE.md").read_text("utf-8") upgrade_tool = (ROOT / "scripts" / "upgrade.py").read_text("utf-8") assert "def downgrade()" in migration assert "tombstone-free" in upgrade_runbook assert "not the production rollback promise" in upgrade_runbook assert "rehearsal-only" in upgrade_tool @pytest.mark.parametrize( ("revision", "expected"), [ ("20260923_0036", Compatibility.TOO_OLD), ("20260923_0037", Compatibility.TOO_OLD), ("20260924_0038", Compatibility.TOO_OLD), ("20260924_0039", Compatibility.COMPATIBLE), ("20260911_0035", Compatibility.TOO_OLD), ("20260910_0034", Compatibility.TOO_OLD), ("20260909_0033", Compatibility.TOO_OLD), ("20260909_0032", Compatibility.TOO_OLD), ("20260908_0028", Compatibility.TOO_OLD), ("20260908_0027", Compatibility.TOO_OLD), ("20260908_0026", Compatibility.TOO_OLD), ("20260907_0025", Compatibility.TOO_OLD), ("20260830_0024", Compatibility.TOO_OLD), ("20260830_0023", Compatibility.TOO_OLD), ("20260828_0022", Compatibility.TOO_OLD), ("20260827_0021", Compatibility.TOO_OLD), ("20260101_0001", Compatibility.TOO_OLD), ("20990101_0099", Compatibility.TOO_NEW), (None, Compatibility.UNKNOWN), ], ) def test_schema_compatibility_is_explicit(revision: str | None, expected: Compatibility) -> None: assert schema_compatibility(revision) is expected @pytest.mark.parametrize( ("protocol", "expected"), [ (1, Compatibility.COMPATIBLE), (0, Compatibility.TOO_OLD), (2, Compatibility.TOO_NEW), (None, Compatibility.UNKNOWN), ], ) def test_agent_protocol_compatibility_is_explicit( protocol: int | None, expected: Compatibility ) -> None: assert agent_protocol_compatibility(protocol) is expected def test_only_a_compatible_answer_requires_no_operator_action() -> None: assert upgrade_required(Compatibility.COMPATIBLE) is None for other in (Compatibility.TOO_OLD, Compatibility.TOO_NEW, Compatibility.UNKNOWN): message = upgrade_required(other) assert message and message.strip(), f"{other} must tell the operator what to do" def test_the_control_plane_speaks_a_protocol_it_supports() -> None: assert CURRENT_AGENT_PROTOCOL_VERSION in SUPPORTED_AGENT_PROTOCOL_VERSIONS def test_the_declared_protocol_matches_the_wire_constant() -> None: from modelforge_api.domain.agent_protocol import AGENT_PROTOCOL_VERSION assert AGENT_PROTOCOL_VERSION == CURRENT_AGENT_PROTOCOL_VERSION def test_build_identity_reports_absent_values_as_null_rather_than_guessing() -> None: identity = build_identity() assert identity.version == PRODUCT_VERSION assert identity.source_commit is None assert identity.built_at is None assert identity.image_digest is None assert identity.channel == RELEASE_CHANNEL assert identity.schema_revision == TARGET_SCHEMA_REVISION @pytest.mark.parametrize("placeholder", ["", " ", "unknown", "UNKNOWN", "none", "null"]) def test_an_unsubstituted_build_argument_is_not_reported_as_a_fact(placeholder: str) -> None: identity = build_identity(source_commit=placeholder, image_digest=placeholder) assert identity.source_commit is None assert identity.image_digest is None def test_build_identity_carries_a_real_commit_when_given_one() -> None: identity = build_identity(source_commit="657ad91", built_at="2026-08-27T12:00:00Z") assert identity.source_commit == "657ad91" assert identity.built_at == "2026-08-27T12:00:00Z" assert "source_commit" in identity.as_dict() def _production(**overrides: object) -> Settings: base: dict[str, object] = { "env": "production", "operator_api_key": SecretStr("k" * 48), "backup_encryption_key": SecretStr("x" * 44), "database_url": "postgresql+psycopg://modelforge_runtime:aVeryLongProductionSecret@db:5432/mf", "redis_url": "redis://cache:6379/0", "cors_origins": "https://console.example.test", "allow_remote_code": False, } base.update(overrides) return Settings(**base) # type: ignore[arg-type] def _codes(settings: Settings) -> set[str]: return {str(problem.code) for problem in validate_settings(settings).problems} def test_production_without_an_operator_key_is_refused() -> None: settings = _production(operator_api_key=None) assert StartupFailureCode.MISSING_REQUIRED_SETTING in _codes(settings) def test_production_with_a_short_operator_key_is_refused() -> None: assert StartupFailureCode.INSECURE_PRODUCTION_SETTING in _codes( _production(operator_api_key=SecretStr("short")) ) def test_production_with_a_development_database_password_is_refused() -> None: settings = _production( database_url="postgresql+psycopg://modelforge:modelforge@db:5432/modelforge" ) assert StartupFailureCode.INSECURE_PRODUCTION_SETTING in _codes(settings) def test_production_never_permits_remote_code_execution() -> None: assert StartupFailureCode.INSECURE_PRODUCTION_SETTING in _codes( _production(allow_remote_code=True) ) def test_production_without_a_backup_encryption_key_is_refused() -> None: assert StartupFailureCode.MISSING_REQUIRED_SETTING in _codes( _production(backup_encryption_key=None) ) def test_production_refuses_a_wildcard_cors_origin() -> None: assert StartupFailureCode.INSECURE_PRODUCTION_SETTING in _codes(_production(cors_origins="*")) @pytest.mark.parametrize( "url", ["", "not-a-url", "mysql://db/mf", "postgresql+psycopg:///mf"] ) def test_an_invalid_database_url_is_refused(url: str) -> None: assert StartupFailureCode.INVALID_URL in _codes(_production(database_url=url)) @pytest.mark.parametrize("url", ["", "http://cache:6379", "memcached://cache"]) def test_an_invalid_redis_url_is_refused(url: str) -> None: assert StartupFailureCode.INVALID_URL in _codes(_production(redis_url=url)) def test_a_missing_storage_root_is_refused(tmp_path: Path) -> None: settings = _production(artifact_root=str(tmp_path / "absent")) problems = validate_settings(settings).problems assert any( problem.code is StartupFailureCode.INVALID_STORAGE_ROOT and problem.setting == "MODELFORGE_ARTIFACT_ROOT" for problem in problems ) def test_a_storage_root_that_is_a_file_is_refused(tmp_path: Path) -> None: target = tmp_path / "not-a-directory" target.write_text("", encoding="utf-8") settings = _production(artifact_root=str(target)) assert StartupFailureCode.INVALID_STORAGE_ROOT in _codes(settings) def test_a_writable_storage_root_is_accepted(tmp_path: Path) -> None: settings = _production( artifact_root=str(tmp_path), quarantine_root=str(tmp_path), backup_root=tmp_path, ) roots = { problem.setting for problem in validate_settings(settings).problems if problem.code is StartupFailureCode.INVALID_STORAGE_ROOT } assert roots == set() @pytest.mark.parametrize( "overrides", [ {"scheduler_safety_reserve_percentage": 0.5}, {"node_stale_after_seconds": 90, "node_offline_after_seconds": 90}, {"gateway_queue_timeout_seconds": 45, "gateway_request_timeout_seconds": 45}, ], ) def test_an_impossible_policy_is_refused(overrides: dict[str, object]) -> None: assert StartupFailureCode.IMPOSSIBLE_POLICY in _codes(_production(**overrides)) def test_a_sound_production_configuration_has_no_problems(tmp_path: Path) -> None: settings = _production( artifact_root=str(tmp_path), quarantine_root=str(tmp_path), backup_root=tmp_path ) report = validate_settings(settings) assert report.ok, [str(problem) for problem in report.problems] def test_production_refuses_to_start_while_development_defaults_survive(tmp_path: Path) -> None: settings = _production( operator_api_key=None, artifact_root=str(tmp_path), quarantine_root=str(tmp_path), backup_root=tmp_path, ) with pytest.raises(StartupValidationError) as raised: enforce_startup(settings) assert "MODELFORGE_OPERATOR_API_KEY" in str(raised.value) def test_development_reports_the_same_problems_without_refusing_to_start(tmp_path: Path) -> None: settings = Settings( env="development", operator_api_key=None, artifact_root=str(tmp_path), quarantine_root=str(tmp_path), backup_root=tmp_path, ) report = enforce_startup(settings) assert report.ok or report.problems is not None def test_the_minimum_postgres_major_is_stated() -> None: assert MINIMUM_POSTGRES_MAJOR >= 16 def test_the_node_agent_pins_the_control_plane_at_the_product_version() -> None: data = tomllib.loads((ROOT / "node-agent" / "pyproject.toml").read_text("utf-8")) pins = [ dependency for dependency in data["project"]["dependencies"] if dependency.startswith("modelforge-api") ] assert pins == [f"modelforge-api=={PRODUCT_VERSION}"], ( f"the Node Agent pins {pins}, but the product version is {PRODUCT_VERSION}" )