mirror of
https://github.com/ThePhaseless/Byparr.git
synced 2026-09-24 14:20:08 +01:00
Nothing calls ClickSolver any more now that the checkbox is clicked through the mouse, so take it out of the browser dependency instead of leaving it constructed but unused. Assert on the returned body as well: a 200 alone passed even when the interstitial itself was handed back, which is the defect this branch fixes. Judge on _cf_chl_opt, since Cloudflare keeps serving cdn-cgi/challenge-platform as a beacon on pages that are already cleared. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
116 lines
4.1 KiB
Python
116 lines
4.1 KiB
Python
from http import HTTPStatus
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
from playwright.async_api import TimeoutError as PlaywrightTimeoutError
|
|
from starlette.testclient import TestClient
|
|
|
|
from main import app
|
|
from src.owui import LoadRequest, load_urls
|
|
from src.utils import BrowserDepClass
|
|
|
|
client = TestClient(app)
|
|
|
|
|
|
def test_owui_load_basic():
|
|
"""/load returns one result per URL with the expected shape."""
|
|
response = client.post("/load", json={"urls": ["https://example.com"]})
|
|
assert response.status_code == HTTPStatus.OK
|
|
results = response.json()
|
|
assert len(results) == 1
|
|
assert results[0]["page_content"]
|
|
assert results[0]["metadata"] == {"source": "https://example.com"}
|
|
|
|
|
|
def test_owui_load_multiple_urls():
|
|
"""/load returns one result per URL, in order."""
|
|
urls = ["https://example.com", "https://example.org"]
|
|
response = client.post("/load", json={"urls": urls})
|
|
assert response.status_code == HTTPStatus.OK
|
|
results = response.json()
|
|
assert [r["metadata"]["source"] for r in results] == urls
|
|
|
|
|
|
def test_owui_load_invalid_url_graceful():
|
|
"""Unreachable URLs yield empty page_content instead of an error."""
|
|
response = client.post(
|
|
"/load", json={"urls": ["https://this-domain-does-not-exist-12345.invalid"]}
|
|
)
|
|
assert response.status_code == HTTPStatus.OK
|
|
results = response.json()
|
|
assert len(results) == 1
|
|
assert results[0]["page_content"] == ""
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"headers",
|
|
[None, {"Authorization": "Bearer wrong-key"}],
|
|
)
|
|
def test_owui_load_rejects_missing_or_wrong_key(headers):
|
|
"""/load returns 401 without a valid bearer token when a key is set."""
|
|
with patch("src.owui.OWUI_API_KEY", "test-secret-key"):
|
|
response = client.post(
|
|
"/load", json={"urls": ["https://example.com"]}, headers=headers
|
|
)
|
|
assert response.status_code == HTTPStatus.UNAUTHORIZED
|
|
|
|
|
|
def test_owui_load_accepts_valid_key():
|
|
"""/load succeeds with the configured bearer token."""
|
|
with patch("src.owui.OWUI_API_KEY", "test-secret-key"):
|
|
response = client.post(
|
|
"/load",
|
|
json={"urls": ["https://example.com"]},
|
|
headers={"Authorization": "Bearer test-secret-key"},
|
|
)
|
|
assert response.status_code == HTTPStatus.OK
|
|
|
|
|
|
ARTICLE_HTML = """<html><head><title>Test</title></head><body>
|
|
<article><h1>Example Title</h1><p>This is the main article body with enough words for
|
|
trafilatura to consider it real content rather than boilerplate.</p></article>
|
|
<nav><a href="/x">nav link</a></nav>
|
|
</body></html>"""
|
|
|
|
|
|
def fake_dep(*, html: str = ARTICLE_HTML) -> BrowserDepClass:
|
|
"""Browser dependency whose page loads HTML but never reaches networkidle."""
|
|
page = AsyncMock()
|
|
page.goto.return_value = MagicMock()
|
|
page.content.return_value = html
|
|
page.locator = MagicMock()
|
|
page.locator.return_value.inner_text = AsyncMock(
|
|
return_value="line one\n\nline two"
|
|
)
|
|
|
|
def wait_for_load_state(state: str, **_kwargs: object) -> None:
|
|
if state == "networkidle":
|
|
message = "load state wait timed out"
|
|
raise PlaywrightTimeoutError(message)
|
|
|
|
page.wait_for_load_state.side_effect = wait_for_load_state
|
|
return BrowserDepClass(page=page, context=AsyncMock())
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_networkidle_timeout_still_extracts_content():
|
|
"""A page that never reaches networkidle still yields its article text."""
|
|
results = await load_urls(
|
|
LoadRequest(urls=["https://example.test"]), None, fake_dep()
|
|
)
|
|
assert results[0].page_content == (
|
|
"Example TitleThis is the main article body with enough words for "
|
|
"trafilatura to consider it real content rather than boilerplate."
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_extraction_falls_back_to_innertext():
|
|
"""Pages trafilatura cannot score fall back to the rendered innerText."""
|
|
results = await load_urls(
|
|
LoadRequest(urls=["https://example.test"]),
|
|
None,
|
|
fake_dep(html="<html><body></body></html>"),
|
|
)
|
|
assert results[0].page_content == "line one\nline two"
|