| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- from __future__ import annotations
- from pathlib import Path
- from typing import Any, Dict, Optional
- import pytest
- from agent.tools.builtin.browser import downloads, providers
- class _FakeResponse:
- def __init__(self, status_code: int, chunks: tuple[bytes, ...] = ()) -> None:
- self.status_code = status_code
- self._chunks = chunks
- async def __aenter__(self) -> "_FakeResponse":
- return self
- async def __aexit__(self, *_args: object) -> None:
- return None
- async def aiter_bytes(self):
- for chunk in self._chunks:
- yield chunk
- class _FakeClient:
- response = _FakeResponse(200)
- def __init__(self, **_kwargs: object) -> None:
- pass
- async def __aenter__(self) -> "_FakeClient":
- return self
- async def __aexit__(self, *_args: object) -> None:
- return None
- def stream(self, _method: str, _url: str) -> _FakeResponse:
- return self.response
- class _ContainerProvider:
- async def create(self, url: str, account_name: str) -> Dict[str, Any]:
- return {"success": True, "url": url, "account_name": account_name}
- class _CookieProvider:
- def get_cookie_row(self, cookie_type: str) -> Optional[Dict[str, Any]]:
- return {"type": cookie_type, "cookies": "session=test"}
- @pytest.fixture(autouse=True)
- def _reset_providers() -> None:
- yield
- providers.reset_browser_providers()
- @pytest.mark.asyncio
- async def test_download_accepts_partial_content_and_sanitizes_name(
- monkeypatch: pytest.MonkeyPatch,
- tmp_path: Path,
- ) -> None:
- _FakeClient.response = _FakeResponse(206, (b"abc", b"def"))
- monkeypatch.setattr(downloads.httpx, "AsyncClient", _FakeClient)
- result = await downloads.download_direct_url(
- "https://files.example.invalid/path/report.txt",
- "../../escaped.txt",
- download_dir=tmp_path,
- )
- assert result.error is None
- assert result.metadata["status_code"] == 206
- assert result.metadata["bytes"] == 6
- assert Path(result.metadata["path"]) == tmp_path / "escaped.txt"
- assert (tmp_path / "escaped.txt").read_bytes() == b"abcdef"
- assert not (tmp_path / ".escaped.txt.part").exists()
- @pytest.mark.asyncio
- async def test_download_failure_always_returns_tool_result(
- monkeypatch: pytest.MonkeyPatch,
- tmp_path: Path,
- ) -> None:
- _FakeClient.response = _FakeResponse(404)
- monkeypatch.setattr(downloads.httpx, "AsyncClient", _FakeClient)
- stale_partial = tmp_path / ".missing.bin.part"
- stale_partial.write_bytes(b"stale")
- result = await downloads.download_direct_url(
- "https://files.example.invalid/missing.bin",
- "",
- download_dir=tmp_path,
- )
- assert result.error == "HTTP 404"
- assert result.metadata["status_code"] == 404
- assert not list(tmp_path.iterdir())
- @pytest.mark.asyncio
- async def test_download_infers_and_decodes_url_filename(
- monkeypatch: pytest.MonkeyPatch,
- tmp_path: Path,
- ) -> None:
- _FakeClient.response = _FakeResponse(200, (b"content",))
- monkeypatch.setattr(downloads.httpx, "AsyncClient", _FakeClient)
- result = await downloads.download_direct_url(
- "https://files.example.invalid/%E6%8A%A5%E5%91%8A.txt",
- "",
- download_dir=tmp_path,
- )
- assert result.error is None
- assert Path(result.metadata["path"]).name == "报告.txt"
- assert (tmp_path / "报告.txt").read_bytes() == b"content"
- @pytest.mark.asyncio
- async def test_container_provider_normalizes_unexpected_failures(
- monkeypatch: pytest.MonkeyPatch,
- ) -> None:
- class _BrokenClient:
- def __init__(self, **_kwargs: object) -> None:
- raise TypeError("malformed service response")
- monkeypatch.setattr(providers.httpx, "AsyncClient", _BrokenClient)
- provider = providers.HttpContainerProvider(
- base_url="https://browser.example.invalid",
- startup_delay=0,
- retry_delay=0,
- )
- result = await provider.create("https://example.invalid", "account")
- assert result["success"] is False
- assert result["error"] == "malformed service response"
- def test_browser_providers_are_host_replaceable() -> None:
- container = _ContainerProvider()
- cookie = _CookieProvider()
- providers.configure_browser_providers(
- container_provider=container,
- cookie_provider=cookie,
- )
- assert providers.get_browser_container_provider() is container
- assert providers.get_browser_cookie_provider() is cookie
- def test_base_class_no_longer_knows_host_endpoint_or_cookie_table() -> None:
- source = (
- Path(__file__).parents[1]
- / "agent"
- / "tools"
- / "builtin"
- / "browser"
- / "baseClass.py"
- ).read_text(encoding="utf-8")
- assert "47.84.182.56" not in source
- assert "agent_channel_cookies" not in source
|