Merge "Convert config document from Ascii doc to markdown" into stable-3.5
diff --git a/BUILD b/BUILD
index 06352ab..4750354 100644
--- a/BUILD
+++ b/BUILD
@@ -1,6 +1,5 @@
 load("@rules_java//java:defs.bzl", "java_library")
 load("//tools/bzl:junit.bzl", "junit_tests")
-load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
 load(
     "//tools/bzl:plugin.bzl",
     "PLUGIN_DEPS",
@@ -8,72 +7,54 @@
     "gerrit_plugin",
 )
 
-gerrit_plugin   (
+gerrit_plugin(
     name = "index-elasticsearch",
     srcs = glob(["src/main/java/**/*.java"]),
     deps = [
-        "//java/com/google/gerrit/common:annotations",
-        "//java/com/google/gerrit/entities",
-        "//java/com/google/gerrit/exceptions",
-        "//java/com/google/gerrit/extensions:api",
-        "//java/com/google/gerrit/index",
-        "//java/com/google/gerrit/index:query_exception",
-        "//java/com/google/gerrit/index/project",
-        "//java/com/google/gerrit/lifecycle",
-        "//java/com/google/gerrit/proto",
-        "//java/com/google/gerrit/server",
-        "//lib:gson",
-        "//lib:guava",
-        "//lib:jgit",
-        "//lib:protobuf",
-        "//lib/commons:lang",
         "@elasticsearch-rest-client//jar",
-        "//lib/flogger:api",
-        "//lib/guice",
-        "//lib/guice:guice-assistedinject",
         "@httpasyncclient//jar",
-        "//lib/httpcomponents:httpclient",
-        "//lib/httpcomponents:httpcore",
         "@httpcore-nio//jar",
         "@jackson-core//jar",
     ],
 )
 
+ELASTICSEARCH_DEPS = [
+    "@docker-java-api//jar",
+    "@docker-java-transport//jar",
+    "@duct-tape//jar",
+    "@httpasyncclient//jar",
+    "@jackson-annotations//jar",
+    "@jackson-core//jar",
+    "@jna//jar",
+    "@testcontainers-elasticsearch//jar",
+    "@testcontainers//jar",
+]
+
 java_library(
-    name = "elasticsearch_test_utils",
+    name = "index-elasticsearch__plugin_test_deps",
     testonly = True,
     srcs = [],
     visibility = ["//visibility:public"],
-    deps = [
-        "//java/com/google/gerrit/index",
-        "//lib:guava",
-        "//lib:jgit",
-        "//lib:junit",
-        "//lib/guice",
-        "//lib/httpcomponents:httpcore",
-        "//lib/jackson:jackson-annotations",
-        "//lib/testcontainers",
-        "//lib/testcontainers:docker-java-api",
-        "//lib/testcontainers:docker-java-transport",
-        "@testcontainers-elasticsearch//jar",
+    exports = ELASTICSEARCH_DEPS,
+)
+
+java_library(
+    name = "elasticsearch_test_utils",
+    testonly = True,
+    srcs = glob(
+        ["src/test/java/**/*.java"],
+        exclude = ["src/test/java/**/*Test.java"],
+    ),
+    visibility = ["//visibility:public"],
+    deps = ELASTICSEARCH_DEPS + PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
         ":index-elasticsearch__plugin",
     ],
 )
 
-ELASTICSEARCH_DEPS = [
-    ":elasticsearch_test_utils",
-    "//java/com/google/gerrit/testing:gerrit-test-util",
-    "//lib/guice",
-    "//lib:jgit",
-]
-
-HTTP_TEST_DEPS = [
-    "@httpasyncclient//jar",
-    "//lib/httpcomponents:httpclient",
-]
-
 QUERY_TESTS_DEP = "//javatests/com/google/gerrit/server/query/%s:abstract_query_tests"
 
+ACCOUNT_QUERY_TESTS_DEP = "//javatests/com/google/gerrit/server/query/account:abstract_query_tests"
+
 TYPES = [
     "account",
     "change",
@@ -85,26 +66,30 @@
 
 ELASTICSEARCH_TESTS_V7 = {i: "ElasticV7Query" + i.capitalize() + SUFFIX for i in TYPES}
 
-ELASTICSEARCH_TAGS = [
-    "docker",
-    "elastic",
-]
+[junit_tests(
+    name = "elasticsearch_query_%ss_test_V7" % name,
+    size = "enormous",
+    srcs = ["src/test/java/com/google/gerrit/elasticsearch/" + src],
+    tags = [
+        "docker",
+        "elastic",
+    ],
+    deps = ELASTICSEARCH_DEPS + PLUGIN_TEST_DEPS + [
+        QUERY_TESTS_DEP % name,
+        ":elasticsearch_test_utils",
+        ":index-elasticsearch__plugin",
+    ],
+) for name, src in ELASTICSEARCH_TESTS_V7.items()]
 
 junit_tests(
     name = "index-elasticsearch_tests",
     size = "small",
     srcs = glob(
         ["src/test/java/**/*Test.java"],
-        exclude = ["Elastic*Query*" + SUFFIX],
+        exclude = ["src/test/java/**/Elastic*Query*" + SUFFIX],
     ),
     tags = ["elastic"],
-    deps = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
-        "//java/com/google/gerrit/testing:gerrit-test-util",
-        "//lib:guava",
-        "//lib:jgit",
-        "//lib/guice",
-        "//lib/httpcomponents:httpcore",
-        "//lib/truth",
-        ":elasticsearch_test_utils",
+    deps = PLUGIN_TEST_DEPS + [
+        ":index-elasticsearch__plugin",
     ],
 )
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index b863175..fc1420c 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -5,6 +5,12 @@
 # Ensure artifacts compatibility by selecting them from the Bill Of Materials
 # https://search.maven.org/artifact/net.openhft/chronicle-bom/2.20.191/pom
 def external_plugin_deps():
+    maven_jar(
+        name = "testcontainers",
+        artifact = "org.testcontainers:testcontainers:" + TESTCONTAINERS_VERSION,
+        sha1 = "95c6cfde71c2209f0c29cb14e432471e0b111880",
+    )
+
     # When upgrading elasticsearch-rest-client, also upgrade httpcore-nio
     # and httpasyncclient as necessary in tools/nongoogle.bzl. Consider
     # also the other org.apache.httpcomponents dependencies in
@@ -21,6 +27,38 @@
         sha1 = "595e3a50f59cd3c1d281ca6c1bc4037e277a1353",
     )
 
+    maven_jar(
+        name = "duct-tape",
+        artifact = "org.rnorth.duct-tape:duct-tape:1.0.8",
+        sha1 = "92edc22a9ab2f3e17c9bf700aaee377d50e8b530",
+    )
+
+    maven_jar(
+        name = "visible-assertions",
+        artifact = "org.rnorth.visible-assertions:visible-assertions:2.1.2",
+        sha1 = "20d31a578030ec8e941888537267d3123c2ad1c1",
+    )
+
+    maven_jar(
+        name = "jna",
+        artifact = "net.java.dev.jna:jna:5.5.0",
+        sha1 = "0e0845217c4907822403912ad6828d8e0b256208",
+    )
+
+    DOCKER_JAVA_VERS = "3.2.8"
+
+    maven_jar(
+        name = "docker-java-api",
+        artifact = "com.github.docker-java:docker-java-api:" + DOCKER_JAVA_VERS,
+        sha1 = "4ac22a72d546a9f3523cd4b5fabffa77c4a6ec7c",
+    )
+
+    maven_jar(
+        name = "docker-java-transport",
+        artifact = "com.github.docker-java:docker-java-transport:" + DOCKER_JAVA_VERS,
+        sha1 = "c3b5598c67d0a5e2e780bf48f520da26b9915eab",
+    )
+
     # elasticsearch-rest-client explicitly depends on this version
     maven_jar(
         name = "httpasyncclient",
@@ -42,8 +80,13 @@
     )
 
     maven_jar(
+        name = "jackson-annotations",
+        artifact = "com.fasterxml.jackson.core:jackson-annotations:2.10.3",
+        sha1 = "0f63b3b1da563767d04d2e4d3fc1ae0cdeffebe7",
+    )
+
+    maven_jar(
         name = "httpasyncclient",
         artifact = "org.apache.httpcomponents:httpasyncclient:4.1.4",
         sha1 = "f3a3240681faae3fa46b573a4c7e50cec9db0d86",
     )
-
diff --git a/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
index 562464d..90ce18b 100644
--- a/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
+++ b/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -152,6 +152,11 @@
   }
 
   @Override
+  public void insert(V obj) {
+    replace(obj);
+  }
+
+  @Override
   public Schema<V> getSchema() {
     return schema;
   }
diff --git a/src/main/resources/Documentation/build.md b/src/main/resources/Documentation/build.md
index b6f6e51..1720f52 100644
--- a/src/main/resources/Documentation/build.md
+++ b/src/main/resources/Documentation/build.md
@@ -34,4 +34,20 @@
 
 ```sh
 bazelisk test plugins/index-elasticsearch/...
-```
\ No newline at end of file
+```
+
+This project can be imported into the Eclipse IDE.
+Add the plugin name to the `CUSTOM_PLUGINS` and to the
+`CUSTOM_PLUGINS_TEST_DEPS` set in Gerrit core in
+`tools/bzl/plugins.bzl`, and execute:
+
+```
+  ./tools/eclipse/project.py
+```
+
+More information about Bazel can be found in the [Gerrit
+documentation](../../../Documentation/dev-bazel.html).
+
+[Back to @PLUGIN@ documentation index][index]
+
+[index]: index.html
diff --git a/src/test/java/com/google/gerrit/elasticsearch/ElasticContainer.java b/src/test/java/com/google/gerrit/elasticsearch/ElasticContainer.java
new file mode 100644
index 0000000..503852b
--- /dev/null
+++ b/src/test/java/com/google/gerrit/elasticsearch/ElasticContainer.java
@@ -0,0 +1,59 @@
+// Copyright (C) 2018 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.google.gerrit.elasticsearch;
+
+import org.apache.http.HttpHost;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.elasticsearch.ElasticsearchContainer;
+import org.testcontainers.utility.DockerImageName;
+
+/* Helper class for running ES integration tests in docker container */
+public class ElasticContainer extends ElasticsearchContainer {
+  private static final int ELASTICSEARCH_DEFAULT_PORT = 9200;
+
+  public static ElasticContainer createAndStart(ElasticVersion version) {
+    ElasticContainer container = new ElasticContainer(version);
+    container.start();
+    return container;
+  }
+
+  private static String getImageName(ElasticVersion version) {
+    switch (version) {
+      case V7_6:
+        return "blacktop/elasticsearch:7.6.2";
+      case V7_7:
+        return "blacktop/elasticsearch:7.7.1";
+      case V7_8:
+        return "blacktop/elasticsearch:7.8.1";
+    }
+    throw new IllegalStateException("No tests for version: " + version.name());
+  }
+
+  private ElasticContainer(ElasticVersion version) {
+    super(
+        DockerImageName.parse(getImageName(version))
+            .asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch"));
+  }
+
+  @Override
+  protected Logger logger() {
+    return LoggerFactory.getLogger("org.testcontainers");
+  }
+
+  public HttpHost getHttpHost() {
+    return new HttpHost(getContainerIpAddress(), getMappedPort(ELASTICSEARCH_DEFAULT_PORT));
+  }
+}
diff --git a/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java b/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java
new file mode 100644
index 0000000..c3ca595
--- /dev/null
+++ b/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java
@@ -0,0 +1,87 @@
+// Copyright (C) 2016 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.google.gerrit.elasticsearch;
+
+import com.google.gerrit.index.IndexDefinition;
+import com.google.gerrit.server.LibModuleType;
+import com.google.gerrit.testing.GerritTestName;
+import com.google.gerrit.testing.InMemoryModule;
+import com.google.gerrit.testing.IndexConfig;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+import java.util.Collection;
+import java.util.UUID;
+import org.eclipse.jgit.lib.Config;
+
+public final class ElasticTestUtils {
+  public static void configure(Config config, ElasticContainer container, String prefix) {
+    String hostname = container.getHttpHost().getHostName();
+    int port = container.getHttpHost().getPort();
+    config.setString("index", null, "type", "elasticsearch");
+    config.setString("elasticsearch", null, "server", "http://" + hostname + ":" + port);
+    config.setString("elasticsearch", null, "prefix", prefix);
+    config.setInt("index", null, "maxLimit", 10000);
+  }
+
+  public static void createAllIndexes(Injector injector) {
+    Collection<IndexDefinition<?, ?, ?>> indexDefs =
+        injector.getInstance(Key.get(new TypeLiteral<Collection<IndexDefinition<?, ?, ?>>>() {}));
+    for (IndexDefinition<?, ?, ?> indexDef : indexDefs) {
+      indexDef.getIndexCollection().getSearchIndex().deleteAll();
+    }
+  }
+
+  public static Config getConfig(ElasticVersion version) {
+    ElasticContainer container = ElasticContainer.createAndStart(version);
+    String indicesPrefix = UUID.randomUUID().toString();
+    Config cfg = new Config();
+    configure(cfg, container, indicesPrefix);
+    return cfg;
+  }
+
+  public static Config createConfig() {
+    Config cfg = IndexConfig.create();
+
+    // For some reason enabling the staleness checker increases the flakiness of the Elasticsearch
+    // tests. Hence disable the staleness checker.
+    cfg.setBoolean("index", null, "autoReindexIfStale", false);
+
+    return cfg;
+  }
+
+  public static void configureElasticModule(Config elasticsearchConfig) {
+    elasticsearchConfig.setString(
+        "index",
+        null,
+        "install" + LibModuleType.INDEX_MODULE_TYPE.getConfigKey(),
+        "com.google.gerrit.elasticsearch.ElasticIndexModule");
+  }
+
+  public static Injector createInjector(
+      Config config, GerritTestName testName, ElasticContainer container) {
+    Config elasticsearchConfig = new Config(config);
+    ElasticTestUtils.configureElasticModule(elasticsearchConfig);
+    InMemoryModule.setDefaults(elasticsearchConfig);
+    String indicesPrefix = testName.getSanitizedMethodName();
+    ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
+    return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
+  }
+
+  private ElasticTestUtils() {
+    // hide default constructor
+  }
+}
diff --git a/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java b/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
new file mode 100644
index 0000000..4ee5a16
--- /dev/null
+++ b/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2018 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.google.gerrit.elasticsearch;
+
+import com.google.gerrit.server.query.account.AbstractQueryAccountsTest;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.inject.Injector;
+import org.eclipse.jgit.lib.Config;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class ElasticV7QueryAccountsTest extends AbstractQueryAccountsTest {
+  @ConfigSuite.Default
+  public static Config defaultConfig() {
+    return ElasticTestUtils.createConfig();
+  }
+
+  private static ElasticContainer container;
+
+  @BeforeClass
+  public static void startIndexService() {
+    if (container == null) {
+      // Only start Elasticsearch once
+      container = ElasticContainer.createAndStart(ElasticVersion.V7_8);
+    }
+  }
+
+  @AfterClass
+  public static void stopElasticsearchServer() {
+    if (container != null) {
+      container.stop();
+    }
+  }
+
+  @Override
+  protected void initAfterLifecycleStart() throws Exception {
+    super.initAfterLifecycleStart();
+    ElasticTestUtils.createAllIndexes(injector);
+  }
+
+  @Override
+  protected Injector createInjector() {
+    return ElasticTestUtils.createInjector(config, testName, container);
+  }
+}
diff --git a/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java b/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
new file mode 100644
index 0000000..6a0ce76
--- /dev/null
+++ b/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
@@ -0,0 +1,88 @@
+// Copyright (C) 2018 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.google.gerrit.elasticsearch;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+
+import com.google.gerrit.server.query.change.AbstractQueryChangesTest;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.gerrit.testing.GerritTestName;
+import com.google.inject.Injector;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.eclipse.jgit.lib.Config;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+
+public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest {
+  @ConfigSuite.Default
+  public static Config defaultConfig() {
+    return ElasticTestUtils.createConfig();
+  }
+
+  private static ElasticContainer container;
+  private static CloseableHttpAsyncClient client;
+
+  @BeforeClass
+  public static void startIndexService() {
+    if (container == null) {
+      // Only start Elasticsearch once
+      container = ElasticContainer.createAndStart(ElasticVersion.V7_8);
+      client = HttpAsyncClients.createDefault();
+      client.start();
+    }
+  }
+
+  @AfterClass
+  public static void stopElasticsearchServer() {
+    if (container != null) {
+      container.stop();
+    }
+  }
+
+  @Rule public final GerritTestName testName = new GerritTestName();
+
+  @After
+  public void closeIndex() throws Exception {
+    // Close the index after each test to prevent exceeding Elasticsearch's
+    // shard limit (see Issue 10120).
+    client
+        .execute(
+            new HttpPost(
+                String.format(
+                    "http://%s:%d/%s*/_close",
+                    container.getHttpHost().getHostName(),
+                    container.getHttpHost().getPort(),
+                    testName.getSanitizedMethodName())),
+            HttpClientContext.create(),
+            null)
+        .get(5, MINUTES);
+  }
+
+  @Override
+  protected void initAfterLifecycleStart() throws Exception {
+    super.initAfterLifecycleStart();
+    ElasticTestUtils.createAllIndexes(injector);
+  }
+
+  @Override
+  protected Injector createInjector() {
+    return ElasticTestUtils.createInjector(config, testName, container);
+  }
+}
diff --git a/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java b/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
new file mode 100644
index 0000000..649c0bc
--- /dev/null
+++ b/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2018 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.google.gerrit.elasticsearch;
+
+import com.google.gerrit.server.query.group.AbstractQueryGroupsTest;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.inject.Injector;
+import org.eclipse.jgit.lib.Config;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class ElasticV7QueryGroupsTest extends AbstractQueryGroupsTest {
+  @ConfigSuite.Default
+  public static Config defaultConfig() {
+    return ElasticTestUtils.createConfig();
+  }
+
+  private static ElasticContainer container;
+
+  @BeforeClass
+  public static void startIndexService() {
+    if (container == null) {
+      // Only start Elasticsearch once
+      container = ElasticContainer.createAndStart(ElasticVersion.V7_8);
+    }
+  }
+
+  @AfterClass
+  public static void stopElasticsearchServer() {
+    if (container != null) {
+      container.stop();
+    }
+  }
+
+  @Override
+  protected void initAfterLifecycleStart() throws Exception {
+    super.initAfterLifecycleStart();
+    ElasticTestUtils.createAllIndexes(injector);
+  }
+
+  @Override
+  protected Injector createInjector() {
+    return ElasticTestUtils.createInjector(config, testName, container);
+  }
+}
diff --git a/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java b/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
new file mode 100644
index 0000000..d3b3d44
--- /dev/null
+++ b/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2018 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.google.gerrit.elasticsearch;
+
+import com.google.gerrit.server.query.project.AbstractQueryProjectsTest;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.inject.Injector;
+import org.eclipse.jgit.lib.Config;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class ElasticV7QueryProjectsTest extends AbstractQueryProjectsTest {
+  @ConfigSuite.Default
+  public static Config defaultConfig() {
+    return ElasticTestUtils.createConfig();
+  }
+
+  private static ElasticContainer container;
+
+  @BeforeClass
+  public static void startIndexService() {
+    if (container == null) {
+      // Only start Elasticsearch once
+      container = ElasticContainer.createAndStart(ElasticVersion.V7_8);
+    }
+  }
+
+  @AfterClass
+  public static void stopElasticsearchServer() {
+    if (container != null) {
+      container.stop();
+    }
+  }
+
+  @Override
+  protected void initAfterLifecycleStart() throws Exception {
+    super.initAfterLifecycleStart();
+    ElasticTestUtils.createAllIndexes(injector);
+  }
+
+  @Override
+  protected Injector createInjector() {
+    return ElasticTestUtils.createInjector(config, testName, container);
+  }
+}