Restore dockerized integration tests

In Ib7b5167ce2 was extracted as index lib module. The dockerized tests
were removed during extraction, as those tests require more work. This
change adds the tests and adapts the build toolchain to support running
them.

Also implement index-elasticsearch__plugin_test_deps build rule and
document how to import this lib module in eclipse IDE.

Change-Id: Ieb5f8aeb22780bf9d013775613147f85670aff3f
diff --git a/BUILD b/BUILD
index 4e5a433..0b914bf 100644
--- a/BUILD
+++ b/BUILD
@@ -1,6 +1,8 @@
+load("@rules_java//java:defs.bzl", "java_library")
 load("//tools/bzl:junit.bzl", "junit_tests")
 load(
     "//tools/bzl:plugin.bzl",
+    "PLUGIN_DEPS",
     "PLUGIN_TEST_DEPS",
     "gerrit_plugin",
 )
@@ -17,10 +19,76 @@
     ],
 )
 
+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 = "index-elasticsearch__plugin_test_deps",
+    testonly = True,
+    srcs = [],
+    visibility = ["//visibility:public"],
+    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",
+    ],
+)
+
+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",
+    "group",
+    "project",
+]
+
+SUFFIX = "sTest.java"
+
+ELASTICSEARCH_TESTS_V7 = {i: "ElasticV7Query" + i.capitalize() + SUFFIX for i in TYPES}
+
+[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"]),
+    srcs = glob(
+        ["src/test/java/**/*Test.java"],
+        exclude = ["src/test/java/**/Elastic*Query*" + SUFFIX],
+    ),
     tags = ["elastic"],
     deps = PLUGIN_TEST_DEPS + [
         ":index-elasticsearch__plugin",
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);
+  }
+}