Merge branch 'stable-2.13'
* stable-2.13:
Forward account indexing events to the other master
Refactor RestForwarderTest for readability
Change-Id: I5ee0bf41ff996c4b02a4c137a3eedebe2bf91c51
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 0d1a0bb..1608042 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
@@ -22,6 +22,14 @@
public interface Forwarder {
/**
+ * Forward a account indexing event to the other master.
+ *
+ * @param accountId the account to index.
+ * @return true if successful, otherwise false.
+ */
+ boolean indexAccount(int accountId);
+
+ /**
* Forward a change indexing event to the other master.
*
* @param changeId the change to index.
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServlet.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServlet.java
new file mode 100644
index 0000000..a96b1c4
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServlet.java
@@ -0,0 +1,113 @@
+// Copyright (C) 2017 Ericsson
+//
+// 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.rest;
+
+import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
+import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
+
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.server.index.account.AccountIndexer;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import com.ericsson.gerrit.plugins.highavailability.forwarder.Context;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Singleton
+class IndexAccountRestApiServlet extends HttpServlet {
+ private static final long serialVersionUID = -1L;
+ private static final Logger logger =
+ LoggerFactory.getLogger(IndexAccountRestApiServlet.class);
+ private static final Map<Account.Id, AtomicInteger> accountIdLocks =
+ new HashMap<>();
+
+ private final AccountIndexer indexer;
+
+ @Inject
+ IndexAccountRestApiServlet(AccountIndexer indexer) {
+ this.indexer = indexer;
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse rsp)
+ throws IOException, ServletException {
+ rsp.setContentType("text/plain");
+ rsp.setCharacterEncoding("UTF-8");
+ String path = req.getPathInfo();
+ String accountId = path.substring(path.lastIndexOf('/') + 1);
+ Account.Id id = Account.Id.parse(accountId);
+ try {
+ Context.setForwardedEvent(true);
+ index(id);
+ rsp.setStatus(SC_NO_CONTENT);
+ } catch (IOException e) {
+ sendError(rsp, SC_CONFLICT, e.getMessage());
+ logger.error("Unable to update account index", e);
+ } finally {
+ Context.unsetForwardedEvent();
+ }
+ }
+
+ private static void sendError(HttpServletResponse rsp, int statusCode,
+ String message) {
+ try {
+ rsp.sendError(statusCode, message);
+ } catch (IOException e) {
+ logger.error("Failed to send error messsage: " + e.getMessage(), e);
+ }
+ }
+
+ private void index(Account.Id id) throws IOException {
+ AtomicInteger accountIdLock = getAndIncrementAccountIdLock(id);
+ synchronized (accountIdLock) {
+ indexer.index(id);;
+ logger.debug("Account {} successfully indexed", id);
+ }
+ if (accountIdLock.decrementAndGet() == 0) {
+ removeAccountIdLock(id);
+ }
+ }
+
+ private AtomicInteger getAndIncrementAccountIdLock(Account.Id id) {
+ synchronized (accountIdLocks) {
+ AtomicInteger accountIdLock = accountIdLocks.get(id);
+ if (accountIdLock == null) {
+ accountIdLock = new AtomicInteger(1);
+ accountIdLocks.put(id, accountIdLock);
+ } else {
+ accountIdLock.incrementAndGet();
+ }
+ return accountIdLock;
+ }
+ }
+
+ private void removeAccountIdLock(Account.Id id) {
+ synchronized (accountIdLocks) {
+ accountIdLocks.remove(id);
+ }
+ }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServlet.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServlet.java
index 0ed9bcc..ad4aa5e 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServlet.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServlet.java
@@ -84,7 +84,7 @@
rsp.setStatus(SC_NO_CONTENT);
} catch (IOException e) {
sendError(rsp,SC_CONFLICT, e.getMessage());
- logger.error("Unable to update index", e);
+ logger.error("Unable to update change index", e);
} catch (OrmException e) {
String msg = "Error trying to find a change \n";
sendError(rsp,SC_NOT_FOUND, msg);
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 cc58e3c..467ae53 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
@@ -51,6 +51,17 @@
}
@Override
+ public boolean indexAccount(final int accountId) {
+ return new Request("index account " + accountId) {
+ @Override
+ HttpResult send() throws IOException {
+ return httpSession.post(Joiner.on("/").join(pluginRelativePath,
+ "index/account", accountId));
+ }
+ }.execute();
+ }
+
+ @Override
public boolean indexChange(final int changeId) {
return new Request("index change " + changeId) {
@Override
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderServletModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderServletModule.java
index 4b0157d..bd093ae 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderServletModule.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderServletModule.java
@@ -19,6 +19,7 @@
public class RestForwarderServletModule extends HttpPluginModule {
@Override
protected void configureServlets() {
+ serveRegex("/index/account/\\d+$").with(IndexAccountRestApiServlet.class);
serveRegex("/index/change/\\d+$").with(IndexChangeRestApiServlet.class);
serve("/event").with(EventRestApiServlet.class);
serve("/cache/*").with(CacheRestApiServlet.class);
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 36a5c82..99fffa6 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
@@ -16,6 +16,7 @@
import com.google.common.base.Objects;
import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.events.AccountIndexedListener;
import com.google.gerrit.extensions.events.ChangeIndexedListener;
import com.google.inject.Inject;
@@ -27,7 +28,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
-class IndexEventHandler implements ChangeIndexedListener {
+class IndexEventHandler implements ChangeIndexedListener, AccountIndexedListener {
private final Executor executor;
private final Forwarder forwarder;
private final String pluginName;
@@ -44,61 +45,117 @@
}
@Override
- public void onChangeIndexed(int id) {
- executeIndexTask(id, false);
- }
-
- @Override
- public void onChangeDeleted(int id) {
- executeIndexTask(id, true);
- }
-
- private void executeIndexTask(int id, boolean deleted) {
+ public void onAccountIndexed(int id) {
if (!Context.isForwardedEvent()) {
- IndexTask indexTask = new IndexTask(id, deleted);
- if (queuedTasks.add(indexTask)) {
- executor.execute(indexTask);
+ IndexAccountTask task = new IndexAccountTask(id);
+ if (queuedTasks.add(task)) {
+ executor.execute(task);
}
}
}
- class IndexTask implements Runnable {
- private int changeId;
- private boolean deleted;
+ @Override
+ public void onChangeIndexed(int id) {
+ executeIndexChangeTask(id, false);
+ }
- IndexTask(int changeId, boolean deleted) {
- this.changeId = changeId;
- this.deleted = deleted;
+ @Override
+ public void onChangeDeleted(int id) {
+ executeIndexChangeTask(id, true);
+ }
+
+ private void executeIndexChangeTask(int id, boolean deleted) {
+ if (!Context.isForwardedEvent()) {
+ IndexChangeTask task = new IndexChangeTask(id, deleted);
+ if (queuedTasks.add(task)) {
+ executor.execute(task);
+ }
+ }
+ }
+
+ abstract class IndexTask implements Runnable {
+ protected int id;
+
+ IndexTask(int id) {
+ this.id = id;
}
@Override
public void run() {
queuedTasks.remove(this);
+ execute();
+ }
+
+ abstract void execute();
+ }
+
+ class IndexChangeTask extends IndexTask {
+ private boolean deleted;
+
+ IndexChangeTask(int changeId, boolean deleted) {
+ super(changeId);
+ this.deleted = deleted;
+ }
+
+ @Override
+ public void execute() {
if (deleted) {
- forwarder.deleteChangeFromIndex(changeId);
+ forwarder.deleteChangeFromIndex(id);
} else {
- forwarder.indexChange(changeId);
+ forwarder.indexChange(id);
}
}
@Override
public int hashCode() {
- return Objects.hashCode(changeId, deleted);
+ return Objects.hashCode(IndexChangeTask.class, id, deleted);
}
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof IndexTask)) {
+ if (!(obj instanceof IndexChangeTask)) {
return false;
}
- IndexTask other = (IndexTask) obj;
- return changeId == other.changeId && deleted == other.deleted;
+ IndexChangeTask other = (IndexChangeTask) obj;
+ return id == other.id && deleted == other.deleted;
}
@Override
public String toString() {
return String.format("[%s] Index change %s in target instance",
- pluginName, changeId);
+ pluginName, id);
+ }
+ }
+
+ class IndexAccountTask extends IndexTask {
+
+ IndexAccountTask(int accountId) {
+ super(accountId);
+ }
+
+ @Override
+ public void execute() {
+ forwarder.indexAccount(id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(IndexAccountTask.class, id);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof IndexAccountTask)) {
+ return false;
+ }
+ IndexAccountTask other = (IndexAccountTask) obj;
+ return id == other.id;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[%s] Index account %s in target instance",
+ pluginName, id);
}
}
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexModule.java
index 7e8b116..cd9839c 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexModule.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexModule.java
@@ -14,6 +14,7 @@
package com.ericsson.gerrit.plugins.highavailability.index;
+import com.google.gerrit.extensions.events.AccountIndexedListener;
import com.google.gerrit.extensions.events.ChangeIndexedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.lifecycle.LifecycleModule;
@@ -30,5 +31,7 @@
listener().to(IndexExecutorProvider.class);
DynamicSet.bind(binder(), ChangeIndexedListener.class).to(
IndexEventHandler.class);
+ DynamicSet.bind(binder(), AccountIndexedListener.class).to(
+ IndexEventHandler.class);
}
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServletTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServletTest.java
new file mode 100644
index 0000000..8c81817
--- /dev/null
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServletTest.java
@@ -0,0 +1,88 @@
+// Copyright (C) 2017 Ericsson
+//
+// 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.rest;
+
+import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
+import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.server.index.account.AccountIndexer;
+import com.google.gwtorm.client.KeyUtil;
+import com.google.gwtorm.server.StandardKeyEncoder;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@RunWith(MockitoJUnitRunner.class)
+public class IndexAccountRestApiServletTest {
+ private static final String ACCOUNT_NUMBER = "1";
+
+ @Mock
+ private AccountIndexer indexer;
+ @Mock
+ private HttpServletRequest req;
+ @Mock
+ private HttpServletResponse rsp;
+
+ private Account.Id id;
+ private IndexAccountRestApiServlet servlet;
+
+ @BeforeClass
+ public static void setup() {
+ KeyUtil.setEncoderImpl(new StandardKeyEncoder());
+ }
+
+ @Before
+ public void setUpMocks() {
+ servlet = new IndexAccountRestApiServlet(indexer);
+ id = Account.Id.parse(ACCOUNT_NUMBER);
+ when(req.getPathInfo()).thenReturn("/index/account/" + ACCOUNT_NUMBER);
+ }
+
+ @Test
+ public void accountIsIndexed() throws Exception {
+ servlet.doPost(req, rsp);
+ verify(indexer, times(1)).index(id);
+ verify(rsp).setStatus(SC_NO_CONTENT);
+ }
+
+ @Test
+ public void indexerThrowsIOExceptionTryingToIndexAccount() throws Exception {
+ doThrow(new IOException("io-error")).when(indexer).index(id);
+ servlet.doPost(req, rsp);
+ verify(rsp).sendError(SC_CONFLICT, "io-error");
+ }
+
+ @Test
+ public void sendErrorThrowsIOException() throws Exception {
+ doThrow(new IOException("io-error")).when(indexer).index(id);
+ doThrow(new IOException("someError")).when(rsp).sendError(SC_CONFLICT, "io-error");
+ servlet.doPost(req, rsp);
+ verify(rsp).sendError(SC_CONFLICT, "io-error");
+ }
+}
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 ecc8fbf..3475e47 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
@@ -15,7 +15,7 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.anyString;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -25,11 +25,15 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.events.Event;
import com.google.gson.GsonBuilder;
+import com.google.gwtorm.client.KeyUtil;
+import com.google.gwtorm.server.StandardKeyEncoder;
import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.ericsson.gerrit.plugins.highavailability.cache.Constants;
import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.HttpResponseHandler.HttpResult;
+import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
@@ -39,277 +43,240 @@
public class RestForwarderTest {
private static final String PLUGIN_NAME = "high-availability";
private static final String EMPTY_MSG = "";
- private static final String ERROR_MSG = "Error";
- private static final String EXCEPTION_MSG = "Exception";
private static final boolean SUCCESSFUL = true;
private static final boolean FAILED = false;
- private static final boolean DO_NOT_THROW_EXCEPTION = false;
- private static final boolean THROW_EXCEPTION = true;
-
- private static final int MAX_TRIES = 3;
- private static final int RETRY_INTERVAL = 250;
//Index
private static final int CHANGE_NUMBER = 1;
- private static final String DELETE_OP = "delete";
- private static final String INDEX_OP = "index/change";
+ private static final String INDEX_CHANGE_ENDPOINT = Joiner.on("/")
+ .join("/plugins", PLUGIN_NAME, "index/change", CHANGE_NUMBER);
+ private static final int ACCOUNT_NUMBER = 2;
+ private static final String INDEX_ACCOUNT_ENDPOINT = Joiner.on("/")
+ .join("/plugins", PLUGIN_NAME, "index/account", ACCOUNT_NUMBER);
- //Evict cache
- private static final String EMPTY_JSON = "{}";
- private static final String EMPTY_JSON2 = "\"{}\"";
- private static final String ID_JSON = "{\"id\":0}";
+ //Event
+ private static final String EVENT_ENDPOINT =
+ Joiner.on("/").join("/plugins", PLUGIN_NAME, "event");
+ private static Event event = new Event("test-event") {};
+ private static String eventJson = new GsonBuilder().create().toJson(event);
- private RestForwarder restForwarder;
+ private RestForwarder forwarder;
+ private HttpSession httpSessionMock;
+ private Configuration configurationMock;
+
+ @BeforeClass
+ public static void setup() {
+ KeyUtil.setEncoderImpl(new StandardKeyEncoder());
+ }
+
+ @Before
+ public void setUp() {
+ httpSessionMock = mock(HttpSession.class);
+ configurationMock = mock(Configuration.class);
+ when(configurationMock.getMaxTries()).thenReturn(3);
+ when(configurationMock.getRetryInterval()).thenReturn(10);
+ forwarder =
+ new RestForwarder(httpSessionMock, PLUGIN_NAME, configurationMock);
+ }
+
+ @Test
+ public void testIndexAccountOK() throws Exception {
+ when(httpSessionMock.post(INDEX_ACCOUNT_ENDPOINT))
+ .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.indexAccount(ACCOUNT_NUMBER)).isTrue();
+ }
+
+ @Test
+ public void testIndexAccountFailed() throws Exception {
+ when(httpSessionMock.post(INDEX_ACCOUNT_ENDPOINT))
+ .thenReturn(new HttpResult(FAILED, EMPTY_MSG));
+ assertThat(forwarder.indexAccount(ACCOUNT_NUMBER)).isFalse();
+ }
+
+ @Test
+ public void testIndexAccountThrowsException() throws Exception {
+ doThrow(new IOException()).when(httpSessionMock)
+ .post(INDEX_ACCOUNT_ENDPOINT);
+ assertThat(forwarder.indexAccount(ACCOUNT_NUMBER)).isFalse();
+ }
@Test
public void testIndexChangeOK() throws Exception {
- setUpMocksForIndex(INDEX_OP, SUCCESSFUL, EMPTY_MSG, DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.indexChange(CHANGE_NUMBER)).isTrue();
+ when(httpSessionMock.post(INDEX_CHANGE_ENDPOINT))
+ .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.indexChange(CHANGE_NUMBER)).isTrue();
}
@Test
public void testIndexChangeFailed() throws Exception {
- setUpMocksForIndex(INDEX_OP, FAILED, ERROR_MSG, DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.indexChange(CHANGE_NUMBER)).isFalse();
+ when(httpSessionMock.post(INDEX_CHANGE_ENDPOINT))
+ .thenReturn(new HttpResult(FAILED, EMPTY_MSG));
+ assertThat(forwarder.indexChange(CHANGE_NUMBER)).isFalse();
}
@Test
public void testIndexChangeThrowsException() throws Exception {
- setUpMocksForIndex(INDEX_OP, FAILED, EXCEPTION_MSG, THROW_EXCEPTION);
- assertThat(restForwarder.indexChange(CHANGE_NUMBER)).isFalse();
+ doThrow(new IOException()).when(httpSessionMock)
+ .post(INDEX_CHANGE_ENDPOINT);
+ assertThat(forwarder.indexChange(CHANGE_NUMBER)).isFalse();
}
@Test
public void testChangeDeletedFromIndexOK() throws Exception {
- setUpMocksForIndex(DELETE_OP, SUCCESSFUL, EMPTY_MSG,
- DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.deleteChangeFromIndex(CHANGE_NUMBER)).isTrue();
+ when(httpSessionMock.delete(INDEX_CHANGE_ENDPOINT))
+ .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.deleteChangeFromIndex(CHANGE_NUMBER)).isTrue();
}
@Test
public void testChangeDeletedFromIndexFailed() throws Exception {
- setUpMocksForIndex(DELETE_OP, FAILED, ERROR_MSG, DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.deleteChangeFromIndex(CHANGE_NUMBER)).isFalse();
+ when(httpSessionMock.delete(INDEX_CHANGE_ENDPOINT))
+ .thenReturn(new HttpResult(FAILED, EMPTY_MSG));
+ assertThat(forwarder.deleteChangeFromIndex(CHANGE_NUMBER)).isFalse();
}
@Test
public void testChangeDeletedFromThrowsException() throws Exception {
- setUpMocksForIndex(DELETE_OP, FAILED, EXCEPTION_MSG, THROW_EXCEPTION);
- assertThat(restForwarder.deleteChangeFromIndex(CHANGE_NUMBER)).isFalse();
- }
-
- private void setUpMocksForIndex(String operation,
- boolean isOperationSuccessful, String msg, boolean exception)
- throws Exception {
- String request =
- Joiner.on("/").join("/plugins", PLUGIN_NAME, INDEX_OP, CHANGE_NUMBER);
- HttpSession httpSession = mock(HttpSession.class);
- if (exception) {
- if (operation.equals(INDEX_OP)) {
- doThrow(new IOException()).when(httpSession).post(request);
- } else {
- doThrow(new IOException()).when(httpSession).delete(request);
- }
- } else {
- HttpResult result = new HttpResult(isOperationSuccessful, msg);
- if (operation.equals(INDEX_OP)) {
- when(httpSession.post(request)).thenReturn(result);
- } else {
- when(httpSession.delete(request)).thenReturn(result);
- }
- }
- Configuration cfg = mock(Configuration.class);
- when(cfg.getMaxTries()).thenReturn(MAX_TRIES);
- when(cfg.getRetryInterval()).thenReturn(RETRY_INTERVAL);
- restForwarder = new RestForwarder(httpSession, PLUGIN_NAME, cfg);
+ doThrow(new IOException()).when(httpSessionMock)
+ .delete(INDEX_CHANGE_ENDPOINT);
+ assertThat(forwarder.deleteChangeFromIndex(CHANGE_NUMBER)).isFalse();
}
@Test
public void testEventSentOK() throws Exception {
- Event event = setUpMocksForEvent(SUCCESSFUL, EMPTY_MSG, DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.send(event)).isTrue();
+ when(httpSessionMock.post(EVENT_ENDPOINT, eventJson))
+ .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.send(event)).isTrue();
}
@Test
public void testEventSentFailed() throws Exception {
- Event event = setUpMocksForEvent(FAILED, ERROR_MSG, DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.send(event)).isFalse();
+ when(httpSessionMock.post(EVENT_ENDPOINT, eventJson))
+ .thenReturn(new HttpResult(FAILED, EMPTY_MSG));
+ assertThat(forwarder.send(event)).isFalse();
}
@Test
public void testEventSentThrowsException() throws Exception {
- Event event = setUpMocksForEvent(FAILED, EXCEPTION_MSG, THROW_EXCEPTION);
- assertThat(restForwarder.send(event)).isFalse();
- }
-
- private Event setUpMocksForEvent(boolean isOperationSuccessful, String msg,
- boolean exception) throws Exception {
- Event event = new EventTest();
- String content = new GsonBuilder().create().toJson(event);
- HttpSession httpSession = mock(HttpSession.class);
- String request = Joiner.on("/").join("/plugins", PLUGIN_NAME, "event");
- if (exception) {
- doThrow(new IOException()).when(httpSession).post(request, content);
- } else {
- HttpResult result = new HttpResult(isOperationSuccessful, msg);
- when(httpSession.post(request, content)).thenReturn(result);
- }
- Configuration cfg = mock(Configuration.class);
- when(cfg.getMaxTries()).thenReturn(MAX_TRIES);
- when(cfg.getRetryInterval()).thenReturn(RETRY_INTERVAL);
- restForwarder = new RestForwarder(httpSession, PLUGIN_NAME, cfg);
- return event;
- }
-
- private class EventTest extends Event {
- public EventTest() {
- super("test-event");
- }
+ doThrow(new IOException()).when(httpSessionMock).post(EVENT_ENDPOINT,
+ eventJson);
+ assertThat(forwarder.send(event)).isFalse();
}
@Test
- public void testEvictCacheOK() throws Exception {
- setupMocksForCache(Constants.PROJECTS, EMPTY_JSON2, SUCCESSFUL,
- DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.evict(Constants.PROJECTS, EMPTY_JSON)).isTrue();
+ public void testEvictProjectOK() throws Exception {
+ String key = "projectName";
+ String keyJson = new GsonBuilder().create().toJson(key);
+ when(httpSessionMock.post(buildCacheEndpoint(Constants.PROJECTS), keyJson))
+ .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.evict(Constants.PROJECTS, key)).isTrue();
}
@Test
public void testEvictAccountsOK() throws Exception {
- setupMocksForCache(Constants.ACCOUNTS, ID_JSON, SUCCESSFUL,
- DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.evict(Constants.ACCOUNTS, mock(Account.Id.class)))
- .isTrue();
+ Account.Id key = Account.Id.parse("123");
+ String keyJson = new GsonBuilder().create().toJson(key);
+ when(httpSessionMock.post(buildCacheEndpoint(Constants.ACCOUNTS), keyJson))
+ .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.evict(Constants.ACCOUNTS, key)).isTrue();
}
@Test
public void testEvictGroupsOK() throws Exception {
- setupMocksForCache(Constants.GROUPS, ID_JSON, SUCCESSFUL,
- DO_NOT_THROW_EXCEPTION);
- assertThat(
- restForwarder.evict(Constants.GROUPS, mock(AccountGroup.Id.class)))
- .isTrue();
+ AccountGroup.Id key = AccountGroup.Id.parse("123");
+ String keyJson = new GsonBuilder().create().toJson(key);
+ when(httpSessionMock.post(buildCacheEndpoint(Constants.GROUPS), keyJson))
+ .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.evict(Constants.GROUPS, key)).isTrue();
}
@Test
public void testEvictGroupsByIncludeOK() throws Exception {
- setupMocksForCache(Constants.GROUPS_BYINCLUDE, EMPTY_JSON, SUCCESSFUL,
- DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.evict(Constants.GROUPS_BYINCLUDE,
- mock(AccountGroup.UUID.class))).isTrue();
+ AccountGroup.UUID key =
+ AccountGroup.UUID.parse("90b3042d9094a37985f3f9281391dbbe9a5addad");
+ String keyJson = new GsonBuilder().create().toJson(key);
+ when(httpSessionMock.post(buildCacheEndpoint(Constants.GROUPS_BYINCLUDE),
+ keyJson)).thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.evict(Constants.GROUPS_BYINCLUDE, key)).isTrue();
}
@Test
public void testEvictGroupsMembersOK() throws Exception {
- setupMocksForCache(Constants.GROUPS_MEMBERS, EMPTY_JSON, SUCCESSFUL,
- DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.evict(Constants.GROUPS_MEMBERS,
- mock(AccountGroup.UUID.class))).isTrue();
+ AccountGroup.UUID key =
+ AccountGroup.UUID.parse("90b3042d9094a37985f3f9281391dbbe9a5addad");
+ String keyJson = new GsonBuilder().create().toJson(key);
+ when(httpSessionMock.post(buildCacheEndpoint(Constants.GROUPS_MEMBERS),
+ keyJson)).thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.evict(Constants.GROUPS_MEMBERS, key)).isTrue();
}
@Test
public void testEvictProjectListOK() throws Exception {
- setupMocksForCache(Constants.PROJECT_LIST, EMPTY_JSON, SUCCESSFUL,
- DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.evict(Constants.PROJECT_LIST, new Object()))
- .isTrue();
+ String key = "all";
+ String keyJson = new GsonBuilder().create().toJson(key);
+ when(httpSessionMock.post(buildCacheEndpoint(Constants.PROJECT_LIST),
+ keyJson)).thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.evict(Constants.PROJECT_LIST, key)).isTrue();
}
@Test
public void testEvictCacheFailed() throws Exception {
- setupMocksForCache(Constants.PROJECTS, EMPTY_JSON2, FAILED,
- DO_NOT_THROW_EXCEPTION);
- assertThat(restForwarder.evict(Constants.PROJECTS, EMPTY_JSON)).isFalse();
+ String key = "projectName";
+ String keyJson = new GsonBuilder().create().toJson(key);
+ when(httpSessionMock.post(buildCacheEndpoint(Constants.PROJECTS), keyJson))
+ .thenReturn(new HttpResult(FAILED, EMPTY_MSG));
+ assertThat(forwarder.evict(Constants.PROJECTS, key)).isFalse();
}
@Test
public void testEvictCacheThrowsException() throws Exception {
- setupMocksForCache(Constants.PROJECTS, EMPTY_JSON2, FAILED,
- THROW_EXCEPTION);
- assertThat(restForwarder.evict(Constants.PROJECTS, EMPTY_JSON)).isFalse();
+ String key = "projectName";
+ String keyJson = new GsonBuilder().create().toJson(key);
+ doThrow(new IOException()).when(httpSessionMock)
+ .post(buildCacheEndpoint(Constants.PROJECTS), keyJson);
+ assertThat(forwarder.evict(Constants.PROJECTS, key)).isFalse();
}
- private void setupMocksForCache(String cacheName, String json,
- boolean isOperationSuccessful, boolean exception) throws IOException {
- String request =
- Joiner.on("/").join("/plugins", PLUGIN_NAME, "cache", cacheName);
- HttpSession httpSession = mock(HttpSession.class);
- if (exception) {
- doThrow(new IOException()).when(httpSession).post(request, json);
- } else {
- HttpResult result = new HttpResult(isOperationSuccessful, "Error");
- when(httpSession.post(request, json)).thenReturn(result);
- }
- Configuration cfg = mock(Configuration.class);
- when(cfg.getMaxTries()).thenReturn(MAX_TRIES);
- when(cfg.getRetryInterval()).thenReturn(RETRY_INTERVAL);
- restForwarder = new RestForwarder(httpSession, PLUGIN_NAME, cfg);
+ private String buildCacheEndpoint(String name) {
+ return Joiner.on("/").join("/plugins", PLUGIN_NAME, "cache", name);
}
@Test
public void testRetryOnErrorThenSuccess() throws IOException {
- Configuration cfg = mock(Configuration.class);
- when(cfg.getMaxTries()).thenReturn(3);
- when(cfg.getRetryInterval()).thenReturn(10);
-
- HttpSession httpSession = mock(HttpSession.class);
- when(httpSession.post(anyString(), anyString()))
+ when(httpSessionMock.post(anyString(), anyString()))
.thenReturn(new HttpResult(false, "Error"))
.thenReturn(new HttpResult(false, "Error"))
.thenReturn(new HttpResult(true, "Success"));
- RestForwarder forwarder = new RestForwarder(httpSession, PLUGIN_NAME, cfg);
- assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object()))
- .isTrue();
+ assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object())).isTrue();
}
@Test
public void testRetryOnIoExceptionThenSuccess() throws IOException {
- Configuration cfg = mock(Configuration.class);
- when(cfg.getMaxTries()).thenReturn(3);
- when(cfg.getRetryInterval()).thenReturn(10);
-
- HttpSession httpSession = mock(HttpSession.class);
- when(httpSession.post(anyString(), anyString()))
+ when(httpSessionMock.post(anyString(), anyString()))
.thenThrow(new IOException())
.thenThrow(new IOException())
.thenReturn(new HttpResult(true, "Success"));
- RestForwarder forwarder = new RestForwarder(httpSession, PLUGIN_NAME, cfg);
- assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object()))
- .isTrue();
+ assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object())).isTrue();
}
@Test
public void testNoRetryAfterNonRecoverableException() throws IOException {
- Configuration cfg = mock(Configuration.class);
- when(cfg.getMaxTries()).thenReturn(3);
- when(cfg.getRetryInterval()).thenReturn(10);
-
- HttpSession httpSession = mock(HttpSession.class);
- when(httpSession.post(anyString(), anyString()))
+ when(httpSessionMock.post(anyString(), anyString()))
.thenThrow(new SSLException("Non Recoverable"))
.thenReturn(new HttpResult(true, "Success"));
- RestForwarder forwarder = new RestForwarder(httpSession, PLUGIN_NAME, cfg);
- assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object()))
- .isFalse();
+ assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object())).isFalse();
}
@Test
public void testFailureAfterMaxTries() throws IOException {
- Configuration cfg = mock(Configuration.class);
- when(cfg.getMaxTries()).thenReturn(3);
- when(cfg.getRetryInterval()).thenReturn(10);
-
- HttpSession httpSession = mock(HttpSession.class);
- when(httpSession.post(anyString(), anyString()))
+ when(httpSessionMock.post(anyString(), anyString()))
.thenReturn(new HttpResult(false, "Error"))
.thenReturn(new HttpResult(false, "Error"))
.thenReturn(new HttpResult(false, "Error"));
- RestForwarder forwarder = new RestForwarder(httpSession, PLUGIN_NAME, cfg);
- assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object()))
- .isFalse();
+ assertThat(forwarder.evict(Constants.PROJECT_LIST, new Object())).isFalse();
}
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
index aa4c365..626af8b 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
@@ -21,6 +21,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import com.google.common.util.concurrent.MoreExecutors;
+import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.git.WorkQueue.Executor;
import com.google.gwtorm.client.KeyUtil;
@@ -28,7 +29,8 @@
import com.ericsson.gerrit.plugins.highavailability.forwarder.Context;
import com.ericsson.gerrit.plugins.highavailability.forwarder.Forwarder;
-import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexTask;
+import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexAccountTask;
+import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexChangeTask;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -41,11 +43,13 @@
public class IndexEventHandlerTest {
private static final String PLUGIN_NAME = "high-availability";
private static final int CHANGE_ID = 1;
+ private static final int ACCOUNT_ID = 2;
private IndexEventHandler indexEventHandler;
@Mock
private Forwarder forwarder;
- private Change.Id id;
+ private Change.Id changeId;
+ private Account.Id accountId;
@BeforeClass
public static void setUp() {
@@ -54,61 +58,94 @@
@Before
public void setUpMocks() {
- id = Change.Id.parse(Integer.toString(CHANGE_ID));
+ changeId = Change.Id.parse(Integer.toString(CHANGE_ID));
+ accountId = Account.Id.parse(Integer.toString(ACCOUNT_ID));
indexEventHandler = new IndexEventHandler(MoreExecutors.directExecutor(),
PLUGIN_NAME, forwarder);
}
@Test
public void shouldIndexInRemoteOnChangeIndexedEvent() throws Exception {
- indexEventHandler.onChangeIndexed(id.get());
+ indexEventHandler.onChangeIndexed(changeId.get());
verify(forwarder).indexChange(CHANGE_ID);
}
@Test
+ public void shouldIndexInRemoteOnAccountIndexedEvent() throws Exception {
+ indexEventHandler.onAccountIndexed(accountId.get());
+ verify(forwarder).indexAccount(ACCOUNT_ID);
+ }
+
+ @Test
public void shouldDeleteFromIndexInRemoteOnChangeDeletedEvent()
throws Exception {
- indexEventHandler.onChangeDeleted(id.get());
+ indexEventHandler.onChangeDeleted(changeId.get());
verify(forwarder).deleteChangeFromIndex(CHANGE_ID);
}
@Test
- public void shouldNotCallRemoteWhenEventIsForwarded() throws Exception {
+ public void shouldNotCallRemoteWhenChangeEventIsForwarded() throws Exception {
Context.setForwardedEvent(true);
- indexEventHandler.onChangeIndexed(id.get());
- indexEventHandler.onChangeDeleted(id.get());
+ indexEventHandler.onChangeIndexed(changeId.get());
+ indexEventHandler.onChangeDeleted(changeId.get());
Context.unsetForwardedEvent();
verifyZeroInteractions(forwarder);
}
@Test
- public void duplicateEventOfAQueuedEventShouldGetDiscarded() {
- Executor poolMock = mock(Executor.class);
- indexEventHandler =
- new IndexEventHandler(poolMock, PLUGIN_NAME, forwarder);
- indexEventHandler.onChangeIndexed(id.get());
- indexEventHandler.onChangeIndexed(id.get());
- verify(poolMock, times(1))
- .execute(indexEventHandler.new IndexTask(CHANGE_ID, false));
+ public void shouldNotCallRemoteWhenAccountEventIsForwarded() throws Exception {
+ Context.setForwardedEvent(true);
+ indexEventHandler.onAccountIndexed(accountId.get());
+ indexEventHandler.onAccountIndexed(accountId.get());
+ Context.unsetForwardedEvent();
+ verifyZeroInteractions(forwarder);
}
@Test
- public void testIndexTaskToString() throws Exception {
- IndexTask indexTask =
- indexEventHandler.new IndexTask(CHANGE_ID, false);
- assertThat(indexTask.toString()).isEqualTo(String.format(
+ public void duplicateChangeEventOfAQueuedEventShouldGetDiscarded() {
+ Executor poolMock = mock(Executor.class);
+ indexEventHandler = new IndexEventHandler(poolMock, PLUGIN_NAME, forwarder);
+ indexEventHandler.onChangeIndexed(changeId.get());
+ indexEventHandler.onChangeIndexed(changeId.get());
+ verify(poolMock, times(1))
+ .execute(indexEventHandler.new IndexChangeTask(CHANGE_ID, false));
+ }
+
+ @Test
+ public void duplicateAccountEventOfAQueuedEventShouldGetDiscarded() {
+ Executor poolMock = mock(Executor.class);
+ indexEventHandler = new IndexEventHandler(poolMock, PLUGIN_NAME, forwarder);
+ indexEventHandler.onAccountIndexed(accountId.get());
+ indexEventHandler.onAccountIndexed(accountId.get());
+ verify(poolMock, times(1))
+ .execute(indexEventHandler.new IndexAccountTask(ACCOUNT_ID));
+ }
+
+ @Test
+ public void testIndexChangeTaskToString() throws Exception {
+ IndexChangeTask task =
+ indexEventHandler.new IndexChangeTask(CHANGE_ID, false);
+ assertThat(task.toString()).isEqualTo(String.format(
"[%s] Index change %s in target instance", PLUGIN_NAME, CHANGE_ID));
}
@Test
- public void testIndexTaskHashCodeAndEquals() {
- IndexTask task = indexEventHandler.new IndexTask(CHANGE_ID, false);
+ public void testIndexAccountTaskToString() throws Exception {
+ IndexAccountTask task = indexEventHandler.new IndexAccountTask(ACCOUNT_ID);
+ assertThat(task.toString()).isEqualTo(String.format(
+ "[%s] Index account %s in target instance", PLUGIN_NAME, ACCOUNT_ID));
+ }
+
+ @Test
+ public void testIndexChangeTaskHashCodeAndEquals() {
+ IndexChangeTask task =
+ indexEventHandler.new IndexChangeTask(CHANGE_ID, false);
assertThat(task.equals(task)).isTrue();
assertThat(task.hashCode()).isEqualTo(task.hashCode());
- IndexTask identicalTask =
- indexEventHandler.new IndexTask(CHANGE_ID, false);
+ IndexChangeTask identicalTask =
+ indexEventHandler.new IndexChangeTask(CHANGE_ID, false);
assertThat(task.equals(identicalTask)).isTrue();
assertThat(task.hashCode()).isEqualTo(identicalTask.hashCode());
@@ -116,14 +153,37 @@
assertThat(task.equals("test")).isFalse();
assertThat(task.hashCode()).isNotEqualTo("test".hashCode());
- IndexTask differentChangeIdTask =
- indexEventHandler.new IndexTask(123, false);
+ IndexChangeTask differentChangeIdTask =
+ indexEventHandler.new IndexChangeTask(123, false);
assertThat(task.equals(differentChangeIdTask)).isFalse();
assertThat(task.hashCode()).isNotEqualTo(differentChangeIdTask.hashCode());
- IndexTask removeTask =
- indexEventHandler.new IndexTask(CHANGE_ID, true);
+ IndexChangeTask removeTask =
+ indexEventHandler.new IndexChangeTask(CHANGE_ID, true);
assertThat(task.equals(removeTask)).isFalse();
assertThat(task.hashCode()).isNotEqualTo(removeTask.hashCode());
}
+
+ @Test
+ public void testIndexAccountTaskHashCodeAndEquals() {
+ IndexAccountTask task = indexEventHandler.new IndexAccountTask(ACCOUNT_ID);
+
+ assertThat(task.equals(task)).isTrue();
+ assertThat(task.hashCode()).isEqualTo(task.hashCode());
+
+ IndexAccountTask identicalTask =
+ indexEventHandler.new IndexAccountTask(ACCOUNT_ID);
+ assertThat(task.equals(identicalTask)).isTrue();
+ assertThat(task.hashCode()).isEqualTo(identicalTask.hashCode());
+
+ assertThat(task.equals(null)).isFalse();
+ assertThat(task.equals("test")).isFalse();
+ assertThat(task.hashCode()).isNotEqualTo("test".hashCode());
+
+ IndexAccountTask differentAccountIdTask =
+ indexEventHandler.new IndexAccountTask(123);
+ assertThat(task.equals(differentAccountIdTask)).isFalse();
+ assertThat(task.hashCode()).isNotEqualTo(differentAccountIdTask.hashCode());
+
+ }
}