test: 补充知识处理、检索与 Agent 回归用例
覆盖切块、向量检索、工具调用和历史预算;模型相关用例使用桩。
This commit is contained in:
1 parent
2dd062787d
commit
d9fe727c3f
123 files changed
+29005
No files matched your search
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
Copyright 2024-2026 ReplyPier
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from app.knowledge import pdf_download, url_safety
|
||||
from app.knowledge.pdf_download import PdfTooLargeError, download_pdf
|
||||
from app.knowledge.url_safety import BlockedHostError
|
||||
|
||||
PDF = b"%PDF-1.4 test body"
|
||||
PUBLIC_IP = [(2, 1, 6, "", ("93.184.216.34", 0))]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def served(monkeypatch):
|
||||
"""Route download_pdf's httpx client to an in-process handler and record
|
||||
every URL it was asked to open."""
|
||||
requested = []
|
||||
routes = {}
|
||||
|
||||
def handler(request: httpx.Request) -> httpx.Response:
|
||||
url = str(request.url)
|
||||
requested.append(url)
|
||||
return routes[url]()
|
||||
|
||||
real_client = httpx.Client
|
||||
monkeypatch.setattr(
|
||||
pdf_download.httpx, "Client",
|
||||
lambda **kwargs: real_client(transport=httpx.MockTransport(handler), **kwargs),
|
||||
)
|
||||
return routes, requested
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def public_dns():
|
||||
with patch.object(url_safety.socket, "getaddrinfo", return_value=PUBLIC_IP):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url", [
|
||||
"http://127.0.0.1/internal.pdf",
|
||||
"http://localhost:8000/api/v1/secret.pdf",
|
||||
"http://169.254.169.254/latest/meta-data/",
|
||||
])
|
||||
def test_internal_url_is_refused_before_any_request(served, tmp_path, url):
|
||||
_, requested = served
|
||||
with pytest.raises(BlockedHostError):
|
||||
download_pdf(url, str(tmp_path / "out.pdf"))
|
||||
assert requested == []
|
||||
|
||||
|
||||
def test_hostname_resolving_to_internal_address_is_refused(served, tmp_path):
|
||||
_, requested = served
|
||||
with patch.object(url_safety.socket, "getaddrinfo",
|
||||
return_value=[(2, 1, 6, "", ("10.0.0.7", 0))]):
|
||||
with pytest.raises(BlockedHostError):
|
||||
download_pdf("https://pdfs.attacker.example/a.pdf", str(tmp_path / "out.pdf"))
|
||||
assert requested == []
|
||||
|
||||
|
||||
def test_public_url_redirecting_to_private_address_is_refused(served, public_dns, tmp_path):
|
||||
routes, requested = served
|
||||
routes["https://files.example.com/doc.pdf"] = lambda: httpx.Response(
|
||||
302, headers={"location": "http://169.254.169.254/latest/meta-data/"})
|
||||
with pytest.raises(BlockedHostError):
|
||||
download_pdf("https://files.example.com/doc.pdf", str(tmp_path / "out.pdf"))
|
||||
# The internal hop was never opened.
|
||||
assert requested == ["https://files.example.com/doc.pdf"]
|
||||
|
||||
|
||||
def test_public_url_is_streamed_to_disk_following_safe_redirects(served, public_dns, tmp_path):
|
||||
routes, _ = served
|
||||
routes["https://files.example.com/doc.pdf"] = lambda: httpx.Response(
|
||||
301, headers={"location": "/v2/doc.pdf"})
|
||||
routes["https://files.example.com/v2/doc.pdf"] = lambda: httpx.Response(200, content=PDF)
|
||||
dest = tmp_path / "out.pdf"
|
||||
|
||||
assert download_pdf("https://files.example.com/doc.pdf", str(dest)) == str(dest)
|
||||
assert dest.read_bytes() == PDF
|
||||
|
||||
|
||||
def test_declared_oversize_is_refused(served, public_dns, tmp_path):
|
||||
routes, _ = served
|
||||
routes["https://files.example.com/big.pdf"] = lambda: httpx.Response(
|
||||
200, content=PDF, headers={"content-length": str(10 ** 9)})
|
||||
with pytest.raises(PdfTooLargeError):
|
||||
download_pdf("https://files.example.com/big.pdf", str(tmp_path / "out.pdf"))
|
||||
|
||||
|
||||
def test_body_past_the_cap_is_refused_even_without_content_length(served, public_dns, tmp_path):
|
||||
routes, _ = served
|
||||
|
||||
def chunked():
|
||||
return httpx.Response(200, content=iter([PDF] * 10)) # streamed: no length
|
||||
|
||||
routes["https://files.example.com/big.pdf"] = chunked
|
||||
with pytest.raises(PdfTooLargeError):
|
||||
download_pdf("https://files.example.com/big.pdf", str(tmp_path / "out.pdf"),
|
||||
max_bytes=len(PDF) * 2)
|
||||
|
||||
|
||||
def test_http_error_is_raised(served, public_dns, tmp_path):
|
||||
routes, _ = served
|
||||
routes["https://files.example.com/gone.pdf"] = lambda: httpx.Response(404)
|
||||
with pytest.raises(httpx.HTTPStatusError):
|
||||
download_pdf("https://files.example.com/gone.pdf", str(tmp_path / "out.pdf"))
|
||||
Reference in new issue
Block a user