Skip to content

Commit b7e03ec

Browse files
authored
Merge pull request #88 from UnitSeeker/add-testing-infrastructure
feat: Set up comprehensive Python testing infrastructure with Poetry
2 parents 559abfc + 29c9e13 commit b7e03ec

File tree

7 files changed

+8210
-1
lines changed

7 files changed

+8210
-1
lines changed

.gitignore

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,48 @@ dmypy.json
145145
**/model/text2vec-base-chinese
146146

147147
/data/milvus/db
148-
/data/mysql/db
148+
/data/mysql/db
149+
150+
# Testing
151+
.pytest_cache/
152+
.coverage
153+
.coverage.*
154+
htmlcov/
155+
coverage.xml
156+
*.py,cover
157+
.hypothesis/
158+
pytest_cache/
159+
test-results/
160+
.tox/
161+
.nox/
162+
163+
# Claude
164+
.claude/*
165+
166+
# Poetry
167+
dist/
168+
169+
# Virtual environments
170+
.venv/
171+
venv/
172+
ENV/
173+
env/
174+
.env
175+
176+
# IDE
177+
.vscode/
178+
.idea/
179+
*.swp
180+
*.swo
181+
*~
182+
183+
# OS
184+
.DS_Store
185+
Thumbs.db
186+
187+
# Temporary files
188+
*.tmp
189+
*.bak
190+
*.orig
191+
tmp/
192+
temp/

poetry.lock

Lines changed: 7703 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
[tool.poetry]
2+
name = "modelcache"
3+
version = "0.1.0"
4+
description = "A caching framework for machine learning models"
5+
authors = ["ModelCache Team"]
6+
readme = "README.md"
7+
packages = [
8+
{ include = "modelcache" },
9+
{ include = "modelcache_mm" }
10+
]
11+
12+
[tool.poetry.dependencies]
13+
python = "^3.8"
14+
cachetools = "5.3.1"
15+
DBUtils = "1.4"
16+
Flask = "3.0.0"
17+
numpy = "1.24.4"
18+
onnxruntime = "1.16.1"
19+
openai = "0.28.1"
20+
pymilvus = "2.3.1"
21+
PyMySQL = "1.1.0"
22+
Requests = "2.31.0"
23+
torch = "2.1.1"
24+
transformers = "4.38.2"
25+
faiss-cpu = "1.7.4"
26+
redis = "5.0.1"
27+
modelscope = "1.14.0"
28+
fastapi = "0.115.5"
29+
uvicorn = "0.32.0"
30+
chromadb = "0.5.23"
31+
elasticsearch = "7.10.0"
32+
snowflake-id = "1.0.2"
33+
34+
[tool.poetry.group.dev.dependencies]
35+
pytest = "^8.0.0"
36+
pytest-cov = "^5.0.0"
37+
pytest-mock = "^3.14.0"
38+
39+
[tool.poetry.scripts]
40+
test = "pytest:main"
41+
tests = "pytest:main"
42+
43+
[tool.pytest.ini_options]
44+
minversion = "8.0"
45+
testpaths = ["tests"]
46+
python_files = ["test_*.py", "*_test.py"]
47+
python_classes = ["Test*"]
48+
python_functions = ["test_*"]
49+
addopts = [
50+
"-ra",
51+
"--strict-markers",
52+
"--cov=modelcache",
53+
"--cov=modelcache_mm",
54+
"--cov-branch",
55+
"--cov-report=term-missing:skip-covered",
56+
"--cov-report=html",
57+
"--cov-report=xml",
58+
"--cov-fail-under=80",
59+
"-v"
60+
]
61+
markers = [
62+
"unit: Unit tests",
63+
"integration: Integration tests",
64+
"slow: Slow running tests"
65+
]
66+
67+
[tool.coverage.run]
68+
source = ["modelcache", "modelcache_mm"]
69+
omit = [
70+
"*/tests/*",
71+
"*/test_*",
72+
"*/__pycache__/*",
73+
"*/site-packages/*",
74+
"*/distutils/*",
75+
"*/venv/*",
76+
"*/.venv/*"
77+
]
78+
79+
[tool.coverage.report]
80+
precision = 2
81+
show_missing = true
82+
skip_covered = false
83+
exclude_lines = [
84+
"pragma: no cover",
85+
"def __repr__",
86+
"if __name__ == .__main__.:",
87+
"raise AssertionError",
88+
"raise NotImplementedError",
89+
"if TYPE_CHECKING:",
90+
"if typing.TYPE_CHECKING:"
91+
]
92+
93+
[tool.coverage.html]
94+
directory = "htmlcov"
95+
96+
[tool.coverage.xml]
97+
output = "coverage.xml"
98+
99+
[build-system]
100+
requires = ["poetry-core"]
101+
build-backend = "poetry.core.masonry.api"

tests/conftest.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
"""
2+
Shared pytest fixtures and configuration for modelcache tests.
3+
"""
4+
import os
5+
import tempfile
6+
import shutil
7+
from pathlib import Path
8+
from typing import Iterator, Dict, Any
9+
import pytest
10+
from unittest.mock import MagicMock
11+
12+
13+
@pytest.fixture
14+
def temp_dir() -> Iterator[Path]:
15+
"""
16+
Create a temporary directory for test files.
17+
18+
Yields:
19+
Path: Path to the temporary directory
20+
"""
21+
temp_path = Path(tempfile.mkdtemp())
22+
yield temp_path
23+
# Cleanup after test
24+
if temp_path.exists():
25+
shutil.rmtree(temp_path)
26+
27+
28+
@pytest.fixture
29+
def mock_config() -> Dict[str, Any]:
30+
"""
31+
Provide a mock configuration dictionary for testing.
32+
33+
Returns:
34+
Dict[str, Any]: Mock configuration
35+
"""
36+
return {
37+
"cache_dir": "/tmp/test_cache",
38+
"max_cache_size": 1000,
39+
"ttl": 3600,
40+
"embedding_model": "test-model",
41+
"similarity_threshold": 0.8,
42+
"vector_dimension": 768,
43+
"batch_size": 32,
44+
"database": {
45+
"type": "memory",
46+
"host": "localhost",
47+
"port": 6379,
48+
"password": None
49+
}
50+
}
51+
52+
53+
@pytest.fixture
54+
def mock_embedding():
55+
"""
56+
Mock embedding object for testing.
57+
58+
Returns:
59+
MagicMock: Mock embedding with common methods
60+
"""
61+
mock = MagicMock()
62+
mock.embed.return_value = [0.1] * 768 # Default 768-dim embedding
63+
mock.embed_batch.return_value = [[0.1] * 768] * 10
64+
mock.dimension = 768
65+
mock.model_name = "test-embedding-model"
66+
return mock
67+
68+
69+
@pytest.fixture
70+
def mock_cache_manager():
71+
"""
72+
Mock cache manager for testing.
73+
74+
Returns:
75+
MagicMock: Mock cache manager with common methods
76+
"""
77+
mock = MagicMock()
78+
mock.get.return_value = None
79+
mock.set.return_value = True
80+
mock.delete.return_value = True
81+
mock.clear.return_value = True
82+
mock.size.return_value = 0
83+
return mock
84+
85+
86+
@pytest.fixture
87+
def sample_vector_data():
88+
"""
89+
Sample vector data for testing vector operations.
90+
91+
Returns:
92+
Dict[str, Any]: Sample vector data
93+
"""
94+
return {
95+
"id": "test_vector_001",
96+
"vector": [0.1, 0.2, 0.3, 0.4, 0.5] * 153 + [0.6, 0.7, 0.8], # 768 dimensions
97+
"metadata": {
98+
"source": "test",
99+
"timestamp": 1234567890,
100+
"model": "test-model"
101+
}
102+
}
103+
104+
105+
@pytest.fixture
106+
def mock_redis_client():
107+
"""
108+
Mock Redis client for testing Redis-based operations.
109+
110+
Returns:
111+
MagicMock: Mock Redis client
112+
"""
113+
mock = MagicMock()
114+
mock.get.return_value = None
115+
mock.set.return_value = True
116+
mock.delete.return_value = 1
117+
mock.exists.return_value = 0
118+
mock.expire.return_value = True
119+
mock.ttl.return_value = -2
120+
return mock
121+
122+
123+
@pytest.fixture
124+
def mock_milvus_client():
125+
"""
126+
Mock Milvus client for testing vector database operations.
127+
128+
Returns:
129+
MagicMock: Mock Milvus client
130+
"""
131+
mock = MagicMock()
132+
mock.create_collection.return_value = True
133+
mock.insert.return_value = MagicMock(primary_keys=[1, 2, 3])
134+
mock.search.return_value = [[]]
135+
mock.query.return_value = []
136+
mock.delete.return_value = MagicMock(delete_count=1)
137+
return mock
138+
139+
140+
@pytest.fixture(autouse=True)
141+
def reset_environment():
142+
"""
143+
Reset environment variables before each test.
144+
"""
145+
# Store original env vars
146+
original_env = os.environ.copy()
147+
148+
# Set test environment variables
149+
os.environ["MODELCACHE_ENV"] = "test"
150+
os.environ["MODELCACHE_LOG_LEVEL"] = "DEBUG"
151+
152+
yield
153+
154+
# Restore original env vars
155+
os.environ.clear()
156+
os.environ.update(original_env)
157+
158+
159+
@pytest.fixture
160+
def sample_text_data():
161+
"""
162+
Sample text data for testing text processing.
163+
164+
Returns:
165+
List[str]: List of sample texts
166+
"""
167+
return [
168+
"This is a test sentence for modelcache.",
169+
"Machine learning models need efficient caching.",
170+
"Vector embeddings help with semantic search.",
171+
"Testing is important for code quality.",
172+
"PyTest makes testing in Python easier."
173+
]
174+
175+
176+
@pytest.fixture
177+
def mock_http_response():
178+
"""
179+
Mock HTTP response for testing API calls.
180+
181+
Returns:
182+
MagicMock: Mock response object
183+
"""
184+
mock = MagicMock()
185+
mock.status_code = 200
186+
mock.json.return_value = {"status": "success", "data": {}}
187+
mock.text = '{"status": "success", "data": {}}'
188+
mock.headers = {"Content-Type": "application/json"}
189+
return mock
190+
191+
192+
# Pytest configuration hooks
193+
def pytest_configure(config):
194+
"""
195+
Configure pytest with custom settings.
196+
"""
197+
# Add custom markers description
198+
config.addinivalue_line(
199+
"markers", "unit: mark test as a unit test"
200+
)
201+
config.addinivalue_line(
202+
"markers", "integration: mark test as an integration test"
203+
)
204+
config.addinivalue_line(
205+
"markers", "slow: mark test as slow running"
206+
)
207+
208+
209+
def pytest_collection_modifyitems(config, items):
210+
"""
211+
Modify test collection to add markers based on test location.
212+
"""
213+
for item in items:
214+
# Auto-mark tests based on their location
215+
if "unit" in str(item.fspath):
216+
item.add_marker(pytest.mark.unit)
217+
elif "integration" in str(item.fspath):
218+
item.add_marker(pytest.mark.integration)

tests/integration/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)