blob: 1c8a8ea53cba1355e5e78e54bfe976b83272032e [file] [log] [blame]
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.googlesource.gerrit.plugins.replication.pull;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.file.Files.createTempDirectory;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.events.ProjectDeletedListener;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
import com.google.gerrit.server.events.BatchRefUpdateEvent;
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.EventDispatcher;
import com.google.gerrit.server.events.RefUpdatedEvent;
import com.google.gerrit.server.git.WorkQueue;
import com.google.inject.Provider;
import com.googlesource.gerrit.plugins.replication.FileConfigResource;
import com.googlesource.gerrit.plugins.replication.MergedConfigResource;
import com.googlesource.gerrit.plugins.replication.ReplicationConfigImpl;
import com.googlesource.gerrit.plugins.replication.api.ReplicationConfig;
import com.googlesource.gerrit.plugins.replication.pull.api.FetchAction.RefInput;
import com.googlesource.gerrit.plugins.replication.pull.api.data.BatchApplyObjectData;
import com.googlesource.gerrit.plugins.replication.pull.api.data.RevisionData;
import com.googlesource.gerrit.plugins.replication.pull.client.FetchApiClient;
import com.googlesource.gerrit.plugins.replication.pull.client.FetchRestApiClient;
import com.googlesource.gerrit.plugins.replication.pull.client.HttpResult;
import com.googlesource.gerrit.plugins.replication.pull.filter.ApplyObjectsRefsFilter;
import com.googlesource.gerrit.plugins.replication.pull.filter.ExcludedRefsFilter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FS;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ReplicationQueueTest {
private static int CONNECTION_TIMEOUT = 1000000;
private static final String LOCAL_INSTANCE_ID = "local instance id";
private static final String FOREIGN_INSTANCE_ID = "any other instance id";
private static final String TEST_REF_NAME = "refs/meta/heads/anyref";
private static final Project.NameKey PROJECT = Project.nameKey("defaultProject");
private static final String NEW_OBJECT_ID =
ObjectId.fromString("3c1ddc050d7906adb0e29bc3bc46af8749b2f63b").getName();
@Mock private WorkQueue wq;
@Mock private Source source;
@Mock private SourcesCollection sourceCollection;
@Mock private Provider<SourcesCollection> rd;
@Mock private DynamicItem<EventDispatcher> dis;
@Mock ReplicationStateListeners sl;
@Mock FetchRestApiClient fetchRestApiClient;
@Mock FetchApiClient.Factory fetchClientFactory;
@Mock AccountAttribute accountAttribute;
@Mock RevisionReader revReader;
@Mock RevisionData revisionData;
@Mock HttpResult successfulHttpResult;
@Mock HttpResult fetchHttpResult;
@Mock HttpResult batchFetchHttpResult;
@Mock RevisionData revisionDataWithParents;
List<ObjectId> revisionDataParentObjectIds;
@Mock HttpResult httpResult;
@Mock HttpResult batchHttpResult;
@Mock ApplyObjectsRefsFilter applyObjectsRefsFilter;
@Mock Config config;
ApplyObjectMetrics applyObjectMetrics;
ReplicationQueueMetrics queueMetrics;
ShutdownState shutdownState;
@Captor ArgumentCaptor<String> stringCaptor;
@Captor ArgumentCaptor<Project.NameKey> projectNameKeyCaptor;
@Captor ArgumentCaptor<List<RevisionData>> revisionsDataCaptor;
@Captor ArgumentCaptor<List<BatchApplyObjectData>> batchRefsCaptor;
private ExcludedRefsFilter refsFilter;
private ReplicationQueue objectUnderTest;
private SitePaths sitePaths;
private Path pluginDataPath;
@Before
public void setup() throws Exception {
Path sitePath = createTempPath("site");
sitePaths = new SitePaths(sitePath);
Path pluginDataPath = createTempPath("data");
ReplicationConfig replicationConfig =
new ReplicationConfigImpl(
MergedConfigResource.withBaseOnly(new FileConfigResource(sitePaths)),
sitePaths,
pluginDataPath);
refsFilter = new ExcludedRefsFilter(replicationConfig);
when(source.getConnectionTimeout()).thenReturn(CONNECTION_TIMEOUT);
when(source.wouldFetchProject(any())).thenReturn(true);
when(source.wouldFetchRef(anyString())).thenReturn(true);
ImmutableList<String> apis = ImmutableList.of("http://localhost:18080");
when(source.getApis()).thenReturn(apis);
when(source.enableBatchedRefs()).thenReturn(true);
when(sourceCollection.getAll()).thenReturn(Lists.newArrayList(source));
when(rd.get()).thenReturn(sourceCollection);
when(config.getBoolean("event", "stream-events", "enableBatchRefUpdatedEvents", false))
.thenReturn(true);
lenient()
.when(revReader.read(any(), any(), anyString(), eq(0)))
.thenReturn(Optional.of(revisionData));
lenient().when(revReader.read(any(), anyString(), eq(0))).thenReturn(Optional.of(revisionData));
lenient()
.when(revReader.read(any(), any(), anyString(), eq(Integer.MAX_VALUE)))
.thenReturn(Optional.of(revisionDataWithParents));
lenient()
.when(revReader.read(any(), anyString(), eq(Integer.MAX_VALUE)))
.thenReturn(Optional.of(revisionDataWithParents));
revisionDataParentObjectIds =
Arrays.asList(
ObjectId.fromString("9f8d52853089a3cf00c02ff7bd0817bd4353a95a"),
ObjectId.fromString("b5d7bcf1d1c5b0f0726d10a16c8315f06f900bfb"));
when(revisionDataWithParents.getParentObjetIds()).thenReturn(revisionDataParentObjectIds);
when(fetchClientFactory.create(any())).thenReturn(fetchRestApiClient);
lenient()
.when(
fetchRestApiClient.callSendObject(
any(), anyString(), anyLong(), anyBoolean(), any(), any()))
.thenReturn(httpResult);
lenient()
.when(fetchRestApiClient.callSendObjects(any(), anyString(), anyLong(), any(), any()))
.thenReturn(httpResult);
lenient()
.when(fetchRestApiClient.callBatchSendObject(any(), any(), anyLong(), any()))
.thenReturn(batchHttpResult);
when(fetchRestApiClient.callFetch(any(), anyString(), any(), anyLong(), anyBoolean()))
.thenReturn(fetchHttpResult);
when(fetchRestApiClient.callBatchFetch(any(), any(), any())).thenReturn(batchFetchHttpResult);
when(fetchRestApiClient.initProject(any(), any(), anyLong(), any()))
.thenReturn(successfulHttpResult);
when(successfulHttpResult.isSuccessful()).thenReturn(true);
when(httpResult.isSuccessful()).thenReturn(true);
lenient().when(batchHttpResult.isSuccessful()).thenReturn(true);
lenient().when(fetchHttpResult.isSuccessful()).thenReturn(true);
when(batchFetchHttpResult.isSuccessful()).thenReturn(true);
when(httpResult.isProjectMissing(any())).thenReturn(false);
when(batchHttpResult.isProjectMissing(any())).thenReturn(false);
when(applyObjectsRefsFilter.match(any())).thenReturn(false);
applyObjectMetrics = new ApplyObjectMetrics("pull-replication", new DisabledMetricMaker());
queueMetrics = new ReplicationQueueMetrics("pull-replication", new DisabledMetricMaker());
shutdownState = new ShutdownState();
objectUnderTest =
new ReplicationQueue(
wq,
rd,
dis,
sl,
fetchClientFactory,
refsFilter,
() -> revReader,
applyObjectMetrics,
queueMetrics,
LOCAL_INSTANCE_ID,
config,
applyObjectsRefsFilter,
shutdownState);
}
@Test
public void shouldCallBatchSendObjectWhenMetaRef() throws Exception {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/meta");
objectUnderTest.start();
objectUnderTest.onEvent(event);
verify(fetchRestApiClient).callBatchSendObject(any(), any(), anyLong(), any());
}
@Test
public void shouldCallSendObjectWhenMetaRefAndRefUpdateEvent() throws Exception {
when(config.getBoolean("event", "stream-events", "enableBatchRefUpdatedEvents", false))
.thenReturn(false);
objectUnderTest =
new ReplicationQueue(
wq,
rd,
dis,
sl,
fetchClientFactory,
refsFilter,
() -> revReader,
applyObjectMetrics,
queueMetrics,
LOCAL_INSTANCE_ID,
config,
applyObjectsRefsFilter,
shutdownState);
Event event = new TestEvent("refs/changes/01/1/meta");
objectUnderTest.start();
objectUnderTest.onEvent(event);
verify(fetchRestApiClient).callBatchSendObject(any(), any(), anyLong(), any());
}
@Test
public void shouldIgnoreEventWhenIsNotLocalInstanceId() throws Exception {
Event event = generateBatchRefUpdateEvent(TEST_REF_NAME);
event.instanceId = FOREIGN_INSTANCE_ID;
objectUnderTest.start();
objectUnderTest.onEvent(event);
verify(fetchRestApiClient, never()).callBatchSendObject(any(), any(), anyLong(), any());
}
@Test
public void shouldCallInitProjectWhenProjectIsMissing() throws Exception {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/meta");
when(batchHttpResult.isSuccessful()).thenReturn(false);
when(batchHttpResult.isProjectMissing(any())).thenReturn(true);
when(source.isCreateMissingRepositories()).thenReturn(true);
objectUnderTest.start();
objectUnderTest.onEvent(event);
verify(fetchRestApiClient).initProject(any(), any(), anyLong(), any());
}
@Test
public void shouldCallSendObjectReorderingRefsHavingMetaAtTheEnd() throws Exception {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/meta", "refs/changes/01/1/1");
objectUnderTest.start();
objectUnderTest.onEvent(event);
verifySendObjectOrdering("refs/changes/01/1/1", "refs/changes/01/1/meta");
}
@Test
public void shouldCallSendObjectKeepingMetaAtTheEnd() throws Exception {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/1", "refs/changes/01/1/meta");
objectUnderTest.start();
objectUnderTest.onEvent(event);
verifySendObjectOrdering("refs/changes/01/1/1", "refs/changes/01/1/meta");
}
@Test
public void shouldNotCallInitProjectWhenReplicateNewRepositoriesNotSet() throws Exception {
Event event = new TestEvent("refs/changes/01/1/meta");
objectUnderTest.start();
objectUnderTest.onEvent(event);
verify(fetchRestApiClient, never()).initProject(any(), any(), anyLong(), any());
}
@Test
public void shouldNotCallInitProjectWhenProjectWithoutConfiguration() throws Exception {
Event event = new TestEvent("refs/changes/01/1/meta");
lenient().when(httpResult.isSuccessful()).thenReturn(false);
lenient().when(httpResult.isProjectMissing(any())).thenReturn(true);
lenient().when(source.isCreateMissingRepositories()).thenReturn(true);
lenient()
.when(revReader.read(any(), any(), eq(RefNames.REFS_CONFIG), anyInt()))
.thenReturn(Optional.empty());
objectUnderTest.start();
objectUnderTest.onEvent(event);
verify(fetchRestApiClient, never()).initProject(any(), any(), anyLong(), any());
}
@Test
public void shouldCallBatchSendObjectWhenPatchSetRefAndRefUpdateEvent() throws Exception {
when(config.getBoolean("event", "stream-events", "enableBatchRefUpdatedEvents", false))
.thenReturn(false);
objectUnderTest =
new ReplicationQueue(
wq,
rd,
dis,
sl,
fetchClientFactory,
refsFilter,
() -> revReader,
applyObjectMetrics,
queueMetrics,
LOCAL_INSTANCE_ID,
config,
applyObjectsRefsFilter,
shutdownState);
}
@Test
public void shouldCallBatchSendObjectWhenPatchSetRef() throws Exception {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/1");
objectUnderTest.start();
objectUnderTest.onEvent(event);
verify(fetchRestApiClient).callBatchSendObject(any(), any(), anyLong(), any());
}
@Test
public void shouldFallbackToCallFetchWhenIOException() throws Exception {
BatchRefUpdateEvent event = generateBatchRefUpdateEvent("refs/changes/01/1/meta");
objectUnderTest.start();
when(revReader.read(any(), any(), anyString(), anyInt())).thenThrow(IOException.class);
objectUnderTest.onEvent(event);
verify(fetchRestApiClient).callBatchFetch(any(), any(), any());
}
@Test
public void shouldFallbackToCallBatchFetchWhenLargeRef() throws Exception {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/1");
objectUnderTest.start();
when(revReader.read(any(), any(), anyString(), anyInt())).thenReturn(Optional.empty());
objectUnderTest.onEvent(event);
verify(fetchRestApiClient).callBatchFetch(any(), any(), any());
}
@Test
public void
shouldFallbackToCallBatchFetchWhenParentObjectIsMissingAndRefDoesntMatchApplyObjectsRefsFilter()
throws Exception {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/1");
objectUnderTest.start();
when(batchHttpResult.isSuccessful()).thenReturn(false);
when(batchHttpResult.isParentObjectMissing()).thenReturn(true);
objectUnderTest.onEvent(event);
verify(fetchRestApiClient).callBatchFetch(any(), any(), any());
}
@Test
public void
shouldFallbackToApplyObjectsForEachRefWhenParentObjectIsMissingAndRefMatchesApplyObjectsRefFilter()
throws Exception {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/1", "refs/changes/02/1/1");
objectUnderTest.start();
when(batchHttpResult.isSuccessful()).thenReturn(false);
when(batchHttpResult.isParentObjectMissing()).thenReturn(true);
when(applyObjectsRefsFilter.match(any())).thenReturn(true);
objectUnderTest.onEvent(event);
verify(fetchRestApiClient, times(2))
.callSendObjects(any(), anyString(), anyLong(), any(), any());
verify(fetchRestApiClient, never()).callFetch(any(), anyString(), any());
}
@Test
public void shouldFallbackToCallBatchFetchWhenParentObjectNotMissingButApplyObjectFails()
throws Exception {
BatchRefUpdateEvent event = generateBatchRefUpdateEvent("refs/changes/01/1/1");
objectUnderTest.start();
when(batchHttpResult.isSuccessful()).thenReturn(false);
when(batchHttpResult.isParentObjectMissing()).thenReturn(false);
lenient().when(httpResult.isSuccessful()).thenReturn(false);
lenient().when(httpResult.isParentObjectMissing()).thenReturn(false);
objectUnderTest.onEvent(event);
verify(fetchRestApiClient).callBatchFetch(any(), any(), any());
}
@Test
public void shouldFallbackToApplyAllParentObjectsWhenParentObjectIsMissingOnMetaRef()
throws Exception {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/meta");
objectUnderTest.start();
when(batchHttpResult.isSuccessful()).thenReturn(false);
when(batchHttpResult.isParentObjectMissing()).thenReturn(true);
objectUnderTest.onEvent(event);
verify(fetchRestApiClient, times(1))
.callSendObjects(any(), anyString(), anyLong(), revisionsDataCaptor.capture(), any());
List<List<RevisionData>> revisionsDataValues = revisionsDataCaptor.getAllValues();
assertThat(revisionsDataValues).hasSize(1);
List<RevisionData> firstRevisionsValues = revisionsDataValues.get(0);
assertThat(firstRevisionsValues).hasSize(1 + revisionDataParentObjectIds.size());
assertThat(firstRevisionsValues).contains(revisionData);
}
@Test
public void shouldFallbackToApplyAllParentObjectsWhenParentObjectIsMissingOnAllowedRefs()
throws Exception {
String refName = "refs/tags/test-tag";
Event event = generateBatchRefUpdateEvent(refName);
objectUnderTest.start();
when(batchHttpResult.isSuccessful()).thenReturn(false);
when(batchHttpResult.isParentObjectMissing()).thenReturn(true);
when(applyObjectsRefsFilter.match(refName)).thenReturn(true);
objectUnderTest.onEvent(event);
verify(fetchRestApiClient, times(1))
.callSendObjects(any(), anyString(), anyLong(), revisionsDataCaptor.capture(), any());
List<List<RevisionData>> revisionsDataValues = revisionsDataCaptor.getAllValues();
assertThat(revisionsDataValues).hasSize(1);
List<RevisionData> firstRevisionsValues = revisionsDataValues.get(0);
assertThat(firstRevisionsValues).hasSize(1 + revisionDataParentObjectIds.size());
assertThat(firstRevisionsValues).contains(revisionData);
}
@Test
public void shouldCallSendObjectsIfBatchedRefsNotEnabledAtSource() throws Exception {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/1");
when(source.enableBatchedRefs()).thenReturn(false);
objectUnderTest.start();
objectUnderTest.onEvent(event);
verify(fetchRestApiClient, never()).callBatchSendObject(any(), any(), anyLong(), any());
verify(fetchRestApiClient).callSendObjects(any(), anyString(), anyLong(), any(), any());
}
@Test
public void shouldCallFetchIfBatchedRefsNotEnabledAtSource() throws Exception {
BatchRefUpdateEvent event = generateBatchRefUpdateEvent("refs/changes/01/1/1");
when(source.enableBatchedRefs()).thenReturn(false);
when(httpResult.isSuccessful()).thenReturn(false);
when(httpResult.isParentObjectMissing()).thenReturn(false);
objectUnderTest.start();
objectUnderTest.onEvent(event);
verify(fetchRestApiClient, never()).callBatchFetch(any(), any(), any());
verifyFallbackToRestApiClientFetchAsync(event);
}
@Test
public void shouldCallBatchFetchForAllTheRefsInTheBatchIfApplyObjectFails()
throws Exception, URISyntaxException {
Event event = generateBatchRefUpdateEvent("refs/changes/01/1/1", "refs/changes/02/1/1");
when(batchHttpResult.isSuccessful()).thenReturn(false);
when(batchHttpResult.isParentObjectMissing()).thenReturn(true);
when(applyObjectsRefsFilter.match(any())).thenReturn(true, true);
when(httpResult.isSuccessful()).thenReturn(true, false);
objectUnderTest.start();
objectUnderTest.onEvent(event);
verify(fetchRestApiClient, times(2))
.callSendObjects(any(), anyString(), anyLong(), any(), any());
verify(fetchRestApiClient)
.callBatchFetch(
PROJECT,
Stream.of("refs/changes/01/1/1", "refs/changes/02/1/1").map(RefInput::create).toList(),
new URIish("http://localhost:18080"));
}
@Test
public void shouldSkipEventWhenMultiSiteVersionRef() throws Exception {
FileBasedConfig fileConfig =
new FileBasedConfig(sitePaths.etc_dir.resolve("replication.config").toFile(), FS.DETECTED);
fileConfig.setString("replication", null, "excludeRefs", "refs/multi-site/version");
fileConfig.save();
ReplicationConfig replicationConfig =
new ReplicationConfigImpl(
MergedConfigResource.withBaseOnly(new FileConfigResource(sitePaths)),
sitePaths,
pluginDataPath);
refsFilter = new ExcludedRefsFilter(replicationConfig);
objectUnderTest =
new ReplicationQueue(
wq,
rd,
dis,
sl,
fetchClientFactory,
refsFilter,
() -> revReader,
applyObjectMetrics,
queueMetrics,
LOCAL_INSTANCE_ID,
config,
applyObjectsRefsFilter,
shutdownState);
Event event = generateBatchRefUpdateEvent("refs/multi-site/version");
objectUnderTest.onEvent(event);
verifyNoInteractions(wq, rd, dis, sl, fetchClientFactory, accountAttribute);
}
@Test
public void shouldSetShutdownStateWhenStopping() throws Exception {
objectUnderTest.stop();
assertThat(shutdownState.isShuttingDown()).isTrue();
}
@Test
public void shouldSkipEventWhenStarredChangesRef() {
Event event = generateBatchRefUpdateEvent("refs/starred-changes/41/2941/1000000");
objectUnderTest.onEvent(event);
verifyNoInteractions(wq, rd, dis, sl, fetchClientFactory, accountAttribute);
}
@Test
public void shouldCallDeleteWhenReplicateProjectDeletionsTrue() {
when(source.wouldDeleteProject(any())).thenReturn(true);
String projectName = "testProject";
FakeProjectDeletedEvent event = new FakeProjectDeletedEvent(projectName);
objectUnderTest.start();
objectUnderTest.onProjectDeleted(event);
verify(source, times(1))
.scheduleDeleteProject(stringCaptor.capture(), projectNameKeyCaptor.capture());
assertThat(stringCaptor.getValue()).isEqualTo(source.getApis().get(0));
assertThat(projectNameKeyCaptor.getValue()).isEqualTo(Project.NameKey.parse(projectName));
}
@Test
public void shouldNotCallDeleteWhenProjectNotToDelete() {
when(source.wouldDeleteProject(any())).thenReturn(false);
FakeProjectDeletedEvent event = new FakeProjectDeletedEvent("testProject");
objectUnderTest.start();
objectUnderTest.onProjectDeleted(event);
verify(source, never()).scheduleDeleteProject(any(), any());
}
@Test
public void shouldScheduleUpdateHeadWhenWouldFetchProject() {
when(source.wouldFetchProject(any())).thenReturn(true);
String projectName = "aProject";
String newHEAD = "newHEAD";
objectUnderTest.start();
objectUnderTest.onHeadUpdated(new FakeHeadUpdateEvent("oldHead", newHEAD, projectName));
verify(source, times(1))
.scheduleUpdateHead(any(), projectNameKeyCaptor.capture(), stringCaptor.capture());
assertThat(stringCaptor.getValue()).isEqualTo(newHEAD);
assertThat(projectNameKeyCaptor.getValue()).isEqualTo(Project.NameKey.parse(projectName));
}
@Test
public void shouldNotScheduleUpdateHeadWhenNotWouldFetchProject() {
when(source.wouldFetchProject(any())).thenReturn(false);
String projectName = "aProject";
String newHEAD = "newHEAD";
objectUnderTest.start();
objectUnderTest.onHeadUpdated(new FakeHeadUpdateEvent("oldHead", newHEAD, projectName));
verify(source, never()).scheduleUpdateHead(any(), any(), any());
}
protected static Path createTempPath(String prefix) throws Exception {
return createTempDirectory(prefix);
}
private BatchRefUpdateEvent generateBatchRefUpdateEvent(String... refs) {
List<RefUpdateAttribute> refUpdates =
Arrays.stream(refs)
.map(
ref -> {
RefUpdateAttribute upd = new RefUpdateAttribute();
upd.newRev = NEW_OBJECT_ID;
upd.oldRev = ObjectId.zeroId().getName();
upd.project = PROJECT.get();
upd.refName = ref;
return upd;
})
.collect(Collectors.toList());
BatchRefUpdateEvent event =
new BatchRefUpdateEvent(
PROJECT, Suppliers.ofInstance(refUpdates), Suppliers.ofInstance(accountAttribute));
event.instanceId = LOCAL_INSTANCE_ID;
return event;
}
private void verifySendObjectOrdering(String firstRef, String secondRef) throws Exception {
verify(fetchRestApiClient)
.callBatchSendObject(any(), batchRefsCaptor.capture(), anyLong(), any());
List<BatchApplyObjectData> batchRefs = batchRefsCaptor.getValue();
assertThat(batchRefs.get(0).refName()).isEqualTo(firstRef);
assertThat(batchRefs.get(1).refName()).isEqualTo(secondRef);
}
private class TestEvent extends RefUpdatedEvent {
public TestEvent() {
this(TEST_REF_NAME);
}
public TestEvent(String refName) {
this(
refName,
"defaultProject",
ObjectId.fromString("3c1ddc050d7906adb0e29bc3bc46af8749b2f63b"));
}
public TestEvent(String refName, String projectName, ObjectId newObjectId) {
RefUpdateAttribute upd = new RefUpdateAttribute();
upd.newRev = newObjectId.getName();
upd.oldRev = ObjectId.zeroId().getName();
upd.project = projectName;
upd.refName = refName;
this.refUpdate = Suppliers.ofInstance(upd);
this.instanceId = LOCAL_INSTANCE_ID;
}
}
private class FakeProjectDeletedEvent implements ProjectDeletedListener.Event {
private String projectName;
public FakeProjectDeletedEvent(String projectName) {
this.projectName = projectName;
}
@Override
public NotifyHandling getNotify() {
return null;
}
@Override
public String getProjectName() {
return projectName;
}
}
private void verifyFallbackToRestApiClientFetchAsync(BatchRefUpdateEvent event)
throws IOException {
verify(fetchRestApiClient)
.callFetch(
eq(event.getProjectNameKey()),
eq(event.getRefNames().get(0)),
any(URIish.class),
any(Long.class),
eq(FetchRestApiClient.FORCE_ASYNC));
}
}