Skip to content

Commit 50861b8

Browse files
committed
🧪 Add global test configuration and common fixtures for improved test management
1 parent 8e4cf61 commit 50861b8

File tree

1 file changed

+206
-0
lines changed

1 file changed

+206
-0
lines changed

‎test/conftest.py‎

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
"""
2+
Global test configuration and common fixtures for all tests.
3+
4+
This file provides shared mocks and fixtures to reduce duplication across test files.
5+
"""
6+
import os
7+
import sys
8+
from pathlib import Path
9+
from unittest.mock import MagicMock, patch
10+
11+
import pytest
12+
13+
# Set up environment variables commonly needed for tests
14+
os.environ.setdefault('MINIO_ENDPOINT', 'http://localhost:9000')
15+
os.environ.setdefault('MINIO_ACCESS_KEY', 'minioadmin')
16+
os.environ.setdefault('MINIO_SECRET_KEY', 'minioadmin')
17+
os.environ.setdefault('MINIO_REGION', 'us-east-1')
18+
os.environ.setdefault('MINIO_DEFAULT_BUCKET', 'test-bucket')
19+
os.environ.setdefault('ELASTICSEARCH_HOST', 'http://localhost:9200')
20+
os.environ.setdefault('ELASTICSEARCH_API_KEY', 'test-key')
21+
os.environ.setdefault('ELASTICSEARCH_USERNAME', 'elastic')
22+
os.environ.setdefault('ELASTICSEARCH_PASSWORD', 'test-password')
23+
os.environ.setdefault('POSTGRES_HOST', 'localhost')
24+
os.environ.setdefault('POSTGRES_USER', 'test_user')
25+
os.environ.setdefault('POSTGRES_PASSWORD', 'test_password')
26+
os.environ.setdefault('POSTGRES_DB', 'test_db')
27+
os.environ.setdefault('POSTGRES_PORT', '5432')
28+
29+
# Set up Python path
30+
current_dir = Path(__file__).resolve().parent
31+
project_root = current_dir.parent
32+
backend_dir = project_root / "backend"
33+
34+
if str(project_root) not in sys.path:
35+
sys.path.insert(0, str(project_root))
36+
if str(backend_dir) not in sys.path:
37+
sys.path.insert(0, str(backend_dir))
38+
39+
# Mock external libraries at module level before any imports
40+
boto3_mock = MagicMock()
41+
psycopg2_mock = MagicMock()
42+
supabase_mock = MagicMock()
43+
44+
# Mock dotenv to prevent file access issues
45+
dotenv_mock = MagicMock()
46+
sys.modules['dotenv'] = dotenv_mock
47+
sys.modules['dotenv.main'] = dotenv_mock.main = MagicMock()
48+
49+
sys.modules['boto3'] = boto3_mock
50+
sys.modules['psycopg2'] = psycopg2_mock
51+
sys.modules['supabase'] = supabase_mock
52+
53+
# Mock other common external dependencies
54+
nexent_mock = MagicMock()
55+
sys.modules['nexent'] = nexent_mock
56+
sys.modules['nexent.core'] = nexent_mock.core = MagicMock()
57+
sys.modules['nexent.core.models'] = nexent_mock.core.models = MagicMock()
58+
sys.modules['nexent.core.models.openai_vlm'] = nexent_mock.core.models.openai_vlm = MagicMock()
59+
sys.modules['nexent.core.models.openai_long_context_model'] = nexent_mock.core.models.openai_long_context_model = MagicMock()
60+
sys.modules['nexent.memory'] = MagicMock()
61+
sys.modules['nexent.memory.memory_service'] = MagicMock()
62+
sys.modules['nexent.storage.storage_client_factory'] = MagicMock()
63+
sys.modules['nexent.storage.minio_config'] = MagicMock()
64+
65+
# Mock nexent.core classes
66+
67+
68+
class MockMessageObserver:
69+
def __init__(self, *args, **kwargs):
70+
pass
71+
72+
73+
class MockOpenAIVLModel:
74+
def __init__(self, *args, **kwargs):
75+
pass
76+
77+
def analyze_image(self, *args, **kwargs):
78+
return MagicMock(content="Mocked image analysis")
79+
80+
81+
class MockOpenAILongContextModel:
82+
def __init__(self, *args, **kwargs):
83+
pass
84+
85+
def analyze_long_text(self, *args, **kwargs):
86+
return (MagicMock(content="Mocked text analysis"), "0")
87+
88+
89+
nexent_mock.core.MessageObserver = MockMessageObserver
90+
nexent_mock.core.models.openai_vlm.OpenAIVLModel = MockOpenAIVLModel
91+
nexent_mock.core.models.openai_long_context_model.OpenAILongContextModel = MockOpenAILongContextModel
92+
93+
# Mock services module
94+
sys.modules['services'] = MagicMock()
95+
sys.modules['services.invitation_service'] = MagicMock()
96+
sys.modules['services.group_service'] = MagicMock()
97+
98+
# Note: database module is not mocked at sys.modules level to avoid import conflicts
99+
# Individual components are mocked via patch decorators instead
100+
101+
# Common logger mock
102+
logger_mock = MagicMock()
103+
104+
105+
@pytest.fixture(scope="session", autouse=True)
106+
def global_mocks():
107+
"""
108+
Global mocks that are applied to all tests.
109+
110+
This fixture runs once per test session and patches common external dependencies
111+
that should be mocked across all tests.
112+
"""
113+
# Mock AWS/MinIO calls
114+
with patch('botocore.client.BaseClient._make_api_call', return_value={}):
115+
116+
# Mock Elasticsearch
117+
with patch('elasticsearch.Elasticsearch', return_value=MagicMock()):
118+
119+
# Mock storage factory and MinIO config validation
120+
storage_client_mock = MagicMock()
121+
minio_client_mock = MagicMock()
122+
minio_client_mock._ensure_bucket_exists = MagicMock()
123+
minio_client_mock.client = MagicMock()
124+
125+
minio_config_mock = MagicMock()
126+
minio_config_mock.validate = MagicMock()
127+
128+
with patch('nexent.storage.storage_client_factory.create_storage_client_from_config',
129+
return_value=storage_client_mock), \
130+
patch('nexent.storage.minio_config.MinIOStorageConfig',
131+
return_value=minio_config_mock), \
132+
patch('backend.database.client.MinioClient',
133+
return_value=minio_client_mock), \
134+
patch('database.client.MinioClient', return_value=minio_client_mock), \
135+
patch('backend.database.client.minio_client', minio_client_mock):
136+
137+
yield {
138+
'boto3': boto3_mock,
139+
'psycopg2': psycopg2_mock,
140+
'supabase': supabase_mock,
141+
'storage_client': storage_client_mock,
142+
'minio_client': minio_client_mock,
143+
'minio_config': minio_config_mock,
144+
'logger': logger_mock
145+
}
146+
147+
148+
@pytest.fixture
149+
def mock_logger():
150+
"""Common logger mock for tests that need logging."""
151+
return logger_mock
152+
153+
154+
@pytest.fixture
155+
def mock_constants():
156+
"""Mock constants object with common test values."""
157+
mock_const = MagicMock()
158+
mock_const.ES_HOST = "http://localhost:9200"
159+
mock_const.ES_API_KEY = "test-es-key"
160+
mock_const.ES_USERNAME = "elastic"
161+
mock_const.ES_PASSWORD = "test-password"
162+
return mock_const
163+
164+
165+
@pytest.fixture
166+
def mock_tenant_config_manager():
167+
"""Mock tenant config manager for tests."""
168+
mock_manager = MagicMock()
169+
# Ensure certain methods/attributes don't exist to match real behavior
170+
del mock_manager._get_cache_key # This method was removed
171+
del mock_manager.clear_cache # This method was removed
172+
return mock_manager
173+
174+
175+
@pytest.fixture
176+
def mock_database_client():
177+
"""Mock database client for tests."""
178+
mock_client = MagicMock()
179+
mock_client.MinioClient = MagicMock()
180+
mock_client.PostgresClient = MagicMock()
181+
mock_client.db_client = MagicMock()
182+
mock_client.get_db_session = MagicMock()
183+
mock_client.as_dict = MagicMock()
184+
mock_client.minio_client = MagicMock()
185+
mock_client.postgres_client = MagicMock()
186+
return mock_client
187+
188+
189+
@pytest.fixture
190+
def mock_model_configs():
191+
"""Common mock model configurations for testing."""
192+
return {
193+
'llm_config': {
194+
"model_name": "gpt-4",
195+
"model_repo": "openai",
196+
"base_url": "https://api.openai.com/v1",
197+
"api_key": "test-llm-key"
198+
},
199+
'embedding_config': {
200+
"model_name": "text-embedding-ada-002",
201+
"model_repo": "openai",
202+
"base_url": "https://api.openai.com/v1",
203+
"api_key": "test-embed-key",
204+
"max_tokens": 1536
205+
}
206+
}

0 commit comments

Comments
 (0)