Skip to content

Commit fa2300d

Browse files
Merge branch 'main' into djanicek/incremental-backups
2 parents c24ecbd + dd41083 commit fa2300d

14 files changed

Lines changed: 1109 additions & 97 deletions

File tree

.github/workflows/main.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ jobs:
8686
- name: Run integration tests with pytest
8787
run: |
8888
pip install pytest-html
89-
pytest test/integration/test_integration.py --html=test-report-${{ matrix.version }}.html --self-contained-html
89+
pytest test/integration/test_integration.py test/integration/test_data_integration.py test/integration/test_create_data_return_collection.py --html=test-report-${{ matrix.version }}.html --self-contained-html
9090
integration-auth-tests:
9191
needs: [unit-tests, get-latest-weaviate-version]
9292
env:

requirements-dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
weaviate-client>=4.20.3
1+
weaviate-client>=4.20.4
22
click==8.1.7
33
twine
44
pytest

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ classifiers =
3737
include_package_data = True
3838
python_requires = >=3.9
3939
install_requires =
40-
weaviate-client>=4.19.0
40+
weaviate-client>=4.20.4
4141
click==8.1.7
4242
semver>=3.0.2
4343
numpy>=1.24.0
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
"""Integration tests verifying that DataManager.create_data returns a usable
2+
Collection object for both single-tenant and multi-tenant collections.
3+
4+
Regression guard for a bug where parallel multi-tenant ingestion returned the
5+
base collection (without tenant context), causing ``len(collection)`` and
6+
``batch.wait_for_vector_indexing()`` to fail with:
7+
8+
"class X has multi-tenancy enabled, but request was without tenant"
9+
"""
10+
11+
import pytest
12+
import weaviate
13+
from weaviate_cli.managers.collection_manager import CollectionManager
14+
from weaviate_cli.managers.config_manager import ConfigManager
15+
from weaviate_cli.managers.data_manager import DataManager
16+
from weaviate_cli.managers.tenant_manager import TenantManager
17+
18+
NUM_OBJECTS = 20
19+
NUM_TENANTS = 3
20+
21+
22+
@pytest.fixture
23+
def client() -> weaviate.WeaviateClient:
24+
config = ConfigManager()
25+
return config.get_client()
26+
27+
28+
@pytest.fixture
29+
def collection_manager(client: weaviate.WeaviateClient) -> CollectionManager:
30+
return CollectionManager(client)
31+
32+
33+
@pytest.fixture
34+
def data_manager(client: weaviate.WeaviateClient) -> DataManager:
35+
return DataManager(client)
36+
37+
38+
@pytest.fixture
39+
def tenant_manager(client: weaviate.WeaviateClient) -> TenantManager:
40+
return TenantManager(client)
41+
42+
43+
def test_create_data_returns_usable_collection_single_tenant(
44+
client: weaviate.WeaviateClient,
45+
collection_manager: CollectionManager,
46+
data_manager: DataManager,
47+
):
48+
"""For a non-MT collection, the returned collection must support len() and
49+
batch.wait_for_vector_indexing() without errors."""
50+
collection_name = "TestReturnCollSingleTenant"
51+
52+
try:
53+
if client.collections.exists(collection_name):
54+
client.collections.delete(collection_name)
55+
56+
collection_manager.create_collection(
57+
collection=collection_name,
58+
vectorizer="none",
59+
replication_factor=1,
60+
async_enabled=True,
61+
)
62+
63+
returned_col = data_manager.create_data(
64+
collection=collection_name,
65+
limit=NUM_OBJECTS,
66+
consistency_level="one",
67+
randomize=True,
68+
skip_seed=True,
69+
vector_dimensions=128,
70+
)
71+
72+
# The returned collection must be usable for these operations
73+
returned_col.batch.wait_for_vector_indexing()
74+
assert (
75+
len(returned_col) == NUM_OBJECTS
76+
), f"Expected {NUM_OBJECTS} objects via returned collection, got {len(returned_col)}"
77+
finally:
78+
if client.collections.exists(collection_name):
79+
client.collections.delete(collection_name)
80+
81+
82+
def test_create_data_returns_usable_collection_multitenant_sequential(
83+
client: weaviate.WeaviateClient,
84+
collection_manager: CollectionManager,
85+
data_manager: DataManager,
86+
tenant_manager: TenantManager,
87+
):
88+
"""For a multi-tenant collection with sequential ingestion (parallel_workers=1),
89+
the returned collection must carry tenant context so len() works."""
90+
collection_name = "TestReturnCollMTSeq"
91+
92+
try:
93+
if client.collections.exists(collection_name):
94+
client.collections.delete(collection_name)
95+
96+
collection_manager.create_collection(
97+
collection=collection_name,
98+
vectorizer="none",
99+
replication_factor=1,
100+
async_enabled=True,
101+
multitenant=True,
102+
)
103+
104+
tenant_manager.create_tenants(
105+
collection=collection_name,
106+
number_tenants=NUM_TENANTS,
107+
)
108+
109+
returned_col = data_manager.create_data(
110+
collection=collection_name,
111+
limit=NUM_OBJECTS,
112+
consistency_level="one",
113+
randomize=True,
114+
skip_seed=True,
115+
vector_dimensions=128,
116+
parallel_workers=1,
117+
)
118+
119+
# Must not raise "multi-tenancy enabled, but request was without tenant"
120+
returned_col.batch.wait_for_vector_indexing()
121+
count = len(returned_col)
122+
assert (
123+
count == NUM_OBJECTS
124+
), f"Expected {NUM_OBJECTS} objects via returned collection, got {count}"
125+
finally:
126+
if client.collections.exists(collection_name):
127+
client.collections.delete(collection_name)
128+
129+
130+
def test_create_data_returns_usable_collection_multitenant_parallel(
131+
client: weaviate.WeaviateClient,
132+
collection_manager: CollectionManager,
133+
data_manager: DataManager,
134+
tenant_manager: TenantManager,
135+
):
136+
"""For a multi-tenant collection with parallel ingestion (parallel_workers>1),
137+
the returned collection must carry tenant context so len() works.
138+
139+
This is the exact scenario that was broken: parallel mode never assigned the
140+
tenant-scoped collection back to the return variable."""
141+
collection_name = "TestReturnCollMTParallel"
142+
143+
try:
144+
if client.collections.exists(collection_name):
145+
client.collections.delete(collection_name)
146+
147+
collection_manager.create_collection(
148+
collection=collection_name,
149+
vectorizer="none",
150+
replication_factor=1,
151+
async_enabled=True,
152+
multitenant=True,
153+
)
154+
155+
tenant_manager.create_tenants(
156+
collection=collection_name,
157+
number_tenants=NUM_TENANTS,
158+
)
159+
160+
returned_col = data_manager.create_data(
161+
collection=collection_name,
162+
limit=NUM_OBJECTS,
163+
consistency_level="one",
164+
randomize=True,
165+
skip_seed=True,
166+
vector_dimensions=128,
167+
parallel_workers=NUM_TENANTS,
168+
)
169+
170+
# Must not raise "multi-tenancy enabled, but request was without tenant"
171+
returned_col.batch.wait_for_vector_indexing()
172+
count = len(returned_col)
173+
assert (
174+
count == NUM_OBJECTS
175+
), f"Expected {NUM_OBJECTS} objects via returned collection, got {count}"
176+
finally:
177+
if client.collections.exists(collection_name):
178+
client.collections.delete(collection_name)

test/integration/test_data_integration.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def test_data_creation_with_different_configs(
9393
assert obj.vector is not None
9494

9595
vector_dimensions = {
96-
"transformers": 384,
96+
"transformers": 256,
9797
"contextionary": 300,
9898
}
9999

@@ -193,11 +193,12 @@ def test_data_creation_with_named_vectors(
193193
named_vector = obj.vector[named_vector_name]
194194
assert named_vector is not None
195195

196-
# Check vector dimensions (should be 768 for transformers)
197-
assert len(named_vector) == 384
196+
# Check vector dimensions (256 for model2vec transformers, 384 for none/custom)
197+
expected_dim = 256 if vectorizer == "transformers" else 384
198+
assert len(named_vector) == expected_dim
198199

199200
# Verify vector is not all zeros
200-
assert not np.allclose(named_vector, np.zeros(384))
201+
assert not np.allclose(named_vector, np.zeros(expected_dim))
201202

202203
# Verify vector has finite values
203204
assert np.all(np.isfinite(named_vector))

0 commit comments

Comments
 (0)