"""Section 43 — Install Script: 7-layer test suite. Covers: - musehub/api/routes/musehub/install.py::sanitize_musehub_url, build_sdist_url, generate_install_script, generate_uninstall_script, GET /install.sh, GET /uninstall.sh """ from __future__ import annotations import re import time import pytest from musehub.api.routes.musehub.install import ( _SAFE_URL_RE, build_sdist_url, generate_install_script, generate_uninstall_script, sanitize_musehub_url, ) from musehub.protocol.version import MUSE_VERSION # ───────────────────────────────────────────────────────────────────────────── # LAYER 1 — UNIT # ───────────────────────────────────────────────────────────────────────────── class TestSanitizeMusehubUrlUnit: """Unit: sanitize_musehub_url rejects unsafe URLs.""" def test_accepts_http_url(self) -> None: assert sanitize_musehub_url("https://localhost:1337") == "https://localhost:1337" def test_accepts_https_url(self) -> None: assert sanitize_musehub_url("https://musehub.example.com") == "https://musehub.example.com" def test_accepts_url_with_path(self) -> None: result = sanitize_musehub_url("https://hub.example.com/muse") assert result == "https://hub.example.com/muse" def test_strips_trailing_slash(self) -> None: result = sanitize_musehub_url("https://hub.example.com/") assert not result.endswith("/") def test_rejects_url_with_double_quote(self) -> None: with pytest.raises(ValueError): sanitize_musehub_url('https://example.com"evil') def test_rejects_url_with_single_quote(self) -> None: with pytest.raises(ValueError): sanitize_musehub_url("https://example.com'evil") def test_rejects_url_with_backtick(self) -> None: with pytest.raises(ValueError): sanitize_musehub_url("https://example.com`id`") def test_rejects_url_with_dollar(self) -> None: with pytest.raises(ValueError): sanitize_musehub_url("https://example.com$HOME") def test_rejects_url_with_semicolon(self) -> None: with pytest.raises(ValueError): sanitize_musehub_url("https://example.com;rm -rf /") def test_rejects_url_with_newline(self) -> None: with pytest.raises(ValueError): sanitize_musehub_url("https://example.com\nevil") def test_rejects_non_http_scheme(self) -> None: with pytest.raises(ValueError): sanitize_musehub_url("ftp://example.com") def test_rejects_empty_string(self) -> None: with pytest.raises(ValueError): sanitize_musehub_url("") class TestBuildSdistUrlUnit: """Unit: build_sdist_url produces correct URLs — no platform/arch needed.""" def test_basic_url(self) -> None: url = build_sdist_url("https://hub.example.com", "1.2.3") assert url == "https://hub.example.com/releases/muse-1.2.3.tar.gz" def test_ends_with_tar_gz(self) -> None: url = build_sdist_url("https://localhost:1337", "0.1.0") assert url.endswith(".tar.gz") def test_version_embedded(self) -> None: url = build_sdist_url("http://localhost", "9.9.9") assert "9.9.9" in url def test_under_releases_path(self) -> None: url = build_sdist_url("https://hub.example.com", "1.0.0") assert "/releases/" in url def test_no_platform_in_url(self) -> None: url = build_sdist_url("https://hub.example.com", "1.0.0") for platform in ("linux", "darwin", "arm64", "x86_64"): assert platform not in url class TestGenerateInstallScriptUnit: """Unit: generate_install_script content and structure.""" def test_starts_with_shebang(self) -> None: script = generate_install_script("https://localhost:1337") assert script.startswith("#!/usr/bin/env sh") def test_contains_musehub_url(self) -> None: url = "https://hub.example.com" script = generate_install_script(url) assert url in script def test_url_is_double_quoted_in_assignment(self) -> None: url = "https://hub.example.com" script = generate_install_script(url) assert f'MUSEHUB_URL="{url}"' in script def test_contains_muse_version(self) -> None: script = generate_install_script("https://localhost:1337") assert MUSE_VERSION in script def test_contains_set_euf(self) -> None: script = generate_install_script("https://localhost:1337") assert "set -euf" in script def test_contains_tar_gz_archive_reference(self) -> None: script = generate_install_script("https://localhost:1337") assert ".tar.gz" in script def test_requires_python_314(self) -> None: script = generate_install_script("https://localhost:1337") assert "3.14" in script def test_creates_venv(self) -> None: script = generate_install_script("https://localhost:1337") assert "venv" in script def test_uses_pip_install(self) -> None: script = generate_install_script("https://localhost:1337") assert "pip" in script and "install" in script def test_creates_symlink(self) -> None: script = generate_install_script("https://localhost:1337") assert "ln -sf" in script def test_no_eval_in_script(self) -> None: script = generate_install_script("https://localhost:1337") assert "\neval " not in script and " eval " not in script def test_raises_for_unsafe_url(self) -> None: with pytest.raises(ValueError): generate_install_script('http://evil.com";rm -rf /') def test_custom_version_embedded(self) -> None: script = generate_install_script("https://localhost:1337", version="99.0.0") assert "99.0.0" in script class TestGenerateUninstallScriptUnit: """Unit: generate_uninstall_script never removes ~/.muse data.""" def test_starts_with_shebang(self) -> None: script = generate_uninstall_script() assert script.startswith("#!/usr/bin/env sh") def test_contains_set_euf(self) -> None: script = generate_uninstall_script() assert "set -euf" in script def test_mentions_muse_dir_preserved(self) -> None: script = generate_uninstall_script() assert "~/.muse" in script assert "preserved" in script.lower() or "never" in script.lower() def test_does_not_rm_rf_muse_dir(self) -> None: script = generate_uninstall_script() for line in script.splitlines(): stripped = line.strip() if stripped.startswith("#"): continue if re.match(r"^rm\s", stripped) and "~/.muse" in stripped: pytest.fail(f"Active rm targeting .muse: {stripped!r}") def test_removes_symlink_and_venv(self) -> None: script = generate_uninstall_script() assert "rm" in script assert "venv" in script.lower() or "VENV" in script # ───────────────────────────────────────────────────────────────────────────── # LAYER 2 — INTEGRATION # ───────────────────────────────────────────────────────────────────────────── class TestInstallScriptIntegration: """Integration: script functions work together coherently.""" def test_install_script_contains_sdist_url_pattern(self) -> None: base = "https://hub.example.com" script = generate_install_script(base) sdist_url = build_sdist_url(base, MUSE_VERSION) # The sdist URL or its components must be constructable from the script's vars assert base in script assert MUSE_VERSION in script assert sdist_url.endswith(".tar.gz") def test_safe_url_re_pattern_matches_valid_urls(self) -> None: valid = [ "https://localhost:1337", "https://musehub.example.com", "https://hub.example.com/muse", "http://192.168.1.1:8080", ] for url in valid: assert _SAFE_URL_RE.match(url), f"Expected {url!r} to be safe" def test_safe_url_re_pattern_rejects_metacharacters(self) -> None: dangerous = [ "http://evil.com\";rm -rf /", "http://evil.com`id`", "http://evil.com$HOME", "http://evil.com;evil", "http://evil.com'x", ] for url in dangerous: assert not _SAFE_URL_RE.match(url), f"Expected {url!r} to be rejected" def test_generate_install_script_version_matches_muse_version_default(self) -> None: script = generate_install_script("http://localhost") assert MUSE_VERSION in script def test_uninstall_does_not_reference_musehub_url(self) -> None: """Uninstall script is URL-independent — no MUSEHUB_URL substitution needed.""" script = generate_uninstall_script() assert "MUSEHUB_URL" not in script def test_build_sdist_url_with_current_version(self) -> None: url = build_sdist_url("https://staging.musehub.ai", MUSE_VERSION) assert MUSE_VERSION in url assert url.endswith(".tar.gz") assert "linux" not in url assert "darwin" not in url # ───────────────────────────────────────────────────────────────────────────── # LAYER 3 — E2E # ───────────────────────────────────────────────────────────────────────────── class TestInstallScriptE2E: """E2E: /install.sh and /uninstall.sh endpoints via async test client.""" async def test_get_install_sh_returns_200(self, client: AsyncClient) -> None: r = await client.get("/install.sh") assert r.status_code == 200 async def test_get_install_sh_content_type_is_shell(self, client: AsyncClient) -> None: r = await client.get("/install.sh") ct = r.headers.get("content-type", "") assert "sh" in ct or "plain" in ct async def test_get_install_sh_starts_with_shebang(self, client: AsyncClient) -> None: r = await client.get("/install.sh") assert r.text.startswith("#!/usr/bin/env sh") async def test_get_install_sh_contains_set_euf(self, client: AsyncClient) -> None: r = await client.get("/install.sh") assert "set -euf" in r.text async def test_get_install_sh_contains_muse_version(self, client: AsyncClient) -> None: r = await client.get("/install.sh") assert MUSE_VERSION in r.text async def test_get_install_sh_contains_tar_gz(self, client: AsyncClient) -> None: r = await client.get("/install.sh") assert ".tar.gz" in r.text async def test_get_install_sh_contains_venv(self, client: AsyncClient) -> None: r = await client.get("/install.sh") assert "venv" in r.text async def test_get_install_sh_no_auth_required(self, client: AsyncClient) -> None: r = await client.get("/install.sh") assert r.status_code == 200 async def test_get_uninstall_sh_returns_200(self, client: AsyncClient) -> None: r = await client.get("/uninstall.sh") assert r.status_code == 200 async def test_get_uninstall_sh_content_type_is_shell(self, client: AsyncClient) -> None: r = await client.get("/uninstall.sh") ct = r.headers.get("content-type", "") assert "sh" in ct or "plain" in ct async def test_get_uninstall_sh_starts_with_shebang(self, client: AsyncClient) -> None: r = await client.get("/uninstall.sh") assert r.text.startswith("#!/usr/bin/env sh") async def test_get_uninstall_sh_no_auth_required(self, client: AsyncClient) -> None: r = await client.get("/uninstall.sh") assert r.status_code == 200 async def test_get_uninstall_sh_does_not_rm_muse_dir(self, client: AsyncClient) -> None: r = await client.get("/uninstall.sh") for line in r.text.splitlines(): stripped = line.strip() if stripped.startswith("#"): continue if re.match(r"^rm\s", stripped) and "~/.muse" in stripped: pytest.fail(f"Uninstall script removes .muse: {stripped!r}") async def test_get_uninstall_sh_mentions_muse_dir_preserved(self, client: AsyncClient) -> None: r = await client.get("/uninstall.sh") text = r.text.lower() assert "~/.muse" in text assert "preserved" in text or "never" in text # ───────────────────────────────────────────────────────────────────────────── # LAYER 4 — STRESS # ───────────────────────────────────────────────────────────────────────────── class TestInstallScriptStress: """Stress: repeated generation and URL validation at volume.""" def test_generate_install_script_1000_times_identical(self) -> None: url = "https://hub.example.com" first = generate_install_script(url) for _ in range(1000): assert generate_install_script(url) == first def test_generate_uninstall_script_1000_times_identical(self) -> None: first = generate_uninstall_script() for _ in range(1000): assert generate_uninstall_script() == first def test_sanitize_url_10000_valid_urls(self) -> None: for i in range(10_000): url = f"https://hub{i}.example.com" result = sanitize_musehub_url(url) assert result == url def test_build_sdist_url_1000_times(self) -> None: for _ in range(1000): url = build_sdist_url("https://hub.example.com", MUSE_VERSION) assert url.endswith(".tar.gz") async def test_get_install_sh_20_sequential_requests_identical(self, client: AsyncClient) -> None: first = None for _ in range(20): r = await client.get("/install.sh") assert r.status_code == 200 if first is None: first = r.text assert r.text == first # ───────────────────────────────────────────────────────────────────────────── # LAYER 5 — DATA INTEGRITY # ───────────────────────────────────────────────────────────────────────────── class TestInstallScriptDataIntegrity: """Data Integrity: script content correctness and determinism.""" def test_install_script_is_valid_utf8(self) -> None: script = generate_install_script("https://localhost:1337") encoded = script.encode("utf-8") assert encoded.decode("utf-8") == script def test_uninstall_script_is_valid_utf8(self) -> None: script = generate_uninstall_script() encoded = script.encode("utf-8") assert encoded.decode("utf-8") == script def test_install_script_deterministic_same_url(self) -> None: url = "https://hub.example.com" assert generate_install_script(url) == generate_install_script(url) def test_install_script_different_for_different_urls(self) -> None: s1 = generate_install_script("https://hub1.example.com") s2 = generate_install_script("https://hub2.example.com") assert s1 != s2 def test_sdist_url_unique_per_version(self) -> None: urls = [build_sdist_url("https://hub.example.com", v) for v in ("1.0.0", "2.0.0", "3.0.0")] assert len(urls) == len(set(urls)) def test_uninstall_script_does_not_change_between_calls(self) -> None: assert generate_uninstall_script() == generate_uninstall_script() def test_install_script_uses_mktemp_for_tmp_dir(self) -> None: script = generate_install_script("http://localhost") assert "mktemp" in script def test_install_script_cleans_up_tmp_on_exit(self) -> None: script = generate_install_script("http://localhost") assert "trap" in script or "cleanup" in script def test_install_script_creates_bin_dir_with_mkdir_p(self) -> None: script = generate_install_script("http://localhost") assert "mkdir -p" in script # ───────────────────────────────────────────────────────────────────────────── # LAYER 6 — SECURITY # ───────────────────────────────────────────────────────────────────────────── class TestInstallScriptSecurity: """Security: shell injection prevention and uninstall data-preservation guarantee.""" @pytest.mark.parametrize("injection", [ 'https://evil.com"; rm -rf / ; echo "', "https://evil.com`id`", "https://evil.com$(id)", "https://evil.com$HOME", "https://evil.com; cat /etc/passwd", "https://evil.com\nrm -rf /", "https://evil.com' && evil", ]) def test_sanitize_rejects_injection_url(self, injection: str) -> None: with pytest.raises(ValueError): sanitize_musehub_url(injection) @pytest.mark.parametrize("injection", [ 'https://evil.com"; rm -rf / ; echo "', "https://evil.com`id`", ]) def test_generate_install_raises_for_injection(self, injection: str) -> None: with pytest.raises(ValueError): generate_install_script(injection) def test_uninstall_script_never_removes_muse_dir(self) -> None: script = generate_uninstall_script() for line in script.splitlines(): stripped = line.strip() if stripped.startswith("#"): continue if re.match(r"^rm\s", stripped) and "~/.muse" in stripped: pytest.fail(f"Active rm targeting .muse: {stripped!r}") def test_install_script_no_eval(self) -> None: script = generate_install_script("http://localhost") for line in script.splitlines(): if line.strip().startswith("#"): continue assert not re.match(r"^\s*eval\s", line), f"eval found: {line!r}" def test_install_script_no_source_untrusted(self) -> None: script = generate_install_script("http://localhost") for line in script.splitlines(): if line.strip().startswith("#"): continue assert not re.search(r"\bsource\s+https?://", line) assert not re.search(r"^\s*\.\s+https?://", line) async def test_endpoints_return_200_not_500(self, client: AsyncClient) -> None: for path in ("/install.sh", "/uninstall.sh"): r = await client.get(path) assert r.status_code == 200, f"{path} returned {r.status_code}" async def test_endpoints_no_stack_trace(self, client: AsyncClient) -> None: for path in ("/install.sh", "/uninstall.sh"): r = await client.get(path) assert "Traceback" not in r.text def test_install_script_musehub_url_quoted(self) -> None: url = "https://hub.example.com" script = generate_install_script(url) assert f'MUSEHUB_URL="{url}"' in script # ───────────────────────────────────────────────────────────────────────────── # LAYER 7 — PERFORMANCE # ───────────────────────────────────────────────────────────────────────────── class TestInstallScriptPerformance: """Performance: script generation and endpoint latency budgets.""" def test_generate_install_script_under_5ms(self) -> None: url = "https://localhost:1337" t0 = time.perf_counter() generate_install_script(url) elapsed = time.perf_counter() - t0 assert elapsed < 0.005, f"generate_install_script took {elapsed*1000:.1f}ms" def test_generate_uninstall_script_under_1ms(self) -> None: t0 = time.perf_counter() generate_uninstall_script() elapsed = time.perf_counter() - t0 assert elapsed < 0.001, f"generate_uninstall_script took {elapsed*1000:.1f}ms" def test_sanitize_url_under_1ms_per_call(self) -> None: url = "https://hub.example.com" t0 = time.perf_counter() for _ in range(1000): sanitize_musehub_url(url) elapsed = time.perf_counter() - t0 assert elapsed < 0.1, f"1K sanitize_url calls took {elapsed*1000:.1f}ms" async def test_get_install_sh_under_100ms(self, client: AsyncClient) -> None: t0 = time.perf_counter() r = await client.get("/install.sh") elapsed = time.perf_counter() - t0 assert r.status_code == 200 assert elapsed < 0.1, f"GET /install.sh took {elapsed*1000:.1f}ms" async def test_get_uninstall_sh_under_100ms(self, client: AsyncClient) -> None: t0 = time.perf_counter() r = await client.get("/uninstall.sh") elapsed = time.perf_counter() - t0 assert r.status_code == 200 assert elapsed < 0.1, f"GET /uninstall.sh took {elapsed*1000:.1f}ms"