# #region TestDemoModeIsolation [C:2] [TYPE Module] # @SEMANTICS: clean-release, demo-mode, isolation, namespace, repository # @PURPOSE: Verify demo and real mode namespace isolation contracts before TUI integration. # @LAYER Tests # @RELATION DEPENDS_ON -> [DemoDataService] from __future__ import annotations from datetime import UTC, datetime from src.models.clean_release import ReleaseCandidate from src.services.clean_release.demo_data_service import ( build_namespaced_id, create_isolated_repository, resolve_namespace, ) # #region test_resolve_namespace_separates_demo_and_real [C:2] [TYPE Function] # @RELATION BINDS_TO -> TestDemoModeIsolation # @PURPOSE: Ensure namespace resolver returns deterministic and distinct namespaces. # @PRE: Mode names are provided as user/runtime strings. # @POST: Demo and real namespaces are different and stable. def test_resolve_namespace_separates_demo_and_real() -> None: demo = resolve_namespace("demo") real = resolve_namespace("real") assert demo == "clean-release:demo" assert real == "clean-release:real" assert demo != real # #endregion test_resolve_namespace_separates_demo_and_real # #region test_build_namespaced_id_prevents_cross_mode_collisions [C:2] [TYPE Function] # @RELATION BINDS_TO -> TestDemoModeIsolation # @PURPOSE: Ensure ID generation prevents demo/real collisions for identical logical IDs. # @PRE: Same logical candidate id is used in two different namespaces. # @POST: Produced physical IDs differ by namespace prefix. def test_build_namespaced_id_prevents_cross_mode_collisions() -> None: logical_id = "2026.03.09-rc1" demo_id = build_namespaced_id(resolve_namespace("demo"), logical_id) real_id = build_namespaced_id(resolve_namespace("real"), logical_id) assert demo_id != real_id assert demo_id.startswith("clean-release:demo::") assert real_id.startswith("clean-release:real::") # #endregion test_build_namespaced_id_prevents_cross_mode_collisions # #region test_create_isolated_repository_keeps_mode_data_separate [C:2] [TYPE Function] # @RELATION BINDS_TO -> TestDemoModeIsolation # @PURPOSE: Verify demo and real repositories do not leak state across mode boundaries. # @PRE: Two repositories are created for distinct modes. # @POST: Candidate mutations in one mode are not visible in the other mode. def test_create_isolated_repository_keeps_mode_data_separate() -> None: demo_repo = create_isolated_repository("demo") real_repo = create_isolated_repository("real") demo_candidate_id = build_namespaced_id(resolve_namespace("demo"), "candidate-1") real_candidate_id = build_namespaced_id(resolve_namespace("real"), "candidate-1") demo_repo.save_candidate( ReleaseCandidate( id=demo_candidate_id, version="1.0.0", source_snapshot_ref="git:sha-demo", created_by="demo-operator", created_at=datetime.now(UTC), status="DRAFT", ) ) real_repo.save_candidate( ReleaseCandidate( id=real_candidate_id, version="1.0.0", source_snapshot_ref="git:sha-real", created_by="real-operator", created_at=datetime.now(UTC), status="DRAFT", ) ) assert demo_repo.get_candidate(demo_candidate_id) is not None assert demo_repo.get_candidate(real_candidate_id) is None assert real_repo.get_candidate(real_candidate_id) is not None assert real_repo.get_candidate(demo_candidate_id) is None # #endregion test_create_isolated_repository_keeps_mode_data_separate # #region test_build_namespaced_id_rejects_empty_namespace [C:2] [TYPE Function] def test_build_namespaced_id_rejects_empty_namespace() -> None: """build_namespaced_id with empty namespace raises ValueError.""" from src.services.clean_release.demo_data_service import build_namespaced_id import pytest with pytest.raises(ValueError, match="namespace must be non-empty"): build_namespaced_id("", "logical-1") with pytest.raises(ValueError, match="namespace must be non-empty"): build_namespaced_id(" ", "logical-1") # #endregion test_build_namespaced_id_rejects_empty_namespace # #region test_build_namespaced_id_rejects_empty_logical_id [C:2] [TYPE Function] def test_build_namespaced_id_rejects_empty_logical_id() -> None: """build_namespaced_id with empty logical_id raises ValueError.""" from src.services.clean_release.demo_data_service import build_namespaced_id import pytest with pytest.raises(ValueError, match="logical_id must be non-empty"): build_namespaced_id("demo", "") with pytest.raises(ValueError, match="logical_id must be non-empty"): build_namespaced_id("demo", " ") # #endregion test_build_namespaced_id_rejects_empty_logical_id # #region test_resolve_namespace_defaults_to_real [C:2] [TYPE Function] def test_resolve_namespace_defaults_to_real() -> None: """Non-demo mode defaults to real namespace.""" from src.services.clean_release.demo_data_service import resolve_namespace assert resolve_namespace("") == "clean-release:real" assert resolve_namespace(None) == "clean-release:real" assert resolve_namespace("production") == "clean-release:real" # #endregion test_resolve_namespace_defaults_to_real # #endregion TestDemoModeIsolation