"""The ONLY accepted credential is an own API key — LOAD-BEARING (§11, §1). The seam this file keeps alive: no run path in this framework may be paid for by a consumer Claude Code subscription. Until v0.1.1 ``run_s10`` printed a ``note:`` when ``ANTHROPIC_API_KEY`` was unset and carried on, letting the bundled CLI resolve its own login credentials. That silent fallback is the defect; the run must REFUSE instead. Why a subprocess and not an import: ``run_s10`` is the fasit run entrance and is never imported by this suite. Driving it as a module proves the real entrance, not a model of it — and the probe is network-free by construction, because the refusal is asserted to fire BEFORE the bundle is even opened. Detach proof (RED when the refusal is removed): with no key and a bundle path that does not exist, the refusal must win the race against the bundle error. Drop the refusal and the process reaches ``navigate_bundle`` instead — a different exit code, no refusal line, and the ``note:`` fallback back in the output. Positive control: the SAME invocation WITH a key set gets past the credential gate and fails on the missing bundle instead — so the gate is a gate, not a wall. """ from __future__ import annotations import os import subprocess import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] MISSING_BUNDLE = "/nonexistent-bundle-this-path-must-not-exist" # Shape only; never validated online and never leaves the process — the run is # asserted to stop on the missing bundle, long before any client is built. _FAKE_KEY = "sk-ant-api03-drill-not-a-real-key" def _run_s10(env_overrides: dict[str, str]) -> subprocess.CompletedProcess[str]: env = dict(os.environ) env.pop("ANTHROPIC_API_KEY", None) env.pop("CLAUDE_CODE_OAUTH_TOKEN", None) env.update(env_overrides) env["PYTHONPATH"] = str(REPO_ROOT / "src") return subprocess.run( [sys.executable, "-m", "portfolio_optimiser_claude.run_s10", "--bundle", MISSING_BUNDLE], capture_output=True, text=True, cwd=REPO_ROOT, env=env, timeout=120, ) class TestMissingApiKeyIsRefused: def test_no_key_refuses_before_anything_else_runs(self) -> None: proc = _run_s10({}) combined = proc.stdout + proc.stderr assert proc.returncode == 2, combined assert "ANTHROPIC_API_KEY" in combined assert "index.md" not in combined # the bundle was never opened def test_no_key_never_falls_back_to_the_cli_login(self) -> None: combined_out = _run_s10({}) combined = combined_out.stdout + combined_out.stderr assert "note:" not in combined assert "CLI's own credentials" not in combined def test_a_subscription_token_alone_is_not_enough(self) -> None: proc = _run_s10({"CLAUDE_CODE_OAUTH_TOKEN": "sk-ant-oat01-real"}) combined = proc.stdout + proc.stderr assert proc.returncode == 2, combined assert "ANTHROPIC_API_KEY" in combined class TestPositiveControl: def test_a_key_clears_the_gate_and_the_run_proceeds_to_the_bundle(self) -> None: # Proves the refusal is conditional on the key, not unconditional: with a # key the process gets PAST the gate and fails on the missing bundle. proc = _run_s10({"ANTHROPIC_API_KEY": _FAKE_KEY}) combined = proc.stdout + proc.stderr assert proc.returncode != 2, combined assert "index.md" in combined