mirror of
https://github.com/milvus-io/milvus.git
synced 2026-07-21 10:15:43 +00:00
test: migrate upsert invalid test cases from orm (#47185)
/kind improvement /assign @yanliang567 **PR Summary** - Migrated upsert invalid test cases including: - data type mismatch - vector type / dimension mismatch - binary vector dimension mismatch - auto_id primary key type mismatch - rows using invalid type default value --------- Signed-off-by: zilliz <jiaming.li@zilliz.com>
This commit is contained in:
@@ -155,6 +155,92 @@ class TestMilvusClientUpsertInvalid(TestMilvusClientV2Base):
|
||||
self.upsert(client, collection_name, data,
|
||||
check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L0)
|
||||
@pytest.mark.parametrize("primary_field", [ct.default_int64_field_name, ct.default_string_field_name])
|
||||
def test_milvus_client_upsert_data_type_dismatch(self, primary_field):
|
||||
"""
|
||||
target: test upsert with invalid data type
|
||||
method: upsert data type string, set, number, float...
|
||||
expected: raise exception
|
||||
"""
|
||||
client = self._client()
|
||||
collection_name = cf.gen_collection_name_by_testcase_name()
|
||||
nb = 100
|
||||
|
||||
# 1. Create schema
|
||||
schema = self.create_schema(client, enable_dynamic_field=False)[0]
|
||||
if primary_field == ct.default_int64_field_name:
|
||||
schema.add_field(primary_field, DataType.INT64, is_primary=True, auto_id=False)
|
||||
else:
|
||||
schema.add_field(primary_field, DataType.VARCHAR, max_length=ct.default_length, is_primary=True, auto_id=False)
|
||||
schema.add_field(default_vector_field_name, DataType.FLOAT_VECTOR, dim=default_dim)
|
||||
schema.add_field(default_float_field_name, DataType.FLOAT)
|
||||
schema.add_field(default_bool_field_name, DataType.BOOL)
|
||||
if primary_field != ct.default_string_field_name:
|
||||
schema.add_field(default_string_field_name, DataType.VARCHAR, max_length=ct.default_length)
|
||||
|
||||
# 2. Create collection
|
||||
self.create_collection(client, collection_name, schema=schema)
|
||||
|
||||
# 3. Generate row data
|
||||
rows = cf.gen_row_data_by_schema(nb=nb, schema=schema)
|
||||
|
||||
# 4. Test invalid data types at different positions (first, middle, last)
|
||||
for dirty_i in [0, nb // 2, nb - 1]: # check the dirty data at first, middle and last
|
||||
log.debug(f"dirty_i: {dirty_i}")
|
||||
# Iterate through all fields in the row
|
||||
for field_name, field_value in rows[dirty_i].items():
|
||||
# Get the actual value type
|
||||
value_type = type(field_value)
|
||||
error = {ct.err_code: 999, ct.err_msg: "The Input data type is inconsistent with defined schema"}
|
||||
|
||||
# Inject type errors based on value type (only for simple scalar types)
|
||||
if value_type in (int, bool, float):
|
||||
tmp = rows[dirty_i][field_name]
|
||||
rows[dirty_i][field_name] = "iamstring"
|
||||
self.upsert(client, collection_name, data=rows,
|
||||
check_task=CheckTasks.err_res, check_items=error)
|
||||
rows[dirty_i][field_name] = tmp
|
||||
elif value_type is str:
|
||||
tmp = rows[dirty_i][field_name]
|
||||
rows[dirty_i][field_name] = random.randint(0, 1000)
|
||||
self.upsert(client, collection_name, data=rows,
|
||||
check_task=CheckTasks.err_res, check_items=error)
|
||||
rows[dirty_i][field_name] = tmp
|
||||
else:
|
||||
continue
|
||||
|
||||
# 5. Verify correct data can be upserted
|
||||
results = self.upsert(client, collection_name, rows)[0]
|
||||
assert results['upsert_count'] == nb
|
||||
|
||||
self.drop_collection(client, collection_name)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
def test_milvus_client_upsert_vector_type_unmatch(self):
|
||||
"""
|
||||
target: test upsert with unmatched vector type
|
||||
method: 1. create a collection with float_vector
|
||||
2. upsert with binary_vector data
|
||||
expected: raise exception
|
||||
"""
|
||||
client = self._client()
|
||||
collection_name = cf.gen_collection_name_by_testcase_name()
|
||||
|
||||
# 1. Create collection with float_vector
|
||||
self.create_collection(client, collection_name, default_dim)
|
||||
|
||||
# 2. Generate binary vector data
|
||||
_, binary_vectors = cf.gen_binary_vectors(default_nb, default_dim)
|
||||
rows = [{default_primary_key_field_name: i, ct.default_binary_vec_field_name: binary_vectors[i],
|
||||
default_float_field_name: i * 1.0, default_string_field_name: str(i)} for i in range(default_nb)]
|
||||
|
||||
# 3. Verify error on upsert
|
||||
error = {ct.err_code: 999, ct.err_msg: "Insert missed an field `vector` to collection without set nullable==true or set default_value"}
|
||||
self.upsert(client, collection_name, data=rows, check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
self.drop_collection(client, collection_name)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L1)
|
||||
def test_milvus_client_upsert_data_empty(self):
|
||||
"""
|
||||
@@ -233,11 +319,13 @@ class TestMilvusClientUpsertInvalid(TestMilvusClientV2Base):
|
||||
check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L1)
|
||||
def test_milvus_client_upsert_data_dim_not_match(self):
|
||||
@pytest.mark.parametrize("dim", [default_dim + 1, 2 * default_dim])
|
||||
def test_milvus_client_upsert_data_dim_not_match(self, dim):
|
||||
"""
|
||||
target: test milvus client: insert extra field than schema
|
||||
method: insert extra field than schema when enable_dynamic_field is False
|
||||
expected: Raise exception
|
||||
target: test upsert with unmatched vector dim
|
||||
method: 1. create a collection with default dim 128
|
||||
2. upsert with mismatched dim (129, 256)
|
||||
expected: raise exception
|
||||
"""
|
||||
client = self._client()
|
||||
collection_name = cf.gen_collection_name_by_testcase_name()
|
||||
@@ -246,11 +334,42 @@ class TestMilvusClientUpsertInvalid(TestMilvusClientV2Base):
|
||||
# 2. insert
|
||||
rng = np.random.default_rng(seed=19530)
|
||||
rows = [
|
||||
{default_primary_key_field_name: i, default_vector_field_name: list(rng.random((1, default_dim + 1))[0]),
|
||||
{default_primary_key_field_name: i, default_vector_field_name: list(rng.random((1, dim))[0]),
|
||||
default_float_field_name: i * 1.0, default_string_field_name: str(i)} for i in range(default_nb)]
|
||||
error = {ct.err_code: 65536, ct.err_msg: f"of float data should divide the dim({default_dim})"}
|
||||
self.upsert(client, collection_name, data=rows,
|
||||
check_task=CheckTasks.err_res, check_items=error)
|
||||
error = {ct.err_code: 65535, ct.err_msg: f"dim"}
|
||||
self.upsert(client, collection_name, data=rows, check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
@pytest.mark.parametrize("dim", [default_dim - 8, default_dim + 8])
|
||||
def test_milvus_client_upsert_binary_dim_unmatch(self, dim):
|
||||
"""
|
||||
target: test upsert with unmatched binary vector dim
|
||||
method: 1. create a collection with default dim 128
|
||||
2. upsert with mismatched dim (120, 136)
|
||||
expected: raise exception
|
||||
"""
|
||||
client = self._client()
|
||||
collection_name = cf.gen_collection_name_by_testcase_name()
|
||||
|
||||
# 1. Create binary vector collection with default dim
|
||||
schema = self.create_schema(client, enable_dynamic_field=False)[0]
|
||||
schema.add_field(default_primary_key_field_name, DataType.INT64, is_primary=True, auto_id=False)
|
||||
schema.add_field(ct.default_binary_vec_field_name, DataType.BINARY_VECTOR, dim=default_dim)
|
||||
schema.add_field(default_float_field_name, DataType.FLOAT)
|
||||
schema.add_field(default_string_field_name, DataType.VARCHAR, max_length=ct.default_length)
|
||||
|
||||
self.create_collection(client, collection_name, schema=schema)
|
||||
|
||||
# 2. Generate binary vector data with mismatched dim
|
||||
binary_vectors = cf.gen_binary_vectors(default_nb, dim)[1]
|
||||
rows = [{default_primary_key_field_name: i, ct.default_binary_vec_field_name: binary_vectors[i],
|
||||
default_float_field_name: i * 1.0, default_string_field_name: str(i)} for i in range(default_nb)]
|
||||
|
||||
# 3. Verify error on upsert
|
||||
error = {ct.err_code: 1100, ct.err_msg: f"the dim ({dim}) of field data(binary_vector) is not equal to schema dim ({default_dim})"}
|
||||
self.upsert(client, collection_name, data=rows, check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
self.drop_collection(client, collection_name)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L1)
|
||||
def test_milvus_client_upsert_not_matched_data(self):
|
||||
@@ -371,6 +490,38 @@ class TestMilvusClientUpsertInvalid(TestMilvusClientV2Base):
|
||||
check_task=CheckTasks.err_res,
|
||||
check_items=error)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
def test_milvus_client_upsert_with_auto_id_pk_type_dismatch(self):
|
||||
"""
|
||||
target: test upsert with primary key type mismatch
|
||||
method: 1. create a collection with INT64 primary key and auto_id=False
|
||||
2. upsert with string type primary key (type mismatch)
|
||||
expected: raise exception
|
||||
"""
|
||||
client = self._client()
|
||||
collection_name = cf.gen_collection_name_by_testcase_name()
|
||||
dim = 16
|
||||
nb = 10
|
||||
|
||||
# 1. Create collection with INT64 primary key, auto_id=False
|
||||
self.create_collection(client, collection_name, dim, auto_id=False)
|
||||
|
||||
# 2. Insert initial data
|
||||
rows = cf.gen_row_data_by_schema(nb=nb, schema=self.describe_collection(client, collection_name)[0])
|
||||
self.insert(client, collection_name, rows)
|
||||
|
||||
# 3. Generate upsert data with string type primary key (type mismatch)
|
||||
upsert_rows = cf.gen_row_data_by_schema(nb=nb, schema=self.describe_collection(client, collection_name)[0])
|
||||
# Set primary key field to string type (should be INT64)
|
||||
for i, row in enumerate(upsert_rows):
|
||||
row[default_primary_key_field_name] = str(i)
|
||||
|
||||
# 4. Verify error on upsert (type mismatch)
|
||||
error = {ct.err_code: 999, ct.err_msg: "The Input data type is inconsistent with defined schema"}
|
||||
self.upsert(client, collection_name, data=upsert_rows, check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
self.drop_collection(client, collection_name)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L1)
|
||||
def test_milvus_client_upsert_duplicate_pk_int64(self):
|
||||
"""
|
||||
@@ -437,6 +588,39 @@ class TestMilvusClientUpsertInvalid(TestMilvusClientV2Base):
|
||||
check_task=CheckTasks.err_res, check_items=error)
|
||||
self.drop_collection(client, collection_name)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
@pytest.mark.parametrize("default_value", [[], 123])
|
||||
def test_milvus_client_upsert_rows_using_default_value(self, default_value):
|
||||
"""
|
||||
target: test upsert with invalid type for field that has default value
|
||||
method: upsert with invalid type (list or int) for VARCHAR field that has default_value
|
||||
expected: raise exception (type check takes precedence over default value)
|
||||
"""
|
||||
client = self._client()
|
||||
collection_name = cf.gen_collection_name_by_testcase_name()
|
||||
|
||||
# 1. Create schema with default value field
|
||||
schema = self.create_schema(client, enable_dynamic_field=False)[0]
|
||||
schema.add_field(default_primary_key_field_name, DataType.INT64, is_primary=True, auto_id=False)
|
||||
schema.add_field(default_float_field_name, DataType.FLOAT)
|
||||
schema.add_field(default_string_field_name, DataType.VARCHAR, max_length=ct.default_length, default_value="abc")
|
||||
schema.add_field(default_vector_field_name, DataType.FLOAT_VECTOR, dim=default_dim)
|
||||
|
||||
# 2. Create collection
|
||||
self.create_collection(client, collection_name, dimension=default_dim, schema=schema)
|
||||
|
||||
# 3. Generate vectors
|
||||
vectors = cf.gen_vectors(ct.default_nb, default_dim)
|
||||
|
||||
# 4. Prepare upsert data with invalid type for varchar field (list or int instead of string)
|
||||
rows = [{default_primary_key_field_name: 1, default_vector_field_name: vectors[1],
|
||||
default_string_field_name: default_value, default_float_field_name: np.float32(1.0)}]
|
||||
|
||||
# 5. Verify error on upsert (type check takes precedence over default value)
|
||||
error = {ct.err_code: 999, ct.err_msg: "The Input data type is inconsistent with defined schema"}
|
||||
self.upsert(client, collection_name, data=rows, check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
self.drop_collection(client, collection_name)
|
||||
|
||||
class TestMilvusClientUpsertValid(TestMilvusClientV2Base):
|
||||
""" Test case of search interface """
|
||||
|
||||
@@ -1570,97 +1570,6 @@ class TestUpsertValid(TestcaseBase):
|
||||
class TestUpsertInvalid(TestcaseBase):
|
||||
""" Invalid test case of Upsert interface """
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L0)
|
||||
@pytest.mark.parametrize("primary_field", [ct.default_int64_field_name, ct.default_string_field_name])
|
||||
def test_upsert_data_type_dismatch(self, primary_field):
|
||||
"""
|
||||
target: test upsert with invalid data type
|
||||
method: upsert data type string, set, number, float...
|
||||
expected: raise exception
|
||||
"""
|
||||
collection_w = self.init_collection_general(pre_upsert, auto_id=False, insert_data=False,
|
||||
primary_field=primary_field, is_index=False,
|
||||
is_all_data_type=True, with_json=True)[0]
|
||||
nb = 100
|
||||
data = cf.gen_column_data_by_schema(schema=collection_w.schema, nb=nb)
|
||||
for dirty_i in [0, nb // 2, nb - 1]: # check the dirty data at first, middle and last
|
||||
log.debug(f"dirty_i: {dirty_i}")
|
||||
for i in range(len(data)):
|
||||
if data[i][dirty_i].__class__ is int:
|
||||
tmp = data[i][dirty_i]
|
||||
data[i][dirty_i] = "iamstring"
|
||||
error = {ct.err_code: 999, ct.err_msg: "The Input data type is inconsistent with defined schema"}
|
||||
collection_w.upsert(data=data, check_task=CheckTasks.err_res, check_items=error)
|
||||
data[i][dirty_i] = tmp
|
||||
elif data[i][dirty_i].__class__ is str:
|
||||
tmp = data[i][dirty_i]
|
||||
data[i][dirty_i] = random.randint(0, 1000)
|
||||
error = {ct.err_code: 999, ct.err_msg: "field (varchar) expects string input, got: <class 'int'>"}
|
||||
collection_w.upsert(data=data, check_task=CheckTasks.err_res, check_items=error)
|
||||
data[i][dirty_i] = tmp
|
||||
elif data[i][dirty_i].__class__ is bool:
|
||||
tmp = data[i][dirty_i]
|
||||
data[i][dirty_i] = "iamstring"
|
||||
error = {ct.err_code: 999, ct.err_msg: "The Input data type is inconsistent with defined schema"}
|
||||
collection_w.upsert(data=data, check_task=CheckTasks.err_res, check_items=error)
|
||||
data[i][dirty_i] = tmp
|
||||
elif data[i][dirty_i].__class__ is float:
|
||||
tmp = data[i][dirty_i]
|
||||
data[i][dirty_i] = "iamstring"
|
||||
error = {ct.err_code: 999, ct.err_msg: "The Input data type is inconsistent with defined schema"}
|
||||
collection_w.upsert(data=data, check_task=CheckTasks.err_res, check_items=error)
|
||||
data[i][dirty_i] = tmp
|
||||
else:
|
||||
continue
|
||||
res = collection_w.upsert(data)[0]
|
||||
assert res.insert_count == nb
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
def test_upsert_vector_unmatch(self):
|
||||
"""
|
||||
target: test upsert with unmatched data vector
|
||||
method: 1. create a collection with dim=128
|
||||
2. upsert with vector dim unmatch
|
||||
expected: raise exception
|
||||
"""
|
||||
c_name = cf.gen_unique_str(pre_upsert)
|
||||
collection_w = self.init_collection_wrap(name=c_name, with_json=False)
|
||||
data = cf.gen_default_binary_dataframe_data()[0]
|
||||
error = {ct.err_code: 999,
|
||||
ct.err_msg: "The name of field doesn't match, expected: float_vector, got binary_vector"}
|
||||
collection_w.upsert(data=data, check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
@pytest.mark.parametrize("dim", [128-8, 128+8])
|
||||
def test_upsert_binary_dim_unmatch(self, dim):
|
||||
"""
|
||||
target: test upsert with unmatched vector dim
|
||||
method: 1. create a collection with default dim 128
|
||||
2. upsert with mismatched dim
|
||||
expected: raise exception
|
||||
"""
|
||||
collection_w = self.init_collection_general(pre_upsert, True, is_binary=True)[0]
|
||||
data = cf.gen_default_binary_dataframe_data(dim=dim)[0]
|
||||
error = {ct.err_code: 1100,
|
||||
ct.err_msg: f"the dim ({dim}) of field data(binary_vector) is not equal to schema dim ({ct.default_dim})"}
|
||||
collection_w.upsert(data=data, check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
@pytest.mark.parametrize("dim", [256])
|
||||
def test_upsert_dim_unmatch(self, dim):
|
||||
"""
|
||||
target: test upsert with unmatched vector dim
|
||||
method: 1. create a collection with default dim 128
|
||||
2. upsert with mismatched dim
|
||||
expected: raise exception
|
||||
"""
|
||||
nb = 10
|
||||
collection_w = self.init_collection_general(pre_upsert, True, with_json=False)[0]
|
||||
data = cf.gen_default_list_data(nb=nb, dim=dim, with_json=False)
|
||||
error = {ct.err_code: 1100,
|
||||
ct.err_msg: f"the dim ({dim}) of field data(float_vector) is not equal to schema dim ({ct.default_dim})"}
|
||||
collection_w.upsert(data=data, check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
@pytest.mark.parametrize("partition_name", ct.invalid_resource_names[4:])
|
||||
def test_upsert_partition_name_non_existing(self, partition_name):
|
||||
@@ -1718,41 +1627,6 @@ class TestUpsertInvalid(TestcaseBase):
|
||||
collection_w.upsert(data=data, partition_name=["partition_1", "partition_2"],
|
||||
check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
def test_upsert_with_auto_id_pk_type_dismacth(self):
|
||||
"""
|
||||
target: test upsert with auto_id and pk type dismatch
|
||||
method: 1. create a collection with pk int64 and auto_id=True
|
||||
2. upsert with pk string type dismatch
|
||||
expected: raise exception
|
||||
"""
|
||||
dim = 16
|
||||
collection_w = self.init_collection_general(pre_upsert, auto_id=False,
|
||||
dim=dim, insert_data=True, with_json=False)[0]
|
||||
nb = 10
|
||||
data = cf.gen_default_list_data(dim=dim, nb=nb, with_json=False)
|
||||
data[0] = [str(i) for i in range(nb)]
|
||||
error = {ct.err_code: 999, ct.err_msg: "The Input data type is inconsistent with defined schema"}
|
||||
collection_w.upsert(data=data, check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
@pytest.mark.parametrize("default_value", [[], 123])
|
||||
def test_upsert_rows_using_default_value(self, default_value):
|
||||
"""
|
||||
target: test upsert with rows
|
||||
method: upsert with invalid rows
|
||||
expected: raise exception
|
||||
"""
|
||||
fields = [cf.gen_int64_field(is_primary=True), cf.gen_float_field(),
|
||||
cf.gen_string_field(default_value="abc"), cf.gen_float_vec_field()]
|
||||
schema = cf.gen_collection_schema(fields)
|
||||
collection_w = self.init_collection_wrap(schema=schema)
|
||||
vectors = cf.gen_vectors(ct.default_nb, ct.default_dim)
|
||||
data = [{"int64": 1, "float_vector": vectors[1],
|
||||
"varchar": default_value, "float": np.float32(1.0)}]
|
||||
error = {ct.err_code: 999, ct.err_msg: "The Input data type is inconsistent with defined schema"}
|
||||
collection_w.upsert(data, check_task=CheckTasks.err_res, check_items=error)
|
||||
|
||||
@pytest.mark.tags(CaseLabel.L2)
|
||||
@pytest.mark.parametrize("default_value", [[], None])
|
||||
def test_upsert_tuple_using_default_value(self, default_value):
|
||||
|
||||
Reference in New Issue
Block a user