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

631 lines
22 KiB
Python

"""
Comprehensive tests for the Wayback Machine search engine.
Tests initialization, URL extraction, snapshot retrieval, and content fetching.
Note: These tests mock HTTP requests to avoid requiring API connections.
"""
import pytest
from unittest.mock import Mock
class TestWaybackSearchEngineInit:
"""Tests for Wayback Machine search engine initialization."""
def test_init_default_values(self):
"""Test initialization with default values."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
engine = WaybackSearchEngine()
assert engine.max_results == 10
assert engine.max_snapshots_per_url == 3
assert engine.language == "English"
assert engine.closest_only is False
assert engine.is_public is True
def test_init_with_custom_max_results(self):
"""Test initialization with custom max results."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
engine = WaybackSearchEngine(max_results=25)
assert engine.max_results == 25
def test_init_with_custom_max_snapshots(self):
"""Test initialization with custom max snapshots per URL."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
engine = WaybackSearchEngine(max_snapshots_per_url=10)
assert engine.max_snapshots_per_url == 10
def test_init_with_closest_only(self):
"""Test initialization with closest only mode."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
engine = WaybackSearchEngine(closest_only=True)
assert engine.closest_only is True
def test_init_with_language(self):
"""Test initialization with different language."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
engine = WaybackSearchEngine(language="German")
assert engine.language == "German"
def test_api_endpoints_set(self):
"""Test that API endpoints are correctly set."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
engine = WaybackSearchEngine()
assert engine.available_api == "https://archive.org/wayback/available"
assert engine.cdx_api == "https://web.archive.org/cdx/search/cdx"
class TestWaybackEngineType:
"""Tests for Wayback engine type identification."""
def test_engine_type_set(self):
"""Test that engine type is properly set."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
engine = WaybackSearchEngine()
assert "wayback" in engine.engine_type.lower()
def test_engine_is_public(self):
"""Test that engine is marked as public."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
engine = WaybackSearchEngine()
assert engine.is_public is True
def test_engine_is_not_generic(self):
"""Test that engine is not marked as generic."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
engine = WaybackSearchEngine()
assert (
not hasattr(engine, "is_generic") or engine.is_generic is not True
)
class TestWaybackURLExtraction:
"""Tests for Wayback URL extraction from queries."""
@pytest.fixture
def engine(self):
"""Create a Wayback engine."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
return WaybackSearchEngine()
def test_extract_url_from_http_url(self, engine):
"""Test extraction of full HTTP URL (domain portion)."""
urls = engine._extract_urls_from_query("https://example.com/page")
# The regex extracts the domain portion of the URL
assert len(urls) >= 1
assert any("example.com" in url for url in urls)
def test_extract_url_from_multiple_urls(self, engine):
"""Test extraction of multiple URLs."""
query = "Check https://example.com and http://test.org"
urls = engine._extract_urls_from_query(query)
assert len(urls) == 2
assert "https://example.com" in urls
assert "http://test.org" in urls
def test_extract_url_from_domain_only(self, engine):
"""Test extraction from domain without protocol."""
urls = engine._extract_urls_from_query("example.com")
assert "http://example.com" in urls
def test_extract_url_from_partial_path(self, engine):
"""Test extraction from partial URL path."""
urls = engine._extract_urls_from_query("example.com/page/test")
assert "http://example.com/page/test" in urls
class TestWaybackTimestampFormatting:
"""Tests for Wayback timestamp formatting."""
@pytest.fixture
def engine(self):
"""Create a Wayback engine."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
return WaybackSearchEngine()
def test_format_timestamp_full(self, engine):
"""Test formatting a full timestamp."""
result = engine._format_timestamp("20240115103045")
assert result == "2024-01-15 10:30:45"
def test_format_timestamp_short(self, engine):
"""Test formatting a short timestamp returns as-is."""
result = engine._format_timestamp("2024")
assert result == "2024"
def test_format_timestamp_invalid(self, engine):
"""Test formatting an invalid timestamp returns as-is."""
result = engine._format_timestamp("invalid")
assert result == "invalid"
class TestWaybackSnapshotRetrieval:
"""Tests for Wayback snapshot retrieval."""
@pytest.fixture
def engine(self):
"""Create a Wayback engine."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
return WaybackSearchEngine()
def test_get_snapshots_closest_only(self, engine, monkeypatch):
"""Test snapshot retrieval in closest only mode."""
engine.closest_only = True
mock_response = Mock()
mock_response.json = Mock(
return_value={
"archived_snapshots": {
"closest": {
"url": "https://web.archive.org/web/20240115/https://example.com",
"timestamp": "20240115000000",
"available": True,
"status": "200",
}
}
}
)
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
snapshots = engine._get_wayback_snapshots("https://example.com")
assert len(snapshots) == 1
assert "20240115" in snapshots[0]["timestamp"]
def test_get_snapshots_cdx_api(self, engine, monkeypatch):
"""Test snapshot retrieval using CDX API."""
engine.closest_only = False
mock_response = Mock()
mock_response.json = Mock(
return_value=[
["timestamp", "original", "statuscode", "mimetype"],
["20240115000000", "https://example.com", "200", "text/html"],
["20230615000000", "https://example.com", "200", "text/html"],
]
)
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
snapshots = engine._get_wayback_snapshots("https://example.com")
assert len(snapshots) == 2
assert snapshots[0]["timestamp"] == "20240115000000"
assert snapshots[1]["timestamp"] == "20230615000000"
def test_get_snapshots_no_results(self, engine, monkeypatch):
"""Test snapshot retrieval with no archived versions."""
engine.closest_only = True
mock_response = Mock()
mock_response.json = Mock(return_value={"archived_snapshots": {}})
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
snapshots = engine._get_wayback_snapshots("https://example.com")
assert snapshots == []
def test_get_snapshots_handles_exception(self, engine, monkeypatch):
"""Test that exceptions are handled gracefully."""
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(side_effect=Exception("Network error")),
)
snapshots = engine._get_wayback_snapshots("https://example.com")
assert snapshots == []
def test_get_snapshots_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_wayback.safe_get",
Mock(return_value=mock_response),
)
engine.closest_only = True
with pytest.raises(RateLimitError):
engine._get_wayback_snapshots("https://example.com")
def test_get_snapshots_cdx_rate_limit_error(self, engine, monkeypatch):
"""Test that CDX API 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_wayback.safe_get",
Mock(return_value=mock_response),
)
engine.closest_only = False
with pytest.raises(RateLimitError):
engine._get_wayback_snapshots("https://example.com")
class TestWaybackPreviewRetrieval:
"""Tests for Wayback preview retrieval."""
@pytest.fixture
def engine(self):
"""Create a Wayback engine."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
return WaybackSearchEngine(closest_only=True)
def test_get_previews_with_url_query(self, engine, monkeypatch):
"""Test preview retrieval with URL query."""
mock_response = Mock()
mock_response.json = Mock(
return_value={
"archived_snapshots": {
"closest": {
"url": "https://web.archive.org/web/20240115/https://example.com",
"timestamp": "20240115000000",
"available": True,
"status": "200",
}
}
}
)
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
previews = engine._get_previews("https://example.com")
assert len(previews) == 1
assert "example.com" in previews[0]["title"]
assert previews[0]["original_url"] == "https://example.com"
def test_get_previews_no_urls_found(self, engine, monkeypatch):
"""Test preview retrieval when no URLs can be extracted."""
# Ensure DuckDuckGo fallback doesn't work either
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.WaybackSearchEngine._extract_urls_from_query",
Mock(return_value=[]),
)
previews = engine._get_previews("random text without urls")
assert previews == []
class TestWaybackContentRetrieval:
"""Tests for Wayback content retrieval."""
@pytest.fixture
def engine(self):
"""Create a Wayback engine."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
return WaybackSearchEngine()
def test_get_wayback_content_success(self, engine, monkeypatch):
"""Test successful content retrieval."""
mock_response = Mock()
mock_response.text = "<html><body><p>Test content</p></body></html>"
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
# Mock extract_content to avoid complex HTML processing
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.extract_content",
Mock(return_value=""),
)
raw_html, cleaned = engine._get_wayback_content(
"https://web.archive.org/web/20240115/https://example.com"
)
assert "<html>" in raw_html
def test_get_wayback_content_handles_error(self, engine, monkeypatch):
"""Test that content retrieval errors are handled."""
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(side_effect=Exception("Network error")),
)
raw_html, cleaned = engine._get_wayback_content(
"https://web.archive.org/web/20240115/https://example.com"
)
assert raw_html == ""
assert "Error" in cleaned
def test_remove_boilerplate_empty_html(self, engine):
"""Test boilerplate removal with empty HTML."""
result = engine._remove_boilerplate("")
assert result == ""
def test_remove_boilerplate_whitespace_only(self, engine):
"""Test boilerplate removal with whitespace only."""
result = engine._remove_boilerplate(" ")
assert result == ""
class TestWaybackSpecialMethods:
"""Tests for Wayback special methods."""
@pytest.fixture
def engine(self):
"""Create a Wayback engine."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
return WaybackSearchEngine()
def test_search_by_url_method_exists(self, engine):
"""Test that search_by_url method exists."""
assert hasattr(engine, "search_by_url")
assert callable(engine.search_by_url)
def test_search_by_date_range_method_exists(self, engine):
"""Test that search_by_date_range method exists."""
assert hasattr(engine, "search_by_date_range")
assert callable(engine.search_by_date_range)
def test_get_latest_snapshot_method_exists(self, engine):
"""Test that get_latest_snapshot method exists."""
assert hasattr(engine, "get_latest_snapshot")
assert callable(engine.get_latest_snapshot)
def test_search_by_url_with_custom_max(self, engine, monkeypatch):
"""Test search_by_url with custom max snapshots."""
mock_response = Mock()
mock_response.json = Mock(
return_value={
"archived_snapshots": {
"closest": {
"url": "https://web.archive.org/web/20240115/https://example.com",
"timestamp": "20240115000000",
"available": True,
"status": "200",
}
}
}
)
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
# Mock _get_wayback_content to avoid network calls
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.WaybackSearchEngine._get_wayback_content",
Mock(return_value=("", "")),
)
engine.closest_only = True
results = engine.search_by_url("https://example.com", max_snapshots=5)
assert len(results) >= 0 # May be 0 or 1 depending on mock
def test_get_latest_snapshot_success(self, engine, monkeypatch):
"""Test successful latest snapshot retrieval."""
mock_response = Mock()
mock_response.json = Mock(
return_value={
"archived_snapshots": {
"closest": {
"url": "https://web.archive.org/web/20240115/https://example.com",
"timestamp": "20240115000000",
"available": True,
"status": "200",
}
}
}
)
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
# Mock _get_wayback_content to avoid network calls
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.WaybackSearchEngine._get_wayback_content",
Mock(return_value=("", "")),
)
result = engine.get_latest_snapshot("https://example.com")
assert result is not None
assert "example.com" in result["title"]
def test_get_latest_snapshot_not_found(self, engine, monkeypatch):
"""Test latest snapshot retrieval when none exists."""
mock_response = Mock()
mock_response.json = Mock(return_value={"archived_snapshots": {}})
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
result = engine.get_latest_snapshot("https://newsite-no-archive.com")
assert result is None
class TestWaybackDateRangeSearch:
"""Tests for Wayback date range search."""
@pytest.fixture
def engine(self):
"""Create a Wayback engine."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
return WaybackSearchEngine()
def test_search_by_date_range_success(self, engine, monkeypatch):
"""Test successful date range search."""
mock_response = Mock()
mock_response.json = Mock(
return_value=[
["timestamp", "original", "statuscode", "mimetype"],
["20240115000000", "https://example.com", "200", "text/html"],
]
)
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
# Mock _get_wayback_content to avoid network calls
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.WaybackSearchEngine._get_wayback_content",
Mock(return_value=("", "")),
)
results = engine.search_by_date_range(
"https://example.com", "20240101", "20240131"
)
assert len(results) == 1
assert "20240115" in results[0]["timestamp"]
def test_search_by_date_range_no_results(self, engine, monkeypatch):
"""Test date range search with no results."""
mock_response = Mock()
mock_response.json = Mock(
return_value=[["timestamp", "original", "statuscode", "mimetype"]]
)
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
results = engine.search_by_date_range(
"https://example.com", "19900101", "19901231"
)
assert results == []
def test_search_by_date_range_handles_error(self, engine, monkeypatch):
"""Test that date range search errors are handled."""
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(side_effect=Exception("Network error")),
)
results = engine.search_by_date_range(
"https://example.com", "20240101", "20240131"
)
assert results == []
class TestWaybackFullContent:
"""Tests for Wayback full content retrieval."""
@pytest.fixture
def engine(self):
"""Create a Wayback engine."""
from local_deep_research.web_search_engines.engines.search_engine_wayback import (
WaybackSearchEngine,
)
return WaybackSearchEngine()
def test_get_full_content_with_retrieval(self, engine, monkeypatch):
"""Test full content retrieval with actual fetching."""
mock_response = Mock()
mock_response.text = "<html><body><p>Full content</p></body></html>"
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.safe_get",
Mock(return_value=mock_response),
)
# Mock extract_content
monkeypatch.setattr(
"local_deep_research.web_search_engines.engines.search_engine_wayback.extract_content",
Mock(return_value=""),
)
items = [
{
"title": "Test Archive",
"link": "https://web.archive.org/web/20240115/https://example.com",
"snippet": "Test snippet",
}
]
results = engine._get_full_content(items)
assert len(results) == 1
assert "raw_html" in results[0]