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:
jiamingli-maker
2026-01-21 14:01:30 +08:00
committed by GitHub
parent 71e4bcf286
commit cc9fee32cb
2 changed files with 192 additions and 134 deletions
@@ -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):