mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 21:26:39 +00:00
105 lines
4.2 KiB
Python
105 lines
4.2 KiB
Python
import json
|
|
import logging
|
|
import os
|
|
from collections.abc import Iterator
|
|
from contextlib import contextmanager
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from deerflow.models import credential_loader
|
|
from deerflow.models.claude_provider import ClaudeChatModel
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _isolate_credentials(tmp_path, monkeypatch):
|
|
for name in (
|
|
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
"ANTHROPIC_AUTH_TOKEN",
|
|
"ANTHROPIC_API_KEY",
|
|
"CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR",
|
|
"CLAUDE_CODE_CREDENTIALS_PATH",
|
|
):
|
|
monkeypatch.delenv(name, raising=False)
|
|
monkeypatch.setenv("HOME", str(tmp_path))
|
|
monkeypatch.setenv("USERPROFILE", str(tmp_path))
|
|
monkeypatch.setattr(credential_loader, "_fd_secret_cache", {})
|
|
|
|
|
|
@contextmanager
|
|
def _handoff(payload: bytes, monkeypatch) -> Iterator[int]:
|
|
read_fd, write_fd = os.pipe()
|
|
try:
|
|
try:
|
|
os.write(write_fd, payload)
|
|
finally:
|
|
os.close(write_fd)
|
|
monkeypatch.setenv("CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR", str(read_fd))
|
|
yield read_fd
|
|
finally:
|
|
os.close(read_fd)
|
|
|
|
|
|
def _credential_file(path: Path) -> Path:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps({"claudeAiOauth": {"accessToken": "sk-ant-oat01-file-fallback"}}), encoding="utf-8")
|
|
return path
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"payload",
|
|
[b"sk-ant-oat01-invalid-byte\xff", b"\xff\xfe" + "sk-ant-oat01-utf16".encode("utf-16-le"), b"sk-ant-oat01-truncated\xe2\x82"],
|
|
ids=["invalid-byte", "utf16", "truncated-utf8"],
|
|
)
|
|
@pytest.mark.parametrize("fallback", ["none", "override", "default"])
|
|
def test_undecodable_descriptor_skips_to_file_credentials(tmp_path, monkeypatch, caplog, payload, fallback):
|
|
if fallback == "override":
|
|
monkeypatch.setenv("CLAUDE_CODE_CREDENTIALS_PATH", str(_credential_file(tmp_path / "override.json")))
|
|
elif fallback == "default":
|
|
_credential_file(tmp_path / ".claude" / ".credentials.json")
|
|
|
|
with caplog.at_level(logging.WARNING, logger=credential_loader.__name__), _handoff(payload, monkeypatch) as read_fd:
|
|
# A failed handoff must not strand either the first model or later loads.
|
|
credentials = [credential_loader.load_claude_code_credential() for _ in range(2)]
|
|
os.fstat(read_fd) # The loader does not own or close the descriptor.
|
|
|
|
if fallback == "none":
|
|
assert credentials == [None, None]
|
|
else:
|
|
assert all(cred is not None and cred.access_token == "sk-ant-oat01-file-fallback" and cred.source == "claude-cli-file" for cred in credentials)
|
|
assert credential_loader._fd_secret_cache == {}
|
|
assert "Failed to read CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR" in caplog.text
|
|
assert "sk-ant-oat01" not in caplog.text
|
|
|
|
|
|
def test_claude_models_use_file_fallback_after_undecodable_descriptor(tmp_path, monkeypatch):
|
|
_credential_file(tmp_path / ".claude" / ".credentials.json")
|
|
|
|
with _handoff(b"sk-ant-oat01-model-handoff\xff", monkeypatch):
|
|
models = [ClaudeChatModel(model="claude-sonnet-4-6") for _ in range(2)]
|
|
|
|
for model in models:
|
|
assert model._is_oauth is True
|
|
assert model._client.api_key is None
|
|
assert model._client.auth_token == "sk-ant-oat01-file-fallback"
|
|
|
|
|
|
def test_valid_descriptor_still_wins_over_file_and_reuses_cached_token(tmp_path, monkeypatch):
|
|
_credential_file(tmp_path / ".claude" / ".credentials.json")
|
|
|
|
with _handoff(b" sk-ant-oat01-valid-handoff\n", monkeypatch):
|
|
credentials = [credential_loader.load_claude_code_credential() for _ in range(2)]
|
|
|
|
assert all(cred is not None and cred.access_token == "sk-ant-oat01-valid-handoff" and cred.source == "claude-cli-fd" for cred in credentials)
|
|
|
|
|
|
def test_direct_environment_token_precedes_undecodable_descriptor(monkeypatch, caplog):
|
|
monkeypatch.setenv("CLAUDE_CODE_OAUTH_TOKEN", "sk-ant-oat01-direct")
|
|
|
|
with _handoff(b"sk-ant-oat01-bad-handoff\xff", monkeypatch) as read_fd:
|
|
cred = credential_loader.load_claude_code_credential()
|
|
assert os.read(read_fd, 1024) == b"sk-ant-oat01-bad-handoff\xff"
|
|
|
|
assert cred is not None and cred.access_token == "sk-ant-oat01-direct" and cred.source == "claude-cli-env"
|
|
assert "Failed to read" not in caplog.text
|