From ef923958bbb797e0874196dee5d4f12e58b8d551 Mon Sep 17 00:00:00 2001 From: zhuwenxing Date: Sat, 19 Oct 2024 19:13:25 +0800 Subject: [PATCH] test: add restful param check testcases (#36931) /kind improvement --------- Signed-off-by: zhuwenxing --- tests/restful_client_v2/base/testbase.py | 8 +- .../testcases/test_collection_operations.py | 114 +++++++++++++++--- 2 files changed, 104 insertions(+), 18 deletions(-) diff --git a/tests/restful_client_v2/base/testbase.py b/tests/restful_client_v2/base/testbase.py index 7d127a34f7..3c08fea27e 100644 --- a/tests/restful_client_v2/base/testbase.py +++ b/tests/restful_client_v2/base/testbase.py @@ -37,6 +37,7 @@ class Base: class TestBase(Base): req = None + def teardown_method(self): self.collection_client.api_key = self.api_key all_collections = self.collection_client.collection_list()['data'] @@ -50,10 +51,6 @@ class TestBase(Base): except Exception as e: logger.error(e) - # def setup_method(self): - # self.req = Requests() - # self.req.uuid = str(uuid.uuid1()) - @pytest.fixture(scope="function", autouse=True) def init_client(self, endpoint, token, minio_host, bucket_name, root_path): _uuid = str(uuid.uuid1()) @@ -162,3 +159,6 @@ class TestBase(Base): self.collection_client.db_name = db_name self.vector_client.db_name = db_name self.import_job_client.db_name = db_name + + + diff --git a/tests/restful_client_v2/testcases/test_collection_operations.py b/tests/restful_client_v2/testcases/test_collection_operations.py index 4febe973c0..e2878b69ff 100644 --- a/tests/restful_client_v2/testcases/test_collection_operations.py +++ b/tests/restful_client_v2/testcases/test_collection_operations.py @@ -138,33 +138,69 @@ class TestCreateCollection(TestBase): assert rsp['data']['consistencyLevel'] == consistency_level assert ttl_seconds_actual == ttl_seconds - def test_create_collections_with_all_params(self): + @pytest.mark.parametrize("primary_key_field", ["book_id"]) + @pytest.mark.parametrize("partition_key_field", ["word_count"]) + @pytest.mark.parametrize("clustering_key_field", ["book_category"]) + @pytest.mark.parametrize("shardsNum", [4]) + @pytest.mark.parametrize("partitionsNum", [16]) + @pytest.mark.parametrize("ttl_seconds", [60]) + @pytest.mark.parametrize("metric_type", ["L2", "COSINE", "IP"]) + @pytest.mark.parametrize("consistency_level", ["Strong", "Bounded"]) + @pytest.mark.parametrize("enable_dynamic_field", [True, False]) + @pytest.mark.parametrize("index_type", ["AUTOINDEX","IVF_SQ8", "HNSW"]) + @pytest.mark.parametrize("dim", [128]) + def test_create_collections_with_all_params(self, + dim, + index_type, + enable_dynamic_field, + consistency_level, + metric_type, + ttl_seconds, + partitionsNum, + shardsNum, + clustering_key_field, + partition_key_field, + primary_key_field, + ): """ target: test create collection method: create a collection with a simple schema expected: create collection success """ name = gen_collection_name() - dim = 128 - metric_type = "COSINE" + dim = dim + metric_type = metric_type client = self.collection_client - num_shards = 2 - num_partitions = 36 - consistency_level = "Strong" - ttl_seconds = 360 + num_shards = shardsNum + num_partitions = partitionsNum + consistency_level = consistency_level + ttl_seconds = ttl_seconds + index_param_map = { + "FLAT": {}, + "IVF_SQ8": {"nlist": 16384}, + "HNSW": {"M": 16, "efConstruction": 500}, + "AUTOINDEX": {} + } + payload = { "collectionName": name, - "enableDynamicField": True, - "params":{ + "params": { "shardsNum": f"{num_shards}", "partitionsNum": f"{num_partitions}", "consistencyLevel": f"{consistency_level}", "ttlSeconds": f"{ttl_seconds}", }, "schema": { + "enableDynamicField": enable_dynamic_field, "fields": [ - {"fieldName": "book_id", "dataType": "Int64", "isPrimary": True, "elementTypeParams": {}}, - {"fieldName": "word_count", "dataType": "Int64", "isPartitionKey": True, "elementTypeParams": {}}, + {"fieldName": "user_id", "dataType": "Int64", "elementTypeParams": {}}, + {"fieldName": "book_id", "dataType": "Int64", + "isPrimary": primary_key_field == "book_id", "elementTypeParams": {}}, + {"fieldName": "word_count", "dataType": "Int64", + "isPartitionKey": partition_key_field == "word_count", "isClusteringKey": clustering_key_field == "word_count", "elementTypeParams": {}}, + {"fieldName": "book_category", "dataType": "Int64", + "isPartitionKey": partition_key_field == "book_category", + "isClusteringKey": clustering_key_field == "book_category", "elementTypeParams": {}}, {"fieldName": "book_describe", "dataType": "VarChar", "elementTypeParams": {"max_length": "256"}}, {"fieldName": "json", "dataType": "JSON", "elementTypeParams": {}}, {"fieldName": "int_array", "dataType": "Array", "elementDataType": "Int64", @@ -173,7 +209,12 @@ class TestCreateCollection(TestBase): ] }, "indexParams": [ - {"fieldName": "book_intro", "indexName": "book_intro_vector", "metricType": f"{metric_type}"}] + {"fieldName": "book_intro", + "indexName": "book_intro_vector", + "metricType": f"{metric_type}", + "indexType": index_type, + "params": index_param_map[index_type] + }] } logging.info(f"create collection {name} with payload: {payload}") @@ -186,11 +227,11 @@ class TestCreateCollection(TestBase): # describe collection by pymilvus c = Collection(name) res = c.describe() - logger.info(f"describe collection: {res}") + logger.info(f"pymilvus describe collection: {res}") # describe collection time.sleep(10) rsp = client.collection_describe(name) - logger.info(f"describe collection: {rsp}") + logger.info(f"restful describe collection: {rsp}") ttl_seconds_actual = None for d in rsp["data"]["properties"]: @@ -198,10 +239,30 @@ class TestCreateCollection(TestBase): ttl_seconds_actual = int(d["value"]) assert rsp['code'] == 0 assert rsp['data']['collectionName'] == name + assert rsp['data']['enableDynamicField'] == enable_dynamic_field assert rsp['data']['shardsNum'] == num_shards assert rsp['data']['partitionsNum'] == num_partitions assert rsp['data']['consistencyLevel'] == consistency_level assert ttl_seconds_actual == ttl_seconds + # + # # check fields properties + fields = rsp['data']['fields'] + assert len(fields) == len(payload['schema']['fields']) + for field in fields: + if field['name'] == primary_key_field: + assert field['primaryKey'] is True + if field['name'] == partition_key_field: + assert field['partitionKey'] is True + if field['name'] == clustering_key_field: + assert field['clusteringKey'] is True + + # check index + index_info = [index.to_dict() for index in c.indexes] + logger.info(f"index_info: {index_info}") + assert len(index_info) == 1 + assert index_info[0]["index_param"]['metric_type'] == metric_type + assert index_info[0]["index_param"]['index_type'] == index_type + assert index_info[0]["index_param"].get("params", {}) == index_param_map[index_type] @pytest.mark.parametrize("auto_id", [True, False]) @@ -583,6 +644,7 @@ class TestCreateCollectionNegative(TestBase): def test_create_collections_custom_with_invalid_datatype(self): """ + VARCHAR is not a valid data type, it should be VarChar """ name = gen_collection_name() dim = 128 @@ -602,6 +664,30 @@ class TestCreateCollectionNegative(TestBase): rsp = client.collection_create(payload) assert rsp['code'] == 1100 + def test_create_collections_custom_with_invalid_params(self): + """ + """ + name = gen_collection_name() + dim = 128 + client = self.collection_client + payload = { + "collectionName": name, + "schema": { + "enableDynamicField": 1, + "fields": [ + {"fieldName": "book_id", "dataType": "Int64", "isPrimary": True, "elementTypeParams": {}}, + {"fieldName": "word_count", "dataType": "Int64", "elementTypeParams": {}}, + {"fieldName": "book_describe", "dataType": "VarChar", "elementTypeParams": {"max_length": "256"}}, + {"fieldName": "book_intro", "dataType": "FloatVector", "elementTypeParams": {"dim": f"{dim}"}} + ] + } + } + logging.info(f"create collection {name} with payload: {payload}") + rsp = client.collection_create(payload) + assert rsp['code'] == 1801 + + + @pytest.mark.parametrize("name", [" ", "test_collection_" * 100, "test collection", "test/collection", "test\collection"])