# Copyright 2025-present the zvec project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import logging import pytest import numpy as np import zvec from zvec import ( CollectionOption, InvertIndexParam, HnswIndexParam, DataType, Collection, Doc, FieldSchema, Query, VectorSchema, ) class TestCollectionExceptionHandling: @pytest.fixture(scope="function") def test_collection(self, tmp_path_factory): """Fixture to create a test collection""" collection_schema = zvec.CollectionSchema( name="test_collection", fields=[ FieldSchema( "id", DataType.INT64, nullable=False, index_param=InvertIndexParam(enable_range_optimization=True), ), FieldSchema( "name", DataType.STRING, nullable=False, index_param=InvertIndexParam(), ), FieldSchema("weight", DataType.FLOAT, nullable=True), ], vectors=[ VectorSchema( "dense", DataType.VECTOR_FP32, dimension=128, index_param=HnswIndexParam(), ), VectorSchema( "sparse", DataType.SPARSE_VECTOR_FP32, index_param=HnswIndexParam() ), ], ) collection_option = CollectionOption(read_only=False, enable_mmap=True) temp_dir = tmp_path_factory.mktemp("zvec") collection_path = temp_dir / "test_collection" coll = zvec.create_and_open( path=str(collection_path), schema=collection_schema, option=collection_option, ) assert coll is not None, "Failed to create and open collection" yield coll # Clean up if hasattr(coll, "destroy") and coll is not None: try: coll.destroy() except Exception as e: print(f"Warning: failed to destroy collection: {e}") def test_create_and_open_missing_path(self, tmp_path_factory): collection_schema = zvec.CollectionSchema( name="test_collection", fields=[ FieldSchema( "id", DataType.INT64, nullable=False, index_param=InvertIndexParam(enable_range_optimization=True), ), FieldSchema( "name", DataType.STRING, nullable=False, index_param=InvertIndexParam(), ), ], vectors=[ VectorSchema( "dense", DataType.VECTOR_FP32, dimension=128, index_param=HnswIndexParam(), ) ], ) collection_option = CollectionOption(read_only=False, enable_mmap=True) with pytest.raises(Exception) as exc_info: coll = zvec.create_and_open( schema=collection_schema, option=collection_option ) assert exc_info.value is not None, ( "Expected exception for missing path parameter" ) def test_create_and_open_missing_schema(self, tmp_path_factory): temp_dir = tmp_path_factory.mktemp("zvec") collection_path = temp_dir / "test_collection" collection_option = CollectionOption(read_only=False, enable_mmap=True) with pytest.raises(Exception) as exc_info: coll = zvec.create_and_open( path=str(collection_path), option=collection_option ) assert exc_info.value is not None, ( "Expected exception for missing schema parameter" ) def test_open_missing_path(self): collection_option = CollectionOption(read_only=False, enable_mmap=True) with pytest.raises(Exception) as exc_info: coll = zvec.open(option=collection_option) assert exc_info.value is not None, ( "Expected exception for missing path parameter" ) def test_insert_missing_docs(self, test_collection: Collection): with pytest.raises(Exception) as exc_info: result = test_collection.insert() assert exc_info.value is not None, ( "Expected exception for missing docs parameter" ) def test_update_missing_docs(self, test_collection: Collection): with pytest.raises(Exception) as exc_info: result = test_collection.update() assert exc_info.value is not None, ( "Expected exception for missing docs parameter" ) def test_upsert_missing_docs(self, test_collection: Collection): with pytest.raises(Exception) as exc_info: result = test_collection.upsert() assert exc_info.value is not None, ( "Expected exception for missing docs parameter" ) def test_delete_missing_ids(self, test_collection: Collection): with pytest.raises(Exception) as exc_info: result = test_collection.delete() assert exc_info.value is not None, ( "Expected exception for missing ids parameter" ) def test_fetch_missing_ids(self, test_collection: Collection): with pytest.raises(Exception) as exc_info: result = test_collection.fetch() assert exc_info.value is not None, ( "Expected exception for missing ids parameter" ) def test_query_missing_query_field_name(self, test_collection: Collection): with pytest.raises(Exception) as exc_info: result = test_collection.query([Query()]) assert exc_info.value is not None, ( "Expected exception for missing Query field_name parameter" ) def test_add_column_missing_field_schema(self, test_collection: Collection): with pytest.raises(Exception) as exc_info: test_collection.add_column() assert exc_info.value is not None, ( "Expected exception for missing field_schema parameter" ) def test_alter_column_missing_old_name(self, test_collection: Collection): with pytest.raises(Exception) as exc_info: test_collection.alter_column(new_name="new_name") assert exc_info.value is not None, ( "Expected exception for missing old_name parameter" ) def test_alter_column_missing_new_name(self, test_collection: Collection): with pytest.raises(Exception) as exc_info: test_collection.alter_column(old_name="old_name") assert exc_info.value is not None, ( "Expected exception for missing new_name parameter" ) def test_drop_column_missing_field_name(self, test_collection: Collection): with pytest.raises(Exception) as exc_info: test_collection.drop_column() assert exc_info.value is not None, ( "Expected exception for missing field_name parameter" ) def test_invalid_parameter_types(self, test_collection: Collection): # This test depends on specific implementation details # Generally, we would expect TypeErrors or similar exceptions pass def test_missing_required_parameters(self, test_collection: Collection): # This test depends on specific implementation details # Generally, we would expect TypeErrors or similar exceptions pass def test_empty_collection_operations(self, tmp_path_factory): collection_schema = zvec.CollectionSchema( name="empty_test_collection", fields=[ FieldSchema( "id", DataType.INT64, nullable=False, index_param=InvertIndexParam(enable_range_optimization=True), ), FieldSchema( "name", DataType.STRING, nullable=False, index_param=InvertIndexParam(), ), ], vectors=[ VectorSchema( "dense", DataType.VECTOR_FP32, dimension=128, index_param=HnswIndexParam(), ) ], ) collection_option = CollectionOption(read_only=False, enable_mmap=True) temp_dir = tmp_path_factory.mktemp("zvec") collection_path = temp_dir / "empty_test_collection" coll = zvec.create_and_open( path=str(collection_path), schema=collection_schema, option=collection_option, ) assert coll is not None, "Failed to create and open collection" # Test fetch on empty collection result = coll.fetch(["1"]) assert len(result) >= 0 # May be empty or have special handling # Test query on empty collection result = coll.query() assert len(result) == 0 # Test update on empty collection doc = Doc( id="1", fields={"id": 1, "name": "test"}, vectors={"dense": np.random.random(128).tolist()}, ) result = coll.update(doc) # Should handle gracefully, possibly with NOT_FOUND status # Clean up if hasattr(coll, "destroy") and coll is not None: try: coll.destroy() except Exception as e: print(f"Warning: failed to destroy collection: {e}") def test_resource_management(self, test_collection: Collection): doc = Doc( id="1", fields={"id": 1, "name": "test", "weight": 80.5}, vectors={ "dense": np.random.random(128).tolist(), "sparse": {1: 1.0, 2: 2.0}, }, ) # Insert result = test_collection.insert(doc) assert result.ok() # Fetch result = test_collection.fetch(["1"]) assert len(result) == 1 # Query result = test_collection.query() assert len(result) >= 0 # Update result = test_collection.update(doc) assert result.ok() # Delete result = test_collection.delete("1") assert result.ok() def test_exception_resource_cleanup(self, test_collection: Collection): # This test would need to simulate exception conditions # which is difficult without specific failure injection points pass