test: pin suite to reserved llm_tester tenant with guard and teardown
Rewrite tests/conftest.py for the shared-services testing model where tenancy is the only isolation wall: - Remove the hardcoded production host default (192.168.86.149): TEST_HOST env with a safe localhost default; LIBRARY_DESK_URL selects the local wakeup server (8778), never the production container (8089). - Pin the suite to the reserved test tenant llm_tester (TEST_TENANT may only choose a tenant inside the reserved llm_tester* namespace). - Session guard (autouse) hard-aborts the whole run if the effective tenant is jpmschweitzer or outside the reserved namespace. - Integration-marked tests only run with RUN_INTEGRATION_TESTS=1 and a passing guard; they are skipped otherwise. - Session-scoped teardown deletes ALL llm_tester artifacts created during the run: Qdrant *_llm_tester collections, Neo4j User_Llm_Tester* nodes, wiki subtree users/llm_tester (and hyphen variant), llm_tester Redis keys on the service DB - with hard assert_safe_test_tenant() checks before every delete. Uses a sync fixture + asyncio.run to avoid the session loop-scope mismatch. - Legacy tests/test_integration.py marked integration and pinned to the test tenant (taxonomy/list reads no longer touch the production namespace; Qdrant tests use the tenant-scoped collection name). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,13 +7,22 @@ These tests require actual service connectivity:
|
||||
- SearXNG running at http://searxng:8080
|
||||
- Ollama running at http://ollama:11434
|
||||
|
||||
Run with: pytest tests/test_integration.py -v
|
||||
These tests run against the SHARED production services under the reserved
|
||||
test tenant only. They are gated behind the session tenant guard and the
|
||||
RUN_INTEGRATION_TESTS=1 environment flag (see tests/conftest.py) and are
|
||||
skipped otherwise.
|
||||
|
||||
Run with:
|
||||
RUN_INTEGRATION_TESTS=1 TEST_HOST=<shared-host> \
|
||||
.venv/bin/python -m pytest tests/test_integration.py -v
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from tests.conftest import TEST_TENANT, assert_safe_test_tenant
|
||||
|
||||
from src.clients.neo4j_client import Neo4jClient
|
||||
from src.clients.qdrant_client import QdrantClientWrapper
|
||||
from src.clients.wikijs_client import WikiJSClient
|
||||
@@ -22,6 +31,9 @@ from src.clients.ollama_client import OllamaClient
|
||||
from src.jobs.job_manager import JobManager, JobType, JobStatus
|
||||
from src.config import get_settings
|
||||
|
||||
# Only run when the tenant guard passes and integration mode is enabled.
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def settings():
|
||||
@@ -100,6 +112,7 @@ class TestNeo4jIntegration:
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_and_get_document(self, neo4j_client, test_user):
|
||||
"""Test creating and retrieving a document node."""
|
||||
assert_safe_test_tenant(test_user)
|
||||
doc_id = "test_doc_integration"
|
||||
|
||||
# Create document
|
||||
@@ -131,9 +144,10 @@ class TestQdrantIntegration:
|
||||
@pytest.mark.asyncio
|
||||
async def test_collection_creation(self, qdrant_client, test_user):
|
||||
"""Test creating a collection."""
|
||||
await qdrant_client.ensure_collection(test_user)
|
||||
|
||||
assert_safe_test_tenant(test_user)
|
||||
collection_name = qdrant_client.get_collection_name(test_user)
|
||||
await qdrant_client.ensure_collection(collection_name)
|
||||
|
||||
collections = qdrant_client.client.get_collections()
|
||||
collection_names = [c.name for c in collections.collections]
|
||||
|
||||
@@ -142,7 +156,10 @@ class TestQdrantIntegration:
|
||||
@pytest.mark.asyncio
|
||||
async def test_upsert_and_search(self, qdrant_client, test_user):
|
||||
"""Test upserting and searching chunks."""
|
||||
await qdrant_client.ensure_collection(test_user)
|
||||
assert_safe_test_tenant(test_user)
|
||||
await qdrant_client.ensure_collection(
|
||||
qdrant_client.get_collection_name(test_user)
|
||||
)
|
||||
|
||||
# Create test chunks with 768-dimensional embeddings
|
||||
chunks = [
|
||||
@@ -190,7 +207,7 @@ class TestWikiJSIntegration:
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_all_pages(self, wikijs_client):
|
||||
"""Test listing all pages with pagination support."""
|
||||
pages = await wikijs_client.list_all_pages(path_prefix="users/")
|
||||
pages = await wikijs_client.list_all_pages(path_prefix=f"users/{TEST_TENANT}")
|
||||
assert isinstance(pages, list)
|
||||
# Verify each page has expected fields
|
||||
for page in pages[:5]: # Check first 5
|
||||
@@ -201,7 +218,7 @@ class TestWikiJSIntegration:
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_taxonomy_structure(self, wikijs_client):
|
||||
"""Test getting taxonomy structure for a user."""
|
||||
taxonomy = await wikijs_client.get_taxonomy_structure("users/jpmschweitzer")
|
||||
taxonomy = await wikijs_client.get_taxonomy_structure(f"users/{TEST_TENANT}")
|
||||
assert isinstance(taxonomy, dict)
|
||||
# Each key should be a category, value should be list of subcategories
|
||||
for category, subcategories in taxonomy.items():
|
||||
|
||||
Reference in New Issue
Block a user