5a558eb09e
TypeScript SDK Compatibility V1.x E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / TypeScript SDK Compatibility V1.x E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
TypeScript SDK E2E Tests / TypeScript SDK E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
Python SDK E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK E2E Tests / Python SDK E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK E2E Tests / build-opik (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Python SDK Compatibility V1.x E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK E2E Tests / build-opik (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer E2E Tests Python ${{matrix.python_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer Integration Smoke Tests (push) Has been cancelled
🐙 Code Quality / detect (push) Has been cancelled
🐙 Code Quality / lint (${{ matrix.leg.name }}) (push) Has been cancelled
🐙 Code Quality / summary (push) Has been cancelled
TypeScript SDK Library Integration Tests / Check Secrets (push) Has been cancelled
TypeScript SDK Library Integration Tests / opik-vercel (Vercel AI SDK / eve) (push) Has been cancelled
SDK Library Integration Tests Runner / Check Secrets (push) Has been cancelled
SDK Library Integration Tests Runner / Missed OpenAI API Key Warning (push) Has been cancelled
SDK Library Integration Tests Runner / Build (push) Has been cancelled
SDK Library Integration Tests Runner / openai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_legacy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / llama_index_tests (push) Has been cancelled
SDK Library Integration Tests Runner / anthropic_tests (push) Has been cancelled
SDK Library Integration Tests Runner / mistral_tests (push) Has been cancelled
SDK Library Integration Tests Runner / groq_tests (push) Has been cancelled
SDK Library Integration Tests Runner / aisuite_tests (push) Has been cancelled
SDK Library Integration Tests Runner / haystack_tests (push) Has been cancelled
SDK Library Integration Tests Runner / dspy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v1_tests (push) Has been cancelled
SDK Library Integration Tests Runner / genai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_legacy_1_3_0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / evaluation_metrics_tests (push) Has been cancelled
SDK Library Integration Tests Runner / bedrock_tests (push) Has been cancelled
SDK Library Integration Tests Runner / litellm_tests (push) Has been cancelled
SDK Library Integration Tests Runner / harbor_tests (push) Has been cancelled
SDK Library Integration Tests Runner / Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / render-equality (push) Has been cancelled
Opik Optimizer - Unit Tests / Opik Optimizer Unit Tests Python ${{matrix.python_version}} (push) Has been cancelled
Python BE E2E Tests / Python BE E2E (push) Has been cancelled
Python Backend Tests / run-python-backend-tests (push) Has been cancelled
Python SDK Unit Tests / Python SDK Unit Tests ${{matrix.python_version}} (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
SDK E2E Libraries Integration Tests / Check Secrets (push) Has been cancelled
SDK E2E Libraries Integration Tests / Missed OpenAI API Key Warning (push) Has been cancelled
SDK E2E Libraries Integration Tests / build-opik (push) Has been cancelled
SDK E2E Libraries Integration Tests / E2E Lib Integration Python ${{matrix.python_version}} (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-gemini) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-langchain) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-openai) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-otel) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-vercel) (push) Has been cancelled
TypeScript SDK Build & Publish / build-and-publish (push) Has been cancelled
TypeScript SDK Unit Tests / Test on Node ${{ matrix.node-version }} (push) Has been cancelled
Backend Tests / discover-tests (push) Has been cancelled
Backend Tests / ${{ matrix.name }} (push) Has been cancelled
Build and Publish SDK / build-and-publish (push) Has been cancelled
Build Opik Docker Images / set-version (push) Has been cancelled
Build Opik Docker Images / build-backend (push) Has been cancelled
Build Opik Docker Images / build-sandbox-executor-python (push) Has been cancelled
Build Opik Docker Images / build-python-backend (push) Has been cancelled
Build Opik Docker Images / build-frontend (push) Has been cancelled
Build Opik Docker Images / create-git-tag (push) Has been cancelled
ClickHouse Migration Cluster Check / validate-clickhouse-migrations (push) Has been cancelled
Docs - Publish / run (push) Has been cancelled
E2E Tests - Post Merge (v2) / 🧪 E2E v2 Tests (${{ github.event.inputs.tier || 't1' }}) (push) Has been cancelled
E2E Tests - Post Merge (v2) / 📢 Slack Notification (push) Has been cancelled
Frontend Unit Tests / Test on Node 20 (push) Has been cancelled
Guardrails E2E Tests / Select Python version matrix (push) Has been cancelled
Guardrails E2E Tests / Guardrails E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Guardrails E2E Tests / 📢 Slack Notification (push) Has been cancelled
Guardrails Backend Unit Tests / Guardrails Backend Unit Tests (push) Has been cancelled
Guardrails Backend Unit Tests / 📢 Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v3.21.0) (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v4.2.0) (push) Has been cancelled
Lint Opik Helm Chart / unittest-helm-chart (push) Has been cancelled
278 lines
10 KiB
Python
278 lines
10 KiB
Python
"""Unit tests for Wikipedia search tools."""
|
|
|
|
from typing import Any
|
|
|
|
from pathlib import Path
|
|
import builtins
|
|
import sys
|
|
from types import SimpleNamespace
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
|
|
from opik_optimizer.utils.tools.wikipedia import (
|
|
search_wikipedia,
|
|
_search_wikipedia_api,
|
|
_search_wikipedia_colbert,
|
|
_search_wikipedia_bm25,
|
|
_download_bm25_index,
|
|
)
|
|
|
|
pytestmark = pytest.mark.usefixtures("suppress_expected_optimizer_warnings")
|
|
|
|
|
|
class TestSearchWikipediaAPI:
|
|
"""Tests for Wikipedia API search."""
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia.requests.get")
|
|
def test_api_search_success(self, mock_get: Mock) -> None:
|
|
"""Test successful API search."""
|
|
mock_response = Mock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {
|
|
"query": {
|
|
"search": [
|
|
{
|
|
"title": "Python (programming language)",
|
|
"snippet": "Python is a <span>high-level</span> programming language",
|
|
}
|
|
]
|
|
}
|
|
}
|
|
mock_get.return_value = mock_response
|
|
|
|
results = _search_wikipedia_api("Python", k=1)
|
|
|
|
assert len(results) == 1
|
|
assert "Python (programming language)" in results[0]
|
|
assert "high-level" in results[0]
|
|
# HTML tags should be stripped
|
|
assert "<span>" not in results[0]
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia.requests.get")
|
|
def test_api_search_no_results(self, mock_get: Mock) -> None:
|
|
"""Test API search with no results."""
|
|
mock_response = Mock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {"query": {"search": []}}
|
|
mock_get.return_value = mock_response
|
|
|
|
results = _search_wikipedia_api("nonexistentquery12345", k=1)
|
|
|
|
assert len(results) == 1
|
|
assert "No Wikipedia results found" in results[0]
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia.requests.get")
|
|
def test_api_search_http_error(self, mock_get: Mock) -> None:
|
|
"""Test API search with HTTP error."""
|
|
mock_response = Mock()
|
|
mock_response.status_code = 500
|
|
mock_get.return_value = mock_response
|
|
|
|
with pytest.raises(Exception, match="Search API returned status 500"):
|
|
_search_wikipedia_api("test", k=1)
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia.requests.get")
|
|
def test_api_search_network_error(self, mock_get: Mock) -> None:
|
|
"""Test API search with network error."""
|
|
mock_get.side_effect = Exception("Network error")
|
|
|
|
with pytest.raises(Exception, match="Wikipedia API request failed"):
|
|
_search_wikipedia_api("test", k=1)
|
|
|
|
|
|
class TestSearchWikipediaColBERT:
|
|
"""Tests for ColBERT search."""
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia._search_wikipedia_api")
|
|
def test_colbert_not_available_fallback(self, mock_api: Mock) -> None:
|
|
"""Test fallback to API when ColBERT not available."""
|
|
mock_api.return_value = ["fallback result"]
|
|
|
|
# ColBERT module won't be available in most test environments
|
|
results = _search_wikipedia_colbert("test", k=1)
|
|
|
|
assert results == ["fallback result"]
|
|
mock_api.assert_called_once_with("test", k=1)
|
|
|
|
|
|
class TestSearchWikipediaBM25:
|
|
"""Tests for BM25 search."""
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia._search_wikipedia_api")
|
|
def test_bm25_not_available_fallback(self, mock_api: Mock) -> None:
|
|
"""Test fallback to API when BM25 not available."""
|
|
mock_api.return_value = ["fallback result"]
|
|
|
|
# bm25s module won't be available in most test environments
|
|
results = _search_wikipedia_bm25("test", k=1, index_dir="/tmp/test")
|
|
|
|
assert results == ["fallback result"]
|
|
mock_api.assert_called_once_with("test", k=1)
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia._search_wikipedia_api")
|
|
def test_bm25_no_index_fallback(self, mock_api: Mock) -> None:
|
|
"""Test fallback when no index provided."""
|
|
mock_api.return_value = ["fallback result"]
|
|
|
|
results = _search_wikipedia_bm25("test", k=1, index_dir=None, hf_repo=None)
|
|
|
|
assert results == ["fallback result"]
|
|
mock_api.assert_called_once_with("test", k=1)
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia._search_wikipedia_api")
|
|
def test_bm25_index_not_found_fallback(self, mock_api: Mock) -> None:
|
|
"""Test fallback when index directory doesn't exist."""
|
|
mock_api.return_value = ["fallback result"]
|
|
|
|
# Test will naturally fall back because /nonexistent/path doesn't exist
|
|
# The import will succeed, but the path check will fail
|
|
results = _search_wikipedia_bm25("test", k=1, index_dir="/nonexistent/path")
|
|
|
|
assert results == ["fallback result"]
|
|
|
|
|
|
class TestSearchWikipediaUnified:
|
|
"""Tests for unified search_wikipedia function."""
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia._search_wikipedia_api")
|
|
def test_default_api_search(self, mock_api: Mock) -> None:
|
|
"""Test default search type is API."""
|
|
mock_api.return_value = ["result"]
|
|
|
|
results = search_wikipedia("test")
|
|
|
|
assert results == ["result"]
|
|
mock_api.assert_called_once_with("test", k=3)
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia._search_wikipedia_api")
|
|
def test_explicit_api_search(self, mock_api: Mock) -> None:
|
|
"""Test explicit API search type."""
|
|
mock_api.return_value = ["result"]
|
|
|
|
results = search_wikipedia("test", search_type="api", k=5)
|
|
|
|
assert results == ["result"]
|
|
mock_api.assert_called_once_with("test", k=5)
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia._search_wikipedia_colbert")
|
|
def test_colbert_search(self, mock_colbert: Mock) -> None:
|
|
"""Test ColBERT search type."""
|
|
mock_colbert.return_value = ["result"]
|
|
|
|
results = search_wikipedia("test", search_type="colbert", k=5)
|
|
|
|
assert results == ["result"]
|
|
mock_colbert.assert_called_once_with("test", k=5)
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia._search_wikipedia_bm25")
|
|
def test_bm25_search(self, mock_bm25: Mock) -> None:
|
|
"""Test BM25 search type."""
|
|
mock_bm25.return_value = ["result"]
|
|
|
|
results = search_wikipedia(
|
|
"test", search_type="bm25", k=5, bm25_index_dir="/tmp/index"
|
|
)
|
|
|
|
assert results == ["result"]
|
|
mock_bm25.assert_called_once_with(
|
|
"test", k=5, index_dir="/tmp/index", hf_repo=None
|
|
)
|
|
|
|
def test_invalid_search_type(self) -> None:
|
|
"""Test invalid search type raises error."""
|
|
with pytest.raises(ValueError, match="Invalid search_type.*Must be"):
|
|
search_wikipedia("test", search_type="invalid") # type: ignore
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia._search_wikipedia_api")
|
|
def test_backward_compat_use_api_true(self, mock_api: Mock) -> None:
|
|
"""Test backward compatibility with use_api=True."""
|
|
mock_api.return_value = ["result"]
|
|
|
|
with pytest.warns(DeprecationWarning, match="use_api parameter is deprecated"):
|
|
results = search_wikipedia("test", use_api=True)
|
|
|
|
assert results == ["result"]
|
|
mock_api.assert_called_once()
|
|
|
|
@patch("opik_optimizer.utils.tools.wikipedia._search_wikipedia_colbert")
|
|
def test_backward_compat_use_api_false(self, mock_colbert: Mock) -> None:
|
|
"""Test backward compatibility with use_api=False."""
|
|
mock_colbert.return_value = ["result"]
|
|
|
|
with pytest.warns(DeprecationWarning, match="use_api parameter is deprecated"):
|
|
results = search_wikipedia("test", use_api=False)
|
|
|
|
assert results == ["result"]
|
|
mock_colbert.assert_called_once()
|
|
|
|
|
|
class TestDownloadBM25Index:
|
|
"""Tests for BM25 index download."""
|
|
|
|
def test_download_with_default_cache(
|
|
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
"""Test downloading index with default cache directory."""
|
|
calls: dict[str, Any] = {}
|
|
|
|
def snapshot_download(**kwargs: Any) -> str:
|
|
calls.update(kwargs)
|
|
return str(tmp_path / "hf-cache")
|
|
|
|
monkeypatch.setitem(
|
|
sys.modules,
|
|
"huggingface_hub",
|
|
SimpleNamespace(snapshot_download=snapshot_download),
|
|
)
|
|
|
|
result = _download_bm25_index("test/repo")
|
|
|
|
assert result == tmp_path / "hf-cache"
|
|
assert calls["repo_id"] == "test/repo"
|
|
assert calls["repo_type"] == "dataset"
|
|
assert calls["local_dir_use_symlinks"] is False
|
|
assert "local_dir" not in calls
|
|
|
|
def test_download_with_custom_cache(
|
|
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
"""Test downloading index with custom cache directory."""
|
|
calls: dict[str, Any] = {}
|
|
|
|
def snapshot_download(**kwargs: Any) -> str:
|
|
calls.update(kwargs)
|
|
return str(tmp_path / "downloaded")
|
|
|
|
monkeypatch.setitem(
|
|
sys.modules,
|
|
"huggingface_hub",
|
|
SimpleNamespace(snapshot_download=snapshot_download),
|
|
)
|
|
|
|
target_dir = tmp_path / "bm25-cache"
|
|
result = _download_bm25_index("test/repo", target_dir=target_dir)
|
|
|
|
assert target_dir.exists()
|
|
assert result == tmp_path / "downloaded"
|
|
assert calls["repo_id"] == "test/repo"
|
|
assert calls["repo_type"] == "dataset"
|
|
assert calls["local_dir"] == str(target_dir)
|
|
assert calls["local_dir_use_symlinks"] is False
|
|
|
|
def test_download_missing_huggingface_hub(
|
|
self, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
"""Test error when huggingface_hub not installed."""
|
|
real_import = builtins.__import__
|
|
|
|
def import_side_effect(name: str, *args: Any, **kwargs: Any) -> Any:
|
|
if name == "huggingface_hub":
|
|
raise ImportError("missing")
|
|
return real_import(name, *args, **kwargs)
|
|
|
|
monkeypatch.setattr(builtins, "__import__", import_side_effect)
|
|
|
|
with pytest.raises(ImportError, match="huggingface_hub not installed"):
|
|
_download_bm25_index("test/repo")
|