Test spanner-refdb using spanner emulator testcontainer
Implement a SpannerEmulatorContainer for running the upstream Spanner
emulator container for tests:
- start the emulator container
- create a Spanner instance
- create a Spanner database
- use a separate plugin config file spanner-refdb.config to configure
the plugin
- mock the PluginConfigFactory so that we can start the plugin from test
code without loading the plugin in a running Gerrit server
- create the SpannerRefDatabase schema
- implement first tests
Change-Id: I9290c4458299aa27bcf6e1d036b2c2245d948ba3
diff --git a/BUILD b/BUILD
index bb0e739..6ae07a1 100644
--- a/BUILD
+++ b/BUILD
@@ -1,9 +1,9 @@
load("//tools/bzl:junit.bzl", "junit_tests")
load(
"//tools/bzl:plugin.bzl",
- "gerrit_plugin",
"PLUGIN_DEPS",
- "PLUGIN_TEST_DEPS"
+ "PLUGIN_TEST_DEPS",
+ "gerrit_plugin",
)
gerrit_plugin(
@@ -114,6 +114,14 @@
visibility = ["//visibility:public"],
exports = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
":spanner-refdb__plugin",
+ "@docker-java-api//jar",
+ "@docker-java-transport-zerodep//jar",
+ "@docker-java-transport//jar",
+ "@duct-tape//jar",
"@global-refdb//jar",
+ "@google-cloud-core//jar",
+ "@google-cloud-spanner//jar",
+ "@testcontainer-localstack//jar",
+ "@testcontainers//jar",
],
)
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index c10bb0d..bf9d40d 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -396,3 +396,43 @@
sha1 = "00b6b0f39b3c8fc280a19d91fb0681954ebccd02",
artifact = "com.gerritforge:global-refdb:3.3.2.1",
)
+
+ TESTCONTAINERS_VERSION = "1.18.3"
+
+ maven_jar(
+ name = "testcontainers",
+ artifact = "org.testcontainers:testcontainers:" + TESTCONTAINERS_VERSION,
+ sha1 = "a82f6258f92d50d278b9c67bdf5eabcaa5c08654",
+ )
+
+ maven_jar(
+ name = "testcontainer-localstack",
+ artifact = "org.testcontainers:localstack:" + TESTCONTAINERS_VERSION,
+ sha1 = "2b7a8d4522330217545c4234b916b6b77f5c6f95",
+ )
+
+ DOCKER_JAVA_VERS = "3.3.2"
+
+ maven_jar(
+ name = "docker-java-api",
+ artifact = "com.github.docker-java:docker-java-api:" + DOCKER_JAVA_VERS,
+ sha1 = "0de6345d2f69638a224f73d9e62de83c7692e436",
+ )
+
+ maven_jar(
+ name = "docker-java-transport",
+ artifact = "com.github.docker-java:docker-java-transport:" + DOCKER_JAVA_VERS,
+ sha1 = "a4c2cba248ccfefe9c5c8d8d4726f3e0b2b51104",
+ )
+
+ maven_jar(
+ name = "docker-java-transport-zerodep",
+ artifact = "com.github.docker-java:docker-java-transport-zerodep:" + DOCKER_JAVA_VERS,
+ sha1 = "36ef508e5e48613afb7fafbf7e89184243738e96",
+ )
+
+ maven_jar(
+ name = "duct-tape",
+ artifact = "org.rnorth.duct-tape:duct-tape:1.0.8",
+ sha1 = "92edc22a9ab2f3e17c9bf700aaee377d50e8b530",
+ )
diff --git a/src/main/java/com/googlesource/gerrit/plugins/spannerrefdb/Configuration.java b/src/main/java/com/googlesource/gerrit/plugins/spannerrefdb/Configuration.java
index bbdea23..e6653a3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/spannerrefdb/Configuration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/spannerrefdb/Configuration.java
@@ -16,31 +16,53 @@
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.SpannerOptions;
+import com.google.common.base.Strings;
import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import org.eclipse.jgit.lib.Config;
@Singleton
class Configuration {
- private final SpannerOptions options;
- private final DatabaseId databaseId;
+ public static final String DATABASE_KEY = "database";
+ public static final String INSTANCE_KEY = "instance";
+ public static final String SECTION = "ref-database";
+ public static final String SUBSECTION = "spanner";
+ private final String spannerInstance;
+ private final String spannerDatabase;
+ private SpannerOptions options;
@Inject
Configuration(PluginConfigFactory configFactory, @PluginName String pluginName) {
- PluginConfig pluginConfig = configFactory.getFromGerritConfig(pluginName);
- String spannerInstance = pluginConfig.getString("spannerInstance", "test-instance");
- String spannerDatabase = pluginConfig.getString("spannerDatabase", "global-refdb");
+ Config cfg = configFactory.getGlobalPluginConfig(pluginName);
+ this.spannerInstance = getString(cfg, SECTION, SUBSECTION, INSTANCE_KEY, "test-instance");
+ this.spannerDatabase = getString(cfg, SECTION, SUBSECTION, DATABASE_KEY, "global-refdb");
this.options = SpannerOptions.newBuilder().build();
- this.databaseId = DatabaseId.of(options.getProjectId(), spannerInstance, spannerDatabase);
+ }
+
+ final String getSpannerInstanceName() {
+ return spannerInstance;
+ }
+
+ final DatabaseId getDatabaseId() {
+ return DatabaseId.of(options.getProjectId(), spannerInstance, spannerDatabase);
}
final SpannerOptions getOptions() {
return options;
}
- final DatabaseId getDatabaseId() {
- return databaseId;
+ void setOptions(SpannerOptions options) {
+ this.options = options;
+ }
+
+ private String getString(
+ Config cfg, String section, String subsection, String name, String defaultValue) {
+ String value = cfg.getString(section, subsection, name);
+ if (!Strings.isNullOrEmpty(value)) {
+ return value;
+ }
+ return defaultValue;
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/spannerrefdb/SpannerRefDatabase.java b/src/main/java/com/googlesource/gerrit/plugins/spannerrefdb/SpannerRefDatabase.java
index 94a34f8..940743e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/spannerrefdb/SpannerRefDatabase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/spannerrefdb/SpannerRefDatabase.java
@@ -24,6 +24,7 @@
import com.google.cloud.spanner.ResultSet;
import com.google.cloud.spanner.Statement;
import com.google.cloud.spanner.Struct;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.Project;
import com.google.inject.Inject;
@@ -213,7 +214,8 @@
Mutation.delete("refs", KeySet.prefixRange(Key.of(project.get())))));
}
- private String get(Project.NameKey project, String refName) throws GlobalRefDbSystemError {
+ @VisibleForTesting
+ String get(Project.NameKey project, String refName) throws GlobalRefDbSystemError {
try (ResultSet resultSet =
dbClient
.singleUse()
@@ -242,4 +244,12 @@
// with this, while the aws implementation opts for this.
return Optional.ofNullable((T) get(project, refName));
}
+
+ public Optional<ObjectId> getObjectId(Project.NameKey project, String refName) {
+ String idName = get(project, refName);
+ if (idName == null) {
+ return Optional.empty();
+ }
+ return Optional.of(ObjectId.fromString(idName));
+ }
}
diff --git a/src/main/resources/Documentation/build.md b/src/main/resources/Documentation/build.md
index fb978d1..2268819 100644
--- a/src/main/resources/Documentation/build.md
+++ b/src/main/resources/Documentation/build.md
@@ -17,4 +17,72 @@
```
gerrit/bazel-bin/plugins/spanner-refdb/spanner-refdb.jar
-```
\ No newline at end of file
+```
+
+## Eclipse project setup
+
+This project can be imported into the Eclipse IDE:
+
+- Add the plugin name to the `CUSTOM_PLUGINS_TEST_DEPS`
+set in Gerrit core in `tools/bzl/plugins.bzl`,
+- execute:
+
+```
+./tools/eclipse/project.py
+```
+
+## Run tests
+
+To execute the tests run
+
+```
+bazelisk test --test_tag_filters=spanner-refdb //...
+```
+or
+```
+bazelisk test //plugins/@PLUGIN@/...
+```
+
+On MacOS you may need to access the docker daemon via TCP.
+
+Run [socat](https://linux.die.net/man/1/socat) to expose the docker daemon socket via TCP
+
+```
+socat TCP-LISTEN:2375,fork UNIX-CONNECT:/var/run/docker.sock
+```
+
+Execute the tests over TCP
+
+```
+bazelisk test --test_env='DOCKER_HOST=tcp://127.0.0.1:2375' //plugins/@PLUGIN@/...
+```
+
+### Debugging tests
+
+```
+bazelisk test --test_output=streamed //plugins/@PLUGIN@/...
+```
+
+If necessary increase log levels in `src/test/resources/log4j.properties`
+to trace testcontainers and docker java API.
+
+### Tracing traffic to docker daemon
+
+If you face issue you can trace traffic to the docker daemon using
+[socat](https://linux.die.net/man/1/socat) exposing the docker daemon via TCP.
+
+Run socat to log diagnostics and show the traffic to the docker daemon
+
+```
+socat -dd -v TCP-LISTEN:2375,fork UNIX-CONNECT:/var/run/docker.sock
+```
+
+Execute the tests over TCP
+
+```
+bazelisk test --test_env='DOCKER_HOST=tcp://127.0.0.1:2375' //plugins/@PLUGIN@/...
+```
+
+[Back to @PLUGIN@ documentation index][index]
+
+[index]: index.html
diff --git a/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/RefFixture.java b/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/RefFixture.java
new file mode 100644
index 0000000..67e8428
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/RefFixture.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.spannerrefdb;
+
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.Ref;
+import org.junit.Ignore;
+
+@Ignore
+public interface RefFixture {
+ static final String PROJECT_NAME = "A_TEST_PROJECT_NAME";
+ static final Project.NameKey PROJECT_NAME_KEY = Project.nameKey(PROJECT_NAME);
+ static final ObjectId OBJECT_ID_1 = new ObjectId(1, 2, 3, 4, 5);
+ static final ObjectId OBJECT_ID_2 = new ObjectId(1, 2, 3, 4, 6);
+ static final ObjectId OBJECT_ID_3 = new ObjectId(1, 2, 3, 4, 7);
+ static final Long UNIX_TIMESTAMP_1 = 1690379900L;
+ static final Long UNIX_TIMESTAMP_2 = 1690815810L;
+ static final String REF_NAME = "refs/heads/master";
+ static final String PATCHSET_REF_NAME = "refs/changes/01/1/1";
+ static final String MULTISITE_VERSION_VALUE_REF_NAME = "refs/multi-site/version/value";
+
+ default String aBranchRef() {
+ return RefNames.REFS_HEADS + testBranch();
+ }
+
+ default String testBranch() {
+ return "foo";
+ }
+
+ default Ref newRef(String refName, ObjectId objectId) {
+ return new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, refName, objectId);
+ }
+
+ default Ref nullRef(String refName) {
+ return newRef(refName, ObjectId.zeroId());
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/SpannerEmulatorContainer.java b/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/SpannerEmulatorContainer.java
new file mode 100644
index 0000000..a331e2c
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/SpannerEmulatorContainer.java
@@ -0,0 +1,172 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.spannerrefdb;
+
+import static com.googlesource.gerrit.plugins.spannerrefdb.Configuration.DATABASE_KEY;
+import static com.googlesource.gerrit.plugins.spannerrefdb.Configuration.INSTANCE_KEY;
+import static com.googlesource.gerrit.plugins.spannerrefdb.Configuration.SECTION;
+import static com.googlesource.gerrit.plugins.spannerrefdb.Configuration.SUBSECTION;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.cloud.NoCredentials;
+import com.google.cloud.spanner.Database;
+import com.google.cloud.spanner.DatabaseAdminClient;
+import com.google.cloud.spanner.DatabaseClient;
+import com.google.cloud.spanner.Instance;
+import com.google.cloud.spanner.InstanceAdminClient;
+import com.google.cloud.spanner.InstanceConfigId;
+import com.google.cloud.spanner.InstanceId;
+import com.google.cloud.spanner.InstanceInfo;
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.inject.Inject;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Ignore;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+@Ignore
+public class SpannerEmulatorContainer {
+ public static class Container extends GenericContainer<Container> {
+ public static String EMULATOR_VERSION = "1.5.7";
+
+ public Container() {
+ super("gcr.io/cloud-spanner-emulator/emulator:" + EMULATOR_VERSION);
+ }
+ }
+
+ public static final String PROJECT_ID = "test";
+ public static final String SPANNER_INSTANCE_ID = "instance";
+ public static final String SPANNER_DATABASE_ID = "refdb";
+ private static final int GRPC_PORT = 9010;
+ private static final int REST_PORT = 9020;
+ private static final String pluginName = "spanner-refdb";
+
+ private final Container container;
+ private final Integer grpcPort;
+ private final Integer restPort;
+
+ private final Spanner spanner;
+ private final InstanceAdminClient instanceAdminClient;
+ private final Instance spannerInstance;
+ private final Database spannerDatabase;
+ private final DatabaseClient databaseClient;
+ private final SpannerRefDatabase spannerRefDb;
+ private final Configuration pluginConfig;
+
+ @Inject
+ @SuppressWarnings("resource")
+ public SpannerEmulatorContainer() throws Exception {
+ container =
+ new Container().withExposedPorts(GRPC_PORT, REST_PORT).waitingFor(Wait.forListeningPort());
+ container.start();
+ grpcPort = container.getMappedPort(GRPC_PORT);
+ restPort = container.getMappedPort(REST_PORT);
+ System.out.println(
+ "Spanner emulator container started and is listening on ports "
+ + grpcPort
+ + ", "
+ + restPort);
+
+ spanner = getEmulatorOptions().getService();
+ instanceAdminClient = spanner.getInstanceAdminClient();
+ spannerInstance = createSpannerInstance();
+ spannerDatabase =
+ spannerInstance.createDatabase(SPANNER_DATABASE_ID, Collections.emptyList()).get();
+ pluginConfig = createEmulatorConfiguration();
+ createSchema();
+ databaseClient = createDatabaseClient();
+ spannerRefDb = new SpannerRefDatabase(databaseClient);
+ }
+
+ private void createSchema() {
+ DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient();
+ SpannerLifeCycleManager lcm = new SpannerLifeCycleManager(pluginConfig, dbAdminClient);
+ lcm.start();
+ }
+
+ public void cleanup() {
+ spannerDatabase.drop();
+ instanceAdminClient.deleteInstance(SPANNER_INSTANCE_ID);
+ spanner.close();
+ container.stop();
+ container.close();
+ System.out.println("Spanner emulator container was stopped");
+ }
+
+ public Container getContainer() {
+ return container;
+ }
+
+ public Integer getGrpcPort() {
+ return grpcPort;
+ }
+
+ public Integer getRestPort() {
+ return restPort;
+ }
+
+ public Database getSpannerDatabase() {
+ return spannerDatabase;
+ }
+
+ public SpannerRefDatabase getSpannerRefDatabase() {
+ return spannerRefDb;
+ }
+
+ private SpannerOptions getEmulatorOptions() {
+ return SpannerOptions.newBuilder()
+ .setEmulatorHost("localhost:" + getGrpcPort())
+ .setCredentials(NoCredentials.getInstance())
+ .setProjectId(PROJECT_ID)
+ .build();
+ }
+
+ private Instance createSpannerInstance() throws InterruptedException, ExecutionException {
+ InstanceConfigId instanceConfig = InstanceConfigId.of(PROJECT_ID, "emulator-config");
+ InstanceId instanceId = InstanceId.of(PROJECT_ID, SPANNER_INSTANCE_ID);
+ InstanceInfo instanceInfo =
+ InstanceInfo.newBuilder(instanceId)
+ .setInstanceConfigId(instanceConfig)
+ .setNodeCount(1)
+ .setDisplayName("test instance")
+ .build();
+ return instanceAdminClient.createInstance(instanceInfo).get();
+ }
+
+ private DatabaseClient createDatabaseClient() {
+ return createDatabaseClient(pluginConfig);
+ }
+
+ private Configuration createEmulatorConfiguration() {
+ Config refDbConfig = new Config();
+ refDbConfig.setString(SECTION, SUBSECTION, INSTANCE_KEY, SPANNER_INSTANCE_ID);
+ refDbConfig.setString(SECTION, SUBSECTION, DATABASE_KEY, SPANNER_DATABASE_ID);
+
+ PluginConfigFactory cfgFactory = mock(PluginConfigFactory.class);
+ when(cfgFactory.getGlobalPluginConfig(pluginName)).thenReturn(refDbConfig);
+ Configuration spannerConfig = new Configuration(cfgFactory, pluginName);
+ spannerConfig.setOptions(getEmulatorOptions());
+ return spannerConfig;
+ }
+
+ private DatabaseClient createDatabaseClient(Configuration configuration) {
+ return configuration.getOptions().getService().getDatabaseClient(configuration.getDatabaseId());
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/SpannerRefDatabaseTest.java b/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/SpannerRefDatabaseTest.java
new file mode 100644
index 0000000..751e7bc
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/SpannerRefDatabaseTest.java
@@ -0,0 +1,138 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.spannerrefdb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gerrit.entities.Project;
+import java.util.Optional;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SpannerRefDatabaseTest implements RefFixture {
+
+ private SpannerEmulatorContainer emulator;
+ private SpannerRefDatabase refdb;
+
+ @Before
+ public void setup() throws Exception {
+ emulator = new SpannerEmulatorContainer();
+ refdb = emulator.getSpannerRefDatabase();
+ }
+
+ @After
+ public void cleanup() {
+ emulator.cleanup();
+ }
+
+ @Test
+ public void testCreateNewRef() throws Exception {
+ createNewRef(PROJECT_NAME_KEY, REF_NAME, OBJECT_ID_1);
+
+ createNewRef(PROJECT_NAME_KEY, PATCHSET_REF_NAME, OBJECT_ID_2);
+
+ createNewRef(PROJECT_NAME_KEY, MULTISITE_VERSION_VALUE_REF_NAME, UNIX_TIMESTAMP_1.toString());
+ }
+
+ @Test
+ public void updateRef() throws Exception {
+ createNewRef(PROJECT_NAME_KEY, REF_NAME, OBJECT_ID_1);
+ updateRef(PROJECT_NAME_KEY, REF_NAME, OBJECT_ID_1, OBJECT_ID_2);
+
+ createNewRef(PROJECT_NAME_KEY, PATCHSET_REF_NAME, OBJECT_ID_2);
+ updateRef(PROJECT_NAME_KEY, PATCHSET_REF_NAME, OBJECT_ID_2, OBJECT_ID_3);
+
+ createNewRef(PROJECT_NAME_KEY, MULTISITE_VERSION_VALUE_REF_NAME, UNIX_TIMESTAMP_1.toString());
+ updateRef(
+ PROJECT_NAME_KEY,
+ MULTISITE_VERSION_VALUE_REF_NAME,
+ UNIX_TIMESTAMP_1.toString(),
+ UNIX_TIMESTAMP_2.toString());
+ }
+
+ @Test
+ public void testDeleteRef() throws Exception {
+ createNewRef(PROJECT_NAME_KEY, REF_NAME, OBJECT_ID_1);
+ deleteRef(PROJECT_NAME_KEY, REF_NAME, OBJECT_ID_1);
+
+ createNewRef(PROJECT_NAME_KEY, PATCHSET_REF_NAME, OBJECT_ID_2);
+ deleteRef(PROJECT_NAME_KEY, PATCHSET_REF_NAME, OBJECT_ID_2);
+
+ createNewRef(PROJECT_NAME_KEY, MULTISITE_VERSION_VALUE_REF_NAME, UNIX_TIMESTAMP_1.toString());
+ deleteRef(PROJECT_NAME_KEY, MULTISITE_VERSION_VALUE_REF_NAME, UNIX_TIMESTAMP_1.toString());
+ }
+
+ private void createNewRef(Project.NameKey project, String refName, ObjectId newValue)
+ throws Exception {
+ Ref ref = nullRef(refName);
+ try (AutoCloseable lock = refdb.lockRef(project, refName)) {
+ assertTrue(refdb.compareAndPut(project, ref, newValue));
+ Optional<ObjectId> oid = refdb.getObjectId(project, refName);
+ assertFalse(oid.isEmpty());
+ assertEquals(newValue, oid.get());
+ }
+ }
+
+ private void updateRef(
+ Project.NameKey project, String refName, ObjectId oldValue, ObjectId newValue)
+ throws Exception {
+ Ref expecRef = newRef(refName, oldValue);
+ try (AutoCloseable lock = refdb.lockRef(project, refName)) {
+ assertTrue(refdb.compareAndPut(project, expecRef, newValue));
+ assertEquals(newValue, refdb.getObjectId(project, refName).get());
+ }
+ }
+
+ private void deleteRef(Project.NameKey project, String refName, ObjectId oldValue)
+ throws Exception {
+ Ref expecRef = newRef(refName, oldValue);
+ try (AutoCloseable lock = refdb.lockRef(project, refName)) {
+ assertTrue(refdb.compareAndPut(project, expecRef, ObjectId.zeroId()));
+ assertTrue(refdb.getObjectId(project, refName).isEmpty());
+ }
+ }
+
+ private void createNewRef(Project.NameKey project, String refName, String newValue)
+ throws Exception {
+ try (AutoCloseable lock = refdb.lockRef(project, refName)) {
+ assertTrue(refdb.compareAndPut(project, refName, ObjectId.zeroId().name(), newValue));
+ assertNotNull(refdb.get(project, refName));
+ assertEquals(newValue, refdb.get(project, refName));
+ }
+ }
+
+ private void updateRef(Project.NameKey project, String refName, String oldValue, String newValue)
+ throws Exception {
+ try (AutoCloseable lock = refdb.lockRef(project, refName)) {
+ assertTrue(refdb.compareAndPut(project, refName, oldValue, newValue));
+ assertEquals(newValue, refdb.get(project, refName));
+ }
+ }
+
+ private void deleteRef(Project.NameKey project, String refName, String oldValue)
+ throws Exception {
+ try (AutoCloseable lock = refdb.lockRef(project, refName)) {
+ assertTrue(refdb.compareAndPut(project, refName, oldValue, ObjectId.zeroId().name()));
+ assertNull(refdb.get(project, refName));
+ }
+ }
+}