← refacto-feature-scout
"""Whose Claude account gets used, and — the point of all of it — whose never does."""
import pytest
from scout import llm
from scout.llm import Access, NoLLMAccess, claude_env, credentials, require_llm_access
# What the operator's own machine has lying about in its environment. None of it may reach a
# model session started by anyone.
OPERATORS_SECRETS = {
"ANTILIB_GMAIL_CLIENT_ID": "operator-mail-id",
"ANTILIB_GMAIL_CLIENT_SECRET": "operator-mail-secret",
"ANTILIB_GMAIL_REFRESH_TOKEN": "operator-mail-token",
"LIBRARYMODE_TOKEN": "librarymode_operator_token",
"FLY_API_TOKEN": "fly-org-token",
"LITESTREAM_SECRET_ACCESS_KEY": "backup-key",
"SCOUT_SENDER": "someone@example.com",
}
@pytest.fixture(autouse=True)
def forget_yesterdays_answer(tmp_path, monkeypatch):
"""Each test decides for itself; nothing is carried in from a remembered check."""
monkeypatch.setattr(llm, "STATE_PATH", tmp_path / "state.json")
def test_none_of_the_operators_settings_reach_a_model_session():
env = {"PATH": "/usr/bin", "HOME": "/home/someone", **OPERATORS_SECRETS,
"ANTHROPIC_API_KEY": "sk-ant-theirs"}
out = claude_env(env)
for name, value in OPERATORS_SECRETS.items():
assert name not in out, f"{name} was handed to the model"
assert value not in out.values()
def test_the_environment_is_built_up_not_copied_down():
"""Anything invented later is excluded by default, not by remembering to exclude it."""
out = claude_env({"PATH": "/usr/bin", "SOME_FUTURE_SECRET": "oops"})
assert "SOME_FUTURE_SECRET" not in out
def test_a_persons_own_claude_settings_travel():
out = claude_env({"PATH": "/usr/bin", "CLAUDE_CONFIG_DIR": "/home/someone/.claude"})
assert out["CLAUDE_CONFIG_DIR"] == "/home/someone/.claude"
def test_a_nested_session_does_not_inherit_the_markers():
out = claude_env({"PATH": "/usr/bin", "CLAUDECODE": "1", "CLAUDE_CODE_ENTRYPOINT": "cli"})
assert "CLAUDECODE" not in out and "CLAUDE_CODE_ENTRYPOINT" not in out
def test_someone_whose_only_access_is_a_key_can_use_it():
"""The old rule threw the key away on any machine that wasn't the cloud, which would have
left a new user with no way to run at all."""
out = claude_env({"PATH": "/usr/bin", "ANTHROPIC_API_KEY": "sk-ant-theirs"})
assert out["ANTHROPIC_API_KEY"] == "sk-ant-theirs"
def test_a_signed_in_subscription_is_preferred_over_a_key(monkeypatch):
monkeypatch.setattr(llm, "_remembered_check", lambda: {"kind": "subscription", "detail": "d"})
out = claude_env({"PATH": "/usr/bin", "ANTHROPIC_API_KEY": "sk-ant-theirs"})
assert "ANTHROPIC_API_KEY" not in out
def test_the_cloud_machine_keeps_working_exactly_as_before(monkeypatch):
"""Its sign-in token travels; with no token it falls back to the key, as it does today."""
monkeypatch.setattr(llm, "CLOUD", True)
out = claude_env({"PATH": "/usr/bin", "CLAUDE_CODE_OAUTH_TOKEN": "oauth", **OPERATORS_SECRETS,
"ANTHROPIC_API_KEY": "sk-ant-operator"})
assert out["CLAUDE_CODE_OAUTH_TOKEN"] == "oauth"
assert "ANTHROPIC_API_KEY" not in out # the subscription wins, as before
assert "FLY_API_TOKEN" not in out # but the deploy token no longer travels
out = claude_env({"PATH": "/usr/bin", "ANTHROPIC_API_KEY": "sk-ant-operator"})
assert out["ANTHROPIC_API_KEY"] == "sk-ant-operator"
def test_what_account_is_in_use_is_said_without_saying_the_secret():
a = credentials({"ANTHROPIC_API_KEY": "sk-ant-abcd1234"})
assert a.kind == "api_key" and "1234" in a.detail and "sk-ant-abcd1234" not in a.detail
assert credentials({"CLAUDE_CODE_OAUTH_TOKEN": "x"}).kind == "subscription"
assert credentials({}).kind == "unknown" # only asking Claude can say
def test_nothing_runs_until_a_person_has_their_own_access():
with pytest.raises(NoLLMAccess) as e:
require_llm_access(prober=lambda: (False, "Claude is installed but not signed in."), env={})
said = str(e.value)
assert "runs on your own Claude account" in said
assert "console.anthropic.com" in said and "claude" in said
def test_a_working_login_is_remembered_so_it_is_not_paid_for_twice():
asked = []
def prober():
asked.append(1)
return True, "Claude answered."
assert llm.check_access(prober, env={}).usable
assert llm.check_access(prober, env={}).usable
assert len(asked) == 1
def test_being_out_of_credit_is_said_plainly():
access = llm.check_access(lambda: (False, "Your Claude account answered, but it's out of credit."),
env={})
assert not access.usable and "out of credit" in access.detail
def test_a_pasted_key_is_kept_on_this_computer_only(tmp_path, monkeypatch):
monkeypatch.setattr(llm, "DATA_DIR", tmp_path)
path = llm.save_api_key("sk-ant-mine")
assert path.read_text().strip().endswith("sk-ant-mine")
assert oct(path.stat().st_mode)[-3:] == "600"
def test_saving_a_key_twice_does_not_leave_two(tmp_path, monkeypatch):
monkeypatch.setattr(llm, "DATA_DIR", tmp_path)
llm.save_api_key("sk-ant-one")
path = llm.save_api_key("sk-ant-two")
keys = [ln for ln in path.read_text().splitlines() if ln.startswith("ANTHROPIC_API_KEY=")]
assert keys == ["ANTHROPIC_API_KEY=sk-ant-two"]
def test_no_personal_address_is_saved_in_the_project():
"""The project is public on librarymo.de. The owner's addresses live in Fly secrets (see
docs/OPERATIONS.md), never in the files that get saved."""
import re
from pathlib import Path
root = Path(__file__).resolve().parents[1]
saved = [p for pat in ("fly.toml", "cloud/*", "launchd/*", "scripts/*", "docs/*.md", "README.md", "scout/**/*.py")
for p in root.glob(pat) if p.is_file()]
assert saved
address = re.compile(r"[A-Za-z0-9._%+-]+@(?!example\.com|localhost)[A-Za-z0-9.-]+\.[a-z]{2,}")
hits = [f"{p.relative_to(root)}: {m.group(0)}" for p in saved
for m in address.finditer(p.read_text(encoding="utf-8", errors="ignore"))
if not m.group(0).endswith(("@librarymo.de", "@anthropic.com"))]
assert hits == [], hits