vectorwave 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tests/__init__.py +0 -0
- tests/batch/__init__.py +0 -0
- tests/batch/test_batch.py +98 -0
- tests/core/__init__.py +0 -0
- tests/core/test_decorator.py +345 -0
- tests/database/__init__.py +0 -0
- tests/database/test_db.py +468 -0
- tests/database/test_db_search.py +163 -0
- tests/exception/__init__.py +0 -0
- tests/models/__init__.py +0 -0
- tests/models/test_db_config.py +152 -0
- tests/monitoring/__init__.py +0 -0
- tests/monitoring/test_tracer.py +202 -0
- tests/prediction/__init__.py +0 -0
- tests/vectorizer/__init__.py +0 -0
- vectorwave/__init__.py +13 -0
- vectorwave/batch/__init__.py +0 -0
- vectorwave/batch/batch.py +68 -0
- vectorwave/core/__init__.py +0 -0
- vectorwave/core/core.py +0 -0
- vectorwave/core/decorator.py +131 -0
- vectorwave/database/__init__.py +0 -0
- vectorwave/database/db.py +328 -0
- vectorwave/database/db_search.py +122 -0
- vectorwave/exception/__init__.py +0 -0
- vectorwave/exception/exceptions.py +22 -0
- vectorwave/models/__init__.py +0 -0
- vectorwave/models/db_config.py +92 -0
- vectorwave/monitoring/__init__.py +0 -0
- vectorwave/monitoring/monitoring.py +0 -0
- vectorwave/monitoring/tracer.py +131 -0
- vectorwave/prediction/__init__.py +0 -0
- vectorwave/prediction/predictor.py +0 -0
- vectorwave/vectorizer/__init__.py +0 -0
- vectorwave/vectorizer/base.py +12 -0
- vectorwave/vectorizer/factory.py +49 -0
- vectorwave/vectorizer/huggingface_vectorizer.py +33 -0
- vectorwave/vectorizer/openai_vectorizer.py +35 -0
- vectorwave-0.1.3.dist-info/METADATA +352 -0
- vectorwave-0.1.3.dist-info/RECORD +44 -0
- vectorwave-0.1.3.dist-info/WHEEL +5 -0
- vectorwave-0.1.3.dist-info/licenses/LICENSE +21 -0
- vectorwave-0.1.3.dist-info/licenses/NOTICE +31 -0
- vectorwave-0.1.3.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from unittest.mock import MagicMock, patch, ANY
|
|
3
|
+
import weaviate
|
|
4
|
+
import weaviate.classes.config as wvc
|
|
5
|
+
# Import the specific driver exception to mock it
|
|
6
|
+
from weaviate.exceptions import WeaviateConnectionError as WeaviateClientConnectionError
|
|
7
|
+
|
|
8
|
+
# Import functions to be tested
|
|
9
|
+
# (Assuming pytest is run from the project root and pytest.ini is set)
|
|
10
|
+
from vectorwave.database.db import get_weaviate_client, create_vectorwave_schema
|
|
11
|
+
from vectorwave.models.db_config import WeaviateSettings, get_weaviate_settings
|
|
12
|
+
from vectorwave.exception.exceptions import (
|
|
13
|
+
WeaviateConnectionError,
|
|
14
|
+
WeaviateNotReadyError,
|
|
15
|
+
SchemaCreationError
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from vectorwave.database.db import create_execution_schema
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# --- Test Fixtures ---
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def test_settings() -> WeaviateSettings:
|
|
25
|
+
"""Returns a test Weaviate settings object."""
|
|
26
|
+
return WeaviateSettings(
|
|
27
|
+
WEAVIATE_HOST="test.host.local",
|
|
28
|
+
WEAVIATE_PORT=1234,
|
|
29
|
+
WEAVIATE_GRPC_PORT=5678,
|
|
30
|
+
COLLECTION_NAME="TestCollection",
|
|
31
|
+
IS_VECTORIZE_COLLECTION_NAME=False
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# --- Tests for get_weaviate_client ---
|
|
35
|
+
|
|
36
|
+
@patch('vectorwave.database.db.weaviate.connect_to_local')
|
|
37
|
+
def test_get_weaviate_client_success(mock_connect_to_local, test_settings):
|
|
38
|
+
"""
|
|
39
|
+
Case 1: Weaviate connection is successful.
|
|
40
|
+
- .connect_to_local() should be called.
|
|
41
|
+
- .is_ready() should return True.
|
|
42
|
+
- The created client object should be returned.
|
|
43
|
+
"""
|
|
44
|
+
# 1. Arrange
|
|
45
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
46
|
+
mock_client.is_ready.return_value = True
|
|
47
|
+
mock_connect_to_local.return_value = mock_client
|
|
48
|
+
|
|
49
|
+
# 2. Act
|
|
50
|
+
client = get_weaviate_client(settings=test_settings)
|
|
51
|
+
|
|
52
|
+
# 3. Assert
|
|
53
|
+
# Check if 'connect_to_local' was called once with the correct args
|
|
54
|
+
mock_connect_to_local.assert_called_once_with(
|
|
55
|
+
host=test_settings.WEAVIATE_HOST,
|
|
56
|
+
port=test_settings.WEAVIATE_PORT,
|
|
57
|
+
grpc_port=test_settings.WEAVIATE_GRPC_PORT,
|
|
58
|
+
additional_config=ANY
|
|
59
|
+
)
|
|
60
|
+
mock_client.is_ready.assert_called_once()
|
|
61
|
+
assert client == mock_client
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@patch('vectorwave.database.db.weaviate.connect_to_local')
|
|
65
|
+
def test_get_weaviate_client_connection_refused(mock_connect_to_local, test_settings):
|
|
66
|
+
"""
|
|
67
|
+
Case 2: Connection is refused because Weaviate server is down.
|
|
68
|
+
- Should raise WeaviateConnectionError.
|
|
69
|
+
"""
|
|
70
|
+
# 1. Arrange
|
|
71
|
+
# Mock the original Weaviate driver exception
|
|
72
|
+
mock_connect_to_local.side_effect = WeaviateClientConnectionError("Connection refused")
|
|
73
|
+
|
|
74
|
+
# 2. Act & 3. Assert
|
|
75
|
+
with pytest.raises(WeaviateConnectionError) as exc_info:
|
|
76
|
+
get_weaviate_client(settings=test_settings)
|
|
77
|
+
|
|
78
|
+
# Check if the error message from the original exception is included
|
|
79
|
+
assert "Connection refused" in str(exc_info.value)
|
|
80
|
+
assert "Failed to connect to Weaviate" in str(exc_info.value)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@patch('vectorwave.database.db.weaviate.connect_to_local')
|
|
84
|
+
def test_get_weaviate_client_not_ready(mock_connect_to_local, test_settings):
|
|
85
|
+
"""
|
|
86
|
+
Case 3: Connected, but Weaviate is not ready (.is_ready() returns False).
|
|
87
|
+
- Should raise WeaviateNotReadyError.
|
|
88
|
+
"""
|
|
89
|
+
# 1. Arrange
|
|
90
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
91
|
+
mock_client.is_ready.return_value = False # This is the trigger
|
|
92
|
+
mock_connect_to_local.return_value = mock_client
|
|
93
|
+
|
|
94
|
+
# 2. Act & 3. Assert
|
|
95
|
+
with pytest.raises(WeaviateNotReadyError) as exc_info:
|
|
96
|
+
get_weaviate_client(settings=test_settings)
|
|
97
|
+
|
|
98
|
+
assert "server is not ready" in str(exc_info.value)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# --- Tests for create_vectorwave_schema ---
|
|
102
|
+
|
|
103
|
+
def test_create_schema_new(test_settings):
|
|
104
|
+
"""
|
|
105
|
+
Case 4: Schema doesn't exist and is created successfully.
|
|
106
|
+
- .collections.exists() returns False.
|
|
107
|
+
- .collections.create() should be called.
|
|
108
|
+
- .collections.get() should not be called.
|
|
109
|
+
"""
|
|
110
|
+
# 1. Arrange
|
|
111
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
112
|
+
mock_collections = MagicMock()
|
|
113
|
+
mock_collections.exists.return_value = False # Trigger for creation
|
|
114
|
+
mock_new_collection = MagicMock()
|
|
115
|
+
mock_collections.create.return_value = mock_new_collection
|
|
116
|
+
mock_client.collections = mock_collections
|
|
117
|
+
|
|
118
|
+
# 2. Act
|
|
119
|
+
collection = create_vectorwave_schema(mock_client, test_settings)
|
|
120
|
+
|
|
121
|
+
# 3. Assert
|
|
122
|
+
mock_collections.exists.assert_called_once_with(test_settings.COLLECTION_NAME)
|
|
123
|
+
mock_collections.create.assert_called_once()
|
|
124
|
+
|
|
125
|
+
# Check if 'create' was called with the correct 'name'
|
|
126
|
+
call_args = mock_collections.create.call_args
|
|
127
|
+
assert call_args.kwargs.get('name') == test_settings.COLLECTION_NAME
|
|
128
|
+
|
|
129
|
+
# Check if key properties were passed
|
|
130
|
+
passed_props = [prop.name for prop in call_args.kwargs.get('properties', [])]
|
|
131
|
+
assert "function_name" in passed_props
|
|
132
|
+
assert "source_code" in passed_props
|
|
133
|
+
|
|
134
|
+
mock_collections.get.assert_not_called()
|
|
135
|
+
assert collection == mock_new_collection
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_create_schema_existing(test_settings):
|
|
139
|
+
"""
|
|
140
|
+
Case 5: Schema already exists.
|
|
141
|
+
- .collections.exists() returns True.
|
|
142
|
+
- .collections.create() should not be called.
|
|
143
|
+
- .collections.get() should be called.
|
|
144
|
+
"""
|
|
145
|
+
# 1. Arrange
|
|
146
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
147
|
+
mock_collections = MagicMock()
|
|
148
|
+
mock_collections.exists.return_value = True # Trigger for skipping
|
|
149
|
+
mock_existing_collection = MagicMock()
|
|
150
|
+
mock_collections.get.return_value = mock_existing_collection
|
|
151
|
+
mock_client.collections = mock_collections
|
|
152
|
+
|
|
153
|
+
# 2. Act
|
|
154
|
+
collection = create_vectorwave_schema(mock_client, test_settings)
|
|
155
|
+
|
|
156
|
+
# 3. Assert
|
|
157
|
+
mock_collections.exists.assert_called_once_with(test_settings.COLLECTION_NAME)
|
|
158
|
+
mock_collections.create.assert_not_called()
|
|
159
|
+
mock_collections.get.assert_called_once_with(test_settings.COLLECTION_NAME)
|
|
160
|
+
assert collection == mock_existing_collection
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def test_create_schema_creation_error(test_settings):
|
|
164
|
+
"""
|
|
165
|
+
Case 6: An error occurs during schema creation (e.g., bad API key).
|
|
166
|
+
- Should raise SchemaCreationError.
|
|
167
|
+
"""
|
|
168
|
+
# 1. Arrange
|
|
169
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
170
|
+
mock_collections = MagicMock()
|
|
171
|
+
mock_collections.exists.return_value = False
|
|
172
|
+
# Set .create() to raise an exception
|
|
173
|
+
mock_collections.create.side_effect = Exception("Invalid OpenAI API Key")
|
|
174
|
+
mock_client.collections = mock_collections
|
|
175
|
+
|
|
176
|
+
# 2. Act & 3. Assert
|
|
177
|
+
with pytest.raises(SchemaCreationError) as exc_info:
|
|
178
|
+
create_vectorwave_schema(mock_client, test_settings)
|
|
179
|
+
|
|
180
|
+
assert "Error during schema creation" in str(exc_info.value)
|
|
181
|
+
assert "Invalid OpenAI API Key" in str(exc_info.value)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@pytest.fixture
|
|
185
|
+
def settings_with_custom_props() -> WeaviateSettings:
|
|
186
|
+
"""
|
|
187
|
+
Returns a WeaviateSettings object assuming 'get_weaviate_settings'
|
|
188
|
+
successfully loaded the JSON.
|
|
189
|
+
"""
|
|
190
|
+
settings = WeaviateSettings(
|
|
191
|
+
COLLECTION_NAME="TestCollection",
|
|
192
|
+
IS_VECTORIZE_COLLECTION_NAME=False
|
|
193
|
+
)
|
|
194
|
+
# Manually inject the loaded data into the custom_properties field
|
|
195
|
+
settings.custom_properties = {
|
|
196
|
+
"run_id": {
|
|
197
|
+
"data_type": "TEXT",
|
|
198
|
+
"description": "The ID of the specific test run"
|
|
199
|
+
},
|
|
200
|
+
"experiment_id": {
|
|
201
|
+
"data_type": "INT",
|
|
202
|
+
"description": "Identifier for the experiment"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return settings
|
|
206
|
+
|
|
207
|
+
@pytest.fixture
|
|
208
|
+
def settings_with_invalid_type_prop() -> WeaviateSettings:
|
|
209
|
+
"""Returns settings with an invalid string in 'data_type'."""
|
|
210
|
+
settings = WeaviateSettings(COLLECTION_NAME="TestCollection")
|
|
211
|
+
settings.custom_properties = {
|
|
212
|
+
"bad_prop": {
|
|
213
|
+
"data_type": "INVALID_WEAVIATE_TYPE", # <-- Invalid type
|
|
214
|
+
"description": "This should fail"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return settings
|
|
218
|
+
|
|
219
|
+
@pytest.fixture
|
|
220
|
+
def settings_with_missing_type_prop() -> WeaviateSettings:
|
|
221
|
+
"""Returns settings where the 'data_type' key itself is missing."""
|
|
222
|
+
settings = WeaviateSettings(COLLECTION_NAME="TestCollection")
|
|
223
|
+
settings.custom_properties = {
|
|
224
|
+
"another_bad_prop": {
|
|
225
|
+
"description": "data_type key is missing" # <-- data_type missing
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return settings
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# [New] Test Cases for Issue #11: Custom Property *Parsing*
|
|
233
|
+
|
|
234
|
+
def test_create_schema_with_custom_properties(settings_with_custom_props):
|
|
235
|
+
"""
|
|
236
|
+
Case 7: Test if custom properties (run_id, experiment_id) are correctly added to the schema
|
|
237
|
+
- .collections.create() should be called with the correct 'properties' argument
|
|
238
|
+
"""
|
|
239
|
+
# 1. Arrange
|
|
240
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
241
|
+
mock_collections = MagicMock()
|
|
242
|
+
mock_collections.exists.return_value = False # Trigger creation
|
|
243
|
+
mock_new_collection = MagicMock()
|
|
244
|
+
mock_collections.create.return_value = mock_new_collection
|
|
245
|
+
mock_client.collections = mock_collections
|
|
246
|
+
|
|
247
|
+
# 2. Act
|
|
248
|
+
# Use the 'settings_with_custom_props' fixture
|
|
249
|
+
create_vectorwave_schema(mock_client, settings_with_custom_props)
|
|
250
|
+
|
|
251
|
+
# 3. Assert
|
|
252
|
+
# Check if create was called
|
|
253
|
+
mock_collections.create.assert_called_once()
|
|
254
|
+
|
|
255
|
+
# Check the arguments (kwargs) passed to create
|
|
256
|
+
call_args = mock_collections.create.call_args
|
|
257
|
+
passed_props_list = call_args.kwargs.get('properties', [])
|
|
258
|
+
|
|
259
|
+
# For convenience, convert the list to a map by name
|
|
260
|
+
passed_props_map = {prop.name: prop for prop in passed_props_list}
|
|
261
|
+
|
|
262
|
+
# Check if base properties still exist
|
|
263
|
+
assert "function_name" in passed_props_map
|
|
264
|
+
assert "source_code" in passed_props_map
|
|
265
|
+
assert passed_props_map["function_name"].dataType == wvc.DataType.TEXT
|
|
266
|
+
|
|
267
|
+
assert "search_description" in passed_props_map
|
|
268
|
+
assert "sequence_narrative" in passed_props_map
|
|
269
|
+
|
|
270
|
+
# --- Custom Property Validation ---
|
|
271
|
+
assert "run_id" in passed_props_map
|
|
272
|
+
assert "experiment_id" in passed_props_map
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# Validate 'run_id' type and description
|
|
276
|
+
run_id_prop = passed_props_map["run_id"]
|
|
277
|
+
assert run_id_prop.dataType == wvc.DataType.TEXT
|
|
278
|
+
assert run_id_prop.description == "The ID of the specific test run"
|
|
279
|
+
|
|
280
|
+
# Validate 'experiment_id' type and description
|
|
281
|
+
exp_id_prop = passed_props_map["experiment_id"]
|
|
282
|
+
assert exp_id_prop.dataType == wvc.DataType.INT
|
|
283
|
+
assert exp_id_prop.description == "Identifier for the experiment"
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# Check total property count (5 base + 2 custom)
|
|
287
|
+
assert len(passed_props_list) == 6 + 2
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def test_create_schema_custom_prop_invalid_type(settings_with_invalid_type_prop):
|
|
291
|
+
"""
|
|
292
|
+
Case 8: Test if SchemaCreationError is raised when 'data_type' has an invalid value
|
|
293
|
+
"""
|
|
294
|
+
# 1. Arrange
|
|
295
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
296
|
+
mock_collections = MagicMock()
|
|
297
|
+
mock_collections.exists.return_value = False
|
|
298
|
+
mock_client.collections = mock_collections
|
|
299
|
+
|
|
300
|
+
# 2. Act & 3. Assert
|
|
301
|
+
with pytest.raises(SchemaCreationError) as exc_info:
|
|
302
|
+
create_vectorwave_schema(mock_client, settings_with_invalid_type_prop)
|
|
303
|
+
|
|
304
|
+
# Check if the error message includes the invalid type name
|
|
305
|
+
assert "Invalid data_type 'INVALID_WEAVIATE_TYPE'" in str(exc_info.value)
|
|
306
|
+
assert "bad_prop" in str(exc_info.value)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def test_create_schema_custom_prop_missing_type(settings_with_missing_type_prop):
|
|
310
|
+
"""
|
|
311
|
+
Case 9: Test if SchemaCreationError is raised when the 'data_type' key is missing
|
|
312
|
+
"""
|
|
313
|
+
# 1. Arrange
|
|
314
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
315
|
+
mock_collections = MagicMock()
|
|
316
|
+
mock_collections.exists.return_value = False
|
|
317
|
+
mock_client.collections = mock_collections
|
|
318
|
+
|
|
319
|
+
# 2. Act & 3. Assert
|
|
320
|
+
with pytest.raises(SchemaCreationError) as exc_info:
|
|
321
|
+
create_vectorwave_schema(mock_client, settings_with_missing_type_prop)
|
|
322
|
+
|
|
323
|
+
# Check if the error message indicates 'data_type' is missing
|
|
324
|
+
assert "missing 'data_type'" in str(exc_info.value)
|
|
325
|
+
assert "another_bad_prop" in str(exc_info.value)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@patch('vectorwave.database.db.wvc.Configure.Vectorizer.none')
|
|
329
|
+
def test_create_execution_schema_new(mock_vectorizer_none, test_settings):
|
|
330
|
+
"""
|
|
331
|
+
Case 10: Test if 'VectorWaveExecutions' schema is created successfully when it does not exist
|
|
332
|
+
"""
|
|
333
|
+
# 1. Arrange
|
|
334
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
335
|
+
mock_collections = MagicMock()
|
|
336
|
+
mock_collections.exists.return_value = False # 생성 트리거
|
|
337
|
+
mock_new_collection = MagicMock()
|
|
338
|
+
mock_collections.create.return_value = mock_new_collection
|
|
339
|
+
mock_client.collections = mock_collections
|
|
340
|
+
|
|
341
|
+
# 2. Act
|
|
342
|
+
collection = create_execution_schema(mock_client, test_settings)
|
|
343
|
+
|
|
344
|
+
# 3. Assert
|
|
345
|
+
mock_collections.exists.assert_called_once_with(test_settings.EXECUTION_COLLECTION_NAME)
|
|
346
|
+
mock_collections.create.assert_called_once()
|
|
347
|
+
|
|
348
|
+
call_args = mock_collections.create.call_args
|
|
349
|
+
assert call_args.kwargs.get('name') == test_settings.EXECUTION_COLLECTION_NAME
|
|
350
|
+
|
|
351
|
+
# Check if base properties are included
|
|
352
|
+
passed_props_map = {prop.name: prop for prop in call_args.kwargs.get('properties', [])}
|
|
353
|
+
assert "function_uuid" in passed_props_map
|
|
354
|
+
assert "timestamp_utc" in passed_props_map
|
|
355
|
+
assert "status" in passed_props_map
|
|
356
|
+
assert "duration_ms" in passed_props_map
|
|
357
|
+
|
|
358
|
+
assert collection == mock_new_collection
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def test_create_execution_schema_existing(test_settings):
|
|
362
|
+
"""
|
|
363
|
+
Case 11: Test if creation is skipped when 'VectorWaveExecutions' schema already exists
|
|
364
|
+
"""
|
|
365
|
+
# 1. Arrange
|
|
366
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
367
|
+
mock_collections = MagicMock()
|
|
368
|
+
mock_collections.exists.return_value = True
|
|
369
|
+
mock_existing_collection = MagicMock()
|
|
370
|
+
mock_collections.get.return_value = mock_existing_collection
|
|
371
|
+
mock_client.collections = mock_collections
|
|
372
|
+
|
|
373
|
+
# 2. Act
|
|
374
|
+
collection = create_execution_schema(mock_client, test_settings)
|
|
375
|
+
|
|
376
|
+
# 3. Assert
|
|
377
|
+
mock_collections.exists.assert_called_once_with(test_settings.EXECUTION_COLLECTION_NAME)
|
|
378
|
+
mock_collections.create.assert_not_called() # create should not be called
|
|
379
|
+
mock_collections.get.assert_called_once_with(test_settings.EXECUTION_COLLECTION_NAME) # get should be called
|
|
380
|
+
assert collection == mock_existing_collection
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def test_create_schema_vectorizer_openai(test_settings):
|
|
386
|
+
"""
|
|
387
|
+
Case 12: Test if the correct dict config is passed when VECTORIZER_CONFIG='text2vec-openai'
|
|
388
|
+
"""
|
|
389
|
+
# 1. Arrange
|
|
390
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
391
|
+
mock_collections = MagicMock()
|
|
392
|
+
mock_collections.exists.return_value = False
|
|
393
|
+
mock_client.collections = mock_collections
|
|
394
|
+
|
|
395
|
+
test_settings.VECTORIZER = "weaviate_module"
|
|
396
|
+
test_settings.WEAVIATE_VECTORIZER_MODULE = "text2vec-openai"
|
|
397
|
+
|
|
398
|
+
# [수정] test_settings.GENERATIVE_CONFIG = "generative-openai" ->
|
|
399
|
+
test_settings.WEAVIATE_GENERATIVE_MODULE = "generative-openai"
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
# 2. Act
|
|
403
|
+
create_vectorwave_schema(mock_client, test_settings)
|
|
404
|
+
|
|
405
|
+
# 3. Assert
|
|
406
|
+
mock_collections.create.assert_called_once()
|
|
407
|
+
|
|
408
|
+
call_args = mock_collections.create.call_args
|
|
409
|
+
|
|
410
|
+
vector_config_arg = call_args.kwargs.get('vector_config')
|
|
411
|
+
# assert isinstance(vector_config_arg, wvc.Configure.Vectorizer)
|
|
412
|
+
# assert vector_config_arg.name == "text2vec-openai"
|
|
413
|
+
|
|
414
|
+
assert vector_config_arg.vectorizer == "text2vec-openai"
|
|
415
|
+
|
|
416
|
+
# 3b. Generative Config 검증
|
|
417
|
+
generative_config_arg = call_args.kwargs.get('generative_config')
|
|
418
|
+
assert generative_config_arg.generative == "generative-openai"
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def test_create_schema_vectorizer_none(test_settings):
|
|
424
|
+
"""
|
|
425
|
+
Case 13: Test if the correct dict config is passed when VECTORIZER_CONFIG='none'
|
|
426
|
+
"""
|
|
427
|
+
# 1. Arrange
|
|
428
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
429
|
+
mock_collections = MagicMock()
|
|
430
|
+
mock_collections.exists.return_value = False
|
|
431
|
+
mock_client.collections = mock_collections
|
|
432
|
+
|
|
433
|
+
test_settings.VECTORIZER = "none"
|
|
434
|
+
test_settings.WEAVIATE_VECTORIZER_MODULE = ""
|
|
435
|
+
|
|
436
|
+
# 2. Act
|
|
437
|
+
create_vectorwave_schema(mock_client, test_settings)
|
|
438
|
+
|
|
439
|
+
# 3. Assert
|
|
440
|
+
mock_collections.create.assert_called_once()
|
|
441
|
+
|
|
442
|
+
call_args = mock_collections.create.call_args
|
|
443
|
+
# mock_none.assert_called_once() -> 삭제
|
|
444
|
+
|
|
445
|
+
vector_config_arg = call_args.kwargs.get('vector_config')
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
assert vector_config_arg.vectorizer == "none"
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def test_create_schema_vectorizer_invalid(test_settings):
|
|
452
|
+
"""
|
|
453
|
+
Case 14: Test if SchemaCreationError is raised for an unsupported VECTORIZER_CONFIG value
|
|
454
|
+
"""
|
|
455
|
+
# 1. Arrange
|
|
456
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
457
|
+
mock_collections = MagicMock()
|
|
458
|
+
mock_collections.exists.return_value = False
|
|
459
|
+
mock_client.collections = mock_collections
|
|
460
|
+
|
|
461
|
+
test_settings.VECTORIZER = "unsupported-module"
|
|
462
|
+
|
|
463
|
+
# 2. Act & 3. Assert
|
|
464
|
+
with pytest.raises(SchemaCreationError) as exc_info:
|
|
465
|
+
create_vectorwave_schema(mock_client, test_settings)
|
|
466
|
+
|
|
467
|
+
assert "Invalid VECTORIZER setting" in str(exc_info.value)
|
|
468
|
+
assert "unsupported-module" in str(exc_info.value)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from unittest.mock import patch, MagicMock, ANY
|
|
3
|
+
import weaviate.classes as wvc
|
|
4
|
+
import weaviate
|
|
5
|
+
|
|
6
|
+
# Functions to test
|
|
7
|
+
from vectorwave.database.db_search import (
|
|
8
|
+
search_functions,
|
|
9
|
+
search_executions,
|
|
10
|
+
_build_weaviate_filters
|
|
11
|
+
)
|
|
12
|
+
from vectorwave.models.db_config import WeaviateSettings
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def mock_search_deps(monkeypatch):
|
|
17
|
+
""" Mock dependencies for search_functions (mocking near_text method) """
|
|
18
|
+
mock_settings = WeaviateSettings(COLLECTION_NAME="TestFunctions")
|
|
19
|
+
mock_get_settings = MagicMock(return_value=mock_settings)
|
|
20
|
+
monkeypatch.setattr("vectorwave.database.db_search.get_weaviate_settings", mock_get_settings)
|
|
21
|
+
|
|
22
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
23
|
+
mock_collections_obj = MagicMock()
|
|
24
|
+
mock_func_collection = MagicMock()
|
|
25
|
+
|
|
26
|
+
mock_query_obj = MagicMock()
|
|
27
|
+
mock_obj = MagicMock()
|
|
28
|
+
mock_obj.properties = {"name": "test_func"}
|
|
29
|
+
mock_obj.metadata = MagicMock(uuid="test-uuid")
|
|
30
|
+
mock_query_obj.near_text.return_value = MagicMock(objects=[mock_obj])
|
|
31
|
+
|
|
32
|
+
mock_func_collection.query = mock_query_obj
|
|
33
|
+
|
|
34
|
+
def get_collection_side_effect(name):
|
|
35
|
+
if name == "TestFunctions":
|
|
36
|
+
return mock_func_collection
|
|
37
|
+
return MagicMock()
|
|
38
|
+
|
|
39
|
+
mock_collections_obj.get = MagicMock(side_effect=get_collection_side_effect)
|
|
40
|
+
mock_client.collections = mock_collections_obj
|
|
41
|
+
|
|
42
|
+
mock_get_client = MagicMock(return_value=mock_client)
|
|
43
|
+
monkeypatch.setattr("vectorwave.database.db_search.get_cached_client", mock_get_client)
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
"client": mock_client,
|
|
47
|
+
"collection": mock_func_collection,
|
|
48
|
+
"query": mock_query_obj, # Return query object for testing
|
|
49
|
+
"settings": mock_settings
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# --- Test _build_weaviate_filters helper function ---
|
|
53
|
+
|
|
54
|
+
def test_build_filters_none_or_empty():
|
|
55
|
+
assert _build_weaviate_filters(None) is None
|
|
56
|
+
assert _build_weaviate_filters({}) is None
|
|
57
|
+
|
|
58
|
+
def test_build_filters_single():
|
|
59
|
+
filters = {"team": "billing"}
|
|
60
|
+
result = _build_weaviate_filters(filters)
|
|
61
|
+
# [MODIFIED] isinstance -> is not None (Fix AssertionError)
|
|
62
|
+
assert result is not None
|
|
63
|
+
|
|
64
|
+
def test_build_filters_multiple():
|
|
65
|
+
filters = {"team": "billing", "priority": 1}
|
|
66
|
+
result = _build_weaviate_filters(filters)
|
|
67
|
+
# [MODIFIED] isinstance -> is not None (Fix AssertionError)
|
|
68
|
+
assert result is not None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# --- Basic tests for search_functions ---
|
|
72
|
+
|
|
73
|
+
def test_search_functions_basic_call(mock_search_deps):
|
|
74
|
+
# [MODIFIED] Test the near_text method instead of fetch_objects.
|
|
75
|
+
mock_query = mock_search_deps["query"]
|
|
76
|
+
search_functions(query="test query", limit=3)
|
|
77
|
+
|
|
78
|
+
mock_query.near_text.assert_called_once_with(
|
|
79
|
+
query="test query",
|
|
80
|
+
limit=3,
|
|
81
|
+
filters=None,
|
|
82
|
+
return_metadata=wvc.query.MetadataQuery(distance=True)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def test_search_functions_with_filters(mock_search_deps):
|
|
86
|
+
mock_query = mock_search_deps["query"]
|
|
87
|
+
test_filters = {"team": "billing"}
|
|
88
|
+
|
|
89
|
+
search_functions(query="filtered query", limit=5, filters=test_filters)
|
|
90
|
+
|
|
91
|
+
mock_query.near_text.assert_called_once()
|
|
92
|
+
call_args = mock_query.near_text.call_args
|
|
93
|
+
|
|
94
|
+
assert call_args.kwargs['query'] == "filtered query"
|
|
95
|
+
assert call_args.kwargs['limit'] == 5
|
|
96
|
+
assert call_args.kwargs['filters'] is not None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# --- Tests for search_executions ---
|
|
100
|
+
|
|
101
|
+
@pytest.fixture
|
|
102
|
+
def mock_search_exec_deps(monkeypatch):
|
|
103
|
+
""" Mock dependencies for search_executions (no change) """
|
|
104
|
+
mock_settings = WeaviateSettings(EXECUTION_COLLECTION_NAME="TestExecutions")
|
|
105
|
+
mock_get_settings = MagicMock(return_value=mock_settings)
|
|
106
|
+
monkeypatch.setattr("vectorwave.database.db_search.get_weaviate_settings", mock_get_settings)
|
|
107
|
+
|
|
108
|
+
mock_client = MagicMock(spec=weaviate.WeaviateClient)
|
|
109
|
+
mock_collections_obj = MagicMock()
|
|
110
|
+
mock_exec_collection = MagicMock()
|
|
111
|
+
mock_exec_collection.query.fetch_objects.return_value = MagicMock(objects=[])
|
|
112
|
+
|
|
113
|
+
def get_collection_side_effect(name):
|
|
114
|
+
if name == "TestExecutions":
|
|
115
|
+
return mock_exec_collection
|
|
116
|
+
return MagicMock()
|
|
117
|
+
|
|
118
|
+
mock_collections_obj.get = MagicMock(side_effect=get_collection_side_effect)
|
|
119
|
+
mock_client.collections = mock_collections_obj
|
|
120
|
+
|
|
121
|
+
mock_get_client = MagicMock(return_value=mock_client)
|
|
122
|
+
monkeypatch.setattr("vectorwave.database.db_search.get_cached_client", mock_get_client)
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
"client": mock_client,
|
|
126
|
+
"collection": mock_exec_collection,
|
|
127
|
+
"settings": mock_settings
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
def test_search_executions_default_sort(mock_search_exec_deps):
|
|
131
|
+
mock_collection = mock_search_exec_deps["collection"]
|
|
132
|
+
search_executions(limit=5)
|
|
133
|
+
|
|
134
|
+
mock_collection.query.fetch_objects.assert_called_once_with(
|
|
135
|
+
limit=5,
|
|
136
|
+
filters=None,
|
|
137
|
+
sort=ANY
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
call_args = mock_collection.query.fetch_objects.call_args
|
|
141
|
+
sort_arg = call_args.kwargs['sort']
|
|
142
|
+
|
|
143
|
+
assert sort_arg is not None
|
|
144
|
+
|
|
145
|
+
def test_search_executions_filter_and_sort_duration(mock_search_exec_deps):
|
|
146
|
+
mock_collection = mock_search_exec_deps["collection"]
|
|
147
|
+
test_filters = {"status": "SUCCESS"}
|
|
148
|
+
|
|
149
|
+
search_executions(
|
|
150
|
+
limit=3,
|
|
151
|
+
filters=test_filters,
|
|
152
|
+
sort_by="duration_ms",
|
|
153
|
+
sort_ascending=False
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
mock_collection.query.fetch_objects.assert_called_once()
|
|
157
|
+
call_args = mock_collection.query.fetch_objects.call_args
|
|
158
|
+
|
|
159
|
+
assert call_args.kwargs['limit'] == 3
|
|
160
|
+
assert call_args.kwargs['filters'] is not None
|
|
161
|
+
|
|
162
|
+
sort_arg = call_args.kwargs['sort']
|
|
163
|
+
assert sort_arg is not None
|
|
File without changes
|
tests/models/__init__.py
ADDED
|
File without changes
|