Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

563 lines
20 KiB
Python

"""
Tests for the Open Library search engine.
Tests initialization, search functionality, and error handling.
"""
import pytest
from unittest.mock import Mock
class TestOpenLibrarySearchEngineInit:
"""Tests for Open Library search engine initialization."""
def test_init_default_parameters(self):
"""Test initialization with default parameters."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine()
assert engine.max_results == 10
assert engine.sort == "relevance"
assert engine.language is None
assert engine.search_field is None
assert engine.is_public is True
assert engine.is_books is True
assert engine.is_generic is False
def test_init_custom_parameters(self):
"""Test initialization with custom parameters."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine(
max_results=50,
sort="new",
language="eng",
search_field="title",
)
assert engine.max_results == 50
assert engine.sort == "new"
assert engine.language == "eng"
assert engine.search_field == "title"
def test_base_url_set(self):
"""Test that API base URL is correctly set."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine()
assert engine.base_url == "https://openlibrary.org"
assert engine.search_url == "https://openlibrary.org/search.json"
def test_user_agent_header_set(self):
"""Test that User-Agent header is set."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine()
assert "User-Agent" in engine.headers
assert "Local-Deep-Research" in engine.headers["User-Agent"]
class TestOpenLibraryQueryBuilding:
"""Tests for Open Library query parameter building."""
def test_build_query_params_basic(self):
"""Test basic query params building."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine(max_results=20)
params = engine._build_query_params("lord of the rings")
assert params["q"] == "lord of the rings"
assert params["limit"] == 20
assert "fields" in params
def test_build_query_params_title_search(self):
"""Test query params with title search field."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine(search_field="title")
params = engine._build_query_params("1984")
assert params["title"] == "1984"
assert "q" not in params
def test_build_query_params_author_search(self):
"""Test query params with author search field."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine(search_field="author")
params = engine._build_query_params("Tolkien")
assert params["author"] == "Tolkien"
assert "q" not in params
def test_build_query_params_with_language(self):
"""Test query params with language filter."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine(language="eng")
params = engine._build_query_params("test")
assert params["language"] == "eng"
def test_build_query_params_with_sort(self):
"""Test query params with sort option."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine(sort="new")
params = engine._build_query_params("test")
assert params["sort"] == "new"
class TestOpenLibraryCoverUrl:
"""Tests for Open Library cover URL generation."""
def test_get_cover_url_with_id(self):
"""Test cover URL generation with valid ID."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine()
url = engine._get_cover_url(12345)
assert url == "https://covers.openlibrary.org/b/id/12345-M.jpg"
def test_get_cover_url_with_size(self):
"""Test cover URL generation with custom size."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine()
url = engine._get_cover_url(12345, size="L")
assert url == "https://covers.openlibrary.org/b/id/12345-L.jpg"
def test_get_cover_url_without_id(self):
"""Test cover URL generation without ID returns None."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine()
url = engine._get_cover_url(None)
assert url is None
class TestOpenLibrarySearchExecution:
"""Tests for Open Library search execution."""
@pytest.fixture
def engine(self):
"""Create an Open Library engine."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
return OpenLibrarySearchEngine(max_results=10)
def test_get_previews_success(self, engine, monkeypatch):
"""Test successful preview retrieval."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json = Mock(
return_value={
"num_found": 100,
"docs": [
{
"key": "/works/OL27448W",
"title": "The Lord of the Rings",
"author_name": ["J.R.R. Tolkien"],
"author_key": ["OL26320A"],
"first_publish_year": 1954,
"publisher": ["HarperCollins"],
"subject": ["Fantasy", "Middle Earth", "Hobbits"],
"isbn": ["9780261103252"],
"cover_i": 8743908,
"edition_count": 500,
"has_fulltext": True,
"ebook_access": "borrowable",
}
],
}
)
mock_response.raise_for_status = Mock()
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(return_value=mock_response),
)
previews = engine._get_previews("lord of the rings")
assert len(previews) == 1
assert previews[0]["title"] == "The Lord of the Rings"
assert "J.R.R. Tolkien" in previews[0]["authors"]
assert previews[0]["first_publish_year"] == 1954
assert previews[0]["source"] == "Open Library"
assert previews[0]["cover_url"] is not None
def test_get_previews_empty_results(self, engine, monkeypatch):
"""Test preview retrieval with no results."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json = Mock(return_value={"num_found": 0, "docs": []})
mock_response.raise_for_status = Mock()
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(return_value=mock_response),
)
previews = engine._get_previews("xyznonexistentbook123")
assert previews == []
def test_get_previews_rate_limit_error(self, engine, monkeypatch):
"""Test that 429 errors raise RateLimitError."""
from local_deep_research.web_search_engines.rate_limiting import (
RateLimitError,
)
mock_response = Mock()
mock_response.status_code = 429
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(return_value=mock_response),
)
with pytest.raises(RateLimitError):
engine._get_previews("test query")
def test_get_previews_handles_exception(self, engine, monkeypatch):
"""Test that exceptions are handled gracefully."""
import requests
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(side_effect=requests.RequestException("Network error")),
)
previews = engine._get_previews("test query")
assert previews == []
def test_get_previews_passes_user_agent_header(self, engine, monkeypatch):
"""Test that User-Agent header is passed to API requests."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json = Mock(return_value={"num_found": 0, "docs": []})
mock_response.raise_for_status = Mock()
mock_safe_get = Mock(return_value=mock_response)
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
mock_safe_get,
)
engine._get_previews("test")
# Verify headers were passed
call_kwargs = mock_safe_get.call_args[1]
assert "headers" in call_kwargs
assert "User-Agent" in call_kwargs["headers"]
class TestOpenLibraryFullContent:
"""Tests for Open Library full content retrieval."""
def test_get_full_content_builds_content(self):
"""Test that full content builds proper content string."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine()
items = [
{
"title": "Test Book",
"authors": ["Author One", "Author Two"],
"first_publish_year": 2020,
"edition_count": 5,
"subjects": ["Fiction", "Adventure"],
"has_fulltext": True,
"_raw": {
"language": ["eng", "fre"],
"subject": ["Fiction", "Adventure", "Fantasy"],
"publisher": ["Publisher One", "Publisher Two"],
},
}
]
from unittest.mock import patch
with patch.object(engine, "_fetch_work_details", return_value=None):
results = engine._get_full_content(items)
assert len(results) == 1
assert "Authors: Author One, Author Two" in results[0]["content"]
assert "First published: 2020" in results[0]["content"]
assert "Subjects: Fiction, Adventure, Fantasy" in results[0]["content"]
assert "Full text available" in results[0]["content"]
assert "_raw" not in results[0]
class TestOpenLibraryEdgeCases:
"""Tests for Open Library edge cases and error handling."""
@pytest.fixture
def engine(self):
"""Create an Open Library engine."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
return OpenLibrarySearchEngine(max_results=10)
def test_get_previews_unicode_in_title(self, engine, monkeypatch):
"""Test handling of Unicode characters in book title."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json = Mock(
return_value={
"num_found": 1,
"docs": [
{
"key": "/works/OL123W",
"title": "Les Misérables",
"author_name": ["Victor Hugo"],
}
],
}
)
mock_response.raise_for_status = Mock()
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(return_value=mock_response),
)
previews = engine._get_previews("les miserables")
assert len(previews) == 1
assert "Misérables" in previews[0]["title"]
def test_get_previews_html_entities_in_title(self, engine, monkeypatch):
"""Test that HTML entities in title are decoded."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json = Mock(
return_value={
"num_found": 1,
"docs": [
{
"key": "/works/OL123W",
"title": "Harry Potter & the Philosopher's Stone",
"author_name": ["J.K. Rowling"],
}
],
}
)
mock_response.raise_for_status = Mock()
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(return_value=mock_response),
)
previews = engine._get_previews("harry potter")
assert len(previews) == 1
# HTML entities should be decoded
assert "&" not in previews[0]["title"]
assert "&" in previews[0]["title"]
def test_get_previews_missing_optional_fields(self, engine, monkeypatch):
"""Test handling of missing optional fields."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json = Mock(
return_value={
"num_found": 1,
"docs": [
{
"key": "/works/OL123W",
"title": "Minimal Book",
# Missing: author_name, first_publish_year, publisher, etc.
}
],
}
)
mock_response.raise_for_status = Mock()
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(return_value=mock_response),
)
previews = engine._get_previews("test")
assert len(previews) == 1
assert previews[0]["title"] == "Minimal Book"
assert previews[0]["authors"] == []
assert previews[0]["cover_url"] is None
def test_get_previews_string_author_converted_to_list(
self, engine, monkeypatch
):
"""Test that string author is converted to list."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json = Mock(
return_value={
"num_found": 1,
"docs": [
{
"key": "/works/OL123W",
"title": "Test Book",
"author_name": "Single Author", # String instead of list
}
],
}
)
mock_response.raise_for_status = Mock()
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(return_value=mock_response),
)
previews = engine._get_previews("test")
assert len(previews) == 1
assert isinstance(previews[0]["authors"], list)
assert "Single Author" in previews[0]["authors"]
def test_get_previews_limits_authors_to_five(self, engine, monkeypatch):
"""Test that authors are limited to 5."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json = Mock(
return_value={
"num_found": 1,
"docs": [
{
"key": "/works/OL123W",
"title": "Multi-Author Book",
"author_name": [f"Author {i}" for i in range(10)],
}
],
}
)
mock_response.raise_for_status = Mock()
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(return_value=mock_response),
)
previews = engine._get_previews("test")
assert len(previews) == 1
assert len(previews[0]["authors"]) == 5
def test_get_full_content_without_raw(self, engine):
"""Test full content handling when _raw is missing."""
items = [
{
"title": "Test Book",
"authors": ["Test Author"],
}
]
results = engine._get_full_content(items)
assert len(results) == 1
# Should not crash
def test_all_sort_options(self):
"""Test that various sort options can be used."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
sort_options = ["relevance", "new", "old", "random"]
for sort in sort_options:
engine = OpenLibrarySearchEngine(sort=sort)
assert engine.sort == sort
def test_all_search_fields(self):
"""Test that all search field options work."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
fields = [None, "title", "author", "subject"]
for field in fields:
engine = OpenLibrarySearchEngine(search_field=field)
assert engine.search_field == field
class TestOpenLibraryHelperMethods:
"""Tests for Open Library helper methods."""
def test_get_book_by_isbn_success(self, monkeypatch):
"""Test successful book retrieval by ISBN."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
engine = OpenLibrarySearchEngine()
mock_response = Mock()
mock_response.status_code = 200
mock_response.json = Mock(
return_value={
"title": "Test Book",
"authors": [{"key": "/authors/OL123A"}],
}
)
mock_response.raise_for_status = Mock()
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(return_value=mock_response),
)
book = engine.get_book_by_isbn("9780261103252")
assert book is not None
assert book["title"] == "Test Book"
def test_get_book_by_isbn_handles_error(self, monkeypatch):
"""Test that errors are handled in ISBN lookup."""
from local_deep_research.web_search_engines.engines.search_engine_openlibrary import (
OpenLibrarySearchEngine,
)
import requests
engine = OpenLibrarySearchEngine()
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_openlibrary.safe_get",
Mock(side_effect=requests.RequestException("Not found")),
)
book = engine.get_book_by_isbn("invalid")
assert book is None