This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git


The following commit(s) were added to refs/heads/main by this push:
     new 87410b9d Add property test cases and related QL (#808)
87410b9d is described below

commit 87410b9da41b0ecb4ceedf74dcbd8f947afdafd8
Author: mrproliu <[email protected]>
AuthorDate: Fri Oct 17 10:36:36 2025 +0800

    Add property test cases and related QL (#808)
---
 pkg/test/property/etcd.go                          |  97 ++++++++++++++
 pkg/test/property/testdata/group.json              |  13 ++
 pkg/test/property/testdata/properties/sw.json      |  16 +++
 pkg/test/setup/setup.go                            |   8 ++
 test/cases/init.go                                 |   4 +
 test/cases/property/data/data.go                   | 141 +++++++++++++++++++++
 test/cases/property/data/input/all.ql              |  19 +++
 test/cases/property/data/input/all.yaml            |  23 ++++
 test/cases/property/data/input/limit.ql            |  20 +++
 test/cases/property/data/input/limit.yaml          |  24 ++++
 .../cases/property/data/input/query_by_criteria.ql |  20 +++
 .../property/data/input/query_by_criteria.yaml     |  30 +++++
 test/cases/property/data/input/query_by_ids.ql     |  20 +++
 test/cases/property/data/input/query_by_ids.yaml   |  25 ++++
 test/cases/property/data/testdata/sw1.json         |  32 +++++
 test/cases/property/data/testdata/sw2.json         |  32 +++++
 test/cases/property/data/want/all.yaml             |  52 ++++++++
 test/cases/property/data/want/limit.yaml           |  35 +++++
 .../property/data/want/query_by_criteria.yaml      |  36 ++++++
 test/cases/property/data/want/query_by_ids.yaml    |  35 +++++
 test/cases/property/property.go                    |  45 +++++++
 test/integration/distributed/backup/common.go      |   3 +
 .../distributed/lifecycle/lifecycle_suite_test.go  |   2 +
 .../multi_segments/multi_segments_suite_test.go    |   7 +
 .../distributed/query/query_suite_test.go          |   7 +
 .../standalone/query/query_suite_test.go           |   5 +
 26 files changed, 751 insertions(+)

diff --git a/pkg/test/property/etcd.go b/pkg/test/property/etcd.go
new file mode 100644
index 00000000..c6bbfa35
--- /dev/null
+++ b/pkg/test/property/etcd.go
@@ -0,0 +1,97 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Package property implements helpers to load schemas for testing.
+package property
+
+import (
+       "context"
+       "embed"
+       "encoding/json"
+       "errors"
+       "path"
+
+       "google.golang.org/protobuf/encoding/protojson"
+
+       commonv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
+       databasev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
+       "github.com/apache/skywalking-banyandb/banyand/metadata/schema"
+       "github.com/apache/skywalking-banyandb/pkg/logger"
+)
+
+const (
+       propertiesDir = "testdata/properties"
+)
+
+var (
+       //go:embed testdata/properties/*.json
+       propertyStore embed.FS
+       //go:embed testdata/group.json
+       groupJSON string
+)
+
+// loadSchemas loads streams, index rules, and index rule bindings.
+func loadSchemas(ctx context.Context, e schema.Registry) error {
+       properties, err := propertyStore.ReadDir(propertiesDir)
+       if err != nil {
+               return err
+       }
+       var data []byte
+       for _, entry := range properties {
+               data, err = propertyStore.ReadFile(path.Join(propertiesDir, 
entry.Name()))
+               if err != nil {
+                       return err
+               }
+               var property databasev1.Property
+               err = protojson.Unmarshal(data, &property)
+               if err != nil {
+                       return err
+               }
+               if innerErr := e.CreateProperty(ctx, &property); innerErr != 
nil {
+                       return innerErr
+               }
+       }
+       return nil
+}
+
+// PreloadSchema loads schemas from files in the booting process.
+// This version loads group without stages.
+func PreloadSchema(ctx context.Context, e schema.Registry) error {
+       if e == nil {
+               return nil
+       }
+       var rawGroups []json.RawMessage
+       if err := json.Unmarshal([]byte(groupJSON), &rawGroups); err != nil {
+               return err
+       }
+       for _, raw := range rawGroups {
+               g := &commonv1.Group{}
+               if err := protojson.Unmarshal(raw, g); err != nil {
+                       return err
+               }
+               _, err := e.GetGroup(ctx, g.Metadata.Name)
+               if !errors.Is(err, schema.ErrGRPCResourceNotFound) {
+                       logger.Infof("group %s already exists", g.Metadata.Name)
+                       return nil
+               }
+               if innerErr := e.CreateGroup(ctx, g); innerErr != nil {
+                       return innerErr
+               }
+       }
+
+       return loadSchemas(ctx, e)
+}
diff --git a/pkg/test/property/testdata/group.json 
b/pkg/test/property/testdata/group.json
new file mode 100644
index 00000000..8b1f2969
--- /dev/null
+++ b/pkg/test/property/testdata/group.json
@@ -0,0 +1,13 @@
+[
+  {
+    "metadata": {
+      "name": "sw"
+    },
+    "catalog": "CATALOG_PROPERTY",
+    "resource_opts": {
+      "shard_num": 2,
+      "replicas": 1
+    },
+    "updated_at": "2021-04-15T01:30:15.01Z"
+  }
+]
\ No newline at end of file
diff --git a/pkg/test/property/testdata/properties/sw.json 
b/pkg/test/property/testdata/properties/sw.json
new file mode 100644
index 00000000..b8738b94
--- /dev/null
+++ b/pkg/test/property/testdata/properties/sw.json
@@ -0,0 +1,16 @@
+{
+  "metadata" : {
+    "group" : "sw",
+    "name" : "ui_menu"
+  },
+  "tags" : [ {
+    "name" : "menu_name",
+    "type" : "TAG_TYPE_STRING"
+  }, {
+    "name" : "configuration",
+    "type" : "TAG_TYPE_STRING"
+  }, {
+    "name" : "update_time",
+    "type" : "TAG_TYPE_INT"
+  } ]
+}
\ No newline at end of file
diff --git a/pkg/test/setup/setup.go b/pkg/test/setup/setup.go
index 804a8cf6..3f7e9598 100644
--- a/pkg/test/setup/setup.go
+++ b/pkg/test/setup/setup.go
@@ -41,6 +41,7 @@ import (
        testflags "github.com/apache/skywalking-banyandb/pkg/test/flags"
        "github.com/apache/skywalking-banyandb/pkg/test/helpers"
        test_measure "github.com/apache/skywalking-banyandb/pkg/test/measure"
+       test_property "github.com/apache/skywalking-banyandb/pkg/test/property"
        test_stream "github.com/apache/skywalking-banyandb/pkg/test/stream"
        test_trace "github.com/apache/skywalking-banyandb/pkg/test/trace"
 )
@@ -53,6 +54,7 @@ func Standalone(flags ...string) (string, string, func()) {
                &preloadService{name: "stream"},
                &preloadService{name: "measure"},
                &preloadService{name: "trace"},
+               &preloadService{name: "property"},
        }, "", "", "", "", flags...)
 }
 
@@ -62,6 +64,7 @@ func StandaloneWithAuth(username, password string, flags 
...string) (string, str
                &preloadService{name: "stream"},
                &preloadService{name: "measure"},
                &preloadService{name: "trace"},
+               &preloadService{name: "property"},
        }, "", "", username, password, flags...)
 }
 
@@ -71,6 +74,7 @@ func StandaloneWithTLS(certFile, keyFile string, flags 
...string) (string, strin
                &preloadService{name: "stream"},
                &preloadService{name: "measure"},
                &preloadService{name: "trace"},
+               &preloadService{name: "property"},
        }, certFile, keyFile, "", "", flags...)
 }
 
@@ -104,6 +108,7 @@ func ClosableStandalone(path string, ports []int, flags 
...string) (string, stri
                &preloadService{name: "stream"},
                &preloadService{name: "measure"},
                &preloadService{name: "trace"},
+               &preloadService{name: "property"},
        }, "", "", flags...)
 }
 
@@ -211,6 +216,9 @@ func (p *preloadService) PreRun(ctx context.Context) error {
        if p.name == "trace" {
                return test_trace.PreloadSchema(ctx, p.registry)
        }
+       if p.name == "property" {
+               return test_property.PreloadSchema(ctx, p.registry)
+       }
        return test_measure.PreloadSchema(ctx, p.registry)
 }
 
diff --git a/test/cases/init.go b/test/cases/init.go
index fa6d96fb..d63c6628 100644
--- a/test/cases/init.go
+++ b/test/cases/init.go
@@ -27,6 +27,7 @@ import (
 
        "github.com/apache/skywalking-banyandb/pkg/grpchelper"
        casesmeasuredata 
"github.com/apache/skywalking-banyandb/test/cases/measure/data"
+       caseproperty 
"github.com/apache/skywalking-banyandb/test/cases/property/data"
        casesstreamdata 
"github.com/apache/skywalking-banyandb/test/cases/stream/data"
        casestrace "github.com/apache/skywalking-banyandb/test/cases/trace/data"
 )
@@ -68,4 +69,7 @@ func Initialize(addr string, now time.Time) {
        casestrace.WriteToGroup(conn, "sw", "test-trace-updated", "sw_updated", 
now.Add(time.Minute), interval)
        time.Sleep(5 * time.Second)
        casestrace.WriteToGroup(conn, "sw", "test-trace-group", 
"sw_mixed_traces", now.Add(time.Minute), interval)
+       // property
+       caseproperty.Write(conn, "sw1")
+       caseproperty.Write(conn, "sw2")
 }
diff --git a/test/cases/property/data/data.go b/test/cases/property/data/data.go
new file mode 100644
index 00000000..901ee391
--- /dev/null
+++ b/test/cases/property/data/data.go
@@ -0,0 +1,141 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Package data includes utilities for property test data management and 
verification.
+package data
+
+import (
+       "context"
+       "embed"
+       "fmt"
+       "slices"
+       "strings"
+
+       "github.com/google/go-cmp/cmp"
+       g "github.com/onsi/ginkgo/v2"
+       gm "github.com/onsi/gomega"
+       grpclib "google.golang.org/grpc"
+       "google.golang.org/protobuf/encoding/protojson"
+       "google.golang.org/protobuf/testing/protocmp"
+       "sigs.k8s.io/yaml"
+
+       commonv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
+       databasev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
+       propertyv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/property/v1"
+       "github.com/apache/skywalking-banyandb/pkg/test/helpers"
+)
+
+//go:embed input/*.yaml
+var inputFS embed.FS
+
+//go:embed want/*.yaml
+var wantFS embed.FS
+
+//go:embed testdata/*.json
+var dataFS embed.FS
+
+// VerifyFn verify whether the query response matches the wanted result.
+var VerifyFn = func(innerGm gm.Gomega, sharedContext helpers.SharedContext, 
args helpers.Args) {
+       ctx := context.Background()
+       verifyWithContext(ctx, innerGm, sharedContext, args)
+}
+
+func verifyWithContext(ctx context.Context, innerGm gm.Gomega, sharedContext 
helpers.SharedContext, args helpers.Args) {
+       i, err := inputFS.ReadFile("input/" + args.Input + ".yaml")
+       innerGm.Expect(err).NotTo(gm.HaveOccurred())
+       query := &propertyv1.QueryRequest{}
+       helpers.UnmarshalYAML(i, query)
+       c := propertyv1.NewPropertyServiceClient(sharedContext.Connection)
+       resp, err := c.Query(ctx, query)
+       innerGm.Expect(err).NotTo(gm.HaveOccurred())
+       if args.WantErr {
+               if err == nil {
+                       g.Fail("expected error")
+               }
+               return
+       }
+       innerGm.Expect(err).NotTo(gm.HaveOccurred(), query.String())
+       if args.WantEmpty {
+               innerGm.Expect(len(resp.GetProperties())).To(gm.BeEmpty(), 
query.String())
+               return
+       }
+       if args.Want == "" {
+               args.Want = args.Input
+       }
+       w, err := wantFS.ReadFile("want/" + args.Want + ".yaml")
+       innerGm.Expect(err).NotTo(gm.HaveOccurred())
+       want := &propertyv1.QueryResponse{}
+       helpers.UnmarshalYAML(w, want)
+       
innerGm.Expect(resp.GetProperties()).To(gm.HaveLen(len(want.GetProperties())), 
query.String())
+       slices.SortFunc(want.GetProperties(), func(a, b *propertyv1.Property) 
int {
+               return strings.Compare(a.Id, b.Id)
+       })
+       success := innerGm.Expect(cmp.Equal(resp, want,
+               protocmp.IgnoreUnknown(),
+               protocmp.IgnoreFields(&propertyv1.Property{}, "updated_at"),
+               protocmp.IgnoreFields(&commonv1.Metadata{}, "create_revision", 
"mod_revision"),
+               protocmp.Transform())).
+               To(gm.BeTrue(), func() string {
+                       var j []byte
+                       j, err = protojson.Marshal(resp)
+                       if err != nil {
+                               return err.Error()
+                       }
+                       var y []byte
+                       y, err = yaml.JSONToYAML(j)
+                       if err != nil {
+                               return err.Error()
+                       }
+                       return string(y)
+               })
+       if !success {
+               return
+       }
+       query.Trace = true
+       resp, err = c.Query(ctx, query)
+       innerGm.Expect(err).NotTo(gm.HaveOccurred())
+       innerGm.Expect(resp.Trace).NotTo(gm.BeNil())
+       innerGm.Expect(resp.Trace.GetSpans()).NotTo(gm.BeEmpty())
+}
+
+// Write writes a property with the given name to the "ui_menu" group and "sw" 
name.
+func Write(conn *grpclib.ClientConn, name string) {
+       WriteToGroup(conn, "ui_menu", "sw", name)
+}
+
+// WriteToGroup writes a property with the given name to the specified group 
and name.
+func WriteToGroup(conn *grpclib.ClientConn, name, group, fileName string) {
+       metadata := &commonv1.Metadata{
+               Name:  name,
+               Group: group,
+       }
+       schema := databasev1.NewPropertyRegistryServiceClient(conn)
+       resp, err := schema.Get(context.Background(), 
&databasev1.PropertyRegistryServiceGetRequest{Metadata: metadata})
+       gm.Expect(err).NotTo(gm.HaveOccurred())
+       metadata = resp.GetProperty().GetMetadata()
+
+       c := propertyv1.NewPropertyServiceClient(conn)
+       ctx := context.Background()
+       var request propertyv1.ApplyRequest
+       content, err := dataFS.ReadFile(fmt.Sprintf("testdata/%s.json", 
fileName))
+       gm.Expect(err).ShouldNot(gm.HaveOccurred())
+       gm.Expect(protojson.Unmarshal(content, 
&request)).ShouldNot(gm.HaveOccurred())
+       request.Property.Metadata = metadata
+
+       _, err = c.Apply(ctx, &request)
+       gm.Expect(err).ShouldNot(gm.HaveOccurred())
+}
diff --git a/test/cases/property/data/input/all.ql 
b/test/cases/property/data/input/all.ql
new file mode 100644
index 00000000..0d345253
--- /dev/null
+++ b/test/cases/property/data/input/all.ql
@@ -0,0 +1,19 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT menu_name, configuration, update_time FROM PROPERTY ui_menu IN sw
diff --git a/test/cases/property/data/input/all.yaml 
b/test/cases/property/data/input/all.yaml
new file mode 100644
index 00000000..545d2f4a
--- /dev/null
+++ b/test/cases/property/data/input/all.yaml
@@ -0,0 +1,23 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "ui_menu"
+groups: ["sw"]
+tag_projection:
+  - menu_name
+  - configuration
+  - update_time
\ No newline at end of file
diff --git a/test/cases/property/data/input/limit.ql 
b/test/cases/property/data/input/limit.ql
new file mode 100644
index 00000000..43b98d97
--- /dev/null
+++ b/test/cases/property/data/input/limit.ql
@@ -0,0 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT menu_name, configuration, update_time FROM PROPERTY ui_menu IN sw
+LIMIT 1
diff --git a/test/cases/property/data/input/limit.yaml 
b/test/cases/property/data/input/limit.yaml
new file mode 100644
index 00000000..004fc103
--- /dev/null
+++ b/test/cases/property/data/input/limit.yaml
@@ -0,0 +1,24 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "ui_menu"
+groups: ["sw"]
+tag_projection:
+  - menu_name
+  - configuration
+  - update_time
+limit: 1
\ No newline at end of file
diff --git a/test/cases/property/data/input/query_by_criteria.ql 
b/test/cases/property/data/input/query_by_criteria.ql
new file mode 100644
index 00000000..9f54f234
--- /dev/null
+++ b/test/cases/property/data/input/query_by_criteria.ql
@@ -0,0 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT menu_name, configuration, update_time FROM PROPERTY ui_menu IN sw
+WHERE menu_name = 'test1'
diff --git a/test/cases/property/data/input/query_by_criteria.yaml 
b/test/cases/property/data/input/query_by_criteria.yaml
new file mode 100644
index 00000000..757cf5c9
--- /dev/null
+++ b/test/cases/property/data/input/query_by_criteria.yaml
@@ -0,0 +1,30 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "ui_menu"
+groups: ["sw"]
+tagProjection:
+  - menu_name
+  - configuration
+  - update_time
+criteria:
+  condition:
+    name: "menu_name"
+    op: "BINARY_OP_EQ"
+    value:
+      str:
+        value: "test1"
\ No newline at end of file
diff --git a/test/cases/property/data/input/query_by_ids.ql 
b/test/cases/property/data/input/query_by_ids.ql
new file mode 100644
index 00000000..210bd92f
--- /dev/null
+++ b/test/cases/property/data/input/query_by_ids.ql
@@ -0,0 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT menu_name, configuration, update_time FROM PROPERTY ui_menu IN sw
+WHERE id = '2'
diff --git a/test/cases/property/data/input/query_by_ids.yaml 
b/test/cases/property/data/input/query_by_ids.yaml
new file mode 100644
index 00000000..0ce7197e
--- /dev/null
+++ b/test/cases/property/data/input/query_by_ids.yaml
@@ -0,0 +1,25 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "ui_menu"
+groups: ["sw"]
+ids:
+  - "2"
+tagProjection:
+  - menu_name
+  - configuration
+  - update_time
\ No newline at end of file
diff --git a/test/cases/property/data/testdata/sw1.json 
b/test/cases/property/data/testdata/sw1.json
new file mode 100644
index 00000000..0196c9f2
--- /dev/null
+++ b/test/cases/property/data/testdata/sw1.json
@@ -0,0 +1,32 @@
+{
+  "property" : {
+    "metadata" : {
+      "group" : "sw",
+      "name" : "ui_menu"
+    },
+    "id" : "1",
+    "tags" : [ {
+      "key" : "menu_name",
+      "value" : {
+        "str" : {
+          "value" : "test1"
+        }
+      }
+    }, {
+      "key" : "configuration",
+      "value" : {
+        "str" : {
+          "value" : "test1"
+        }
+      }
+    }, {
+      "key" : "update_time",
+      "value" : {
+        "int" : {
+          "value" : "1760606198934"
+        }
+      }
+    } ]
+  },
+  "strategy" : "STRATEGY_MERGE"
+}
\ No newline at end of file
diff --git a/test/cases/property/data/testdata/sw2.json 
b/test/cases/property/data/testdata/sw2.json
new file mode 100644
index 00000000..3822d7d0
--- /dev/null
+++ b/test/cases/property/data/testdata/sw2.json
@@ -0,0 +1,32 @@
+{
+  "property" : {
+    "metadata" : {
+      "group" : "sw",
+      "name" : "ui_menu"
+    },
+    "id" : "2",
+    "tags" : [ {
+      "key" : "menu_name",
+      "value" : {
+        "str" : {
+          "value" : "test2"
+        }
+      }
+    }, {
+      "key" : "configuration",
+      "value" : {
+        "str" : {
+          "value" : "test2"
+        }
+      }
+    }, {
+      "key" : "update_time",
+      "value" : {
+        "int" : {
+          "value" : "1760606208842"
+        }
+      }
+    } ]
+  },
+  "strategy" : "STRATEGY_MERGE"
+}
\ No newline at end of file
diff --git a/test/cases/property/data/want/all.yaml 
b/test/cases/property/data/want/all.yaml
new file mode 100644
index 00000000..1b43f19e
--- /dev/null
+++ b/test/cases/property/data/want/all.yaml
@@ -0,0 +1,52 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+properties:
+  - id: "1"
+    metadata:
+      group: sw
+      name: ui_menu
+    tags:
+      - key: menu_name
+        value:
+          str:
+            value: test1
+      - key: configuration
+        value:
+          str:
+            value: test1
+      - key: update_time
+        value:
+          int:
+            value: "1760606198934"
+  - id: "2"
+    metadata:
+      group: sw
+      name: ui_menu
+    tags:
+      - key: menu_name
+        value:
+          str:
+            value: test2
+      - key: configuration
+        value:
+          str:
+            value: test2
+      - key: update_time
+        value:
+          int:
+            value: "1760606208842"
diff --git a/test/cases/property/data/want/limit.yaml 
b/test/cases/property/data/want/limit.yaml
new file mode 100644
index 00000000..c654f02b
--- /dev/null
+++ b/test/cases/property/data/want/limit.yaml
@@ -0,0 +1,35 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+properties:
+  - id: "1"
+    metadata:
+      group: sw
+      name: ui_menu
+    tags:
+      - key: menu_name
+        value:
+          str:
+            value: test1
+      - key: configuration
+        value:
+          str:
+            value: test1
+      - key: update_time
+        value:
+          int:
+            value: "1760606198934"
\ No newline at end of file
diff --git a/test/cases/property/data/want/query_by_criteria.yaml 
b/test/cases/property/data/want/query_by_criteria.yaml
new file mode 100644
index 00000000..ea1e7651
--- /dev/null
+++ b/test/cases/property/data/want/query_by_criteria.yaml
@@ -0,0 +1,36 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+properties:
+  - id: "1"
+    metadata:
+      group: sw
+      name: ui_menu
+    tags:
+      - key: menu_name
+        value:
+          str:
+            value: test1
+      - key: configuration
+        value:
+          str:
+            value: test1
+      - key: update_time
+        value:
+          int:
+            value: "1760606198934"
+
diff --git a/test/cases/property/data/want/query_by_ids.yaml 
b/test/cases/property/data/want/query_by_ids.yaml
new file mode 100644
index 00000000..7996e304
--- /dev/null
+++ b/test/cases/property/data/want/query_by_ids.yaml
@@ -0,0 +1,35 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+properties:
+  - id: "2"
+    metadata:
+      group: sw
+      name: ui_menu
+    tags:
+      - key: menu_name
+        value:
+          str:
+            value: test2
+      - key: configuration
+        value:
+          str:
+            value: test2
+      - key: update_time
+        value:
+          int:
+            value: "1760606208842"
\ No newline at end of file
diff --git a/test/cases/property/property.go b/test/cases/property/property.go
new file mode 100644
index 00000000..41c72bbb
--- /dev/null
+++ b/test/cases/property/property.go
@@ -0,0 +1,45 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Package property includes test cases for property queries.
+package property
+
+import (
+       g "github.com/onsi/ginkgo/v2"
+       gm "github.com/onsi/gomega"
+
+       "github.com/apache/skywalking-banyandb/pkg/test/flags"
+       "github.com/apache/skywalking-banyandb/pkg/test/helpers"
+       propertyTestData 
"github.com/apache/skywalking-banyandb/test/cases/property/data"
+)
+
+var (
+       // SharedContext is the parallel execution context.
+       SharedContext helpers.SharedContext
+       verify        = func(args helpers.Args) {
+               gm.Eventually(func(innerGm gm.Gomega) {
+                       propertyTestData.VerifyFn(innerGm, SharedContext, args)
+               }, flags.EventuallyTimeout).Should(gm.Succeed())
+       }
+)
+
+var _ = g.DescribeTable("Scanning Properties", verify,
+       g.Entry("all", helpers.Args{Input: "all"}),
+       g.Entry("limit", helpers.Args{Input: "limit"}),
+       g.Entry("query by criteria", helpers.Args{Input: "query_by_criteria"}),
+       g.Entry("query by ids", helpers.Args{Input: "query_by_ids"}),
+)
diff --git a/test/integration/distributed/backup/common.go 
b/test/integration/distributed/backup/common.go
index b96b78e0..6029fdcd 100644
--- a/test/integration/distributed/backup/common.go
+++ b/test/integration/distributed/backup/common.go
@@ -46,6 +46,7 @@ import (
        "github.com/apache/skywalking-banyandb/pkg/test/gmatcher"
        "github.com/apache/skywalking-banyandb/pkg/test/helpers"
        test_measure "github.com/apache/skywalking-banyandb/pkg/test/measure"
+       test_property "github.com/apache/skywalking-banyandb/pkg/test/property"
        "github.com/apache/skywalking-banyandb/pkg/test/setup"
        test_stream "github.com/apache/skywalking-banyandb/pkg/test/stream"
        "github.com/apache/skywalking-banyandb/pkg/timestamp"
@@ -104,6 +105,8 @@ func InitializeTestSuite() (*CommonTestVars, error) {
        gomega.Expect(err).NotTo(gomega.HaveOccurred())
        err = test_measure.PreloadSchema(ctx, schemaRegistry)
        gomega.Expect(err).NotTo(gomega.HaveOccurred())
+       err = test_property.PreloadSchema(ctx, schemaRegistry)
+       gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
        ginkgo.By("Starting data node 0")
        var closeDataNode0 func()
diff --git a/test/integration/distributed/lifecycle/lifecycle_suite_test.go 
b/test/integration/distributed/lifecycle/lifecycle_suite_test.go
index 79429104..8b7b2923 100644
--- a/test/integration/distributed/lifecycle/lifecycle_suite_test.go
+++ b/test/integration/distributed/lifecycle/lifecycle_suite_test.go
@@ -38,6 +38,7 @@ import (
        "github.com/apache/skywalking-banyandb/pkg/test/flags"
        "github.com/apache/skywalking-banyandb/pkg/test/helpers"
        test_measure "github.com/apache/skywalking-banyandb/pkg/test/measure"
+       test_property "github.com/apache/skywalking-banyandb/pkg/test/property"
        "github.com/apache/skywalking-banyandb/pkg/test/setup"
        test_stream "github.com/apache/skywalking-banyandb/pkg/test/stream"
        "github.com/apache/skywalking-banyandb/pkg/timestamp"
@@ -94,6 +95,7 @@ var _ = SynchronizedBeforeSuite(func() []byte {
        ctx := context.Background()
        test_stream.LoadSchemaWithStages(ctx, schemaRegistry)
        test_measure.LoadSchemaWithStages(ctx, schemaRegistry)
+       test_property.PreloadSchema(ctx, schemaRegistry)
        By("Starting hot data node")
        var closeDataNode0 func()
        dataAddr, srcDir, closeDataNode0 = setup.DataNodeWithAddrAndDir(ep, 
"--node-labels", "type=hot", "--measure-flush-timeout", "0s", 
"--stream-flush-timeout", "0s")
diff --git 
a/test/integration/distributed/multi_segments/multi_segments_suite_test.go 
b/test/integration/distributed/multi_segments/multi_segments_suite_test.go
index e331f6e6..2539c1e0 100644
--- a/test/integration/distributed/multi_segments/multi_segments_suite_test.go
+++ b/test/integration/distributed/multi_segments/multi_segments_suite_test.go
@@ -40,12 +40,14 @@ import (
        "github.com/apache/skywalking-banyandb/pkg/test/gmatcher"
        "github.com/apache/skywalking-banyandb/pkg/test/helpers"
        test_measure "github.com/apache/skywalking-banyandb/pkg/test/measure"
+       test_property "github.com/apache/skywalking-banyandb/pkg/test/property"
        "github.com/apache/skywalking-banyandb/pkg/test/setup"
        test_stream "github.com/apache/skywalking-banyandb/pkg/test/stream"
        test_trace "github.com/apache/skywalking-banyandb/pkg/test/trace"
        "github.com/apache/skywalking-banyandb/pkg/timestamp"
        test_cases "github.com/apache/skywalking-banyandb/test/cases"
        casesmeasure "github.com/apache/skywalking-banyandb/test/cases/measure"
+       casesproperty 
"github.com/apache/skywalking-banyandb/test/cases/property"
        casesstream "github.com/apache/skywalking-banyandb/test/cases/stream"
        casestopn "github.com/apache/skywalking-banyandb/test/cases/topn"
 )
@@ -97,6 +99,7 @@ var _ = SynchronizedBeforeSuite(func() []byte {
        test_stream.PreloadSchema(ctx, schemaRegistry)
        test_measure.PreloadSchema(ctx, schemaRegistry)
        test_trace.PreloadSchema(ctx, schemaRegistry)
+       test_property.PreloadSchema(ctx, schemaRegistry)
 
        By("Starting data node 0")
        closeDataNode0 := setup.DataNode(ep)
@@ -136,6 +139,10 @@ var _ = SynchronizedBeforeSuite(func() []byte {
                Connection: connection,
                BaseTime:   baseTime,
        }
+       casesproperty.SharedContext = helpers.SharedContext{
+               Connection: connection,
+               BaseTime:   baseTime,
+       }
        Expect(err).NotTo(HaveOccurred())
 })
 
diff --git a/test/integration/distributed/query/query_suite_test.go 
b/test/integration/distributed/query/query_suite_test.go
index 297445ba..b15779c5 100644
--- a/test/integration/distributed/query/query_suite_test.go
+++ b/test/integration/distributed/query/query_suite_test.go
@@ -41,12 +41,14 @@ import (
        "github.com/apache/skywalking-banyandb/pkg/test/gmatcher"
        "github.com/apache/skywalking-banyandb/pkg/test/helpers"
        test_measure "github.com/apache/skywalking-banyandb/pkg/test/measure"
+       test_property "github.com/apache/skywalking-banyandb/pkg/test/property"
        "github.com/apache/skywalking-banyandb/pkg/test/setup"
        test_stream "github.com/apache/skywalking-banyandb/pkg/test/stream"
        test_trace "github.com/apache/skywalking-banyandb/pkg/test/trace"
        "github.com/apache/skywalking-banyandb/pkg/timestamp"
        test_cases "github.com/apache/skywalking-banyandb/test/cases"
        casesmeasure "github.com/apache/skywalking-banyandb/test/cases/measure"
+       casesproperty 
"github.com/apache/skywalking-banyandb/test/cases/property"
        casesstream "github.com/apache/skywalking-banyandb/test/cases/stream"
        casestopn "github.com/apache/skywalking-banyandb/test/cases/topn"
        casestrace "github.com/apache/skywalking-banyandb/test/cases/trace"
@@ -96,6 +98,7 @@ var _ = SynchronizedBeforeSuite(func() []byte {
        test_stream.PreloadSchema(ctx, schemaRegistry)
        test_measure.PreloadSchema(ctx, schemaRegistry)
        test_trace.PreloadSchema(ctx, schemaRegistry)
+       test_property.PreloadSchema(ctx, schemaRegistry)
        By("Starting data node 0")
        closeDataNode0 := setup.DataNode(ep)
        By("Starting data node 1")
@@ -135,6 +138,10 @@ var _ = SynchronizedBeforeSuite(func() []byte {
                Connection: connection,
                BaseTime:   now,
        }
+       casesproperty.SharedContext = helpers.SharedContext{
+               Connection: connection,
+               BaseTime:   now,
+       }
        Expect(err).NotTo(HaveOccurred())
 })
 
diff --git a/test/integration/standalone/query/query_suite_test.go 
b/test/integration/standalone/query/query_suite_test.go
index 453ad6b3..596902f3 100644
--- a/test/integration/standalone/query/query_suite_test.go
+++ b/test/integration/standalone/query/query_suite_test.go
@@ -37,6 +37,7 @@ import (
        "github.com/apache/skywalking-banyandb/pkg/timestamp"
        test_cases "github.com/apache/skywalking-banyandb/test/cases"
        casesmeasure "github.com/apache/skywalking-banyandb/test/cases/measure"
+       casesproperty 
"github.com/apache/skywalking-banyandb/test/cases/property"
        casesstream "github.com/apache/skywalking-banyandb/test/cases/stream"
        casestopn "github.com/apache/skywalking-banyandb/test/cases/topn"
        casestrace "github.com/apache/skywalking-banyandb/test/cases/trace"
@@ -87,6 +88,10 @@ var _ = SynchronizedBeforeSuite(func() []byte {
                Connection: connection,
                BaseTime:   now,
        }
+       casesproperty.SharedContext = helpers.SharedContext{
+               Connection: connection,
+               BaseTime:   now,
+       }
        Expect(err).NotTo(HaveOccurred())
 })
 


Reply via email to