You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by lu...@apache.org on 2022/04/06 15:35:54 UTC

[skywalking-banyandb-java-client] branch add-property-api updated: add tests

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

lujiajing pushed a commit to branch add-property-api
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb-java-client.git


The following commit(s) were added to refs/heads/add-property-api by this push:
     new c7c7e6b  add tests
c7c7e6b is described below

commit c7c7e6ba294e99caa167e5efadb1fd75753aa4bc
Author: Megrez Lu <lu...@gmail.com>
AuthorDate: Wed Apr 6 23:35:46 2022 +0800

    add tests
---
 .../banyandb/v1/client/BanyanDBClient.java         |  28 ++++
 .../skywalking/banyandb/v1/client/TagAndValue.java |  32 ++++-
 .../banyandb/v1/client/metadata/Property.java      |  17 ++-
 .../banyandb/v1/client/metadata/PropertyStore.java |  76 +++++++++++
 ...Tests.java => ITBanyanDBMeasureQueryTests.java} |   2 +-
 .../v1/client/ITBanyanDBPropertyTests.java         |  83 ++++++++++++
 ...nTests.java => ITBanyanDBStreamQueryTests.java} |   2 +-
 .../v1/client/metadata/PropertyStoreTest.java      | 148 +++++++++++++++++++++
 8 files changed, 384 insertions(+), 4 deletions(-)

diff --git a/src/main/java/org/apache/skywalking/banyandb/v1/client/BanyanDBClient.java b/src/main/java/org/apache/skywalking/banyandb/v1/client/BanyanDBClient.java
index 70d9a68..4939c1b 100644
--- a/src/main/java/org/apache/skywalking/banyandb/v1/client/BanyanDBClient.java
+++ b/src/main/java/org/apache/skywalking/banyandb/v1/client/BanyanDBClient.java
@@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Collectors;
 
+import io.grpc.Status;
 import io.grpc.stub.StreamObserver;
 import lombok.AccessLevel;
 import lombok.Getter;
@@ -54,6 +55,8 @@ import org.apache.skywalking.banyandb.v1.client.metadata.IndexRuleMetadataRegist
 import org.apache.skywalking.banyandb.v1.client.metadata.Measure;
 import org.apache.skywalking.banyandb.v1.client.metadata.MeasureMetadataRegistry;
 import org.apache.skywalking.banyandb.v1.client.metadata.MetadataCache;
+import org.apache.skywalking.banyandb.v1.client.metadata.Property;
+import org.apache.skywalking.banyandb.v1.client.metadata.PropertyStore;
 import org.apache.skywalking.banyandb.v1.client.metadata.Stream;
 import org.apache.skywalking.banyandb.v1.client.metadata.StreamMetadataRegistry;
 
@@ -314,6 +317,31 @@ public class BanyanDBClient implements Closeable {
         MetadataCache.INSTANCE.register(measure);
     }
 
+    /**
+     * Create or update the property
+     *
+     * @param property the property to be stored in the BanyanBD
+     */
+    public void save(Property property) throws BanyanDBException {
+        PropertyStore store = new PropertyStore(checkNotNull(this.channel));
+        try {
+            store.get(property.group(), property.name(), property.id());
+            store.update(property);
+        } catch (BanyanDBException ex) {
+            // TODO: polish "404" response code in BanyanDB
+            if (ex.getStatus().equals(Status.Code.UNKNOWN)) {
+                store.create(property);
+                return;
+            }
+            throw ex;
+        }
+    }
+
+    public Property findProperty(String group, String name, String id) throws BanyanDBException {
+        PropertyStore store = new PropertyStore(checkNotNull(this.channel));
+        return store.get(group, name, id);
+    }
+
     /**
      * Bind index rule to the stream
      *
diff --git a/src/main/java/org/apache/skywalking/banyandb/v1/client/TagAndValue.java b/src/main/java/org/apache/skywalking/banyandb/v1/client/TagAndValue.java
index cf4ad0f..32cd163 100644
--- a/src/main/java/org/apache/skywalking/banyandb/v1/client/TagAndValue.java
+++ b/src/main/java/org/apache/skywalking/banyandb/v1/client/TagAndValue.java
@@ -65,7 +65,7 @@ public abstract class TagAndValue<T> extends Value<T> {
                 .build();
     }
 
-    static TagAndValue<?> fromProtobuf(BanyandbModel.Tag tag) {
+    public static TagAndValue<?> fromProtobuf(BanyandbModel.Tag tag) {
         switch (tag.getValue().getValueCase()) {
             case INT:
                 return new LongTagPair(tag.getKey(), tag.getValue().getInt().getValue());
@@ -84,6 +84,7 @@ public abstract class TagAndValue<T> extends Value<T> {
         }
     }
 
+    @EqualsAndHashCode(callSuper = true)
     public static class StringTagPair extends TagAndValue<String> {
         StringTagPair(final String tagName, final String value) {
             super(tagName, value);
@@ -99,6 +100,11 @@ public abstract class TagAndValue<T> extends Value<T> {
         }
     }
 
+    public static TagAndValue<String> newStringTag(final String tagName, final String value) {
+        return new StringTagPair(tagName, value);
+    }
+
+    @EqualsAndHashCode(callSuper = true)
     public static class StringArrayTagPair extends TagAndValue<List<String>> {
         StringArrayTagPair(final String tagName, final List<String> value) {
             super(tagName, value);
@@ -114,6 +120,11 @@ public abstract class TagAndValue<T> extends Value<T> {
         }
     }
 
+    public static TagAndValue<List<String>> newStringArrayTagPair(final String tagName, final List<String> value) {
+        return new StringArrayTagPair(tagName, value);
+    }
+
+    @EqualsAndHashCode(callSuper = true)
     public static class LongTagPair extends TagAndValue<Long> {
         LongTagPair(final String tagName, final Long value) {
             super(tagName, value);
@@ -129,6 +140,11 @@ public abstract class TagAndValue<T> extends Value<T> {
         }
     }
 
+    public static TagAndValue<Long> newLongTag(final String tagName, final long value) {
+        return new LongTagPair(tagName, value);
+    }
+
+    @EqualsAndHashCode(callSuper = true)
     public static class LongArrayTagPair extends TagAndValue<List<Long>> {
         LongArrayTagPair(final String tagName, final List<Long> value) {
             super(tagName, value);
@@ -144,6 +160,11 @@ public abstract class TagAndValue<T> extends Value<T> {
         }
     }
 
+    public static TagAndValue<Long> newLongArrayTag(final String tagName, final long value) {
+        return new LongTagPair(tagName, value);
+    }
+
+    @EqualsAndHashCode(callSuper = true)
     public static class BinaryTagPair extends TagAndValue<ByteString> {
         public BinaryTagPair(String fieldName, ByteString byteString) {
             super(fieldName, byteString);
@@ -157,6 +178,11 @@ public abstract class TagAndValue<T> extends Value<T> {
         }
     }
 
+    public static TagAndValue<ByteString> newBinaryTag(final String tagName, final byte[] bytes) {
+        return new BinaryTagPair(tagName, ByteString.copyFrom(bytes));
+    }
+
+    @EqualsAndHashCode(callSuper = true)
     public static class NullTagPair extends TagAndValue<Void> {
         NullTagPair(final String tagName) {
             super(tagName, null);
@@ -174,4 +200,8 @@ public abstract class TagAndValue<T> extends Value<T> {
             return true;
         }
     }
+
+    public static TagAndValue<Void> newNullTag(final String tagName) {
+        return new NullTagPair(tagName);
+    }
 }
diff --git a/src/main/java/org/apache/skywalking/banyandb/v1/client/metadata/Property.java b/src/main/java/org/apache/skywalking/banyandb/v1/client/metadata/Property.java
index 5669bc2..785389b 100644
--- a/src/main/java/org/apache/skywalking/banyandb/v1/client/metadata/Property.java
+++ b/src/main/java/org/apache/skywalking/banyandb/v1/client/metadata/Property.java
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList;
 import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
 import org.apache.skywalking.banyandb.property.v1.BanyandbProperty;
 import org.apache.skywalking.banyandb.v1.client.TagAndValue;
+import org.apache.skywalking.banyandb.v1.client.util.TimeUtils;
 
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
@@ -30,7 +31,7 @@ import java.util.List;
 
 @AutoValue
 public abstract class Property extends NamedSchema<BanyandbProperty.Property> {
-    abstract String id();
+    public abstract String id();
 
     abstract ImmutableList<TagAndValue<?>> tags();
 
@@ -53,6 +54,20 @@ public abstract class Property extends NamedSchema<BanyandbProperty.Property> {
         return new AutoValue_Property.Builder().setGroup(group).setName(name).setId(id);
     }
 
+    static Property fromProtobuf(BanyandbProperty.Property pb) {
+        final Property.Builder b = Property.create(pb.getMetadata().getContainer().getGroup(),
+                        pb.getMetadata().getContainer().getName(),
+                        pb.getMetadata().getId())
+                .setUpdatedAt(TimeUtils.parseTimestamp(pb.getUpdatedAt()));
+
+        // build tag family spec
+        for (int i = 0; i < pb.getTagsCount(); i++) {
+            b.addTag(TagAndValue.fromProtobuf(pb.getTags(i)));
+        }
+
+        return b.build();
+    }
+
     @AutoValue.Builder
     public abstract static class Builder {
         abstract Builder setGroup(String group);
diff --git a/src/main/java/org/apache/skywalking/banyandb/v1/client/metadata/PropertyStore.java b/src/main/java/org/apache/skywalking/banyandb/v1/client/metadata/PropertyStore.java
new file mode 100644
index 0000000..976008e
--- /dev/null
+++ b/src/main/java/org/apache/skywalking/banyandb/v1/client/metadata/PropertyStore.java
@@ -0,0 +1,76 @@
+package org.apache.skywalking.banyandb.v1.client.metadata;
+
+import io.grpc.Channel;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty;
+import org.apache.skywalking.banyandb.property.v1.PropertyServiceGrpc;
+import org.apache.skywalking.banyandb.v1.client.grpc.HandleExceptionsWith;
+import org.apache.skywalking.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class PropertyStore {
+    private final PropertyServiceGrpc.PropertyServiceBlockingStub stub;
+
+    public PropertyStore(Channel channel) {
+        this.stub = PropertyServiceGrpc.newBlockingStub(channel);
+    }
+
+    public void create(Property payload) throws BanyanDBException {
+        HandleExceptionsWith.callAndTranslateApiException(() ->
+                this.stub.create(BanyandbProperty.CreateRequest.newBuilder()
+                        .setProperty(payload.serialize())
+                        .build()));
+    }
+
+    public void update(Property payload) throws BanyanDBException {
+        HandleExceptionsWith.callAndTranslateApiException(() ->
+                this.stub.update(BanyandbProperty.UpdateRequest.newBuilder()
+                        .setProperty(payload.serialize())
+                        .build()));
+    }
+
+    public boolean delete(String group, String name, String id) throws BanyanDBException {
+        BanyandbProperty.DeleteResponse resp = HandleExceptionsWith.callAndTranslateApiException(() ->
+                this.stub.delete(BanyandbProperty.DeleteRequest.newBuilder()
+                        .setMetadata(BanyandbProperty.Metadata
+                                .newBuilder()
+                                .setContainer(BanyandbCommon.Metadata.newBuilder()
+                                        .setGroup(group)
+                                        .setName(name)
+                                        .build())
+                                .setId(id)
+                                .build())
+                        .build()));
+        return resp != null && resp.getDeleted();
+    }
+
+    public Property get(String group, String name, String id) throws BanyanDBException {
+        BanyandbProperty.GetResponse resp = HandleExceptionsWith.callAndTranslateApiException(() ->
+                this.stub.get(BanyandbProperty.GetRequest.newBuilder()
+                        .setMetadata(BanyandbProperty.Metadata
+                                .newBuilder()
+                                .setContainer(BanyandbCommon.Metadata.newBuilder()
+                                        .setGroup(group)
+                                        .setName(name)
+                                        .build())
+                                .setId(id)
+                                .build())
+                        .build()));
+
+        return Property.fromProtobuf(resp.getProperty());
+    }
+
+    public List<Property> list(String group, String name) throws BanyanDBException {
+        BanyandbProperty.ListResponse resp = HandleExceptionsWith.callAndTranslateApiException(() ->
+                this.stub.list(BanyandbProperty.ListRequest.newBuilder()
+                        .setContainer(BanyandbCommon.Metadata.newBuilder()
+                                .setGroup(group)
+                                .setName(name)
+                                .build())
+                        .build()));
+
+        return resp.getPropertyList().stream().map(Property::fromProtobuf).collect(Collectors.toList());
+    }
+}
diff --git a/src/test/java/org/apache/skywalking/banyandb/v1/client/BanyanDBMeasureQueryIntegrationTests.java b/src/test/java/org/apache/skywalking/banyandb/v1/client/ITBanyanDBMeasureQueryTests.java
similarity index 98%
rename from src/test/java/org/apache/skywalking/banyandb/v1/client/BanyanDBMeasureQueryIntegrationTests.java
rename to src/test/java/org/apache/skywalking/banyandb/v1/client/ITBanyanDBMeasureQueryTests.java
index c03c9ed..740335d 100644
--- a/src/test/java/org/apache/skywalking/banyandb/v1/client/BanyanDBMeasureQueryIntegrationTests.java
+++ b/src/test/java/org/apache/skywalking/banyandb/v1/client/ITBanyanDBMeasureQueryTests.java
@@ -38,7 +38,7 @@ import java.util.concurrent.TimeUnit;
 
 import static org.awaitility.Awaitility.await;
 
-public class BanyanDBMeasureQueryIntegrationTests extends BanyanDBClientTestCI {
+public class ITBanyanDBMeasureQueryTests extends BanyanDBClientTestCI {
     private MeasureBulkWriteProcessor processor;
 
     @Before
diff --git a/src/test/java/org/apache/skywalking/banyandb/v1/client/ITBanyanDBPropertyTests.java b/src/test/java/org/apache/skywalking/banyandb/v1/client/ITBanyanDBPropertyTests.java
new file mode 100644
index 0000000..63b1349
--- /dev/null
+++ b/src/test/java/org/apache/skywalking/banyandb/v1/client/ITBanyanDBPropertyTests.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the 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.
+ * The 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 org.apache.skywalking.banyandb.v1.client;
+
+import org.apache.skywalking.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.banyandb.v1.client.metadata.Catalog;
+import org.apache.skywalking.banyandb.v1.client.metadata.Duration;
+import org.apache.skywalking.banyandb.v1.client.metadata.Group;
+import org.apache.skywalking.banyandb.v1.client.metadata.Property;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import static org.awaitility.Awaitility.await;
+
+public class ITBanyanDBPropertyTests extends BanyanDBClientTestCI {
+    @Before
+    public void setUp() throws IOException, BanyanDBException, InterruptedException {
+        this.setUpConnection();
+        Group expectedGroup = this.client.define(
+                Group.create("default", Catalog.STREAM, 2, 0, Duration.ofDays(7))
+        );
+        Assert.assertNotNull(expectedGroup);
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        this.closeClient();
+    }
+
+    @Test
+    public void test_PropertyCreateAndGet() throws BanyanDBException {
+        Property property = Property.create("default", "sw", "ui_template")
+                .addTag(TagAndValue.newStringTag("name", "hello"))
+                .build();
+        this.client.save(property);
+
+        await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+            Property gotProperty = client.findProperty("default", "sw", "ui_template");
+            Assert.assertNotNull(gotProperty);
+            Assert.assertEquals(property, gotProperty);
+        });
+    }
+
+    @Test
+    public void test_PropertyCreateUpdateAndGet() throws BanyanDBException {
+        Property property1 = Property.create("default", "sw", "ui_template")
+                .addTag(TagAndValue.newStringTag("name", "hello"))
+                .build();
+        this.client.save(property1);
+
+        Property property2 = Property.create("default", "sw", "ui_template")
+                .addTag(TagAndValue.newStringTag("name", "world"))
+                .build();
+        this.client.save(property2);
+
+        await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+            Property gotProperty = client.findProperty("default", "sw", "ui_template");
+            Assert.assertNotNull(gotProperty);
+            Assert.assertEquals(property2, gotProperty);
+        });
+    }
+}
diff --git a/src/test/java/org/apache/skywalking/banyandb/v1/client/BanyanDBStreamQueryIntegrationTests.java b/src/test/java/org/apache/skywalking/banyandb/v1/client/ITBanyanDBStreamQueryTests.java
similarity index 98%
rename from src/test/java/org/apache/skywalking/banyandb/v1/client/BanyanDBStreamQueryIntegrationTests.java
rename to src/test/java/org/apache/skywalking/banyandb/v1/client/ITBanyanDBStreamQueryTests.java
index 0c1034f..90e8079 100644
--- a/src/test/java/org/apache/skywalking/banyandb/v1/client/BanyanDBStreamQueryIntegrationTests.java
+++ b/src/test/java/org/apache/skywalking/banyandb/v1/client/ITBanyanDBStreamQueryTests.java
@@ -37,7 +37,7 @@ import java.util.concurrent.TimeUnit;
 
 import static org.awaitility.Awaitility.await;
 
-public class BanyanDBStreamQueryIntegrationTests extends BanyanDBClientTestCI {
+public class ITBanyanDBStreamQueryTests extends BanyanDBClientTestCI {
     private StreamBulkWriteProcessor processor;
 
     @Before
diff --git a/src/test/java/org/apache/skywalking/banyandb/v1/client/metadata/PropertyStoreTest.java b/src/test/java/org/apache/skywalking/banyandb/v1/client/metadata/PropertyStoreTest.java
new file mode 100644
index 0000000..062c932
--- /dev/null
+++ b/src/test/java/org/apache/skywalking/banyandb/v1/client/metadata/PropertyStoreTest.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the 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.
+ * The 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 org.apache.skywalking.banyandb.v1.client.metadata;
+
+import io.grpc.stub.StreamObserver;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty;
+import org.apache.skywalking.banyandb.property.v1.PropertyServiceGrpc;
+import org.apache.skywalking.banyandb.v1.client.AbstractBanyanDBClientTest;
+import org.apache.skywalking.banyandb.v1.client.TagAndValue;
+import org.apache.skywalking.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.banyandb.v1.client.util.TimeUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.io.IOException;
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.AdditionalAnswers.delegatesTo;
+import static org.powermock.api.mockito.PowerMockito.mock;
+
+@RunWith(PowerMockRunner.class)
+@PowerMockIgnore("javax.management.*")
+public class PropertyStoreTest extends AbstractBanyanDBClientTest {
+    private PropertyStore store;
+
+    private Map<String, BanyandbProperty.Property> memory;
+
+    private final PropertyServiceGrpc.PropertyServiceImplBase propertyServiceImpl = mock(PropertyServiceGrpc.PropertyServiceImplBase.class, delegatesTo(
+            new PropertyServiceGrpc.PropertyServiceImplBase() {
+                @Override
+                public void create(BanyandbProperty.CreateRequest request, StreamObserver<BanyandbProperty.CreateResponse> responseObserver) {
+                    BanyandbProperty.Property p = request.getProperty().toBuilder()
+                            .setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+                            .build();
+                    memory.put(format(p.getMetadata()), p);
+                    responseObserver.onNext(BanyandbProperty.CreateResponse.newBuilder().build());
+                    responseObserver.onCompleted();
+                }
+
+                @Override
+                public void update(BanyandbProperty.UpdateRequest request, StreamObserver<BanyandbProperty.UpdateResponse> responseObserver) {
+                    BanyandbProperty.Property p = request.getProperty().toBuilder()
+                            .setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+                            .build();
+                    memory.put(format(p.getMetadata()), p);
+                    responseObserver.onNext(BanyandbProperty.UpdateResponse.newBuilder().build());
+                    responseObserver.onCompleted();
+                }
+
+                @Override
+                public void delete(BanyandbProperty.DeleteRequest request, StreamObserver<BanyandbProperty.DeleteResponse> responseObserver) {
+                    final BanyandbProperty.Property p = memory.remove(format(request.getMetadata()));
+                    responseObserver.onNext(BanyandbProperty.DeleteResponse.newBuilder().setDeleted(p != null).build());
+                    responseObserver.onCompleted();
+                }
+
+                @Override
+                public void get(BanyandbProperty.GetRequest request, StreamObserver<BanyandbProperty.GetResponse> responseObserver) {
+                    final BanyandbProperty.Property p = memory.get(format(request.getMetadata()));
+                    responseObserver.onNext(BanyandbProperty.GetResponse.newBuilder().setProperty(p).build());
+                    responseObserver.onCompleted();
+                }
+
+                @Override
+                public void list(BanyandbProperty.ListRequest request, StreamObserver<BanyandbProperty.ListResponse> responseObserver) {
+                    responseObserver.onNext(BanyandbProperty.ListResponse.newBuilder().addAllProperty(memory.values()).build());
+                    responseObserver.onCompleted();
+                }
+            }));
+
+    @Before
+    public void setUp() throws IOException {
+        super.setUp(bindService(propertyServiceImpl));
+        this.memory = new HashMap<>();
+        this.store = new PropertyStore(this.channel);
+    }
+
+    @Test
+    public void testPropertyStore_create() throws BanyanDBException {
+        Property property = Property.create("default", "sw", "ui_template")
+                .addTag(TagAndValue.newStringTag("name", "hello"))
+                .build();
+        this.store.create(property);
+        Assert.assertEquals(memory.size(), 1);
+    }
+
+    @Test
+    public void testPropertyStore_createAndGet() throws BanyanDBException {
+        Property property = Property.create("default", "sw", "ui_template")
+                .addTag(TagAndValue.newStringTag("name", "hello"))
+                .build();
+        this.store.create(property);
+        Property gotProperty = this.store.get("default", "sw", "ui_template");
+        Assert.assertNotNull(gotProperty);
+        Assert.assertEquals(property, gotProperty);
+        Assert.assertNotNull(gotProperty.updatedAt());
+    }
+
+    @Test
+    public void testPropertyStore_createAndList() throws BanyanDBException {
+        Property property = Property.create("default", "sw", "ui_template")
+                .addTag(TagAndValue.newStringTag("name", "hello"))
+                .build();
+        this.store.create(property);
+        List<Property> listProperties = this.store.list("default", "sw");
+        Assert.assertNotNull(listProperties);
+        Assert.assertEquals(1, listProperties.size());
+        Assert.assertEquals(listProperties.get(0), property);
+    }
+
+    @Test
+    public void testPropertyStore_createAndDelete() throws BanyanDBException {
+        Property property = Property.create("default", "sw", "ui_template")
+                .addTag(TagAndValue.newStringTag("name", "hello"))
+                .build();
+        this.store.create(property);
+        boolean deleted = this.store.delete("default", "sw", "ui_template");
+        Assert.assertTrue(deleted);
+        Assert.assertEquals(0, memory.size());
+    }
+
+    static String format(BanyandbProperty.Metadata metadata) {
+        return metadata.getContainer().getGroup() + ":" + metadata.getContainer().getName() + "/" + metadata.getId();
+    }
+}