weaviate-cli 3.2.1__tar.gz → 3.2.3__tar.gz
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.
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/PKG-INFO +5 -5
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/README.md +3 -3
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/cli.py +14 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/requirements-dev.txt +1 -1
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/setup.cfg +1 -1
- weaviate_cli-3.2.3/test/integration/test_data_integration.py +391 -0
- weaviate_cli-3.2.3/test/unittests/test_managers/test_alias_manager.py +209 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/unittests/test_utils.py +61 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/commands/cancel.py +22 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/commands/create.py +142 -5
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/commands/delete.py +72 -1
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/commands/get.py +128 -1
- weaviate_cli-3.2.3/weaviate_cli/commands/query.py +225 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/commands/update.py +22 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/defaults.py +34 -8
- weaviate_cli-3.2.3/weaviate_cli/managers/alias_manager.py +52 -0
- weaviate_cli-3.2.3/weaviate_cli/managers/cluster_manager.py +220 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/managers/collection_manager.py +182 -34
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/managers/data_manager.py +75 -43
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/managers/node_manager.py +5 -3
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/managers/role_manager.py +15 -1
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/managers/tenant_manager.py +29 -17
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/utils.py +28 -6
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli.egg-info/PKG-INFO +5 -5
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli.egg-info/SOURCES.txt +4 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli.egg-info/requires.txt +1 -1
- weaviate_cli-3.2.1/weaviate_cli/commands/query.py +0 -84
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/.github/dependabot.yml +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/.github/workflows/main.yaml +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/.github/workflows/release.yaml +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/.gitignore +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/.pre-commit-config.yaml +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/CONTRIBUTING.md +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/Dockerfile +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/LICENSE +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/MANIFEST.in +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/Makefile +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/publish.md +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/pyproject.toml +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/setup.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/README.md +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/__init__.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/integration/test_auth_integration.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/integration/test_integration.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/unittests/conftest.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/unittests/test_cli.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/unittests/test_defaults.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/unittests/test_managers/test_collection_manager.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/unittests/test_managers/test_config_manager.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/unittests/test_managers/test_data_manager.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/unittests/test_managers/test_node_manager.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/unittests/test_managers/test_shard_manager.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/test/unittests/test_managers/test_user_manager.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/__init__.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/commands/__init__.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/commands/assign.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/commands/restore.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/commands/revoke.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/completion/__init__.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/completion/complete.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/datasets/__init__.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/datasets/movies.json +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/managers/__init__.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/managers/backup_manager.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/managers/config_manager.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/managers/shard_manager.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/managers/user_manager.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli/types/models.py +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli.egg-info/dependency_links.txt +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli.egg-info/entry_points.txt +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli.egg-info/not-zip-safe +0 -0
- {weaviate_cli-3.2.1 → weaviate_cli-3.2.3}/weaviate_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: weaviate-cli
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.3
|
|
4
4
|
Summary: Command line interface to interact with weaviate
|
|
5
5
|
Home-page: https://github.com/weaviate/weaviate-cli
|
|
6
6
|
Download-URL: https://github.com/weaviate/weaviate-cli
|
|
@@ -28,7 +28,7 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
28
28
|
Requires-Python: >=3.9
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
License-File: LICENSE
|
|
31
|
-
Requires-Dist: weaviate-client>=4.
|
|
31
|
+
Requires-Dist: weaviate-client>=4.16.0
|
|
32
32
|
Requires-Dist: click==8.1.7
|
|
33
33
|
Requires-Dist: semver>=3.0.2
|
|
34
34
|
Requires-Dist: numpy>=1.24.0
|
|
@@ -86,10 +86,10 @@ weaviate-cli query data --collection movies --search-type hybrid --query "action
|
|
|
86
86
|
|
|
87
87
|
## Core Commands
|
|
88
88
|
|
|
89
|
-
- **create**: Create collections, tenants, backups or import data
|
|
90
|
-
- **delete**: Remove collections, tenants or data
|
|
89
|
+
- **create**: Create collections, tenants, backups, replications or import data
|
|
90
|
+
- **delete**: Remove collections, tenants, replications or data
|
|
91
91
|
- **update**: Modify collection settings, tenant states or data
|
|
92
|
-
- **get**: Retrieve collection info, tenant details or shard status
|
|
92
|
+
- **get**: Retrieve collection info, tenant details, replication operations or shard status
|
|
93
93
|
- **query**: Search data using various methods
|
|
94
94
|
- **restore**: Restore backups from supported backends
|
|
95
95
|
- **assign**: Assign roles and permissions to users
|
|
@@ -46,10 +46,10 @@ weaviate-cli query data --collection movies --search-type hybrid --query "action
|
|
|
46
46
|
|
|
47
47
|
## Core Commands
|
|
48
48
|
|
|
49
|
-
- **create**: Create collections, tenants, backups or import data
|
|
50
|
-
- **delete**: Remove collections, tenants or data
|
|
49
|
+
- **create**: Create collections, tenants, backups, replications or import data
|
|
50
|
+
- **delete**: Remove collections, tenants, replications or data
|
|
51
51
|
- **update**: Modify collection settings, tenant states or data
|
|
52
|
-
- **get**: Retrieve collection info, tenant details or shard status
|
|
52
|
+
- **get**: Retrieve collection info, tenant details, replication operations or shard status
|
|
53
53
|
- **query**: Search data using various methods
|
|
54
54
|
- **restore**: Restore backups from supported backends
|
|
55
55
|
- **assign**: Assign roles and permissions to users
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
warnings.filterwarnings(
|
|
4
|
+
"ignore", message="pkg_resources is deprecated as an API.", category=UserWarning
|
|
5
|
+
)
|
|
6
|
+
warnings.filterwarnings(
|
|
7
|
+
"ignore",
|
|
8
|
+
message=(
|
|
9
|
+
r"Protobuf gencode version [\d\.]+ is exactly one major version older than the runtime version [\d\.]+ at v1/tenants\.proto\. "
|
|
10
|
+
r"Please update the gencode to avoid compatibility violations in the next runtime release\."
|
|
11
|
+
),
|
|
12
|
+
category=UserWarning,
|
|
13
|
+
)
|
|
14
|
+
|
|
1
15
|
from typing import Optional
|
|
2
16
|
import click
|
|
3
17
|
import sys
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import weaviate
|
|
3
|
+
import numpy as np
|
|
4
|
+
from weaviate_cli.managers.collection_manager import CollectionManager
|
|
5
|
+
from weaviate_cli.managers.config_manager import ConfigManager
|
|
6
|
+
from weaviate_cli.managers.data_manager import DataManager
|
|
7
|
+
import weaviate.classes.config as wvc
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def client() -> weaviate.Client:
|
|
12
|
+
config = ConfigManager()
|
|
13
|
+
return config.get_client()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def collection_manager(client: weaviate.Client) -> CollectionManager:
|
|
18
|
+
return CollectionManager(client)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def data_manager(client: weaviate.Client) -> DataManager:
|
|
23
|
+
return DataManager(client)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.mark.parametrize("randomize", [False, True])
|
|
27
|
+
@pytest.mark.parametrize("vectorizer", ["transformers", "contextionary"])
|
|
28
|
+
def test_data_creation_with_different_configs(
|
|
29
|
+
collection_manager: CollectionManager,
|
|
30
|
+
data_manager: DataManager,
|
|
31
|
+
randomize: bool,
|
|
32
|
+
vectorizer: str,
|
|
33
|
+
):
|
|
34
|
+
"""Test data creation with different randomize and vectorizer configurations."""
|
|
35
|
+
collection_name = f"TestData{randomize}{vectorizer.capitalize()[:7]}"
|
|
36
|
+
# Contextionary does not know the word "contextionary", therefore we truncate it to "context" [0:7]
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
# Create collection with specified vectorizer
|
|
40
|
+
collection_manager.create_collection(
|
|
41
|
+
collection=collection_name,
|
|
42
|
+
vectorizer=vectorizer,
|
|
43
|
+
replication_factor=1,
|
|
44
|
+
training_limit=1000,
|
|
45
|
+
async_enabled=True,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Verify collection exists
|
|
49
|
+
assert collection_manager.client.collections.exists(collection_name)
|
|
50
|
+
|
|
51
|
+
# Create data with specified randomize setting
|
|
52
|
+
data_manager.create_data(
|
|
53
|
+
collection=collection_name,
|
|
54
|
+
limit=50,
|
|
55
|
+
consistency_level="one",
|
|
56
|
+
randomize=randomize,
|
|
57
|
+
skip_seed=True,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Get the collection and verify data was created
|
|
61
|
+
collection = collection_manager.client.collections.get(collection_name)
|
|
62
|
+
|
|
63
|
+
# Wait for indexing to complete
|
|
64
|
+
collection.batch.wait_for_vector_indexing()
|
|
65
|
+
|
|
66
|
+
# Query objects to verify they have content and vectors
|
|
67
|
+
objects = collection.query.fetch_objects(limit=50, include_vector=True)
|
|
68
|
+
|
|
69
|
+
# Verify we got the expected number of objects
|
|
70
|
+
assert len(objects.objects) == 50
|
|
71
|
+
|
|
72
|
+
# Verify each object has content and vectors
|
|
73
|
+
for obj in objects.objects:
|
|
74
|
+
# Check that object has properties
|
|
75
|
+
assert hasattr(obj, "properties")
|
|
76
|
+
assert obj.properties is not None
|
|
77
|
+
|
|
78
|
+
# Check that object has a title (required field)
|
|
79
|
+
assert "title" in obj.properties
|
|
80
|
+
assert obj.properties["title"] is not None
|
|
81
|
+
assert len(obj.properties["title"]) > 0
|
|
82
|
+
|
|
83
|
+
# Check that object has genres
|
|
84
|
+
assert "genres" in obj.properties
|
|
85
|
+
assert obj.properties["genres"] is not None
|
|
86
|
+
|
|
87
|
+
# Check that object has keywords
|
|
88
|
+
assert "keywords" in obj.properties
|
|
89
|
+
assert obj.properties["keywords"] is not None
|
|
90
|
+
|
|
91
|
+
# Verify vector was created
|
|
92
|
+
assert hasattr(obj, "vector")
|
|
93
|
+
assert obj.vector is not None
|
|
94
|
+
|
|
95
|
+
vector_dimensions = {
|
|
96
|
+
"transformers": 384,
|
|
97
|
+
"contextionary": 300,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Check vector dimensions (should be 1536 for default)
|
|
101
|
+
assert len(obj.vector["default"]) == vector_dimensions[vectorizer]
|
|
102
|
+
|
|
103
|
+
# Verify vector is not all zeros (should have meaningful values)
|
|
104
|
+
assert not np.allclose(
|
|
105
|
+
obj.vector["default"], np.zeros(vector_dimensions[vectorizer])
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Verify vector has finite values
|
|
109
|
+
assert np.all(np.isfinite(obj.vector["default"]))
|
|
110
|
+
|
|
111
|
+
finally:
|
|
112
|
+
# Clean up
|
|
113
|
+
if collection_manager.client.collections.exists(collection_name):
|
|
114
|
+
collection_manager.delete_collection(collection=collection_name)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@pytest.mark.parametrize("vectorizer", ["transformers", "none"])
|
|
118
|
+
@pytest.mark.parametrize(
|
|
119
|
+
"named_vector_name", ["custom_vector", "movie_embedding", "content_vector"]
|
|
120
|
+
)
|
|
121
|
+
def test_data_creation_with_named_vectors(
|
|
122
|
+
collection_manager: CollectionManager,
|
|
123
|
+
data_manager: DataManager,
|
|
124
|
+
named_vector_name: str,
|
|
125
|
+
vectorizer: str,
|
|
126
|
+
):
|
|
127
|
+
"""Test data creation with named vectors and verify the correct vector name is set."""
|
|
128
|
+
collection_name = f"TestNamedVector{named_vector_name}{vectorizer.capitalize()}"
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
# Create collection with named vector
|
|
132
|
+
collection_manager.create_collection(
|
|
133
|
+
collection=collection_name,
|
|
134
|
+
vectorizer=vectorizer,
|
|
135
|
+
replication_factor=1,
|
|
136
|
+
training_limit=1000,
|
|
137
|
+
async_enabled=True,
|
|
138
|
+
named_vector=True,
|
|
139
|
+
named_vector_name=named_vector_name,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Verify collection exists
|
|
143
|
+
assert collection_manager.client.collections.exists(collection_name)
|
|
144
|
+
|
|
145
|
+
# Create data
|
|
146
|
+
data_manager.create_data(
|
|
147
|
+
collection=collection_name,
|
|
148
|
+
limit=30,
|
|
149
|
+
consistency_level="one",
|
|
150
|
+
vector_dimensions=384 if vectorizer == "none" else None,
|
|
151
|
+
randomize=True,
|
|
152
|
+
skip_seed=True,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Get the collection and verify data was created
|
|
156
|
+
collection = collection_manager.client.collections.get(collection_name)
|
|
157
|
+
|
|
158
|
+
# Wait for indexing to complete
|
|
159
|
+
collection.batch.wait_for_vector_indexing()
|
|
160
|
+
|
|
161
|
+
# Query objects to verify they have content and named vectors
|
|
162
|
+
objects = collection.query.fetch_objects(limit=30, include_vector=True)
|
|
163
|
+
|
|
164
|
+
# Verify we got the expected number of objects
|
|
165
|
+
assert len(objects.objects) == 30
|
|
166
|
+
|
|
167
|
+
# Verify each object has content and the correct named vector
|
|
168
|
+
for obj in objects.objects:
|
|
169
|
+
# Check that object has properties
|
|
170
|
+
assert hasattr(obj, "properties")
|
|
171
|
+
assert obj.properties is not None
|
|
172
|
+
|
|
173
|
+
# Check that object has a title
|
|
174
|
+
assert "title" in obj.properties
|
|
175
|
+
assert obj.properties["title"] is not None
|
|
176
|
+
|
|
177
|
+
# Check that object has genres
|
|
178
|
+
assert "genres" in obj.properties
|
|
179
|
+
assert obj.properties["genres"] is not None
|
|
180
|
+
|
|
181
|
+
# Check that object has keywords
|
|
182
|
+
assert "keywords" in obj.properties
|
|
183
|
+
assert obj.properties["keywords"] is not None
|
|
184
|
+
|
|
185
|
+
# Verify named vector was created with correct name
|
|
186
|
+
assert hasattr(obj, "vector")
|
|
187
|
+
assert obj.vector is not None
|
|
188
|
+
|
|
189
|
+
# Check that the named vector exists
|
|
190
|
+
assert named_vector_name in obj.vector
|
|
191
|
+
|
|
192
|
+
# Get the named vector
|
|
193
|
+
named_vector = obj.vector[named_vector_name]
|
|
194
|
+
assert named_vector is not None
|
|
195
|
+
|
|
196
|
+
# Check vector dimensions (should be 768 for transformers)
|
|
197
|
+
assert len(named_vector) == 384
|
|
198
|
+
|
|
199
|
+
# Verify vector is not all zeros
|
|
200
|
+
assert not np.allclose(named_vector, np.zeros(384))
|
|
201
|
+
|
|
202
|
+
# Verify vector has finite values
|
|
203
|
+
assert np.all(np.isfinite(named_vector))
|
|
204
|
+
|
|
205
|
+
finally:
|
|
206
|
+
# Clean up
|
|
207
|
+
if collection_manager.client.collections.exists(collection_name):
|
|
208
|
+
collection_manager.delete_collection(collection=collection_name)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_data_creation_with_multi_vector(
|
|
212
|
+
collection_manager: CollectionManager,
|
|
213
|
+
data_manager: DataManager,
|
|
214
|
+
):
|
|
215
|
+
"""Test data creation with multi-vector enabled."""
|
|
216
|
+
collection_name = "TestMultiVector"
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
# Create collection
|
|
220
|
+
collection_manager.create_collection(
|
|
221
|
+
collection=collection_name,
|
|
222
|
+
vectorizer="none",
|
|
223
|
+
replication_factor=1,
|
|
224
|
+
training_limit=1000,
|
|
225
|
+
async_enabled=True,
|
|
226
|
+
vector_index="hnsw_multivector",
|
|
227
|
+
named_vector=True,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Verify collection exists
|
|
231
|
+
assert collection_manager.client.collections.exists(collection_name)
|
|
232
|
+
|
|
233
|
+
# Create data with multi-vector enabled
|
|
234
|
+
data_manager.create_data(
|
|
235
|
+
collection=collection_name,
|
|
236
|
+
limit=25,
|
|
237
|
+
consistency_level="one",
|
|
238
|
+
randomize=True,
|
|
239
|
+
skip_seed=True,
|
|
240
|
+
multi_vector=True,
|
|
241
|
+
vector_dimensions=1536,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Get the collection and verify data was created
|
|
245
|
+
collection = collection_manager.client.collections.get(collection_name)
|
|
246
|
+
|
|
247
|
+
# Wait for indexing to complete
|
|
248
|
+
collection.batch.wait_for_vector_indexing()
|
|
249
|
+
|
|
250
|
+
# Query objects to verify they have content and vectors
|
|
251
|
+
objects = collection.query.fetch_objects(limit=25, include_vector=True)
|
|
252
|
+
|
|
253
|
+
# Verify we got the expected number of objects
|
|
254
|
+
assert len(objects.objects) == 25
|
|
255
|
+
|
|
256
|
+
# Verify each object has content and vectors
|
|
257
|
+
for obj in objects.objects:
|
|
258
|
+
# Check that object has properties
|
|
259
|
+
assert hasattr(obj, "properties")
|
|
260
|
+
assert obj.properties is not None
|
|
261
|
+
|
|
262
|
+
# Check that object has a title
|
|
263
|
+
assert "title" in obj.properties
|
|
264
|
+
assert obj.properties["title"] is not None
|
|
265
|
+
|
|
266
|
+
# Check that object has genres
|
|
267
|
+
assert "genres" in obj.properties
|
|
268
|
+
assert obj.properties["genres"] is not None
|
|
269
|
+
|
|
270
|
+
# Check that object has keywords
|
|
271
|
+
assert "keywords" in obj.properties
|
|
272
|
+
assert obj.properties["keywords"] is not None
|
|
273
|
+
|
|
274
|
+
# Verify vector was created
|
|
275
|
+
assert hasattr(obj, "vector")
|
|
276
|
+
assert obj.vector is not None
|
|
277
|
+
|
|
278
|
+
# Check vector dimensions
|
|
279
|
+
for vector in obj.vector["default"]:
|
|
280
|
+
assert len(vector) == 1536
|
|
281
|
+
|
|
282
|
+
# Verify vector is not all zeros
|
|
283
|
+
for vector in obj.vector["default"]:
|
|
284
|
+
assert not np.allclose(vector, np.zeros(1536))
|
|
285
|
+
|
|
286
|
+
# Verify vector has finite values
|
|
287
|
+
for vector in obj.vector["default"]:
|
|
288
|
+
assert np.all(np.isfinite(vector))
|
|
289
|
+
|
|
290
|
+
finally:
|
|
291
|
+
# Clean up
|
|
292
|
+
if collection_manager.client.collections.exists(collection_name):
|
|
293
|
+
collection_manager.delete_collection(collection=collection_name)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_data_creation_with_custom_vector_dimensions(
|
|
297
|
+
collection_manager: CollectionManager,
|
|
298
|
+
data_manager: DataManager,
|
|
299
|
+
):
|
|
300
|
+
"""Test data creation with custom vector dimensions."""
|
|
301
|
+
collection_name = "TestCustomDimensions"
|
|
302
|
+
custom_dimensions = 768 # Different from default 1536
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
# Create collection
|
|
306
|
+
collection_manager.create_collection(
|
|
307
|
+
collection=collection_name,
|
|
308
|
+
vectorizer="none",
|
|
309
|
+
replication_factor=1,
|
|
310
|
+
training_limit=1000,
|
|
311
|
+
async_enabled=True,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Verify collection exists
|
|
315
|
+
assert collection_manager.client.collections.exists(collection_name)
|
|
316
|
+
|
|
317
|
+
# Create data with custom vector dimensions
|
|
318
|
+
data_manager.create_data(
|
|
319
|
+
collection=collection_name,
|
|
320
|
+
limit=20,
|
|
321
|
+
consistency_level="one",
|
|
322
|
+
randomize=True,
|
|
323
|
+
skip_seed=True,
|
|
324
|
+
vector_dimensions=custom_dimensions,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# Get the collection and verify data was created
|
|
328
|
+
collection = collection_manager.client.collections.get(collection_name)
|
|
329
|
+
|
|
330
|
+
# Wait for indexing to complete
|
|
331
|
+
collection.batch.wait_for_vector_indexing()
|
|
332
|
+
|
|
333
|
+
# Query objects to verify they have content and vectors with custom dimensions
|
|
334
|
+
objects = collection.query.fetch_objects(limit=20, include_vector=True)
|
|
335
|
+
|
|
336
|
+
# Verify we got the expected number of objects
|
|
337
|
+
assert len(objects.objects) == 20
|
|
338
|
+
|
|
339
|
+
# Verify each object has content and vectors with correct dimensions
|
|
340
|
+
for obj in objects.objects:
|
|
341
|
+
# Check that object has properties
|
|
342
|
+
assert hasattr(obj, "properties")
|
|
343
|
+
assert obj.properties is not None
|
|
344
|
+
|
|
345
|
+
# Check that object has a title
|
|
346
|
+
assert "title" in obj.properties
|
|
347
|
+
assert obj.properties["title"] is not None
|
|
348
|
+
|
|
349
|
+
# Check that object has genres
|
|
350
|
+
assert "genres" in obj.properties
|
|
351
|
+
assert obj.properties["genres"] is not None
|
|
352
|
+
|
|
353
|
+
# Check that object has keywords
|
|
354
|
+
assert "keywords" in obj.properties
|
|
355
|
+
assert obj.properties["keywords"] is not None
|
|
356
|
+
|
|
357
|
+
# Verify vector was created with custom dimensions
|
|
358
|
+
assert hasattr(obj, "vector")
|
|
359
|
+
assert obj.vector is not None
|
|
360
|
+
|
|
361
|
+
# Check vector dimensions (should be custom_dimensions)
|
|
362
|
+
assert len(obj.vector["default"]) == custom_dimensions
|
|
363
|
+
|
|
364
|
+
# Verify vector is not all zeros
|
|
365
|
+
assert not np.allclose(obj.vector["default"], np.zeros(custom_dimensions))
|
|
366
|
+
|
|
367
|
+
# Verify vector has finite values
|
|
368
|
+
assert np.all(np.isfinite(obj.vector["default"]))
|
|
369
|
+
|
|
370
|
+
finally:
|
|
371
|
+
# Clean up
|
|
372
|
+
if collection_manager.client.collections.exists(collection_name):
|
|
373
|
+
collection_manager.delete_collection(collection=collection_name)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def test_data_creation_error_handling(
|
|
377
|
+
collection_manager: CollectionManager,
|
|
378
|
+
data_manager: DataManager,
|
|
379
|
+
):
|
|
380
|
+
"""Test error handling when creating data in non-existent collection."""
|
|
381
|
+
|
|
382
|
+
# Try to create data in non-existent collection
|
|
383
|
+
with pytest.raises(Exception) as exc_info:
|
|
384
|
+
data_manager.create_data(
|
|
385
|
+
collection="NonExistentCollection",
|
|
386
|
+
limit=10,
|
|
387
|
+
consistency_level="one",
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# Verify error message
|
|
391
|
+
assert "does not exist in Weaviate" in str(exc_info.value)
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from unittest.mock import MagicMock
|
|
3
|
+
from weaviate.aliases.alias import AliasReturn
|
|
4
|
+
|
|
5
|
+
from weaviate_cli.managers.alias_manager import AliasManager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def alias_manager(mock_client: MagicMock) -> AliasManager:
|
|
10
|
+
"""
|
|
11
|
+
Returns an AliasManager instance with a mocked WeaviateClient.
|
|
12
|
+
"""
|
|
13
|
+
mock_client.alias = MagicMock()
|
|
14
|
+
return AliasManager(mock_client)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_create_alias_success(
|
|
18
|
+
alias_manager: AliasManager, mock_client: MagicMock
|
|
19
|
+
) -> None:
|
|
20
|
+
"""
|
|
21
|
+
Test successful alias creation.
|
|
22
|
+
"""
|
|
23
|
+
alias_name = "test_alias"
|
|
24
|
+
collection_name = "TestCollection"
|
|
25
|
+
|
|
26
|
+
alias_manager.create_alias(alias_name, collection_name)
|
|
27
|
+
|
|
28
|
+
mock_client.alias.create.assert_called_once_with(
|
|
29
|
+
alias_name=alias_name, target_collection=collection_name
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_create_alias_error(
|
|
34
|
+
alias_manager: AliasManager, mock_client: MagicMock
|
|
35
|
+
) -> None:
|
|
36
|
+
"""
|
|
37
|
+
Test alias creation with an exception.
|
|
38
|
+
"""
|
|
39
|
+
alias_name = "test_alias"
|
|
40
|
+
collection_name = "TestCollection"
|
|
41
|
+
error_message = "Failed to create"
|
|
42
|
+
mock_client.alias.create.side_effect = Exception(error_message)
|
|
43
|
+
|
|
44
|
+
with pytest.raises(Exception) as exc_info:
|
|
45
|
+
alias_manager.create_alias(alias_name, collection_name)
|
|
46
|
+
|
|
47
|
+
assert f"Error creating alias '{alias_name}': {error_message}" in str(
|
|
48
|
+
exc_info.value
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_update_alias_success(
|
|
53
|
+
alias_manager: AliasManager, mock_client: MagicMock
|
|
54
|
+
) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Test successful alias update.
|
|
57
|
+
"""
|
|
58
|
+
alias_name = "test_alias"
|
|
59
|
+
collection_name = "NewTestCollection"
|
|
60
|
+
|
|
61
|
+
alias_manager.update_alias(alias_name, collection_name)
|
|
62
|
+
|
|
63
|
+
mock_client.alias.update.assert_called_once_with(
|
|
64
|
+
alias_name=alias_name, new_target_collection=collection_name
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_update_alias_error(
|
|
69
|
+
alias_manager: AliasManager, mock_client: MagicMock
|
|
70
|
+
) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Test alias update with an exception.
|
|
73
|
+
"""
|
|
74
|
+
alias_name = "test_alias"
|
|
75
|
+
collection_name = "NewTestCollection"
|
|
76
|
+
error_message = "Failed to update"
|
|
77
|
+
mock_client.alias.update.side_effect = Exception(error_message)
|
|
78
|
+
|
|
79
|
+
with pytest.raises(Exception) as exc_info:
|
|
80
|
+
alias_manager.update_alias(alias_name, collection_name)
|
|
81
|
+
|
|
82
|
+
assert f"Error updating alias '{alias_name}': {error_message}" in str(
|
|
83
|
+
exc_info.value
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_get_alias_success(alias_manager: AliasManager, mock_client: MagicMock) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Test successful alias retrieval.
|
|
90
|
+
"""
|
|
91
|
+
alias_name = "test_alias"
|
|
92
|
+
expected_alias = AliasReturn(alias=alias_name, collection="SomeCollection")
|
|
93
|
+
mock_client.alias.get.return_value = expected_alias
|
|
94
|
+
|
|
95
|
+
result = alias_manager.get_alias(alias_name)
|
|
96
|
+
|
|
97
|
+
assert result == expected_alias
|
|
98
|
+
mock_client.alias.get.assert_called_once_with(alias_name=alias_name)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_get_alias_error(alias_manager: AliasManager, mock_client: MagicMock) -> None:
|
|
102
|
+
"""
|
|
103
|
+
Test alias retrieval with an exception.
|
|
104
|
+
"""
|
|
105
|
+
alias_name = "test_alias"
|
|
106
|
+
error_message = "Failed to get"
|
|
107
|
+
mock_client.alias.get.side_effect = Exception(error_message)
|
|
108
|
+
|
|
109
|
+
with pytest.raises(Exception) as exc_info:
|
|
110
|
+
alias_manager.get_alias(alias_name)
|
|
111
|
+
|
|
112
|
+
assert f"Error getting alias '{alias_name}': {error_message}" in str(exc_info.value)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_delete_alias_success(
|
|
116
|
+
alias_manager: AliasManager, mock_client: MagicMock
|
|
117
|
+
) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Test successful alias deletion.
|
|
120
|
+
"""
|
|
121
|
+
alias_name = "test_alias"
|
|
122
|
+
|
|
123
|
+
alias_manager.delete_alias(alias_name)
|
|
124
|
+
|
|
125
|
+
mock_client.alias.delete.assert_called_once_with(alias_name=alias_name)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_delete_alias_error(
|
|
129
|
+
alias_manager: AliasManager, mock_client: MagicMock
|
|
130
|
+
) -> None:
|
|
131
|
+
"""
|
|
132
|
+
Test alias deletion with an exception.
|
|
133
|
+
"""
|
|
134
|
+
alias_name = "test_alias"
|
|
135
|
+
error_message = "Failed to delete"
|
|
136
|
+
mock_client.alias.delete.side_effect = Exception(error_message)
|
|
137
|
+
|
|
138
|
+
with pytest.raises(Exception) as exc_info:
|
|
139
|
+
alias_manager.delete_alias(alias_name)
|
|
140
|
+
|
|
141
|
+
assert f"Error deleting alias '{alias_name}': {error_message}" in str(
|
|
142
|
+
exc_info.value
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_list_aliases_all_success(
|
|
147
|
+
alias_manager: AliasManager, mock_client: MagicMock
|
|
148
|
+
) -> None:
|
|
149
|
+
"""
|
|
150
|
+
Test successful listing of all aliases.
|
|
151
|
+
"""
|
|
152
|
+
expected_aliases = {
|
|
153
|
+
"alias1": AliasReturn(alias="alias1", collection="collection1"),
|
|
154
|
+
"alias2": AliasReturn(alias="alias2", collection="collection2"),
|
|
155
|
+
}
|
|
156
|
+
mock_client.alias.list_all.return_value = expected_aliases
|
|
157
|
+
|
|
158
|
+
result = alias_manager.list_aliases()
|
|
159
|
+
|
|
160
|
+
assert result == expected_aliases
|
|
161
|
+
mock_client.alias.list_all.assert_called_once_with(collection=None)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_list_aliases_for_collection_success(
|
|
165
|
+
alias_manager: AliasManager, mock_client: MagicMock
|
|
166
|
+
) -> None:
|
|
167
|
+
"""
|
|
168
|
+
Test successful listing of aliases for a specific collection.
|
|
169
|
+
"""
|
|
170
|
+
collection_name = "TestCollection"
|
|
171
|
+
expected_aliases = {
|
|
172
|
+
"alias1": AliasReturn(alias="alias1", collection="TestCollection"),
|
|
173
|
+
"alias2": AliasReturn(alias="alias2", collection="TestCollection"),
|
|
174
|
+
}
|
|
175
|
+
mock_client.alias.list_all.return_value = expected_aliases
|
|
176
|
+
|
|
177
|
+
result = alias_manager.list_aliases(collection=collection_name)
|
|
178
|
+
|
|
179
|
+
assert result == expected_aliases
|
|
180
|
+
mock_client.alias.list_all.assert_called_once_with(collection=collection_name)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def test_list_aliases_error(
|
|
184
|
+
alias_manager: AliasManager, mock_client: MagicMock
|
|
185
|
+
) -> None:
|
|
186
|
+
"""
|
|
187
|
+
Test listing aliases with an exception.
|
|
188
|
+
"""
|
|
189
|
+
error_message = "Failed to list"
|
|
190
|
+
mock_client.alias.list_all.side_effect = Exception(error_message)
|
|
191
|
+
|
|
192
|
+
with pytest.raises(Exception) as exc_info:
|
|
193
|
+
alias_manager.list_aliases()
|
|
194
|
+
|
|
195
|
+
assert f"Error listing aliases: {error_message}" in str(exc_info.value)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_print_alias(alias_manager: AliasManager, capsys) -> None:
|
|
199
|
+
"""
|
|
200
|
+
Test the output of the print_alias method.
|
|
201
|
+
"""
|
|
202
|
+
alias_name = "my_alias"
|
|
203
|
+
collection_name = "MyCollection"
|
|
204
|
+
alias = AliasReturn(alias=alias_name, collection=collection_name)
|
|
205
|
+
|
|
206
|
+
alias_manager.print_alias(alias)
|
|
207
|
+
|
|
208
|
+
captured = capsys.readouterr()
|
|
209
|
+
assert captured.out == f"Alias: {alias_name} -> {collection_name}\n"
|