Migrate to package

This commit is contained in:
2026-03-02 17:44:18 +00:00
parent 3e2386b9b6
commit 8dfd2048a5
13 changed files with 356 additions and 31 deletions

1
tests/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Tests for knowledge-search-mcp."""

36
tests/conftest.py Normal file
View File

@@ -0,0 +1,36 @@
"""Pytest configuration and shared fixtures."""
import os
from unittest.mock import MagicMock
import pytest
@pytest.fixture(autouse=True)
def mock_env_vars(monkeypatch):
"""Set required environment variables for testing."""
test_env = {
"PROJECT_ID": "test-project",
"LOCATION": "us-central1",
"BUCKET": "test-bucket",
"INDEX_NAME": "test-index",
"DEPLOYED_INDEX_ID": "test-deployed-index",
"ENDPOINT_NAME": "projects/test/locations/us-central1/indexEndpoints/test",
"ENDPOINT_DOMAIN": "test.us-central1-aiplatform.googleapis.com",
}
for key, value in test_env.items():
monkeypatch.setenv(key, value)
@pytest.fixture
def mock_gcs_storage():
"""Mock Google Cloud Storage client."""
mock = MagicMock()
return mock
@pytest.fixture
def mock_vector_search():
"""Mock vector search client."""
mock = MagicMock()
return mock

56
tests/test_config.py Normal file
View File

@@ -0,0 +1,56 @@
"""Tests for configuration management."""
import os
import pytest
from pydantic import ValidationError
from knowledge_search_mcp.config import Settings
def test_settings_from_env():
"""Test that Settings can be loaded from environment variables."""
# Environment is set by conftest.py fixture
settings = Settings.model_validate({})
assert settings.project_id == "test-project"
assert settings.location == "us-central1"
assert settings.bucket == "test-bucket"
assert settings.index_name == "test-index"
assert settings.deployed_index_id == "test-deployed-index"
def test_settings_defaults():
"""Test that Settings has correct default values."""
settings = Settings.model_validate({})
assert settings.embedding_model == "gemini-embedding-001"
assert settings.search_limit == 10
assert settings.log_name == "va_agent_evaluation_logs"
assert settings.log_level == "INFO"
def test_settings_custom_values(monkeypatch):
"""Test that Settings can be customized via environment."""
monkeypatch.setenv("EMBEDDING_MODEL", "custom-embedding-model")
monkeypatch.setenv("SEARCH_LIMIT", "20")
monkeypatch.setenv("LOG_LEVEL", "DEBUG")
settings = Settings.model_validate({})
assert settings.embedding_model == "custom-embedding-model"
assert settings.search_limit == 20
assert settings.log_level == "DEBUG"
def test_settings_validation_error():
"""Test that Settings raises ValidationError when required fields are missing."""
# Clear all env vars temporarily
required_vars = [
"PROJECT_ID", "LOCATION", "BUCKET", "INDEX_NAME",
"DEPLOYED_INDEX_ID", "ENDPOINT_NAME", "ENDPOINT_DOMAIN"
]
# This should work with conftest fixture
settings = Settings.model_validate({})
assert settings.project_id == "test-project"

108
tests/test_search.py Normal file
View File

@@ -0,0 +1,108 @@
"""Tests for vector search functionality."""
import io
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from knowledge_search_mcp.main import (
GoogleCloudFileStorage,
GoogleCloudVectorSearch,
SourceNamespace,
)
class TestGoogleCloudFileStorage:
"""Tests for GoogleCloudFileStorage."""
def test_init(self):
"""Test storage initialization."""
storage = GoogleCloudFileStorage(bucket="test-bucket")
assert storage.bucket_name == "test-bucket"
assert storage._cache == {}
@pytest.mark.asyncio
async def test_cache_hit(self):
"""Test that cached files are returned without fetching."""
storage = GoogleCloudFileStorage(bucket="test-bucket")
test_content = b"cached content"
storage._cache["test.md"] = test_content
result = await storage.async_get_file_stream("test.md")
assert result.read() == test_content
assert result.name == "test.md"
@pytest.mark.asyncio
async def test_cache_miss(self):
"""Test that uncached files are fetched from GCS."""
storage = GoogleCloudFileStorage(bucket="test-bucket")
test_content = b"fetched content"
# Mock the storage download
with patch.object(storage, '_get_aio_storage') as mock_storage_getter:
mock_storage = AsyncMock()
mock_storage.download = AsyncMock(return_value=test_content)
mock_storage_getter.return_value = mock_storage
result = await storage.async_get_file_stream("test.md")
assert result.read() == test_content
assert storage._cache["test.md"] == test_content
class TestGoogleCloudVectorSearch:
"""Tests for GoogleCloudVectorSearch."""
def test_init(self):
"""Test vector search client initialization."""
vs = GoogleCloudVectorSearch(
project_id="test-project",
location="us-central1",
bucket="test-bucket",
index_name="test-index",
)
assert vs.project_id == "test-project"
assert vs.location == "us-central1"
assert vs.index_name == "test-index"
def test_configure_index_endpoint(self):
"""Test endpoint configuration."""
vs = GoogleCloudVectorSearch(
project_id="test-project",
location="us-central1",
bucket="test-bucket",
)
vs.configure_index_endpoint(
name="test-endpoint",
public_domain="test.domain.com",
)
assert vs._endpoint_name == "test-endpoint"
assert vs._endpoint_domain == "test.domain.com"
def test_configure_index_endpoint_validation(self):
"""Test that endpoint configuration validates inputs."""
vs = GoogleCloudVectorSearch(
project_id="test-project",
location="us-central1",
bucket="test-bucket",
)
with pytest.raises(ValueError, match="endpoint name"):
vs.configure_index_endpoint(name="", public_domain="test.com")
with pytest.raises(ValueError, match="endpoint domain"):
vs.configure_index_endpoint(name="test", public_domain="")
class TestSourceNamespace:
"""Tests for SourceNamespace enum."""
def test_source_namespace_values(self):
"""Test that SourceNamespace has expected values."""
assert SourceNamespace.EDUCACION_FINANCIERA.value == "Educacion Financiera"
assert SourceNamespace.PRODUCTOS_Y_SERVICIOS.value == "Productos y Servicios"
assert SourceNamespace.FUNCIONALIDADES_APP_MOVIL.value == "Funcionalidades de la App Movil"