"""Shared helpers for Claude Agent SDK tests. Loads ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN from the project root .env and injects them into the process environment and ClaudeAgentOptions.env. """ from __future__ import annotations import os import shutil import tempfile from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[2] DOTENV_PATH = PROJECT_ROOT / ".env" def load_dotenv(dotenv_path: Path = DOTENV_PATH) -> dict[str, str]: """Parse KEY=VALUE lines from a .env file (no third-party deps).""" values: dict[str, str] = {} if not dotenv_path.exists(): return values for raw_line in dotenv_path.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#") or "=" not in line: continue key, value = line.split("=", 1) values[key.strip()] = value.strip().strip('"').strip("'") return values def setup_auth() -> tuple[bool, str]: """Inject .env auth vars into os.environ; clear ANTHROPIC_API_KEY to avoid conflicts.""" values = load_dotenv() base_url = values.get("ANTHROPIC_BASE_URL") auth_token = values.get("ANTHROPIC_AUTH_TOKEN") oauth_token = values.get("CLAUDE_CODE_OAUTH_TOKEN") if not base_url: return False, "Missing ANTHROPIC_BASE_URL in .env" if not auth_token and not oauth_token: return False, "Missing ANTHROPIC_AUTH_TOKEN or CLAUDE_CODE_OAUTH_TOKEN in .env" os.environ["ANTHROPIC_BASE_URL"] = base_url if auth_token: os.environ["ANTHROPIC_AUTH_TOKEN"] = auth_token if oauth_token: os.environ["CLAUDE_CODE_OAUTH_TOKEN"] = oauth_token os.environ.pop("ANTHROPIC_API_KEY", None) return True, f"Auth loaded from {DOTENV_PATH}" def auth_env() -> dict[str, str]: """Return env dict suitable for ClaudeAgentOptions(env=...).""" return { "ANTHROPIC_BASE_URL": os.environ["ANTHROPIC_BASE_URL"], "ANTHROPIC_AUTH_TOKEN": os.environ.get("ANTHROPIC_AUTH_TOKEN", ""), "CLAUDE_CODE_OAUTH_TOKEN": os.environ.get("CLAUDE_CODE_OAUTH_TOKEN", ""), } def make_tmpdir(prefix: str = "sdk-test-") -> Path: """Create a temporary directory manually (caller is responsible for cleanup).""" return Path(tempfile.mkdtemp(prefix=prefix)) def remove_tmpdir(path: Path) -> None: """Remove a temporary directory, retrying once on Windows lock errors.""" try: shutil.rmtree(path) except OSError: import time time.sleep(1) shutil.rmtree(path, ignore_errors=True)