Merge branch 'stable-3.12'

* stable-3.12:
  Rename master word to primary
  Forward project-wide change index deletions

Change-Id: Ied18b2e9494a61e72dde33c8dc53723eb382fd83
diff --git a/README.md b/README.md
index 1542d71..11a33b2 100644
--- a/README.md
+++ b/README.md
@@ -88,7 +88,11 @@
 
 It is the simplest and safest configuration, where only one Gerrit primary at a
 time serves the incoming requests.
+<<<<<<< HEAD
 In case of failure of the active primary, the traffic is forwarded to the backup.
+=======
+In case of failure of the primary, the traffic is forwarded to the backup.
+>>>>>>> stable-3.12
 
 Assuming a load-balancing implemented using [HAProxy](http://www.haproxy.org/)
 associated with the domain name `gerrit.mycompany.com`, exposing Gerrit cluster nodes
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/SetupLocalHAReplica.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/SetupLocalHAReplica.java
index b5c9730..374d327 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/SetupLocalHAReplica.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/SetupLocalHAReplica.java
@@ -46,17 +46,17 @@
 class SetupLocalHAReplica {
   private static final String DATABASE = "database";
 
-  private final SitePaths masterSitePaths;
-  private final FileBasedConfig masterConfig;
+  private final SitePaths primarySitePaths;
+  private final FileBasedConfig primaryConfig;
   private final Path sharedDir;
 
   private SitePaths replicaSitePaths;
 
   @Inject
-  SetupLocalHAReplica(SitePaths masterSitePaths, InitFlags flags) {
-    this.masterSitePaths = masterSitePaths;
-    this.masterConfig = flags.cfg;
-    this.sharedDir = masterSitePaths.site_path.resolve(DEFAULT_SHARED_DIRECTORY);
+  SetupLocalHAReplica(SitePaths primarySitePaths, InitFlags flags) {
+    this.primarySitePaths = primarySitePaths;
+    this.primaryConfig = flags.cfg;
+    this.sharedDir = primarySitePaths.site_path.resolve(DEFAULT_SHARED_DIRECTORY);
   }
 
   void run(SitePaths replica, FileBasedConfig pluginConfig)
@@ -74,15 +74,15 @@
 
     mkdir(replicaSitePaths.logs_dir);
     mkdir(replicaSitePaths.tmp_dir);
-    symlink(Paths.get(masterConfig.getString("gerrit", null, "basePath")));
+    symlink(Paths.get(primaryConfig.getString("gerrit", null, "basePath")));
     symlink(sharedDir);
 
     FileBasedConfig replicaConfig =
         new FileBasedConfig(replicaSitePaths.gerrit_config.toFile(), FS.DETECTED);
     replicaConfig.load();
 
-    if ("h2".equals(masterConfig.getString(DATABASE, null, "type"))) {
-      masterConfig.setBoolean(DATABASE, "h2", "autoServer", true);
+    if ("h2".equals(primaryConfig.getString(DATABASE, null, "type"))) {
+      primaryConfig.setBoolean(DATABASE, "h2", "autoServer", true);
       replicaConfig.setBoolean(DATABASE, "h2", "autoServer", true);
       symlinkH2ReviewDbDir();
     }
@@ -91,32 +91,32 @@
   private List<Path> listDirsForCopy() throws IOException {
     ImmutableList.Builder<Path> toSkipBuilder = ImmutableList.builder();
     toSkipBuilder.add(
-        masterSitePaths.resolve(masterConfig.getString("gerrit", null, "basePath")),
-        masterSitePaths.db_dir,
-        masterSitePaths.logs_dir,
+        primarySitePaths.resolve(primaryConfig.getString("gerrit", null, "basePath")),
+        primarySitePaths.db_dir,
+        primarySitePaths.logs_dir,
         replicaSitePaths.site_path,
-        masterSitePaths.site_path.resolve(sharedDir),
-        masterSitePaths.tmp_dir);
-    if ("h2".equals(masterConfig.getString(DATABASE, null, "type"))) {
+        primarySitePaths.site_path.resolve(sharedDir),
+        primarySitePaths.tmp_dir);
+    if ("h2".equals(primaryConfig.getString(DATABASE, null, "type"))) {
       toSkipBuilder.add(
-          masterSitePaths.resolve(masterConfig.getString(DATABASE, null, DATABASE)).getParent());
+          primarySitePaths.resolve(primaryConfig.getString(DATABASE, null, DATABASE)).getParent());
     }
     final ImmutableList<Path> toSkip = toSkipBuilder.build();
 
     final ArrayList<Path> dirsForCopy = new ArrayList<>();
     Files.walkFileTree(
-        masterSitePaths.site_path,
+        primarySitePaths.site_path,
         EnumSet.of(FileVisitOption.FOLLOW_LINKS),
         Integer.MAX_VALUE,
         new SimpleFileVisitor<Path>() {
           @Override
           public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
               throws IOException {
-            if (Files.isSameFile(dir, masterSitePaths.site_path)) {
+            if (Files.isSameFile(dir, primarySitePaths.site_path)) {
               return FileVisitResult.CONTINUE;
             }
 
-            Path p = masterSitePaths.site_path.relativize(dir);
+            Path p = primarySitePaths.site_path.relativize(dir);
             if (shouldSkip(p)) {
               return FileVisitResult.SKIP_SUBTREE;
             }
@@ -125,7 +125,7 @@
           }
 
           private boolean shouldSkip(Path p) throws IOException {
-            Path resolved = masterSitePaths.site_path.resolve(p);
+            Path resolved = primarySitePaths.site_path.resolve(p);
             for (Path skip : toSkip) {
               if (skip.toFile().exists() && Files.isSameFile(resolved, skip)) {
                 return true;
@@ -139,7 +139,7 @@
   }
 
   private void copyFiles(Path dir) throws IOException {
-    final Path source = masterSitePaths.site_path.resolve(dir);
+    final Path source = primarySitePaths.site_path.resolve(dir);
     final Path target = replicaSitePaths.site_path.resolve(dir);
     Files.createDirectories(target);
     Files.walkFileTree(
@@ -167,12 +167,12 @@
     if (!path.isAbsolute()) {
       Files.createSymbolicLink(
           replicaSitePaths.site_path.resolve(path),
-          masterSitePaths.site_path.resolve(path).toAbsolutePath().normalize());
+          primarySitePaths.site_path.resolve(path).toAbsolutePath().normalize());
     }
   }
 
   private void symlinkH2ReviewDbDir() throws IOException {
-    symlink(Paths.get(masterConfig.getString(DATABASE, null, DATABASE)).getParent());
+    symlink(Paths.get(primaryConfig.getString(DATABASE, null, DATABASE)).getParent());
   }
 
   private void configureMainSection(FileBasedConfig pluginConfig) throws IOException {
@@ -180,7 +180,7 @@
         MAIN_SECTION,
         null,
         SHARED_DIRECTORY_KEY,
-        masterSitePaths.site_path.relativize(sharedDir).toString());
+        primarySitePaths.site_path.relativize(sharedDir).toString());
     pluginConfig.save();
   }
 
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/SharedDirectory.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/SharedDirectory.java
index 29e2a6b..d0d7d25 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/SharedDirectory.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/SharedDirectory.java
@@ -20,7 +20,7 @@
 import java.lang.annotation.Retention;
 
 /**
- * {@link java.nio.file.Path} to a directory accessible from both master instances.
+ * {@link java.nio.file.Path} to a directory accessible from both primary instances.
  *
  * <p>Example of usage:
  *
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandler.java
index a787d3a..fd654ba 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandler.java
@@ -14,6 +14,8 @@
 
 package com.ericsson.gerrit.plugins.highavailability.forwarder;
 
+import static com.ericsson.gerrit.plugins.highavailability.forwarder.rest.RestForwarder.ALL_CHANGES_FOR_PROJECT;
+
 import com.ericsson.gerrit.plugins.highavailability.index.ChangeChecker;
 import com.ericsson.gerrit.plugins.highavailability.index.ChangeCheckerImpl;
 import com.ericsson.gerrit.plugins.highavailability.index.ForwardedIndexExecutor;
@@ -119,19 +121,29 @@
   @Override
   protected CompletableFuture<Boolean> doDelete(String id, Optional<IndexEvent> indexEvent)
       throws IOException {
-    indexer.delete(parseChangeId(id));
-    log.atFine().log("Change %s successfully deleted from index", id);
+    if (ALL_CHANGES_FOR_PROJECT.equals(extractChangeId(id))) {
+      Project.NameKey projectName = parseProject(id);
+      indexer.deleteAllForProject(projectName);
+      log.atFine().log("All %s changes successfully deleted from index", projectName.get());
+    } else {
+      indexer.delete(parseChangeId(id));
+      log.atFine().log("Change %s successfully deleted from index", id);
+    }
     return CompletableFuture.completedFuture(true);
   }
 
   private static Change.Id parseChangeId(String id) {
-    return Change.id(Integer.parseInt(getChangeIdParts(id).get(1)));
+    return Change.id(Integer.parseInt(extractChangeId(id)));
   }
 
   private static Project.NameKey parseProject(String id) {
     return Project.nameKey(getChangeIdParts(id).get(0));
   }
 
+  private static String extractChangeId(String id) {
+    return getChangeIdParts(id).get(1);
+  }
+
   private static List<String> getChangeIdParts(String id) {
     return Splitter.on("~").splitToList(id);
   }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java
index b73b676..3854ee3 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java
@@ -14,14 +14,15 @@
 
 package com.ericsson.gerrit.plugins.highavailability.forwarder;
 
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.events.Event;
 import java.util.concurrent.CompletableFuture;
 
-/** Forward indexing, stream events and cache evictions to the other master */
+/** Forward indexing, stream events and cache evictions to the other primary */
 public interface Forwarder {
 
   /**
-   * Forward a account indexing event to the other master.
+   * Forward an account indexing event to the other primary.
    *
    * @param accountId the account to index.
    * @param indexEvent the details of the index event.
@@ -31,7 +32,7 @@
   CompletableFuture<Boolean> indexAccount(int accountId, IndexEvent indexEvent);
 
   /**
-   * Forward a change indexing event to the other master.
+   * Forward a change indexing event to the other primary.
    *
    * @param projectName the project of the change to index.
    * @param changeId the change to index.
@@ -42,7 +43,7 @@
   CompletableFuture<Boolean> indexChange(String projectName, int changeId, IndexEvent indexEvent);
 
   /**
-   * Forward a change indexing event to the other master using batch index endpoint.
+   * Forward a change indexing event to the other primary using batch index endpoint.
    *
    * @param projectName the project of the change to index.
    * @param changeId the change to index.
@@ -54,7 +55,7 @@
       String projectName, int changeId, IndexEvent indexEvent);
 
   /**
-   * Forward a delete change from index event to the other master.
+   * Forward a delete change from index event to the other primary.
    *
    * @param changeId the change to remove from the index.
    * @param indexEvent the details of the index event.
@@ -64,7 +65,7 @@
   CompletableFuture<Boolean> deleteChangeFromIndex(int changeId, IndexEvent indexEvent);
 
   /**
-   * Forward a group indexing event to the other master.
+   * Forward a group indexing event to the other primary.
    *
    * @param uuid the group to index.
    * @param indexEvent the details of the index event.
@@ -74,7 +75,7 @@
   CompletableFuture<Boolean> indexGroup(String uuid, IndexEvent indexEvent);
 
   /**
-   * Forward a project indexing event to the other master.
+   * Forward a project indexing event to the other primary.
    *
    * @param projectName the project to index.
    * @param indexEvent the details of the index event.
@@ -84,7 +85,7 @@
   CompletableFuture<Boolean> indexProject(String projectName, IndexEvent indexEvent);
 
   /**
-   * Forward a stream event to the other master.
+   * Forward a stream event to the other primary.
    *
    * @param event the event to forward.
    * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
@@ -93,7 +94,7 @@
   CompletableFuture<Boolean> send(Event event);
 
   /**
-   * Forward a cache eviction event to the other master.
+   * Forward a cache eviction event to the other primary.
    *
    * @param cacheName the name of the cache to evict an entry from.
    * @param key the key identifying the entry to evict from the cache.
@@ -103,7 +104,7 @@
   CompletableFuture<Boolean> evict(String cacheName, Object key);
 
   /**
-   * Forward an addition to the project list cache to the other master.
+   * Forward an addition to the project list cache to the other primary.
    *
    * @param projectName the name of the project to add to the project list cache
    * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
@@ -112,11 +113,20 @@
   CompletableFuture<Boolean> addToProjectList(String projectName);
 
   /**
-   * Forward a removal from the project list cache to the other master.
+   * Forward a removal from the project list cache to the other primary.
    *
    * @param projectName the name of the project to remove from the project list cache
    * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
    *     false.
    */
   CompletableFuture<Boolean> removeFromProjectList(String projectName);
+
+  /**
+   * Forward the removal of all project changes from index to the other primary.
+   *
+   * @param projectName the name of the project whose changes should be removed from the index
+   * @return {@link CompletableFuture} of true if successful, otherwise {@link CompletableFuture} of
+   *     false.
+   */
+  CompletableFuture<Boolean> deleteAllChangesForProject(Project.NameKey projectName);
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/DeleteAllProjectChangesFromIndex.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/DeleteAllProjectChangesFromIndex.java
new file mode 100644
index 0000000..79933fb
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/DeleteAllProjectChangesFromIndex.java
@@ -0,0 +1,32 @@
+// Copyright (C) 2025 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.ericsson.gerrit.plugins.highavailability.forwarder.jgroups;
+
+import com.google.gerrit.entities.Project;
+
+public class DeleteAllProjectChangesFromIndex extends Command {
+  static final String TYPE = "delete-all-project-changes-from-index";
+
+  private final Project.NameKey projectName;
+
+  protected DeleteAllProjectChangesFromIndex(Project.NameKey projectName) {
+    super(TYPE);
+    this.projectName = projectName;
+  }
+
+  public String getProjectName() {
+    return projectName.get();
+  }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/JGroupsForwarder.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/JGroupsForwarder.java
index 40a358e..9f1f179 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/JGroupsForwarder.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/JGroupsForwarder.java
@@ -19,6 +19,7 @@
 import com.ericsson.gerrit.plugins.highavailability.forwarder.Forwarder;
 import com.ericsson.gerrit.plugins.highavailability.forwarder.IndexEvent;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.events.Event;
 import com.google.gson.Gson;
 import com.google.inject.Inject;
@@ -108,6 +109,11 @@
     return execute(new RemoveFromProjectList(projectName));
   }
 
+  @Override
+  public CompletableFuture<Boolean> deleteAllChangesForProject(Project.NameKey projectName) {
+    return execute(new DeleteAllProjectChangesFromIndex(projectName));
+  }
+
   private CompletableFuture<Boolean> execute(Command cmd) {
     return executor.getAsync(() -> executeOnce(cmd));
   }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
index 922fbd6..d53cd82 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
@@ -20,8 +20,10 @@
 import com.ericsson.gerrit.plugins.highavailability.forwarder.IndexEvent;
 import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.HttpResponseHandler.HttpResult;
 import com.ericsson.gerrit.plugins.highavailability.peers.PeerInfo;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.server.events.Event;
@@ -37,12 +39,14 @@
 import org.apache.http.HttpException;
 import org.apache.http.client.ClientProtocolException;
 
-class RestForwarder implements Forwarder {
+public class RestForwarder implements Forwarder {
   enum RequestMethod {
     POST,
     DELETE
   }
 
+  public static final String ALL_CHANGES_FOR_PROJECT = "0";
+
   private static final FluentLogger log = FluentLogger.forEnclosingClass();
 
   private final HttpSession httpSession;
@@ -115,6 +119,12 @@
     return escapedProjectName + '~' + changeId;
   }
 
+  @VisibleForTesting
+  public static String buildAllChangesForProjectEndpoint(String projectName) {
+    String escapedProjectName = Url.encode(projectName);
+    return escapedProjectName + '~' + ALL_CHANGES_FOR_PROJECT;
+  }
+
   @Override
   public CompletableFuture<Boolean> indexProject(String projectName, IndexEvent event) {
     return execute(
@@ -150,6 +160,15 @@
         Url.encode(projectName));
   }
 
+  @Override
+  public CompletableFuture<Boolean> deleteAllChangesForProject(Project.NameKey projectName) {
+    return execute(
+        RequestMethod.DELETE,
+        "Delete all project changes from index",
+        "index/change",
+        buildAllChangesForProjectEndpoint(projectName.get()));
+  }
+
   private static String buildProjectListEndpoint() {
     return Joiner.on("/").join("cache", Constants.PROJECT_LIST);
   }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java
index ec2a091..6475d99 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java
@@ -18,6 +18,7 @@
 import com.ericsson.gerrit.plugins.highavailability.forwarder.Forwarder;
 import com.ericsson.gerrit.plugins.highavailability.forwarder.IndexEvent;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.events.AccountIndexedListener;
 import com.google.gerrit.extensions.events.ChangeIndexedListener;
 import com.google.gerrit.extensions.events.GroupIndexedListener;
@@ -58,6 +59,17 @@
     currCtx.onlyWithContext((ctx) -> executeIndexChangeTask(projectName, id));
   }
 
+  @Override
+  public void onAllChangesDeletedForProject(String projectName) {
+    currCtx.onlyWithContext((ctx) -> executeAllChangesDeletedForProject(projectName));
+  }
+
+  private void executeAllChangesDeletedForProject(String projectName) {
+    if (!Context.isForwardedEvent()) {
+      forwarder.deleteAllChangesForProject(Project.nameKey(projectName));
+    }
+  }
+
   private void executeIndexChangeTask(String projectName, int id) {
     if (!Context.isForwardedEvent()) {
       String changeId = projectName + "~" + id;
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java
index cc6939c..6a3a348 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java
@@ -14,6 +14,7 @@
 
 package com.ericsson.gerrit.plugins.highavailability.forwarder;
 
+import static com.ericsson.gerrit.plugins.highavailability.forwarder.rest.RestForwarder.buildAllChangesForProjectEndpoint;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -54,6 +55,7 @@
 
   private static final int TEST_CHANGE_NUMBER = 123;
   private static String TEST_PROJECT = "test/project";
+  private static final String TEST_PROJECT_ENCODED = "test%2Fproject";
   private static String TEST_CHANGE_ID = TEST_PROJECT + "~" + TEST_CHANGE_NUMBER;
   private static final boolean CHANGE_EXISTS = true;
   private static final boolean CHANGE_DOES_NOT_EXIST = false;
@@ -112,6 +114,14 @@
   }
 
   @Test
+  public void AllChangesAreDeletedFromIndex() throws Exception {
+    handler
+        .index(buildAllChangesForProjectEndpoint(TEST_PROJECT), Operation.DELETE, Optional.empty())
+        .get(10, SECONDS);
+    verify(indexerMock, times(1)).deleteAllForProject(Project.nameKey(TEST_PROJECT_ENCODED));
+  }
+
+  @Test
   public void changeToIndexDoesNotExist() throws Exception {
     setupChangeAccessRelatedMocks(CHANGE_DOES_NOT_EXIST, CHANGE_OUTDATED);
     handler.index(TEST_CHANGE_ID, Operation.INDEX, Optional.empty()).get(10, SECONDS);
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
index 8a2745e..4b98fe1 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
@@ -14,6 +14,7 @@
 
 package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
 
+import static com.ericsson.gerrit.plugins.highavailability.forwarder.rest.RestForwarder.buildAllChangesForProjectEndpoint;
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -32,6 +33,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.gson.Gson;
@@ -80,6 +82,14 @@
               PROJECT_NAME_URL_END + "~" + CHANGE_NUMBER);
   private static final String DELETE_CHANGE_ENDPOINT =
       Joiner.on("/").join(URL, PLUGINS, PLUGIN_NAME, "index/change", "~" + CHANGE_NUMBER);
+  private static final String DELETE_ALL_CHANGES_ENDPOINT =
+      Joiner.on("/")
+          .join(
+              URL,
+              PLUGINS,
+              PLUGIN_NAME,
+              "index/change",
+              buildAllChangesForProjectEndpoint(PROJECT_NAME));
   private static final int ACCOUNT_NUMBER = 2;
   private static final String INDEX_ACCOUNT_ENDPOINT =
       Joiner.on("/").join(URL, PLUGINS, PLUGIN_NAME, "index/account", ACCOUNT_NUMBER);
@@ -246,6 +256,17 @@
   }
 
   @Test
+  public void testAllChangesDeletedFromIndexOK() throws Exception {
+    when(httpSessionMock.delete(eq(DELETE_ALL_CHANGES_ENDPOINT)))
+        .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+    assertThat(
+            forwarder
+                .deleteAllChangesForProject(Project.nameKey(PROJECT_NAME))
+                .get(TEST_TIMEOUT, TEST_TIMEOUT_UNITS))
+        .isTrue();
+  }
+
+  @Test
   public void testChangeDeletedFromIndexFailed() throws Exception {
     when(httpSessionMock.delete(eq(DELETE_CHANGE_ENDPOINT)))
         .thenReturn(new HttpResult(FAILED, EMPTY_MSG));