mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-27 02:22:21 +02:00
697 lines
24 KiB
Python
697 lines
24 KiB
Python
"""Regression tests for the canonical services.search provider implementation.
|
|
|
|
The old src.search provider path aliases this module; these tests pin the
|
|
behavior at the single implementation point.
|
|
"""
|
|
|
|
import sys
|
|
|
|
from services.search import core
|
|
from services.search import providers
|
|
|
|
|
|
def test_dead_credentialed_fallback_is_skipped_for_same_instance_engine(monkeypatch):
|
|
monkeypatch.setattr(core, "_get_search_settings", lambda: {"search_fallback_chain": ["google_pse"]})
|
|
monkeypatch.setattr(providers, "_get_search_settings", lambda: {})
|
|
monkeypatch.setattr(providers, "_get_provider_key", lambda name: "")
|
|
assert core._build_provider_chain("searxng") == ["searxng", "searxng_yep"]
|
|
|
|
|
|
def test_valid_configured_fallback_is_preserved(monkeypatch):
|
|
monkeypatch.setattr(core, "_get_search_settings", lambda: {"search_fallback_chain": ["google_pse"]})
|
|
monkeypatch.setattr(providers, "_get_search_settings", lambda: {"google_pse_cx": "test-cx"})
|
|
monkeypatch.setattr(providers, "_get_provider_key", lambda name: "test-key")
|
|
assert core._build_provider_chain("searxng") == ["searxng", "google_pse"]
|
|
|
|
|
|
def test_service_safesearch_values_match_provider_contract(monkeypatch):
|
|
monkeypatch.setattr(providers, "_get_search_settings", lambda: {"search_safesearch": "strict"})
|
|
assert providers._safesearch_for("searxng") == "2"
|
|
assert providers._safesearch_for("brave") == "strict"
|
|
assert providers._safesearch_for("duckduckgo_lib") == "on"
|
|
assert providers._safesearch_for("duckduckgo_html") == "1"
|
|
assert providers._safesearch_for("google_pse") == "active"
|
|
assert providers._safesearch_for("serper") == "active"
|
|
|
|
monkeypatch.setattr(providers, "_get_search_settings", lambda: {"search_safesearch": "off"})
|
|
assert providers._safesearch_for("searxng") == "0"
|
|
assert providers._safesearch_for("brave") == "off"
|
|
assert providers._safesearch_for("duckduckgo_lib") == "off"
|
|
assert providers._safesearch_for("duckduckgo_html") == "-2"
|
|
assert providers._safesearch_for("google_pse") is None
|
|
assert providers._safesearch_for("serper") is None
|
|
|
|
|
|
def test_service_searxng_json_sends_safesearch(monkeypatch):
|
|
seen = {}
|
|
|
|
class _Response:
|
|
def raise_for_status(self):
|
|
return None
|
|
|
|
def json(self):
|
|
return {
|
|
"results": [
|
|
{"title": "Result", "url": "https://example.com", "content": "Snippet"}
|
|
]
|
|
}
|
|
|
|
def fake_get(url, **kwargs):
|
|
seen["url"] = url
|
|
seen["params"] = kwargs["params"]
|
|
return _Response()
|
|
|
|
monkeypatch.setattr(providers, "_get_search_instance", lambda: "http://searx.test")
|
|
monkeypatch.setattr(providers, "_get_search_settings", lambda: {"search_safesearch": "moderate"})
|
|
monkeypatch.setattr(providers.httpx, "get", fake_get)
|
|
|
|
results = providers.searxng_search_api("odysseus", count=1)
|
|
|
|
assert results
|
|
assert seen["url"] == "http://searx.test/search"
|
|
assert seen["params"]["safesearch"] == "1"
|
|
|
|
|
|
def test_service_searxng_latest_release_uses_general_search(monkeypatch):
|
|
seen = {}
|
|
|
|
class _Response:
|
|
def raise_for_status(self):
|
|
return None
|
|
|
|
def json(self):
|
|
return {
|
|
"results": [
|
|
{
|
|
"title": "ollama/ollama releases",
|
|
"url": "https://github.com/ollama/ollama/releases",
|
|
"content": "Latest release v0.33.0",
|
|
}
|
|
]
|
|
}
|
|
|
|
def fake_get(url, **kwargs):
|
|
seen["params"] = kwargs["params"]
|
|
return _Response()
|
|
|
|
monkeypatch.setattr(providers, "_get_search_instance", lambda: "http://searx.test")
|
|
monkeypatch.setattr(providers, "_get_search_settings", lambda: {"search_safesearch": "strict"})
|
|
monkeypatch.setattr(providers.httpx, "get", fake_get)
|
|
|
|
results = providers.searxng_search_api(
|
|
"latest ollama release version github",
|
|
count=1,
|
|
time_filter="day",
|
|
)
|
|
|
|
assert results
|
|
assert seen["params"]["categories"] == "general"
|
|
assert seen["params"]["engines"] == providers._GENERAL_ENGINES
|
|
assert "time_range" not in seen["params"]
|
|
|
|
|
|
def test_service_searxng_specific_current_event_uses_news_search(monkeypatch):
|
|
seen = {}
|
|
|
|
class _Response:
|
|
def raise_for_status(self):
|
|
return None
|
|
|
|
def json(self):
|
|
return {
|
|
"results": [
|
|
{
|
|
"title": "British widow faces deportation from Sweden",
|
|
"url": "https://example.com/news/story",
|
|
"content": "The 78-year-old has lived in Sweden for 22 years.",
|
|
}
|
|
]
|
|
}
|
|
|
|
def fake_get(url, **kwargs):
|
|
seen["params"] = kwargs["params"]
|
|
return _Response()
|
|
|
|
monkeypatch.setattr(providers, "_get_search_instance", lambda: "http://sear.test")
|
|
monkeypatch.setattr(providers, "_get_search_settings", lambda: {"search_safesearch": "moderate"})
|
|
monkeypatch.setattr(providers.httpx, "get", fake_get)
|
|
|
|
results = providers.searxng_search_api(
|
|
"Sweden 78 year old British woman deportation Brexit residence application",
|
|
count=5,
|
|
)
|
|
|
|
assert results
|
|
assert seen["params"]["categories"] == "news"
|
|
assert "engines" not in seen["params"]
|
|
|
|
|
|
def test_low_relevance_filter_ignores_generic_freshness_terms():
|
|
results = core._filter_low_relevance_results(
|
|
"latest ollama release version github",
|
|
[
|
|
{
|
|
"title": "Fox News - Breaking News Updates",
|
|
"url": "https://www.foxnews.com/",
|
|
"snippet": "Latest Current News: U.S., World, Entertainment.",
|
|
},
|
|
{
|
|
"title": "Releases · ollama/ollama - GitHub",
|
|
"url": "https://github.com/ollama/ollama/releases",
|
|
"snippet": "Get up and running with Kimi, DeepSeek, Qwen and other models.",
|
|
},
|
|
],
|
|
)
|
|
|
|
assert [r["url"] for r in results] == ["https://github.com/ollama/ollama/releases"]
|
|
|
|
|
|
def test_low_relevance_filter_rejects_one_broad_match_for_specific_query():
|
|
results = core._filter_low_relevance_results(
|
|
"Sweden 78 year old British woman deportation Brexit residence application",
|
|
[
|
|
{
|
|
"title": "Geography of Sweden",
|
|
"url": "https://en.wikipedia.org/wiki/Geography_of_Sweden",
|
|
"snippet": "Sweden is a country in Northern Europe.",
|
|
},
|
|
{
|
|
"title": "British widow faces deportation from Sweden after 22 years",
|
|
"url": "https://example.com/news/british-widow-sweden",
|
|
"snippet": "A 78-year-old woman missed a Brexit residence application.",
|
|
},
|
|
],
|
|
)
|
|
|
|
assert [r["url"] for r in results] == [
|
|
"https://example.com/news/british-widow-sweden"
|
|
]
|
|
|
|
|
|
def test_low_relevance_filter_treats_ai_as_subject_not_news_as_subject():
|
|
results = core._filter_low_relevance_results(
|
|
"Latest news in AI",
|
|
[
|
|
{
|
|
"title": "Anthropic researcher quits over AI risks",
|
|
"url": "https://example.com/technology/anthropic-ai",
|
|
"snippet": "The departure highlights concern inside AI labs.",
|
|
},
|
|
{
|
|
"title": "Latest world news and headlines",
|
|
"url": "https://example.com/world",
|
|
"snippet": "Breaking updates from around the world.",
|
|
},
|
|
],
|
|
)
|
|
|
|
assert [r["url"] for r in results] == [
|
|
"https://example.com/technology/anthropic-ai"
|
|
]
|
|
|
|
|
|
def test_provider_query_removes_generic_interrogative_shell():
|
|
assert core._provider_friendly_query(
|
|
"What year did Ethiopia become independent"
|
|
) == "Ethiopia become independent year"
|
|
|
|
|
|
def test_provider_query_separates_conversational_freshness_shell_from_subject():
|
|
assert core._provider_friendly_query(
|
|
"Any latest info on quantum physics"
|
|
) == "quantum physics"
|
|
assert core._meaningful_query_terms(
|
|
"Any latest info on quantum physics"
|
|
) == ["quantum", "physics"]
|
|
|
|
|
|
def test_relevance_matching_accepts_conservative_word_family_variants():
|
|
results = core._filter_low_relevance_results(
|
|
"Ethiopia become independent year",
|
|
[
|
|
{
|
|
"title": "History of Ethiopia",
|
|
"url": "https://example.com/ethiopia-history",
|
|
"snippet": "The country's independence and periods of occupation.",
|
|
},
|
|
{
|
|
"title": "Calendar year",
|
|
"url": "https://example.com/calendar-year",
|
|
"snippet": "A year has twelve months.",
|
|
},
|
|
],
|
|
)
|
|
|
|
assert [r["url"] for r in results] == ["https://example.com/ethiopia-history"]
|
|
|
|
|
|
def test_low_relevance_filter_weather_queries_require_location_terms():
|
|
results = core._filter_low_relevance_results(
|
|
"Kyoto weather forecast tomorrow August 27 2026",
|
|
[
|
|
{
|
|
"title": "Weather Tomorrow for Kyoto-shi, Kyoto, Japan",
|
|
"url": "https://www.accuweather.com/en/jp/kyoto-shi/224436/weather-tomorrow/224436",
|
|
"snippet": "Detailed forecast including temperature and rain.",
|
|
},
|
|
{
|
|
"title": "Seattle, WA Weather Forecast",
|
|
"url": "https://www.accuweather.com/en/us/seattle/98104/weather-forecast/351409",
|
|
"snippet": "Seattle weather forecast with current conditions.",
|
|
},
|
|
{
|
|
"title": "Kyoto - Wikipedia",
|
|
"url": "https://en.wikipedia.org/wiki/Kyoto",
|
|
"snippet": "Kyoto is a city in Japan.",
|
|
},
|
|
{
|
|
"title": "Kyoto Travel | Kyoto City Official Guide",
|
|
"url": "https://kyoto.travel/en/",
|
|
"snippet": "Kyoto tourism tips, itineraries, and things to do.",
|
|
},
|
|
],
|
|
)
|
|
|
|
assert [r["url"] for r in results] == [
|
|
"https://www.accuweather.com/en/jp/kyoto-shi/224436/weather-tomorrow/224436"
|
|
]
|
|
|
|
|
|
def test_weather_query_rewrite_moves_location_first():
|
|
assert (
|
|
core._subject_first_weather_query("What is the weather in Kyoto tomorrow?")
|
|
== "Kyoto weather forecast tomorrow"
|
|
)
|
|
|
|
|
|
def test_scholarly_title_extraction_prefers_probable_paper_title():
|
|
assert core._scholarly_title_from_query(
|
|
'"Attention Is All You Need" "Table 2" "Training Cost" FLOPS'
|
|
) == "Attention Is All You Need"
|
|
assert core._scholarly_title_from_query(
|
|
'Find the paper "LLaVA-OneVision: Easy Visual Task Transfer" Table 5'
|
|
) == "LLaVA-OneVision: Easy Visual Task Transfer"
|
|
assert core._scholarly_title_from_query('search for "ordinary quoted phrase"') == ""
|
|
|
|
|
|
def test_scholarly_arxiv_fallback_prepends_only_strong_title_match(monkeypatch):
|
|
seen = {}
|
|
atom = """\
|
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
|
<entry>
|
|
<id>http://arxiv.org/abs/1706.03762v7</id>
|
|
<title>Attention Is All You Need</title>
|
|
<summary>We propose the Transformer architecture.</summary>
|
|
</entry>
|
|
<entry>
|
|
<id>http://arxiv.org/abs/0000.00000v1</id>
|
|
<title>Attention Mechanisms in an Unrelated Setting</title>
|
|
<summary>An unrelated result.</summary>
|
|
</entry>
|
|
</feed>
|
|
"""
|
|
|
|
class _Response:
|
|
text = atom
|
|
|
|
def raise_for_status(self):
|
|
return None
|
|
|
|
def _fake_get(url, **kwargs):
|
|
seen["url"] = url
|
|
seen["params"] = kwargs["params"]
|
|
return _Response()
|
|
|
|
monkeypatch.setattr(core, "searxng_search_api", lambda *args, **kwargs: [])
|
|
monkeypatch.setattr(core, "_openalex_title_results", lambda *args, **kwargs: [])
|
|
monkeypatch.setattr(core.httpx, "get", _fake_get)
|
|
generic = [{
|
|
"title": "ATTENTION | English meaning",
|
|
"url": "https://dictionary.example/attention",
|
|
"snippet": "A definition of attention.",
|
|
}]
|
|
|
|
results = core._augment_scholarly_results(
|
|
'"Attention Is All You Need" "Table 2" "Training Cost" FLOPS',
|
|
generic,
|
|
5,
|
|
)
|
|
|
|
assert seen["url"] == "https://export.arxiv.org/api/query"
|
|
assert seen["params"]["search_query"] == 'ti:"Attention Is All You Need"'
|
|
assert results[0]["title"] == "Attention Is All You Need"
|
|
assert results[0]["url"] == "https://arxiv.org/abs/1706.03762v7"
|
|
assert results[1:] == generic
|
|
|
|
|
|
def test_explicit_arxiv_identifier_bypasses_noisy_or_empty_serp():
|
|
noisy = [{
|
|
"title": "arXiv - Wikipedia",
|
|
"url": "https://en.wikipedia.org/wiki/ArXiv",
|
|
"snippet": "Generic repository article.",
|
|
}]
|
|
|
|
results = core._augment_scholarly_results(
|
|
"arXiv:2404.14219 Phi-3 publication venue",
|
|
noisy,
|
|
5,
|
|
)
|
|
|
|
assert results[0] == {
|
|
"title": "arXiv:2404.14219 — exact identifier match",
|
|
"url": "https://arxiv.org/abs/2404.14219",
|
|
"snippet": (
|
|
"Official arXiv landing page resolved directly from the exact "
|
|
"identifier in the query."
|
|
),
|
|
"source": "arxiv",
|
|
}
|
|
assert results[1:] == noisy
|
|
|
|
|
|
def test_explicit_arxiv_identifier_deduplicates_versions_and_urls():
|
|
results = core._augment_scholarly_results(
|
|
"Compare arxiv.org/pdf/2408.03326v2 with arXiv:2408.03326",
|
|
[{
|
|
"title": "existing",
|
|
"url": "https://arxiv.org/abs/2408.03326",
|
|
"snippet": "duplicate",
|
|
}],
|
|
5,
|
|
)
|
|
|
|
assert [item["url"] for item in results] == [
|
|
"https://arxiv.org/abs/2408.03326"
|
|
]
|
|
|
|
|
|
def test_explicit_arxiv_publication_query_prepends_formal_openalex_match(monkeypatch):
|
|
seen = []
|
|
|
|
def _fake_openalex(title, count):
|
|
seen.append((title, count))
|
|
return [
|
|
{
|
|
"title": (
|
|
"Molmo and PixMo: Open Weights and Open Data for "
|
|
"State-of-the-Art Vision-Language Models"
|
|
),
|
|
"url": "https://doi.org/10.1109/cvpr52734.2025.00018",
|
|
"snippet": (
|
|
"Exact scholarly-title match from OpenAlex metadata. "
|
|
"Formal publication: 2025 IEEE/CVF Conference on Computer "
|
|
"Vision and Pattern Recognition (CVPR), 2025."
|
|
),
|
|
"source": "openalex",
|
|
},
|
|
{
|
|
"title": (
|
|
"Molmo and PixMo: Open Weights and Open Data for "
|
|
"State-of-the-Art Vision-Language Models"
|
|
),
|
|
"url": "https://arxiv.org/abs/2409.17146",
|
|
"snippet": "Preprint record.",
|
|
"source": "openalex",
|
|
},
|
|
]
|
|
|
|
monkeypatch.setattr(core, "_openalex_title_results", _fake_openalex)
|
|
|
|
results = core._augment_scholarly_results(
|
|
"Molmo and PixMo arXiv abs/2409.17146 published venue conference 2025",
|
|
[],
|
|
5,
|
|
)
|
|
|
|
assert seen == [("Molmo and PixMo", 3)]
|
|
assert results[0]["url"] == "https://doi.org/10.1109/cvpr52734.2025.00018"
|
|
assert results[1]["url"] == "https://arxiv.org/abs/2409.17146"
|
|
|
|
|
|
def test_scholarly_fallback_prefers_exact_bare_title_search(monkeypatch):
|
|
seen = []
|
|
|
|
def _fake_search(query, count, **kwargs):
|
|
seen.append((query, count))
|
|
return [{
|
|
"title": "LLaVA-OneVision: Easy Visual Task Transfer",
|
|
"url": "https://arxiv.org/abs/2408.03326",
|
|
"snippet": "The matching paper.",
|
|
}]
|
|
|
|
monkeypatch.setattr(core, "searxng_search_api", _fake_search)
|
|
monkeypatch.setattr(
|
|
core,
|
|
"_arxiv_title_results",
|
|
lambda *args, **kwargs: (_ for _ in ()).throw(
|
|
AssertionError("arXiv API should not run after a strong bare-title match")
|
|
),
|
|
)
|
|
|
|
results = core._augment_scholarly_results(
|
|
'Find the paper "LLaVA-OneVision: Easy Visual Task Transfer" Table 5',
|
|
[{
|
|
"title": "LLaVA: Large Language and Vision Assistant",
|
|
"url": "https://example.test/older-llava",
|
|
"snippet": "An older project.",
|
|
}],
|
|
5,
|
|
)
|
|
|
|
assert seen == [("LLaVA-OneVision: Easy Visual Task Transfer", 3)]
|
|
assert results[0]["url"] == "https://arxiv.org/abs/2408.03326"
|
|
|
|
|
|
def test_openalex_title_fallback_returns_exact_arxiv_landing_page(monkeypatch):
|
|
class _Response:
|
|
def raise_for_status(self):
|
|
return None
|
|
|
|
def json(self):
|
|
return {
|
|
"results": [
|
|
{
|
|
"display_name": "LLaVA-OneVision: Easy Visual Task Transfer",
|
|
"doi": "https://doi.org/10.48550/arxiv.2408.03326",
|
|
"primary_location": {
|
|
"landing_page_url": "http://arxiv.org/abs/2408.03326",
|
|
},
|
|
},
|
|
{
|
|
"display_name": "LLaVA: Large Language and Vision Assistant",
|
|
"primary_location": {
|
|
"landing_page_url": "https://arxiv.org/abs/2304.08485",
|
|
},
|
|
},
|
|
],
|
|
}
|
|
|
|
monkeypatch.setattr(core.httpx, "get", lambda *args, **kwargs: _Response())
|
|
|
|
assert core._openalex_title_results(
|
|
"LLaVA-OneVision: Easy Visual Task Transfer",
|
|
3,
|
|
) == [{
|
|
"title": "LLaVA-OneVision: Easy Visual Task Transfer",
|
|
"url": "https://arxiv.org/abs/2408.03326",
|
|
"snippet": "Exact scholarly-title match from OpenAlex metadata.",
|
|
"source": "openalex",
|
|
}]
|
|
|
|
|
|
def test_openalex_title_result_exposes_formal_publication_metadata(monkeypatch):
|
|
class _Response:
|
|
def raise_for_status(self):
|
|
return None
|
|
|
|
def json(self):
|
|
return {
|
|
"results": [{
|
|
"display_name": (
|
|
"Molmo and PixMo: Open Weights and Open Data for "
|
|
"State-of-the-Art Vision-Language Models"
|
|
),
|
|
"doi": "https://doi.org/10.1109/cvpr52734.2025.00018",
|
|
"publication_year": 2025,
|
|
"type": "conference-paper",
|
|
"primary_location": {
|
|
"landing_page_url": (
|
|
"https://doi.org/10.1109/cvpr52734.2025.00018"
|
|
),
|
|
"raw_source_name": (
|
|
"2025 IEEE/CVF Conference on Computer Vision and "
|
|
"Pattern Recognition (CVPR)"
|
|
),
|
|
"version": "publishedVersion",
|
|
"is_published": True,
|
|
},
|
|
}],
|
|
}
|
|
|
|
monkeypatch.setattr(core.httpx, "get", lambda *args, **kwargs: _Response())
|
|
|
|
result = core._openalex_title_results("Molmo and PixMo", 3)[0]
|
|
|
|
assert result["url"] == "https://doi.org/10.1109/cvpr52734.2025.00018"
|
|
assert result["snippet"] == (
|
|
"Exact scholarly-title match from OpenAlex metadata. Formal publication: "
|
|
"2025 IEEE/CVF Conference on Computer Vision and Pattern Recognition "
|
|
"(CVPR), 2025; type: conference-paper; version: publishedVersion."
|
|
)
|
|
|
|
|
|
def test_named_paper_search_returns_exact_metadata_before_generic_providers(
|
|
monkeypatch,
|
|
tmp_path,
|
|
):
|
|
"""Paper-source discovery must not burn its budget on generic providers."""
|
|
|
|
provider_calls = []
|
|
|
|
monkeypatch.setattr(core, "SEARCH_CACHE_DIR", tmp_path)
|
|
monkeypatch.setattr(core, "search_cache_index", {})
|
|
monkeypatch.setattr(core, "_record_query", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(
|
|
core,
|
|
"_get_search_settings",
|
|
lambda: {"search_provider": "searxng", "search_fallback_chain": []},
|
|
)
|
|
monkeypatch.setattr(core, "_build_provider_chain", lambda provider: [provider])
|
|
monkeypatch.setattr(
|
|
core,
|
|
"_call_provider",
|
|
lambda *args, **kwargs: provider_calls.append((args, kwargs)) or [],
|
|
)
|
|
monkeypatch.setattr(
|
|
core,
|
|
"_arxiv_title_results",
|
|
lambda *args, **kwargs: (_ for _ in ()).throw(
|
|
AssertionError("OpenAlex should resolve the title before arXiv fallback")
|
|
),
|
|
)
|
|
monkeypatch.setattr(
|
|
core,
|
|
"_openalex_title_results",
|
|
lambda title, count: [{
|
|
"title": "LLaVA-OneVision: Easy Visual Task Transfer",
|
|
"url": "https://arxiv.org/abs/2408.03326",
|
|
"snippet": "Exact scholarly-title match from OpenAlex metadata.",
|
|
"source": "openalex",
|
|
}] if title == "LLaVA-OneVision: Easy Visual Task Transfer" else [],
|
|
)
|
|
|
|
results = core.searxng_search_results(
|
|
'LLaVA-OneVision paper "LLaVA-OneVision: Easy Visual Task Transfer" '
|
|
"Table 3 Table 5",
|
|
count=5,
|
|
)
|
|
|
|
assert provider_calls == []
|
|
assert results[0]["url"] == "https://arxiv.org/abs/2408.03326"
|
|
|
|
|
|
def test_short_distinctive_paper_name_uses_exact_metadata_before_providers(
|
|
monkeypatch,
|
|
tmp_path,
|
|
):
|
|
provider_calls = []
|
|
monkeypatch.setattr(core, "SEARCH_CACHE_DIR", tmp_path)
|
|
monkeypatch.setattr(core, "search_cache_index", {})
|
|
monkeypatch.setattr(core, "_record_query", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(
|
|
core,
|
|
"_get_search_settings",
|
|
lambda: {"search_provider": "searxng", "search_fallback_chain": []},
|
|
)
|
|
monkeypatch.setattr(core, "_build_provider_chain", lambda provider: [provider])
|
|
monkeypatch.setattr(
|
|
core,
|
|
"_call_provider",
|
|
lambda *args, **kwargs: provider_calls.append((args, kwargs)) or [],
|
|
)
|
|
monkeypatch.setattr(
|
|
core,
|
|
"_direct_scholarly_title_results",
|
|
lambda title, count: [{
|
|
"title": "Qwen2-VL: Enhancing Vision-Language Model Perception",
|
|
"url": "https://arxiv.org/abs/2409.12191",
|
|
"snippet": "Official paper.",
|
|
"source": "openalex",
|
|
}] if title == "Qwen2-VL" else [],
|
|
)
|
|
|
|
results = core.searxng_search_results(
|
|
"Qwen2-VL paper multimodal benchmarks Table 2 Table 4",
|
|
count=5,
|
|
)
|
|
|
|
assert provider_calls == []
|
|
assert results[0]["url"] == "https://arxiv.org/abs/2409.12191"
|
|
|
|
|
|
def test_scholarly_fallback_skips_network_for_existing_strong_match(monkeypatch):
|
|
def _unexpected_get(*args, **kwargs):
|
|
raise AssertionError("arXiv should not be queried for an existing title match")
|
|
|
|
monkeypatch.setattr(core, "searxng_search_api", _unexpected_get)
|
|
monkeypatch.setattr(core.httpx, "get", _unexpected_get)
|
|
exact = {
|
|
"title": "LLaVA-OneVision: Easy Visual Task Transfer",
|
|
"url": "https://arxiv.org/abs/2408.03326",
|
|
"snippet": "Paper abstract.",
|
|
}
|
|
generic = {
|
|
"title": "LLaVA: Large Language and Vision Assistant",
|
|
"url": "https://example.test/older-llava",
|
|
"snippet": "An older project.",
|
|
}
|
|
|
|
assert core._augment_scholarly_results(
|
|
'Find the paper "LLaVA-OneVision: Easy Visual Task Transfer" Table 5',
|
|
[generic, exact],
|
|
5,
|
|
) == [exact, generic]
|
|
|
|
|
|
def test_service_ddg_redirect_ignores_lookalike_hosts():
|
|
for host in ("duckduckgo.com.evil.com", "notduckduckgo.com"):
|
|
url = f"https://{host}/l/?uddg=https%3A%2F%2Fexample.com"
|
|
assert providers._resolve_ddg_redirect(url) == url
|
|
|
|
assert providers._resolve_ddg_redirect(
|
|
"https://duckduckgo.com/l/?uddg=https%3A%2F%2Fexample.com"
|
|
) == "https://example.com"
|
|
|
|
|
|
def test_service_ddg_html_fallback_sends_safesearch(monkeypatch):
|
|
seen = {}
|
|
html = """
|
|
<html><body>
|
|
<div class="result">
|
|
<a class="result__a" href="https://notduckduckgo.com/l/?uddg=https%3A%2F%2Fevil.example">
|
|
Lookalike
|
|
</a>
|
|
<a class="result__snippet">Snippet</a>
|
|
</div>
|
|
</body></html>
|
|
"""
|
|
|
|
class _Response:
|
|
text = html
|
|
|
|
def raise_for_status(self):
|
|
return None
|
|
|
|
def fake_get(url, **kwargs):
|
|
seen["params"] = kwargs["params"]
|
|
return _Response()
|
|
|
|
monkeypatch.setattr(providers, "_get_search_settings", lambda: {"search_safesearch": "off"})
|
|
monkeypatch.setitem(sys.modules, "ddgs", None)
|
|
monkeypatch.setattr(providers.httpx, "get", fake_get)
|
|
|
|
results = providers.duckduckgo_search("odysseus", count=1)
|
|
|
|
assert seen["params"]["kp"] == "-2"
|
|
assert results[0]["url"].startswith("https://notduckduckgo.com/")
|