| // Copyright (C) 2021 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.multisite.consumer; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.when; |
| |
| import com.google.common.cache.Cache; |
| import com.google.common.cache.CacheBuilder; |
| import com.google.common.collect.ImmutableSortedSet; |
| import com.google.gerrit.entities.Project; |
| import com.google.gerrit.extensions.events.ProjectDeletedListener; |
| import com.google.gerrit.server.project.ProjectCache; |
| import com.googlesource.gerrit.plugins.multisite.ProjectVersionLogger; |
| import com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate; |
| import java.util.Optional; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Mock; |
| import org.mockito.junit.MockitoJUnitRunner; |
| |
| @RunWith(MockitoJUnitRunner.class) |
| public class ReplicationStatusTest { |
| |
| @Mock private ProjectVersionLogger verLogger; |
| @Mock private ProjectCache projectCache; |
| @Mock private ProjectVersionRefUpdate projectVersionRefUpdate; |
| private ReplicationStatus objectUnderTest; |
| private Cache<String, Long> replicationStatusCache; |
| |
| @Before |
| public void setup() throws Exception { |
| when(projectCache.all()) |
| .thenReturn( |
| ImmutableSortedSet.of(Project.nameKey("projectA"), Project.nameKey("projectB"))); |
| replicationStatusCache = CacheBuilder.newBuilder().build(); |
| objectUnderTest = |
| new ReplicationStatus( |
| replicationStatusCache, projectVersionRefUpdate, verLogger, projectCache); |
| } |
| |
| @Test |
| public void shouldPopulateLagsFromPersistedCacheOnStart() { |
| replicationStatusCache.put("projectA", 10L); |
| replicationStatusCache.put("projectB", 3L); |
| |
| objectUnderTest.start(); |
| assertThat(objectUnderTest.getMaxLag()).isEqualTo(10L); |
| } |
| |
| @Test |
| public void shouldBeAbleToUpdatePersistedCacheValues() { |
| replicationStatusCache.put("projectA", 3L); |
| |
| objectUnderTest.start(); |
| |
| objectUnderTest.doUpdateLag(Project.nameKey("projectA"), 20L); |
| assertThat(objectUnderTest.getMaxLag()).isEqualTo(20L); |
| } |
| |
| @Test |
| public void shouldCombinePersistedProjectsWithNewEntries() { |
| replicationStatusCache.put("projectA", 3L); |
| objectUnderTest.start(); |
| |
| objectUnderTest.doUpdateLag(Project.nameKey("projectB"), 20L); |
| |
| assertThat(objectUnderTest.getReplicationLags(2).keySet()) |
| .containsExactly("projectA", "projectB"); |
| } |
| |
| @Test |
| public void shouldUpdatePersistedCacheWhenUpdatingLagValue() { |
| objectUnderTest.doUpdateLag(Project.nameKey("projectA"), 20L); |
| |
| assertThat(replicationStatusCache.getIfPresent("projectA")).isEqualTo(20L); |
| } |
| |
| @Test |
| public void shouldRemoveProjectFromPersistedCache() { |
| String projectName = "projectA"; |
| long lag = 100; |
| setupReplicationLag(projectName, lag); |
| when(projectVersionRefUpdate.getProjectLocalVersion(projectName)).thenReturn(Optional.empty()); |
| |
| objectUnderTest.onProjectDeleted(projectDeletedEvent(projectName)); |
| |
| assertThat(replicationStatusCache.getIfPresent(projectName)).isNull(); |
| } |
| |
| @Test |
| public void shouldRemoveProjectFromReplicationLags() { |
| String projectName = "projectA"; |
| long lag = 100; |
| setupReplicationLag(projectName, lag); |
| when(projectVersionRefUpdate.getProjectLocalVersion(projectName)).thenReturn(Optional.empty()); |
| |
| assertThat(objectUnderTest.getReplicationLags(1).keySet()).containsExactly(projectName); |
| |
| objectUnderTest.onProjectDeleted(projectDeletedEvent(projectName)); |
| |
| assertThat(objectUnderTest.getReplicationLags(1).keySet()).isEmpty(); |
| } |
| |
| @Test |
| public void shouldNotRemoveProjectFromReplicationLagsIfLocalVersionStillExists() { |
| String projectName = "projectA"; |
| long lag = 100; |
| setupReplicationLag(projectName, lag); |
| when(projectVersionRefUpdate.getProjectLocalVersion(projectName)) |
| .thenReturn(Optional.of(System.currentTimeMillis())); |
| |
| objectUnderTest.onProjectDeleted(projectDeletedEvent(projectName)); |
| |
| assertThat(objectUnderTest.getReplicationLags(1).keySet()).containsExactly(projectName); |
| } |
| |
| @Test |
| public void shouldNotEvictFromPersistentCacheIfLocalVersionStillExists() { |
| String projectName = "projectA"; |
| long lag = 100; |
| setupReplicationLag(projectName, lag); |
| when(projectVersionRefUpdate.getProjectLocalVersion(projectName)) |
| .thenReturn(Optional.of(System.currentTimeMillis())); |
| |
| objectUnderTest.onProjectDeleted(projectDeletedEvent(projectName)); |
| |
| assertThat(replicationStatusCache.getIfPresent(projectName)).isEqualTo(lag); |
| } |
| |
| private void setupReplicationLag(String projectName, long lag) { |
| long currentVersion = System.currentTimeMillis(); |
| long newVersion = currentVersion + lag; |
| replicationStatusCache.put(projectName, 3L); |
| when(projectVersionRefUpdate.getProjectRemoteVersion(projectName)) |
| .thenReturn(Optional.of(newVersion)); |
| when(projectVersionRefUpdate.getProjectLocalVersion(projectName)) |
| .thenReturn(Optional.of(currentVersion)); |
| objectUnderTest.updateReplicationLag(Project.nameKey(projectName)); |
| } |
| |
| private ProjectDeletedListener.Event projectDeletedEvent(String projectName) { |
| ProjectDeletedListener.Event event = mock(ProjectDeletedListener.Event.class); |
| when(event.getProjectName()).thenReturn(projectName); |
| return event; |
| } |
| } |