diff --git a/package/package.sh b/package/package.sh
index b022b766e7aa515c4f101f19a95fc01257f17d9c..f672eef1ee3a74f93cc87976e7df3b0892cf746e 100755
--- a/package/package.sh
+++ b/package/package.sh
@@ -64,27 +64,27 @@ do
 done
 
 # version is null, get from tag name
-[[ -z $version ]] && version=`git describe --exact-match --abbrev=0 --tags | sed 's/^v//'`
+[[ -z $version ]] && version=$(git describe --exact-match --abbrev=0 --tags | sed 's/^v//')
 # version is null, use UTC date as version
 [[ -z $version ]] && version=$(date -u +%Y.%m.%d)-nightly
 
 if [[ -z $version ]]; then
     echo "version is null, exit"
     echo ${usage}
-    exit -1
+    exit 1
 fi
 
 
 if [[ $strip_enable != TRUE ]] && [[ $strip_enable != FALSE ]]; then
     echo "strip enable is wrong, exit"
     echo ${usage}
-    exit -1
+    exit 1
 fi
 
 echo "current version is [ $version ], strip enable is [$strip_enable], enablesanitizer is [$enablesanitizer], static_sanitizer is [$static_sanitizer]"
 
 function _build_storage {
-    if [ ! -d ${storage_dir} && ! -L ${storage_dir} ]; then
+    if [[ ! -d ${storage_dir} && ! -L ${storage_dir} ]]; then
         git clone --single-branch --branch ${branch} https://github.com/vesoft-inc/nebula-storage.git ${storage_dir}
     fi
 
@@ -101,9 +101,9 @@ function _build_storage {
           -S ${storage_dir} \
           -B ${storage_build_dir}
 
-    if !( cmake --build ${storage_build_dir} -j ${jobs} ); then
+    if ! ( cmake --build ${storage_build_dir} -j ${jobs} ); then
         echo ">>> build nebula storage failed <<<"
-        exit -1
+        exit 1
     fi
     echo ">>> build nebula storage successfully <<<"
 }
@@ -123,9 +123,9 @@ function _build_graph {
           -S ${project_dir} \
           -B ${build_dir}
 
-    if !( cmake --build ${build_dir} -j ${jobs} ); then
+    if ! ( cmake --build ${build_dir} -j ${jobs} ); then
         echo ">>> build nebula graph failed <<<"
-        exit -1
+        exit 1
     fi
     echo ">>> build nebula graph successfully <<<"
 }
@@ -152,7 +152,7 @@ function package {
     # The package CMakeLists.txt in ${project_dir}/package/build
     package_dir=${build_dir}/package/
     if [[ -d $package_dir ]]; then
-        rm -rf ${package_dir}/*
+        rm -rf ${package_dir:?}/*
     else
         mkdir ${package_dir}
     fi
@@ -174,30 +174,30 @@ function package {
     sys_ver=""
     pType="RPM"
     if [[ -f "/etc/redhat-release" ]]; then
-        sys_name=`cat /etc/redhat-release | cut -d ' ' -f1`
+        sys_name=$(< /etc/redhat-release cut -d ' ' -f1)
         if [[ ${sys_name} == "CentOS" ]]; then
-            sys_ver=`cat /etc/redhat-release | tr -dc '0-9.' | cut -d \. -f1`
+            sys_ver=$(< /etc/redhat-release tr -dc '0-9.' | cut -d \. -f1)
             sys_ver=.el${sys_ver}.x86_64
         elif [[ ${sys_name} == "Fedora" ]]; then
-            sys_ver=`cat /etc/redhat-release | cut -d ' ' -f3`
+            sys_ver=$(< /etc/redhat-release cut -d ' ' -f3)
             sys_ver=.fc${sys_ver}.x86_64
         fi
         pType="RPM"
     elif [[ -f "/etc/lsb-release" ]]; then
-        sys_ver=`cat /etc/lsb-release | grep DISTRIB_RELEASE | cut -d "=" -f 2 | sed 's/\.//'`
+        sys_ver=$(< /etc/lsb-release grep DISTRIB_RELEASE | cut -d "=" -f 2 | sed 's/\.//')
         sys_ver=.ubuntu${sys_ver}.amd64
         pType="DEB"
     fi
 
-    if !( cpack -G ${pType} --verbose $args ); then
+    if ! ( cpack -G ${pType} --verbose $args ); then
         echo ">>> package nebula failed <<<"
-        exit -1
+        exit 1
     else
         # rename package file
-        pkg_names=`ls | grep nebula | grep ${version}`
+        pkg_names=$(ls ./*nebula*-${version}*)
         outputDir=$build_dir/cpack_output
         mkdir -p ${outputDir}
-        for pkg_name in ${pkg_names[@]};
+        for pkg_name in "${pkg_names[@]}";
         do
             new_pkg_name=${pkg_name/\-Linux/${sys_ver}}
             mv ${pkg_name} ${outputDir}/${new_pkg_name}
diff --git a/tests/maintain/test_index.py b/tests/maintain/test_index.py
deleted file mode 100644
index b22698f9566d2b455b802949e014617aa73df8ba..0000000000000000000000000000000000000000
--- a/tests/maintain/test_index.py
+++ /dev/null
@@ -1,355 +0,0 @@
-# --coding:utf-8--
-#
-# Copyright (c) 2020 vesoft inc. All rights reserved.
-#
-# This source code is licensed under Apache 2.0 License,
-# attached with Common Clause Condition 1.0, found in the LICENSES directory.
-
-
-import time
-import re
-
-from tests.common.nebula_test_suite import NebulaTestSuite
-from tests.common.nebula_test_suite import T_EMPTY, T_NULL
-from tests.common.utils import find_in_rows
-
-
-class TestIndex(NebulaTestSuite):
-
-    @classmethod
-    def prepare(self):
-        resp = self.client.execute('CREATE SPACE index_space(partition_num=9)')
-        self.check_resp_succeeded(resp)
-
-        time.sleep(self.delay)
-        resp = self.client.execute('USE index_space')
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('CREATE TAG tag_1(col1 string, col2 int, col3 double, col4 timestamp)')
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('CREATE EDGE edge_1(col1 string, col2 int, col3 double, col4 timestamp)')
-        self.check_resp_succeeded(resp)
-        time.sleep(self.delay)
-
-        resp = self.client.execute("INSERT VERTEX tag_1(col1, col2, col3, col4) VALUES '101':('Tom', 18, 35.4, `timestamp`('2010-09-01T08:00:00'))")
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute("INSERT VERTEX tag_1(col1, col2, col3, col4) VALUES '102':('Jerry', 22, 38.4, `timestamp`('2011-09-01T08:00:00'))")
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute("INSERT VERTEX tag_1(col1, col2, col3, col4) VALUES '103':('Bob', 19, 36.4, `timestamp`('2010-09-01T12:00:00'))")
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute("INSERT EDGE edge_1(col1, col2, col3, col4) VALUES '101'->'102':('Red', 81, 45.3, `timestamp`('2010-09-01T08:00:00'))")
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute("INSERT EDGE edge_1(col1, col2, col3, col4) VALUES '102'->'103':('Yellow', 22, 423.8, `timestamp`('2011-09-01T08:00:00'))")
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute("INSERT EDGE edge_1(col1, col2, col3, col4) VALUES '103'->'101':('Blue', 91, 43.1, `timestamp`('2010-09-01T12:00:00'))")
-        self.check_resp_succeeded(resp)
-
-    @classmethod
-    def cleanup(self):
-        resp = self.execute('DROP SPACE index_space;')
-        self.check_resp_succeeded(resp)
-
-
-    def test_tag_index(self):
-        # Single Tag Single Field
-        resp = self.client.execute('CREATE TAG INDEX single_tag_index ON tag_1(col2)')
-        self.check_resp_succeeded(resp)
-
-        # Duplicate Index
-        resp = self.client.execute('CREATE TAG INDEX duplicate_tag_index_1 ON tag_1(col2)')
-        self.check_resp_failed(resp)
-
-        # Tag not exist
-        resp = self.client.execute('CREATE TAG INDEX single_person_index ON student(name)')
-        self.check_resp_failed(resp)
-
-        # Property not exist
-        resp = self.client.execute('CREATE TAG INDEX single_tag_index ON tag_1(col5)')
-        self.check_resp_failed(resp)
-
-        # Property is empty
-        resp = self.client.execute('CREATE TAG INDEX single_tag_index ON tag_1()')
-        self.check_resp_failed(resp)
-
-        # Single Tag Multi Field
-        resp = self.client.execute('CREATE TAG INDEX multi_tag_index ON tag_1(col2, col3)')
-        self.check_resp_succeeded(resp)
-
-        # Duplicate Index
-        resp = self.client.execute('CREATE TAG INDEX duplicate_person_index ON tag_1(col2, col3)')
-        self.check_resp_failed(resp)
-
-        # Duplicate Field
-        resp = self.client.execute('CREATE TAG INDEX duplicate_index ON tag_1(col2, col2)')
-        self.check_resp_failed(resp)
-
-        resp = self.client.execute('CREATE TAG INDEX disorder_tag_index ON tag_1(col3, col2)')
-        self.check_resp_succeeded(resp)
-
-        time.sleep(self.delay)
-        # Rebuild Tag Index
-        resp = self.client.execute('REBUILD TAG INDEX single_tag_index')
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('REBUILD TAG INDEX multi_tag_index')
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('REBUILD TAG INDEX disorder_tag_index')
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('REBUILD TAG INDEX non_existent_tag_index')
-        self.check_resp_failed(resp) # need to check if index exists in validator in future
-
-        time.sleep(self.delay)
-        # Show Tag Index Status
-        resp = self.client.execute('SHOW TAG INDEX STATUS')
-        self.check_resp_succeeded(resp)
-        self.search_result(resp, [['single_tag_index', 'FINISHED'],
-                                   ['multi_tag_index', 'FINISHED'],
-                                   ['disorder_tag_index', 'FINISHED']])
-
-        # Lookup
-        resp = self.client.execute('LOOKUP ON tag_1 WHERE tag_1.col2 == 18 YIELD tag_1.col1')
-        self.check_resp_succeeded(resp)
-        expect = [['101', 'Tom']]
-        self.check_out_of_order_result(resp, expect)
-
-        resp = self.client.execute('LOOKUP ON tag_1 WHERE tag_1.col3 > 35.7 YIELD tag_1.col1')
-        self.check_resp_succeeded(resp)
-        expect = [['102', 'Jerry'], ['103', 'Bob']]
-        self.check_out_of_order_result(resp, expect)
-
-        resp = self.client.execute('LOOKUP ON tag_1 WHERE tag_1.col2 > 18 AND tag_1.col3 < 37.2 YIELD tag_1.col1')
-        self.check_resp_succeeded(resp)
-        expect = [['103', 'Bob']]
-        self.check_out_of_order_result(resp, expect)
-
-        # Describe Tag Index
-        resp = self.client.execute('DESC TAG INDEX single_tag_index')
-        self.check_resp_succeeded(resp)
-        expect = [['col2', 'int64']]
-        self.check_result(resp, expect)
-
-        resp = self.client.execute('DESC TAG INDEX multi_tag_index')
-        self.check_resp_succeeded(resp)
-        expect = [['col2', 'int64'],
-                  ['col3', 'double']]
-        self.check_result(resp, expect)
-
-        resp = self.client.execute('DESC TAG INDEX disorder_tag_index')
-        self.check_resp_succeeded(resp)
-        expect = [['col3', 'double'],
-                  ['col2', 'int64']]
-        self.check_result(resp, expect)
-
-        resp = self.client.execute('DESC TAG INDEX non_existent_tag_index')
-        self.check_resp_failed(resp)
-
-        # Show Create Tag Index
-        resp = self.client.execute('SHOW CREATE TAG INDEX single_tag_index')
-        self.check_resp_succeeded(resp)
-        expect = [['single_tag_index', 'CREATE TAG INDEX `single_tag_index` ON `tag_1` (\n `col2`\n)']]
-        self.check_result(resp, expect)
-
-        resp = self.client.execute('SHOW CREATE TAG INDEX multi_tag_index')
-        self.check_resp_succeeded(resp)
-        expect = [['multi_tag_index', 'CREATE TAG INDEX `multi_tag_index` ON `tag_1` (\n `col2`,\n `col3`\n)']]
-        self.check_result(resp, expect)
-        # Check if show create tag index works well
-        resp = self.client.execute('DROP TAG INDEX multi_tag_index')
-        self.check_resp_succeeded(resp)
-        resp = self.client.execute(expect[0][1])
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('SHOW CREATE TAG INDEX disorder_tag_index')
-        self.check_resp_succeeded(resp)
-        expect = [['disorder_tag_index', 'CREATE TAG INDEX `disorder_tag_index` ON `tag_1` (\n `col3`,\n `col2`\n)']]
-        self.check_result(resp, expect)
-        # Check if show create tag index works well
-        resp = self.client.execute('DROP TAG INDEX disorder_tag_index')
-        self.check_resp_succeeded(resp)
-        resp = self.client.execute(expect[0][1])
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('SHOW CREATE TAG INDEX non_existent_tag_index')
-        self.check_resp_failed(resp)
-
-        # Show Tag Indexes
-        resp = self.client.execute('SHOW TAG INDEXES')
-        self.check_resp_succeeded(resp)
-        self.check_out_of_order_result(resp, [['single_tag_index'], ['multi_tag_index'], ['disorder_tag_index']])
-
-        # Drop Tag Index
-        resp = self.client.execute('DROP TAG INDEX single_tag_index')
-        self.check_resp_succeeded(resp)
-        # Check if the index is truly dropped
-        resp = self.client.execute('DESC TAG INDEX single_tag_index')
-        self.check_resp_failed(resp)
-
-        resp = self.client.execute('DROP TAG INDEX multi_tag_index')
-        self.check_resp_succeeded(resp)
-        resp = self.client.execute('DESC TAG INDEX multi_tag_index')
-        self.check_resp_failed(resp)
-
-        resp = self.client.execute('DROP TAG INDEX disorder_tag_index')
-        self.check_resp_succeeded(resp)
-        resp = self.client.execute('DESC TAG INDEX disorder_tag_index')
-        self.check_resp_failed(resp)
-
-        resp = self.client.execute('DROP TAG INDEX non_existent_tag_index')
-        self.check_resp_failed(resp)
-
-    def test_edge_index(self):
-        # Single Tag Single Field
-        resp = self.client.execute('CREATE EDGE INDEX single_edge_index ON edge_1(col2)')
-        self.check_resp_succeeded(resp)
-
-        # Duplicate Index
-        resp = self.client.execute('CREATE EDGE INDEX duplicate_edge_index ON edge_1(col2)')
-        self.check_resp_failed(resp)
-
-        # Edge not exist
-        resp = self.client.execute('CREATE EDGE INDEX single_edge_index ON edge_1_ship(name)')
-        self.check_resp_failed(resp)
-
-        # Property not exist
-        resp = self.client.execute('CREATE EDGE INDEX single_edge_index ON edge_1(startTime)')
-        self.check_resp_failed(resp)
-
-        # Property is empty
-        resp = self.client.execute('CREATE EDGE INDEX single_edge_index ON edge_1()')
-        self.check_resp_failed(resp)
-
-        # Single Edge Multi Field
-        resp = self.client.execute('CREATE EDGE INDEX multi_edge_index ON edge_1(col2, col3)')
-        self.check_resp_succeeded(resp)
-
-        # Duplicate Index
-        resp = self.client.execute('CREATE EDGE INDEX duplicate_edge_index ON edge_1(col2, col3)')
-        self.check_resp_failed(resp)
-
-        # Duplicate Field
-        resp = self.client.execute('CREATE EDGE INDEX duplicate_index ON edge_1(col2, col2)')
-        self.check_resp_failed(resp)
-
-        resp = self.client.execute('CREATE EDGE INDEX disorder_edge_index ON edge_1(col3, col2)')
-        self.check_resp_succeeded(resp)
-
-        time.sleep(self.delay)
-        # Rebuild Edge Index
-        resp = self.client.execute('REBUILD EDGE INDEX single_edge_index')
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('REBUILD EDGE INDEX multi_edge_index')
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('REBUILD EDGE INDEX disorder_edge_index')
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('REBUILD EDGE INDEX non_existent_edge_index')
-        self.check_resp_failed(resp)
-
-        time.sleep(self.delay)
-        # Show Edge Index Status
-        resp = self.client.execute('SHOW EDGE INDEX STATUS')
-        self.check_resp_succeeded(resp)
-        self.search_result(resp, [['single_edge_index', 'FINISHED'],
-                                 ['multi_edge_index', 'FINISHED'],
-                                 ['disorder_edge_index', 'FINISHED']])
-
-        # Lookup
-        resp = self.client.execute('LOOKUP ON edge_1 WHERE edge_1.col2 == 22 YIELD edge_1.col2')
-        self.check_resp_succeeded(resp)
-        expect = [['102', '103', 0, 22]]
-        self.check_out_of_order_result(resp, expect)
-
-        resp = self.client.execute('LOOKUP ON edge_1 WHERE edge_1.col3 > 43.4 YIELD edge_1.col1')
-        self.check_resp_succeeded(resp)
-        expect = [['102', '103', 0, 'Yellow'], ['101', '102', 0, 'Red']]
-        self.check_out_of_order_result(resp, expect)
-
-        resp = self.client.execute('LOOKUP ON edge_1 WHERE edge_1.col2 > 45 AND edge_1.col3 < 44.3 YIELD edge_1.col1')
-        self.check_resp_succeeded(resp)
-        expect = [['103', '101', 0, 'Blue']]
-        self.check_out_of_order_result(resp, expect)
-
-        # Describe Edge Index
-        resp = self.client.execute('DESC EDGE INDEX single_edge_index')
-        self.check_resp_succeeded(resp)
-        expect = [['col2', 'int64']]
-        self.check_result(resp, expect)
-
-        resp = self.client.execute('DESC EDGE INDEX multi_edge_index')
-        self.check_resp_succeeded(resp)
-        expect = [['col2', 'int64'],
-                  ['col3', 'double']]
-        self.check_result(resp, expect)
-
-        resp = self.client.execute('DESC EDGE INDEX disorder_edge_index')
-        self.check_resp_succeeded(resp)
-        expect = [['col3', 'double'],
-                  ['col2', 'int64']]
-        self.check_result(resp, expect)
-
-        resp = self.client.execute('DESC EDGE INDEX non_existent_edge_index')
-        self.check_resp_failed(resp)
-
-        # Show Create Edge Index
-        resp = self.client.execute('SHOW CREATE EDGE INDEX single_edge_index')
-        self.check_resp_succeeded(resp)
-        expect = [['single_edge_index', 'CREATE EDGE INDEX `single_edge_index` ON `edge_1` (\n `col2`\n)']]
-        self.check_result(resp, expect)
-
-        resp = self.client.execute('SHOW CREATE EDGE INDEX multi_edge_index')
-        self.check_resp_succeeded(resp)
-        expect = [['multi_edge_index', 'CREATE EDGE INDEX `multi_edge_index` ON `edge_1` (\n `col2`,\n `col3`\n)']]
-        self.check_result(resp, expect)
-        # Check if show create edge index works well
-        resp = self.client.execute('DROP EDGE INDEX multi_edge_index')
-        self.check_resp_succeeded(resp)
-        resp = self.client.execute(expect[0][1])
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('SHOW CREATE EDGE INDEX disorder_edge_index')
-        self.check_resp_succeeded(resp)
-        expect = [['disorder_edge_index', 'CREATE EDGE INDEX `disorder_edge_index` ON `edge_1` (\n `col3`,\n `col2`\n)']]
-        self.check_result(resp, expect)
-        # Check if show create edge index works well
-        resp = self.client.execute('DROP EDGE INDEX disorder_edge_index')
-        self.check_resp_succeeded(resp)
-        resp = self.client.execute(expect[0][1])
-        self.check_resp_succeeded(resp)
-
-        resp = self.client.execute('SHOW CREATE EDGE INDEX non_existent_edge_index')
-        self.check_resp_failed(resp)
-
-        # Show Edge Indexes
-        resp = self.client.execute('SHOW EDGE INDEXES')
-        self.check_resp_succeeded(resp)
-        self.check_out_of_order_result(resp, [['single_edge_index'], ['multi_edge_index'], ['disorder_edge_index']])
-
-        # Drop Edge Index
-        resp = self.client.execute('DROP EDGE INDEX single_edge_index')
-        self.check_resp_succeeded(resp)
-        # Check if the index is truly dropped
-        resp = self.client.execute('DESC EDGE INDEX single_edge_index')
-        self.check_resp_failed(resp)
-
-        resp = self.client.execute('DROP EDGE INDEX multi_edge_index')
-        self.check_resp_succeeded(resp)
-        resp = self.client.execute('DESC EDGE INDEX multi_edge_index')
-        self.check_resp_failed(resp)
-
-        resp = self.client.execute('DROP EDGE INDEX disorder_edge_index')
-        self.check_resp_succeeded(resp)
-        resp = self.client.execute('DESC EDGE INDEX disorder_edge_index')
-        self.check_resp_failed(resp)
-
-        resp = self.client.execute('DROP EDGE INDEX non_existent_edge_index')
-        self.check_resp_failed(resp)
diff --git a/tests/maintain/test_zone.py b/tests/maintain/test_zone.py
index f3d5e94ae61a4ade81a750aedc443c72be1f6206..c1c4e3008ec60910a5cb7e78b8e8e578dbde794b 100644
--- a/tests/maintain/test_zone.py
+++ b/tests/maintain/test_zone.py
@@ -5,16 +5,11 @@
 # This source code is licensed under Apache 2.0 License,
 # attached with Common Clause Condition 1.0, found in the LICENSES directory.
 
-import time
 
 from tests.common.nebula_test_suite import NebulaTestSuite
-from tests.common.nebula_test_suite import T_EMPTY, T_NULL
 
-class TestZone(NebulaTestSuite):
-    @classmethod
-    def prepare(self):
-        pass
 
+class TestZone(NebulaTestSuite):
     def test_zone(self):
         resp = self.client.execute('SHOW HOSTS')
         self.check_resp_succeeded(resp)
@@ -26,7 +21,7 @@ class TestZone(NebulaTestSuite):
 
         # Get Zone
         resp = self.client.execute('DESC ZONE zone_0')
-        self.check_resp_succeeded(resp) 
+        self.check_resp_succeeded(resp)
 
         resp = self.client.execute('DESCRIBE ZONE zone_0')
         self.check_resp_succeeded(resp)
@@ -79,7 +74,3 @@ class TestZone(NebulaTestSuite):
         # Drop Zone which is not exist
         resp = self.client.execute('DROP ZONE zone_0')
         self.check_resp_failed(resp)
-
-    @classmethod
-    def cleanup(self):
-        pass
diff --git a/tests/nebula-test-run.py b/tests/nebula-test-run.py
index 3dc05eb8d8226ea546f685ccaf69851aae5f4b5c..8e723eca0c79af185b2a15169a299a5e5eda7252 100755
--- a/tests/nebula-test-run.py
+++ b/tests/nebula-test-run.py
@@ -69,6 +69,7 @@ def start_nebula(nb, configs):
             "work_dir": nb.work_dir
         }
         f.write(json.dumps(data))
+    print('Start nebula successfully')
 
 
 def stop_nebula(nb):
@@ -77,6 +78,7 @@ def stop_nebula(nb):
         nb.set_work_dir(data["work_dir"])
     nb.stop()
     shutil.rmtree(TMP_DIR, ignore_errors=True)
+    print('nebula services have been stopped.')
 
 
 def opt_is(val, expect):
diff --git a/tests/query/stateless/test_lookup.py b/tests/query/stateless/test_lookup.py
deleted file mode 100644
index a5c2e6705f9bdc8806e05df38d0f4e47ad28a68e..0000000000000000000000000000000000000000
--- a/tests/query/stateless/test_lookup.py
+++ /dev/null
@@ -1,217 +0,0 @@
-# --coding:utf-8--
-#
-# Copyright (c) 2020 vesoft inc. All rights reserved.
-#
-# This source code is licensed under Apache 2.0 License,
-# attached with Common Clause Condition 1.0, found in the LICENSES directory.
-import time
-
-from tests.common.nebula_test_suite import NebulaTestSuite
-
-
-class TestIndex(NebulaTestSuite):
-
-    @classmethod
-    def prepare(self):
-        resp = self.execute(
-            'CREATE SPACE IF NOT EXISTS nbaLookup(partition_num={partition_num}, replica_factor={replica_factor})'
-            .format(partition_num=self.partition_num,
-                    replica_factor=self.replica_factor))
-        self.check_resp_succeeded(resp)
-
-        time.sleep(self.delay)
-        resp = self.execute('USE nbaLookup')
-        self.check_resp_succeeded(resp)
-        resp = self.execute("CREATE EDGE like(likeness int)")
-        self.check_resp_succeeded(resp)
-        resp = self.execute("CREATE EDGE serve(start_year int, end_year int)")
-        self.check_resp_succeeded(resp)
-        time.sleep(self.delay)
-        resp = self.execute("CREATE EDGE INDEX serve_index_1 on serve(start_year)")
-        self.check_resp_succeeded(resp)
-        resp = self.execute("CREATE EDGE INDEX serve_index_2 on serve(end_year)")
-        self.check_resp_succeeded(resp)
-        resp = self.execute("CREATE EDGE INDEX serve_index_3 on serve(start_year,end_year)")
-        self.check_resp_succeeded(resp)
-        resp = self.execute("CREATE EDGE INDEX like_index_1 on like(likeness)")
-        self.check_resp_succeeded(resp)
-        time.sleep(self.delay)
-        resp = self.execute('INSERT EDGE like(likeness) VALUES "100" -> "101":(95)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE like(likeness) VALUES "101" -> "102":(95)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE like(likeness) VALUES "102" -> "104":(85)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE like(likeness) VALUES "102" -> "103":(85)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE like(likeness) VALUES "105" -> "106":(90)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE like(likeness) VALUES "106" -> "100":(75)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE serve(start_year, end_year) VALUES "100" -> "200":(1997, 2016)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE serve(start_year, end_year) VALUES "101" -> "201":(1999, 2018)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE serve(start_year, end_year) VALUES "102" -> "202":(1997, 2016)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE serve(start_year, end_year) VALUES "103" -> "203":(1999, 2018)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE serve(start_year, end_year) VALUES "105" -> "204":(1997, 2016)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT EDGE serve(start_year, end_year) VALUES "121" -> "201":(1999, 2018)')
-        self.check_resp_succeeded(resp)
-
-        resp = self.execute("CREATE TAG player (name FIXED_STRING(30), age INT)")
-        self.check_resp_succeeded(resp)
-        resp = self.execute("CREATE TAG team (name FIXED_STRING(30))")
-        self.check_resp_succeeded(resp)
-        time.sleep(self.delay)
-        resp = self.execute("CREATE TAG INDEX player_index_1 on player(name)")
-        self.check_resp_succeeded(resp)
-        resp = self.execute("CREATE TAG INDEX player_index_2 on player(age)")
-        self.check_resp_succeeded(resp)
-        resp = self.execute("CREATE TAG INDEX player_index_3 on player(name,age)")
-        self.check_resp_succeeded(resp)
-        resp = self.execute("CREATE TAG INDEX team_index_1 on team(name)")
-        self.check_resp_succeeded(resp)
-        time.sleep(self.delay)
-        resp = self.execute('INSERT VERTEX player(name, age) VALUES "100":("Tim Duncan", 42)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX player(name, age) VALUES "101":("Tony Parker", 36)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX player(name, age) VALUES "102":("LaMarcus Aldridge", 33)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX player(name, age) VALUES "103":("xxx", 35)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX player(name, age) VALUES "104":("yyy", 28)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX player(name, age) VALUES "105":("zzz", 21)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX player(name, age) VALUES "106":("kkk", 21)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX player(name, age) VALUES "121":("Useless", 60)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX player(name, age) VALUES "121":("Useless", 20)')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX team(name) VALUES "200":("Warriors")')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX team(name) VALUES "201":("Nuggets")')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX team(name) VALUES "202":("oopp")')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX team(name) VALUES "203":("iiiooo")')
-        self.check_resp_succeeded(resp)
-        resp = self.execute('INSERT VERTEX team(name) VALUES "204":("opl")')
-        self.check_resp_succeeded(resp)
-
-    def test_edge_index(self):
-        resp = self.execute('LOOKUP ON serve where serve.start_year > 0')
-        self.check_resp_succeeded(resp)
-        col_names = ['SrcVID', 'DstVID', 'Ranking']
-        self.check_column_names(resp, col_names)
-        expected_result = [['100', '200', 0],
-                           ['101', '201', 0],
-                           ['102', '202', 0],
-                           ['103', '203', 0],
-                           ['105', '204', 0],
-                           ['121', '201', 0]]
-        self.check_out_of_order_result(resp, expected_result)
-
-        resp = self.execute('LOOKUP ON serve where serve.start_year > 1997 and serve.end_year < 2020')
-        self.check_resp_succeeded(resp)
-        col_names = ['SrcVID', 'DstVID', 'Ranking']
-        self.check_column_names(resp, col_names)
-        expected_result = [['101', '201', 0],
-                           ['103', '203', 0],
-                           ['121', '201', 0]]
-        self.check_out_of_order_result(resp, expected_result)
-
-        resp = self.execute('LOOKUP ON serve where serve.start_year > 2000 and serve.end_year < 2020')
-        self.check_resp_succeeded(resp)
-        col_names = ['SrcVID', 'DstVID', 'Ranking']
-        self.check_column_names(resp, col_names)
-        self.check_empty_result(resp)
-
-        resp = self.execute('LOOKUP ON like where like.likeness > 89')
-        self.check_resp_succeeded(resp)
-        col_names = ['SrcVID', 'DstVID', 'Ranking']
-        self.check_column_names(resp, col_names)
-        expected_result = [['100', '101', 0],
-                           ['101', '102', 0],
-                           ['105', '106', 0]]
-        self.check_out_of_order_result(resp, expected_result)
-
-        resp = self.execute('LOOKUP ON like where like.likeness < 39')
-        self.check_resp_succeeded(resp)
-        col_names = ['SrcVID', 'DstVID', 'Ranking']
-        self.check_column_names(resp, col_names)
-        self.check_empty_result(resp)
-
-    def test_tag_index(self):
-        resp = self.execute('LOOKUP ON player where player.age == 35')
-        self.check_resp_succeeded(resp)
-        col_names = ['VertexID']
-        self.check_column_names(resp, col_names)
-        expected_result = [['103']]
-        self.check_out_of_order_result(resp, expected_result)
-
-        resp = self.execute('LOOKUP ON player where player.age > 0')
-        self.check_resp_succeeded(resp)
-        col_names = ['VertexID']
-        self.check_column_names(resp, col_names)
-        expected_result = [['100'],
-                           ['101'],
-                           ['102'],
-                           ['103'],
-                           ['104'],
-                           ['105'],
-                           ['106'],
-                           ['121']]
-        self.check_out_of_order_result(resp, expected_result)
-
-        resp = self.execute('LOOKUP ON player where player.age < 100')
-        self.check_resp_succeeded(resp)
-        col_names = ['VertexID']
-        self.check_column_names(resp, col_names)
-        expected_result = [['100'],
-                           ['101'],
-                           ['102'],
-                           ['103'],
-                           ['104'],
-                           ['105'],
-                           ['106'],
-                           ['121']]
-        self.check_out_of_order_result(resp, expected_result)
-
-        resp = self.execute('LOOKUP ON player where player.name == "Useless"')
-        self.check_resp_succeeded(resp)
-        col_names = ['VertexID']
-        self.check_column_names(resp, col_names)
-        expected_result = [['121']]
-        self.check_out_of_order_result(resp, expected_result)
-
-        resp = self.execute('LOOKUP ON player where player.name == "Useless" and player.age < 30')
-        self.check_resp_succeeded(resp)
-        col_names = ['VertexID']
-        self.check_column_names(resp, col_names)
-        expected_result = [['121']]
-        self.check_out_of_order_result(resp, expected_result)
-
-        resp = self.execute('LOOKUP ON team where team.name == "Warriors"')
-        self.check_resp_succeeded(resp)
-        col_names = ['VertexID']
-        self.check_column_names(resp, col_names)
-        expected_result = [['200']]
-        self.check_out_of_order_result(resp, expected_result)
-
-        resp = self.execute('LOOKUP ON team where team.name == "oopp"')
-        self.check_resp_succeeded(resp)
-        col_names = ['VertexID']
-        self.check_column_names(resp, col_names)
-        expected_result = [['202']]
-        self.check_out_of_order_result(resp, expected_result)
-
-    @classmethod
-    def cleanup(self):
-        resp = self.execute('drop space nbaLookup')
-        self.check_resp_succeeded(resp)
diff --git a/tests/query/stateless/test_range.py b/tests/query/stateless/test_range.py
index 3d11d52891a9f6529514c4dd41c52bfe8730af64..dd1f815267b5897a912138f3ccb04c99c381967e 100644
--- a/tests/query/stateless/test_range.py
+++ b/tests/query/stateless/test_range.py
@@ -5,8 +5,6 @@
 # This source code is licensed under Apache 2.0 License,
 # attached with Common Clause Condition 1.0, found in the LICENSES directory.
 
-import re
-import sys
 import time
 import string
 import random
@@ -17,6 +15,7 @@ from nebula2.graph import ttypes
 
 from tests.common.nebula_test_suite import NebulaTestSuite
 
+
 class TestRangeChecking(NebulaTestSuite):
     @classmethod
     def prepare(self):
@@ -32,17 +31,20 @@ class TestRangeChecking(NebulaTestSuite):
 
         resp = self.execute('CREATE TAG test(id int)')
         self.check_resp_succeeded(resp)
-        time.sleep(self.delay)
+        time.sleep(self.delay+1)
+
+    def gen_name(self, length: int) -> str:
+        return ''.join(random.choice(string.ascii_letters) for i in range(length))
 
     @pytest.mark.parametrize('length', [1, 2048, 4096])
     def test_label_length_valid(self, length):
-        query = 'CREATE TAG {}(id int)'.format(''.join(random.choice(string.ascii_letters) for i in range(length)))
+        query = 'CREATE TAG {}(id int)'.format(self.gen_name(length))
         resp = self.execute(query)
         self.check_resp_succeeded(resp)
 
     @pytest.mark.parametrize('length', [4097, 4444, 10240])
     def test_label_length_invalid(self, length):
-        query = 'CREATE TAG {}(id int)'.format(''.join(random.choice(string.ascii_letters) for i in range(length)))
+        query = 'CREATE TAG {}(id int)'.format(self.gen_name(length))
         resp = self.execute(query)
         self.check_resp_failed(resp, ttypes.ErrorCode.E_SYNTAX_ERROR)
 
diff --git a/tests/tck/conftest.py b/tests/tck/conftest.py
index aaf1337a00a29c93cc37ebda9e029b57c589918f..f83bc6f7da5b0bb7c05130cf03c2a0ab467f162e 100644
--- a/tests/tck/conftest.py
+++ b/tests/tck/conftest.py
@@ -401,8 +401,8 @@ def execution_should_be_succ(graph_spaces):
     check_resp(rs, stmt)
 
 
-@then(rparse(r"a (?P<err_type>\w+) should be raised at (?P<time>runtime|compile time)(?P<sym>:|.)(?P<msg>.*)"))
-def raised_type_error(err_type, time, sym, msg, graph_spaces):
+@then(rparse(r"(?P<unit>a|an) (?P<err_type>\w+) should be raised at (?P<time>runtime|compile time)(?P<sym>:|.)(?P<msg>.*)"))
+def raised_type_error(unit, err_type, time, sym, msg, graph_spaces):
     res = graph_spaces["result_set"]
     ngql = graph_spaces['ngql']
     assert not res.is_succeeded(), f"Response should be failed: ngql:{ngql}"
diff --git a/tests/tck/features/index/TagEdgeIndex.feature b/tests/tck/features/index/TagEdgeIndex.feature
new file mode 100644
index 0000000000000000000000000000000000000000..f49f0ed015f816e7f3c3baed31d7677ce6e3a599
--- /dev/null
+++ b/tests/tck/features/index/TagEdgeIndex.feature
@@ -0,0 +1,504 @@
+Feature: tag and edge index tests from pytest
+
+  Background:
+    Given an empty graph
+    And create a space with following options:
+      | partition_num  | 9                |
+      | replica_factor | 1                |
+      | vid_type       | FIXED_STRING(30) |
+      | charset        | utf8             |
+      | collate        | utf8_bin         |
+    And having executed:
+      """
+      CREATE TAG tag_1(col1 string, col2 int, col3 double, col4 timestamp);
+      CREATE EDGE edge_1(col1 string, col2 int, col3 double, col4 timestamp);
+      INSERT VERTEX
+        tag_1(col1, col2, col3, col4)
+      VALUES
+        '101':('Tom', 18, 35.4, `timestamp`('2010-09-01T08:00:00')),
+        '102':('Jerry', 22, 38.4, `timestamp`('2011-09-01T08:00:00')),
+        '103':('Bob', 19, 36.4, `timestamp`('2010-09-01T12:00:00'));
+      INSERT EDGE
+        edge_1(col1, col2, col3, col4)
+      VALUES
+        '101'->'102':('Red', 81, 45.3, `timestamp`('2010-09-01T08:00:00')),
+        '102'->'103':('Yellow', 22, 423.8, `timestamp`('2011-09-01T08:00:00')),
+        '103'->'101':('Blue', 91, 43.1, `timestamp`('2010-09-01T12:00:00'));
+      """
+
+  Scenario: test tag index from pytest
+    # Single Tag Single Field
+    When executing query:
+      """
+      CREATE TAG INDEX single_tag_index ON tag_1(col2);
+      """
+    Then the execution should be successful
+    # Duplicate Index
+    When executing query:
+      """
+      CREATE TAG INDEX duplicate_tag_index_1 ON tag_1(col2)
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Tag not exist
+    When executing query:
+      """
+      CREATE TAG INDEX single_person_index ON student(name)
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Property not exist
+    When executing query:
+      """
+      CREATE TAG INDEX single_tag_index ON tag_1(col5)
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Property is empty
+    When executing query:
+      """
+      CREATE TAG INDEX single_tag_index ON tag_1()
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Single Tag Multi Field
+    When executing query:
+      """
+      CREATE TAG INDEX multi_tag_index ON tag_1(col2, col3)
+      """
+    Then the execution should be successful
+    # Duplicate Index
+    When executing query:
+      """
+      CREATE TAG INDEX duplicate_person_index ON tag_1(col2, col3)
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Duplicate Field
+    When executing query:
+      """
+      CREATE TAG INDEX duplicate_index ON tag_1(col2, col2)
+      """
+    Then an ExecutionError should be raised at runtime.
+    When executing query:
+      """
+      CREATE TAG INDEX disorder_tag_index ON tag_1(col3, col2)
+      """
+    Then the execution should be successful
+    And wait 3 seconds
+    When submit a job:
+      """
+      REBUILD TAG INDEX single_tag_index
+      """
+    Then wait the job to finish
+    When submit a job:
+      """
+      REBUILD TAG INDEX multi_tag_index
+      """
+    Then wait the job to finish
+    When submit a job:
+      """
+      REBUILD TAG INDEX disorder_tag_index
+      """
+    Then wait the job to finish
+    When executing query:
+      """
+      REBUILD TAG INDEX non_existent_tag_index
+      """
+    Then a SemanticError should be raised at runtime.
+    When executing query:
+      """
+      SHOW TAG INDEX STATUS
+      """
+    Then the result should contain:
+      | Name                 | Index Status |
+      | 'single_tag_index'   | 'FINISHED'   |
+      | 'multi_tag_index'    | 'FINISHED'   |
+      | 'disorder_tag_index' | 'FINISHED'   |
+    When executing query:
+      """
+      LOOKUP ON tag_1 WHERE tag_1.col2 == 18 YIELD tag_1.col1
+      """
+    Then the result should be, in any order:
+      | VertexID | tag_1.col1 |
+      | '101'    | 'Tom'      |
+    When executing query:
+      """
+      LOOKUP ON tag_1 WHERE tag_1.col3 > 35.7 YIELD tag_1.col1
+      """
+    Then the result should be, in any order:
+      | VertexID | tag_1.col1 |
+      | '102'    | 'Jerry'    |
+      | '103'    | 'Bob'      |
+    When executing query:
+      """
+      LOOKUP ON tag_1 WHERE tag_1.col2 > 18 AND tag_1.col3 < 37.2 YIELD tag_1.col1
+      """
+    Then the result should be, in any order:
+      | VertexID | tag_1.col1 |
+      | '103'    | 'Bob'      |
+    When executing query:
+      """
+      DESC TAG INDEX single_tag_index
+      """
+    Then the result should be, in any order:
+      | Field  | Type    |
+      | 'col2' | 'int64' |
+    When executing query:
+      """
+      DESC TAG INDEX multi_tag_index
+      """
+    Then the result should be, in any order:
+      | Field  | Type     |
+      | 'col2' | 'int64'  |
+      | 'col3' | 'double' |
+    When executing query:
+      """
+      DESC TAG INDEX disorder_tag_index
+      """
+    Then the result should be, in any order:
+      | Field  | Type     |
+      | 'col2' | 'int64'  |
+      | 'col3' | 'double' |
+    When executing query:
+      """
+      DESC TAG INDEX non_existent_tag_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    When executing query:
+      """
+      SHOW CREATE TAG INDEX single_tag_index
+      """
+    Then the result should be, in any order:
+      | Tag Index Name     | Create Tag Index                                               |
+      | 'single_tag_index' | 'CREATE TAG INDEX `single_tag_index` ON `tag_1` (\n `col2`\n)' |
+    When executing query:
+      """
+      SHOW CREATE TAG INDEX multi_tag_index
+      """
+    Then the result should be, in any order:
+      | Tag Index Name    | Create Tag Index                                                        |
+      | 'multi_tag_index' | 'CREATE TAG INDEX `multi_tag_index` ON `tag_1` (\n `col2`,\n `col3`\n)' |
+    When executing query:
+      """
+      DROP TAG INDEX multi_tag_index
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      CREATE TAG INDEX `multi_tag_index` ON `tag_1` (
+       `col2`,
+       `col3`
+      )
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      SHOW CREATE TAG INDEX disorder_tag_index
+      """
+    Then the result should be, in any order:
+      | Tag Index Name       | Create Tag Index                                                           |
+      | 'disorder_tag_index' | 'CREATE TAG INDEX `disorder_tag_index` ON `tag_1` (\n `col3`,\n `col2`\n)' |
+    When executing query:
+      """
+      DROP TAG INDEX disorder_tag_index
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      CREATE TAG INDEX `disorder_tag_index` ON `tag_1` (
+       `col3`,
+       `col2`
+      )
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      SHOW CREATE TAG INDEX non_existent_tag_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    When executing query:
+      """
+      SHOW TAG INDEXES
+      """
+    Then the result should contain:
+      | Names                |
+      | 'single_tag_index'   |
+      | 'multi_tag_index'    |
+      | 'disorder_tag_index' |
+    When executing query:
+      """
+      DROP TAG INDEX single_tag_index
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      DESC TAG INDEX single_tag_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    When executing query:
+      """
+      DROP TAG INDEX multi_tag_index
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      DESC TAG INDEX multi_tag_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    When executing query:
+      """
+      DROP TAG INDEX disorder_tag_index
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      DESC TAG INDEX disorder_tag_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    When executing query:
+      """
+      DROP TAG INDEX non_existent_tag_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    And drop the used space
+
+  Scenario: test edge index from pytest
+    # Single Tag Single Field
+    When executing query:
+      """
+      CREATE EDGE INDEX single_edge_index ON edge_1(col2)
+      """
+    Then the execution should be successful
+    # Duplicate Index
+    When executing query:
+      """
+      CREATE EDGE INDEX duplicate_edge_index ON edge_1(col2)
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Edge not exist
+    When executing query:
+      """
+      CREATE EDGE INDEX single_edge_index ON edge_1_ship(name)
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Property not exist
+    When executing query:
+      """
+      CREATE EDGE INDEX single_edge_index ON edge_1(startTime)
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Property is empty
+    When executing query:
+      """
+      CREATE EDGE INDEX single_edge_index ON edge_1()
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Single Edge Multi Field
+    When executing query:
+      """
+      CREATE EDGE INDEX multi_edge_index ON edge_1(col2, col3)
+      """
+    Then the execution should be successful
+    # Duplicate Index
+    When executing query:
+      """
+      CREATE EDGE INDEX duplicate_edge_index ON edge_1(col2, col3)
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Duplicate Field
+    When executing query:
+      """
+      CREATE EDGE INDEX duplicate_index ON edge_1(col2, col2)
+      """
+    Then an ExecutionError should be raised at runtime.
+    When executing query:
+      """
+      CREATE EDGE INDEX disorder_edge_index ON edge_1(col3, col2)
+      """
+    Then the execution should be successful
+    And wait 3 seconds
+    # Rebuild Edge Index
+    When submit a job:
+      """
+      REBUILD EDGE INDEX single_edge_index
+      """
+    Then wait the job to finish
+    When submit a job:
+      """
+      REBUILD EDGE INDEX multi_edge_index
+      """
+    Then wait the job to finish
+    When submit a job:
+      """
+      REBUILD EDGE INDEX disorder_edge_index
+      """
+    Then wait the job to finish
+    When executing query:
+      """
+      REBUILD EDGE INDEX non_existent_edge_index
+      """
+    Then a SemanticError should be raised at runtime.
+    When executing query:
+      """
+      SHOW EDGE INDEX STATUS
+      """
+    Then the result should contain:
+      | Name                  | Index Status |
+      | 'single_edge_index'   | 'FINISHED'   |
+      | 'multi_edge_index'    | 'FINISHED'   |
+      | 'disorder_edge_index' | 'FINISHED'   |
+    # Lookup
+    When executing query:
+      """
+      LOOKUP ON edge_1 WHERE edge_1.col2 == 22 YIELD edge_1.col2
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking | edge_1.col2 |
+      | '102'  | '103'  | 0       | 22          |
+    When executing query:
+      """
+      LOOKUP ON edge_1 WHERE edge_1.col3 > 43.4 YIELD edge_1.col1
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking | edge_1.col1 |
+      | '102'  | '103'  | 0       | 'Yellow'    |
+      | '101'  | '102'  | 0       | 'Red'       |
+    When executing query:
+      """
+      LOOKUP ON edge_1 WHERE edge_1.col2 > 45 AND edge_1.col3 < 44.3 YIELD edge_1.col1
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking | edge_1.col1 |
+      | '103'  | '101'  | 0       | 'Blue'      |
+    # Describe Edge Index
+    When executing query:
+      """
+      DESC EDGE INDEX single_edge_index
+      """
+    Then the result should be, in any order:
+      | Field  | Type    |
+      | 'col2' | 'int64' |
+    When executing query:
+      """
+      DESC EDGE INDEX multi_edge_index
+      """
+    Then the result should be, in any order:
+      | Field  | Type     |
+      | 'col2' | 'int64'  |
+      | 'col3' | 'double' |
+    When executing query:
+      """
+      DESC EDGE INDEX disorder_edge_index
+      """
+    Then the result should be, in any order:
+      | Field  | Type     |
+      | 'col2' | 'int64'  |
+      | 'col3' | 'double' |
+    When executing query:
+      """
+      DESC EDGE INDEX non_existent_edge_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Show Create Edge Index
+    When executing query:
+      """
+      SHOW CREATE EDGE INDEX single_edge_index
+      """
+    Then the result should be, in any order:
+      | Edge Index Name     | Create Edge Index                                                 |
+      | 'single_edge_index' | 'CREATE EDGE INDEX `single_edge_index` ON `edge_1` (\n `col2`\n)' |
+    When executing query:
+      """
+      SHOW CREATE EDGE INDEX multi_edge_index
+      """
+    Then the result should be, in any order:
+      | Edge Index Name    | Create Edge Index                                                          |
+      | 'multi_edge_index' | 'CREATE EDGE INDEX `multi_edge_index` ON `edge_1` (\n `col2`,\n `col3`\n)' |
+    # Check if show create edge index works well
+    When executing query:
+      """
+      DROP EDGE INDEX multi_edge_index
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      CREATE EDGE INDEX `multi_edge_index` ON `edge_1` (
+       `col2`,
+       `col3`
+      )
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      SHOW CREATE EDGE INDEX disorder_edge_index
+      """
+    Then the result should be, in any order:
+      | Edge Index Name       | Create Edge Index                                                             |
+      | 'disorder_edge_index' | 'CREATE EDGE INDEX `disorder_edge_index` ON `edge_1` (\n `col3`,\n `col2`\n)' |
+    # Check if show create edge index works well
+    When executing query:
+      """
+      DROP EDGE INDEX disorder_edge_index
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      CREATE EDGE INDEX `disorder_edge_index` ON `edge_1` (
+       `col3`,
+       `col2`
+      )
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      SHOW CREATE EDGE INDEX non_existent_edge_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Show Edge Indexes
+    When executing query:
+      """
+      SHOW EDGE INDEXES
+      """
+    Then the result should contain:
+      | Names                 |
+      | 'single_edge_index'   |
+      | 'multi_edge_index'    |
+      | 'disorder_edge_index' |
+    # Drop Edge Index
+    When executing query:
+      """
+      DROP EDGE INDEX single_edge_index
+      """
+    Then the execution should be successful
+    # Check if the index is truly dropped
+    When executing query:
+      """
+      DESC EDGE INDEX single_edge_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Drop Edge Index
+    When executing query:
+      """
+      DROP EDGE INDEX multi_edge_index
+      """
+    Then the execution should be successful
+    # Check if the index is truly dropped
+    When executing query:
+      """
+      DESC EDGE INDEX multi_edge_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    # Drop Edge Index
+    When executing query:
+      """
+      DROP EDGE INDEX disorder_edge_index
+      """
+    Then the execution should be successful
+    # Check if the index is truly dropped
+    When executing query:
+      """
+      DESC EDGE INDEX disorder_edge_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    When executing query:
+      """
+      DROP EDGE INDEX non_existent_edge_index
+      """
+    Then an ExecutionError should be raised at runtime.
+    And drop the used space
diff --git a/tests/tck/features/lookup/LookUp.feature b/tests/tck/features/lookup/LookUp.feature
index c140c4192468e162d92dd67e7d085d76b907c91f..4d6bc852bc7dec58677aa9e14d569bf013672e0e 100644
--- a/tests/tck/features/lookup/LookUp.feature
+++ b/tests/tck/features/lookup/LookUp.feature
@@ -875,3 +875,168 @@ Feature: LookUpTest_Vid_String
     Then the result should be, in any order:
       | VertexID |
     Then drop the used space
+
+  Scenario: LookupTest from pytest
+    Given having executed:
+      """
+      CREATE EDGE like(likeness int);
+      CREATE EDGE serve(start_year int, end_year int);
+      CREATE EDGE INDEX serve_index_1 on serve(start_year);
+      CREATE EDGE INDEX serve_index_2 on serve(end_year);
+      CREATE EDGE INDEX serve_index_3 on serve(start_year,end_year);
+      CREATE EDGE INDEX like_index_1 on like(likeness);
+      CREATE TAG player (name FIXED_STRING(30), age INT);
+      CREATE TAG team (name FIXED_STRING(30));
+      CREATE TAG INDEX player_index_1 on player(name);
+      CREATE TAG INDEX player_index_2 on player(age);
+      CREATE TAG INDEX player_index_3 on player(name,age);
+      CREATE TAG INDEX team_index_1 on team(name);
+      """
+    And wait 4 seconds
+    And having executed:
+      """
+      INSERT EDGE
+        like(likeness)
+      VALUES
+        "100" -> "101":(95),
+        "101" -> "102":(95),
+        "102" -> "104":(85),
+        "102" -> "103":(85),
+        "105" -> "106":(90),
+        "106" -> "100":(75);
+      INSERT EDGE
+        serve(start_year, end_year)
+      VALUES
+        "100" -> "200":(1997, 2016),
+        "101" -> "201":(1999, 2018),
+        "102" -> "202":(1997, 2016),
+        "103" -> "203":(1999, 2018),
+        "105" -> "204":(1997, 2016),
+        "121" -> "201":(1999, 2018);
+      INSERT VERTEX
+        player(name, age)
+      VALUES
+        "100":("Tim Duncan", 42),
+        "101":("Tony Parker", 36),
+        "102":("LaMarcus Aldridge", 33),
+        "103":("xxx", 35),
+        "104":("yyy", 28),
+        "105":("zzz", 21),
+        "106":("kkk", 21),
+        "121":("Useless", 60),
+        "121":("Useless", 20);
+        INSERT VERTEX
+          team(name)
+        VALUES
+          "200":("Warriors"),
+          "201":("Nuggets"),
+          "202":("oopp"),
+          "203":("iiiooo"),
+          "204":("opl");
+      """
+    When executing query:
+      """
+      LOOKUP ON serve where serve.start_year > 0
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking |
+      | '100'  | '200'  | 0       |
+      | '101'  | '201'  | 0       |
+      | '102'  | '202'  | 0       |
+      | '103'  | '203'  | 0       |
+      | '105'  | '204'  | 0       |
+      | '121'  | '201'  | 0       |
+    When executing query:
+      """
+      LOOKUP ON serve where serve.start_year > 1997 and serve.end_year < 2020
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking |
+      | '101'  | '201'  | 0       |
+      | '103'  | '203'  | 0       |
+      | '121'  | '201'  | 0       |
+    When executing query:
+      """
+      LOOKUP ON serve where serve.start_year > 2000 and serve.end_year < 2020
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking |
+    When executing query:
+      """
+      LOOKUP ON like where like.likeness > 89
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking |
+      | '100'  | '101'  | 0       |
+      | '101'  | '102'  | 0       |
+      | '105'  | '106'  | 0       |
+    When executing query:
+      """
+      LOOKUP ON like where like.likeness < 39
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking |
+    When executing query:
+      """
+      LOOKUP ON player where player.age == 35
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | '103'    |
+    When executing query:
+      """
+      LOOKUP ON player where player.age > 0
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | '100'    |
+      | '101'    |
+      | '102'    |
+      | '103'    |
+      | '104'    |
+      | '105'    |
+      | '106'    |
+      | '121'    |
+    When executing query:
+      """
+      LOOKUP ON player where player.age < 100
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | '100'    |
+      | '101'    |
+      | '102'    |
+      | '103'    |
+      | '104'    |
+      | '105'    |
+      | '106'    |
+      | '121'    |
+    When executing query:
+      """
+      LOOKUP ON player where player.name == "Useless"
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | '121'    |
+    When executing query:
+      """
+      LOOKUP ON player where player.name == "Useless" and player.age < 30
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | '121'    |
+    When executing query:
+      """
+      LOOKUP ON team where team.name == "Warriors"
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | '200'    |
+    When executing query:
+      """
+      LOOKUP ON team where team.name == "oopp"
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | '202'    |
+    Then drop the used space