| // 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; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.google.common.jimfs.Configuration; |
| import com.google.common.jimfs.Jimfs; |
| import com.google.common.truth.IterableSubject; |
| import com.google.gerrit.entities.Project; |
| import com.googlesource.gerrit.plugins.replication.ReplicationTasksStorage.ReplicateRefUpdate; |
| import java.net.URISyntaxException; |
| import java.nio.file.FileSystem; |
| import java.nio.file.Path; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| import org.eclipse.jgit.transport.URIish; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class ReplicationTasksStorageTest { |
| protected static final String PROJECT = "myProject"; |
| protected static final String REF = "myRef"; |
| protected static final String REF_2 = "myRef2"; |
| protected static final String REMOTE = "myDest"; |
| protected static final URIish URISH = getUrish("http://example.com/" + PROJECT + ".git"); |
| protected static final ReplicateRefUpdate REF_UPDATE = |
| ReplicateRefUpdate.create(PROJECT, Set.of(REF), URISH, REMOTE); |
| protected static final ReplicateRefUpdate STORED_REF_UPDATE = |
| ReplicateRefUpdate.create(REF_UPDATE, REF_UPDATE.sha1()); |
| protected static final ReplicateRefUpdate REFS_UPDATE = |
| ReplicateRefUpdate.create(PROJECT, Set.of(REF, REF_2), URISH, REMOTE); |
| protected static final ReplicateRefUpdate STORED_REFS_UPDATE = |
| ReplicateRefUpdate.create(REFS_UPDATE, REFS_UPDATE.sha1()); |
| |
| protected ReplicationTasksStorage storage; |
| protected FileSystem fileSystem; |
| protected Path storageSite; |
| protected UriUpdates uriUpdates; |
| |
| @Before |
| public void setUp() throws Exception { |
| fileSystem = Jimfs.newFileSystem(Configuration.unix()); |
| storageSite = fileSystem.getPath("replication_site"); |
| storage = new ReplicationTasksStorage(storageSite); |
| uriUpdates = new TestUriUpdates(REF_UPDATE); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| fileSystem.close(); |
| } |
| |
| @Test |
| public void canListEmptyStorage() throws Exception { |
| assertThatStream(storage.streamWaiting()).isEmpty(); |
| assertThatStream(storage.streamRunning()).isEmpty(); |
| } |
| |
| @Test |
| public void canListWaitingUpdate() throws Exception { |
| storage.create(REF_UPDATE); |
| assertThatStream(storage.streamWaiting()).containsExactly(STORED_REF_UPDATE); |
| } |
| |
| @Test |
| public void canCheckIfUpdateIsWaiting() { |
| storage.create(REF_UPDATE); |
| assertTrue(storage.isWaiting(uriUpdates)); |
| |
| storage.start(uriUpdates); |
| assertFalse(storage.isWaiting(uriUpdates)); |
| } |
| |
| @Test |
| public void canStartWaitingUpdate() throws Exception { |
| storage.create(REF_UPDATE); |
| assertThat(storage.start(uriUpdates)).containsExactly(REF_UPDATE.refs()); |
| assertThatStream(storage.streamWaiting()).isEmpty(); |
| assertFalse(storage.isWaiting(uriUpdates)); |
| assertThatStream(storage.streamRunning()).containsExactly(STORED_REF_UPDATE); |
| } |
| |
| @Test |
| public void canStartWaitingUpdateWithMultipleRefs() throws Exception { |
| TestUriUpdates updates = new TestUriUpdates(REFS_UPDATE); |
| storage.create(REFS_UPDATE); |
| assertThat(storage.start(updates)).containsExactly(REFS_UPDATE.refs()); |
| assertThatStream(storage.streamWaiting()).isEmpty(); |
| assertFalse(storage.isWaiting(updates)); |
| assertThatStream(storage.streamRunning()).containsExactly(STORED_REFS_UPDATE); |
| } |
| |
| @Test |
| public void canFinishRunningUpdate() throws Exception { |
| storage.create(REF_UPDATE); |
| storage.start(uriUpdates); |
| storage.finish(uriUpdates); |
| assertNoIncompleteTasks(storage); |
| } |
| |
| @Test |
| public void instancesOfTheSameStorageHaveTheSameElements() throws Exception { |
| ReplicationTasksStorage persistedView = new ReplicationTasksStorage(storageSite); |
| |
| assertThatStream(storage.streamWaiting()).isEmpty(); |
| assertThatStream(persistedView.streamWaiting()).isEmpty(); |
| |
| storage.create(REF_UPDATE); |
| assertThatStream(storage.streamWaiting()).containsExactly(STORED_REF_UPDATE); |
| assertThatStream(persistedView.streamWaiting()).containsExactly(STORED_REF_UPDATE); |
| |
| storage.start(uriUpdates); |
| assertThatStream(storage.streamWaiting()).isEmpty(); |
| assertThatStream(persistedView.streamWaiting()).isEmpty(); |
| assertThatStream(storage.streamRunning()).containsExactly(STORED_REF_UPDATE); |
| assertThatStream(persistedView.streamRunning()).containsExactly(STORED_REF_UPDATE); |
| |
| storage.finish(uriUpdates); |
| assertThatStream(storage.streamRunning()).isEmpty(); |
| assertThatStream(persistedView.streamRunning()).isEmpty(); |
| } |
| |
| @Test |
| public void sameRefUpdateCreatedTwiceIsStoredOnce() throws Exception { |
| String key = storage.create(REF_UPDATE); |
| String secondKey = storage.create(REF_UPDATE); |
| assertEquals(key, secondKey); |
| assertThatStream(storage.streamWaiting()).containsExactly(STORED_REF_UPDATE); |
| } |
| |
| @Test |
| public void canCreateDifferentUris() throws Exception { |
| ReplicateRefUpdate updateB = |
| ReplicateRefUpdate.create( |
| PROJECT, |
| Set.of(REF), |
| getUrish("ssh://example.com/" + PROJECT + ".git"), // uses ssh not http |
| REMOTE); |
| |
| String keyA = storage.create(REF_UPDATE); |
| String keyB = storage.create(updateB); |
| assertThatStream(storage.streamWaiting()).hasSize(2); |
| assertTrue(storage.isWaiting(uriUpdates)); |
| assertTrue(storage.isWaiting(new TestUriUpdates(updateB))); |
| assertNotEquals(keyA, keyB); |
| } |
| |
| @Test |
| public void canStoreAndReadUrisWithEncodedChars() throws Exception { |
| String urlBase = "https://git.server.example.com"; |
| String url = urlBase + "/${name}.git"; |
| URIish template = new URIish(url); |
| String strangeValidName = |
| "project/with/a/strange/name key=a-value=1, --option1 \"OPTION_VALUE_1\" --option-2 <option_VALUE-2> --option-without-value"; |
| Project.NameKey project = Project.nameKey(strangeValidName); |
| URIish expanded = Destination.getURI(template, project, "slash", false); |
| ReplicateRefUpdate update = |
| ReplicateRefUpdate.create(strangeValidName, Set.of(REF), expanded, REMOTE); |
| storage.create(update); |
| |
| assertThat(new URIish(update.uri())).isEqualTo(expanded); |
| assertThatStream(storage.streamWaiting()).hasSize(1); |
| for (ReplicateRefUpdate rru : storage.streamWaiting().collect(Collectors.toList())) { |
| assertThat(new URIish(rru.uri())).isEqualTo(expanded); |
| } |
| } |
| |
| @Test |
| public void canStartDifferentUris() throws Exception { |
| ReplicateRefUpdate updateB = |
| ReplicateRefUpdate.create( |
| PROJECT, |
| Set.of(REF), |
| getUrish("ssh://example.com/" + PROJECT + ".git"), // uses ssh not http |
| REMOTE); |
| UriUpdates uriUpdatesB = new TestUriUpdates(updateB); |
| storage.create(REF_UPDATE); |
| storage.create(updateB); |
| |
| ReplicateRefUpdate storedUpdateB = ReplicateRefUpdate.create(updateB, updateB.sha1()); |
| storage.start(uriUpdates); |
| assertThatStream(storage.streamWaiting()).containsExactly(storedUpdateB); |
| assertThatStream(storage.streamRunning()).containsExactly(STORED_REF_UPDATE); |
| |
| storage.start(uriUpdatesB); |
| assertThatStream(storage.streamWaiting()).isEmpty(); |
| assertThatStream(storage.streamRunning()).containsExactly(STORED_REF_UPDATE, storedUpdateB); |
| } |
| |
| @Test |
| public void canFinishDifferentUris() throws Exception { |
| ReplicateRefUpdate updateB = |
| ReplicateRefUpdate.create( |
| PROJECT, |
| Set.of(REF), |
| getUrish("ssh://example.com/" + PROJECT + ".git"), // uses ssh not http |
| REMOTE); |
| UriUpdates uriUpdatesB = new TestUriUpdates(updateB); |
| storage.create(REF_UPDATE); |
| storage.create(updateB); |
| storage.start(uriUpdates); |
| storage.start(uriUpdatesB); |
| |
| storage.finish(uriUpdates); |
| assertThatStream(storage.streamRunning()) |
| .containsExactly(ReplicateRefUpdate.create(updateB, updateB.sha1())); |
| |
| storage.finish(uriUpdatesB); |
| assertThatStream(storage.streamRunning()).isEmpty(); |
| } |
| |
| @Test |
| public void differentUrisCreatedTwiceIsStoredOnce() throws Exception { |
| ReplicateRefUpdate updateB = |
| ReplicateRefUpdate.create( |
| PROJECT, |
| Set.of(REF), |
| getUrish("ssh://example.com/" + PROJECT + ".git"), // uses ssh not http |
| REMOTE); |
| |
| storage.create(REF_UPDATE); |
| storage.create(updateB); |
| storage.create(REF_UPDATE); |
| storage.create(updateB); |
| assertThatStream(storage.streamWaiting()).hasSize(2); |
| assertTrue(storage.isWaiting(uriUpdates)); |
| assertTrue(storage.isWaiting(new TestUriUpdates(updateB))); |
| } |
| |
| @Test |
| public void canCreateMulipleRefsForSameUri() throws Exception { |
| ReplicateRefUpdate refA = ReplicateRefUpdate.create(PROJECT, Set.of("refA"), URISH, REMOTE); |
| ReplicateRefUpdate refB = ReplicateRefUpdate.create(PROJECT, Set.of("refB"), URISH, REMOTE); |
| |
| String keyA = storage.create(refA); |
| String keyB = storage.create(refB); |
| assertThatStream(storage.streamWaiting()).hasSize(2); |
| assertNotEquals(keyA, keyB); |
| assertTrue(storage.isWaiting(new TestUriUpdates(refA))); |
| assertTrue(storage.isWaiting(new TestUriUpdates(refB))); |
| } |
| |
| @Test |
| public void canFinishMulipleRefsForSameUri() throws Exception { |
| ReplicateRefUpdate refUpdateA = |
| ReplicateRefUpdate.create(PROJECT, Set.of("refA"), URISH, REMOTE); |
| ReplicateRefUpdate refUpdateB = |
| ReplicateRefUpdate.create(PROJECT, Set.of("refB"), URISH, REMOTE); |
| UriUpdates uriUpdatesA = new TestUriUpdates(refUpdateA); |
| UriUpdates uriUpdatesB = new TestUriUpdates(refUpdateB); |
| storage.create(refUpdateA); |
| storage.create(refUpdateB); |
| storage.start(uriUpdatesA); |
| storage.start(uriUpdatesB); |
| |
| storage.finish(uriUpdatesA); |
| assertThatStream(storage.streamRunning()) |
| .containsExactly(ReplicateRefUpdate.create(refUpdateB, refUpdateB.sha1())); |
| |
| storage.finish(uriUpdatesB); |
| assertThatStream(storage.streamRunning()).isEmpty(); |
| } |
| |
| @Test |
| public void canResetUpdate() throws Exception { |
| storage.create(REF_UPDATE); |
| storage.start(uriUpdates); |
| |
| storage.reset(uriUpdates); |
| assertThatStream(storage.streamWaiting()).containsExactly(STORED_REF_UPDATE); |
| assertThatStream(storage.streamRunning()).isEmpty(); |
| } |
| |
| @Test |
| public void canCompleteResetUpdate() throws Exception { |
| storage.create(REF_UPDATE); |
| storage.start(uriUpdates); |
| storage.reset(uriUpdates); |
| |
| storage.start(uriUpdates); |
| assertThatStream(storage.streamRunning()).containsExactly(STORED_REF_UPDATE); |
| assertThatStream(storage.streamWaiting()).isEmpty(); |
| assertFalse(storage.isWaiting(uriUpdates)); |
| |
| storage.finish(uriUpdates); |
| assertNoIncompleteTasks(storage); |
| } |
| |
| @Test |
| public void canRecoverEmpty() throws Exception { |
| storage.recoverAll(); |
| assertNoIncompleteTasks(storage); |
| } |
| |
| @Test |
| public void canRecoverUpdate() throws Exception { |
| storage.create(REF_UPDATE); |
| storage.start(uriUpdates); |
| |
| storage.recoverAll(); |
| assertThatStream(storage.streamWaiting()).containsExactly(STORED_REF_UPDATE); |
| assertThatStream(storage.streamRunning()).isEmpty(); |
| assertTrue(storage.isWaiting(uriUpdates)); |
| } |
| |
| @Test |
| public void canCompleteRecoveredUpdate() throws Exception { |
| storage.create(REF_UPDATE); |
| storage.start(uriUpdates); |
| storage.recoverAll(); |
| |
| storage.start(uriUpdates); |
| assertThatStream(storage.streamRunning()).containsExactly(STORED_REF_UPDATE); |
| assertThatStream(storage.streamWaiting()).isEmpty(); |
| assertFalse(storage.isWaiting(uriUpdates)); |
| |
| storage.finish(uriUpdates); |
| assertNoIncompleteTasks(storage); |
| } |
| |
| @Test |
| public void canRecoverMultipleUpdates() throws Exception { |
| ReplicateRefUpdate updateB = |
| ReplicateRefUpdate.create( |
| PROJECT, |
| Set.of(REF), |
| getUrish("ssh://example.com/" + PROJECT + ".git"), // uses ssh not http |
| REMOTE); |
| UriUpdates uriUpdatesB = new TestUriUpdates(updateB); |
| storage.create(REF_UPDATE); |
| storage.create(updateB); |
| storage.start(uriUpdates); |
| storage.start(uriUpdatesB); |
| |
| storage.recoverAll(); |
| assertThatStream(storage.streamWaiting()) |
| .containsExactly(STORED_REF_UPDATE, ReplicateRefUpdate.create(updateB, updateB.sha1())); |
| } |
| |
| @Test |
| public void canCompleteMultipleRecoveredUpdates() throws Exception { |
| ReplicateRefUpdate updateB = |
| ReplicateRefUpdate.create( |
| PROJECT, |
| Set.of(REF), |
| getUrish("ssh://example.com/" + PROJECT + ".git"), // uses ssh not http |
| REMOTE); |
| UriUpdates uriUpdatesB = new TestUriUpdates(updateB); |
| storage.create(REF_UPDATE); |
| storage.create(updateB); |
| storage.start(uriUpdates); |
| storage.start(uriUpdatesB); |
| storage.recoverAll(); |
| |
| ReplicateRefUpdate storedUpdateB = ReplicateRefUpdate.create(updateB, updateB.sha1()); |
| storage.start(uriUpdates); |
| assertThatStream(storage.streamRunning()).containsExactly(STORED_REF_UPDATE); |
| assertThatStream(storage.streamWaiting()).containsExactly(storedUpdateB); |
| |
| storage.start(uriUpdatesB); |
| assertThatStream(storage.streamRunning()).containsExactly(STORED_REF_UPDATE, storedUpdateB); |
| assertThatStream(storage.streamWaiting()).isEmpty(); |
| |
| storage.finish(uriUpdates); |
| storage.finish(uriUpdatesB); |
| assertNoIncompleteTasks(storage); |
| } |
| |
| @Test(expected = Test.None.class /* no exception expected */) |
| public void illegalFinishUncreatedIsGraceful() throws Exception { |
| storage.finish(uriUpdates); |
| } |
| |
| @Test(expected = Test.None.class /* no exception expected */) |
| public void illegalDoubleFinishIsGraceful() throws Exception { |
| storage.create(REF_UPDATE); |
| storage.start(uriUpdates); |
| storage.finish(uriUpdates); |
| |
| storage.finish(uriUpdates); |
| } |
| |
| @Test(expected = Test.None.class /* no exception expected */) |
| public void illegalDoubleFinishDifferentUriIsGraceful() throws Exception { |
| ReplicateRefUpdate updateB = |
| ReplicateRefUpdate.create( |
| PROJECT, |
| Set.of(REF), |
| getUrish("ssh://example.com/" + PROJECT + ".git"), // uses ssh not http |
| REMOTE); |
| UriUpdates uriUpdatesB = new TestUriUpdates(updateB); |
| storage.create(REF_UPDATE); |
| storage.create(updateB); |
| storage.start(uriUpdates); |
| storage.start(uriUpdatesB); |
| storage.finish(uriUpdates); |
| storage.finish(uriUpdatesB); |
| |
| storage.finish(uriUpdates); |
| storage.finish(uriUpdatesB); |
| assertThatStream(storage.streamRunning()).isEmpty(); |
| } |
| |
| protected static void assertNoIncompleteTasks(ReplicationTasksStorage storage) { |
| assertThatStream(storage.streamWaiting()).isEmpty(); |
| assertThatStream(storage.streamRunning()).isEmpty(); |
| } |
| |
| protected static IterableSubject assertThatStream(Stream<?> stream) { |
| return assertThat(stream.collect(Collectors.toList())); |
| } |
| |
| public static URIish getUrish(String uri) { |
| try { |
| return new URIish(uri); |
| } catch (URISyntaxException e) { |
| throw new RuntimeException("Cannot instantiate URIish object", e); |
| } |
| } |
| } |