package com.englishtown.bitbucket.hook; import com.atlassian.bitbucket.concurrent.BucketedExecutor; import com.atlassian.bitbucket.concurrent.ConcurrencyService; import com.atlassian.bitbucket.hook.repository.*; import com.atlassian.bitbucket.project.Project; import com.atlassian.bitbucket.repository.Repository; import com.atlassian.bitbucket.scm.git.GitScm; import com.atlassian.bitbucket.scope.Scope; import com.atlassian.bitbucket.scope.Scopes; import com.atlassian.bitbucket.server.ApplicationPropertiesService; import com.atlassian.bitbucket.setting.Settings; import com.atlassian.bitbucket.setting.SettingsValidationErrors; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import static com.atlassian.bitbucket.mockito.MockitoUtils.returnArg; import static com.englishtown.bitbucket.hook.MirrorRepositoryHook.PROP_ATTEMPTS; import static com.englishtown.bitbucket.hook.MirrorRepositoryHook.PROP_THREADS; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; /** * Unit tests for {@link MirrorRepositoryHook}. */ public class MirrorRepositoryHookTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule().silent(); private final String mirrorRepoUrlHttp = "https://bitbucket-mirror.englishtown.com/scm/test/test.git"; private final String mirrorRepoUrlSsh = "ssh://[email protected]/scm/test/test.git"; private final String password = "test-password"; private final String refspec = "+refs/heads/master:refs/heads/master +refs/heads/develop:refs/heads/develop"; private final String username = "test-user"; @Mock private BucketedExecutor<MirrorRequest> bucketedExecutor; @Mock private MirrorBucketProcessor bucketProcessor; @Mock private ConcurrencyService concurrencyService; private MirrorRepositoryHook hook; @Mock private PasswordEncryptor passwordEncryptor; @Mock private ApplicationPropertiesService propertiesService; @Captor private ArgumentCaptor<MirrorRequest> requestCaptor; @Mock private SettingsReflectionHelper settingsReflectionHelper; @Before public void setup() { doReturn(bucketedExecutor).when(concurrencyService).getBucketedExecutor(anyString(), any()); when(propertiesService.getPluginProperty(eq(PROP_ATTEMPTS), anyInt())).thenAnswer(returnArg(1)); when(propertiesService.getPluginProperty(eq(PROP_THREADS), anyInt())).thenAnswer(returnArg(1)); hook = new MirrorRepositoryHook(concurrencyService, passwordEncryptor, propertiesService, bucketProcessor, settingsReflectionHelper); } @Test public void testPostUpdate() { when(passwordEncryptor.decrypt(anyString())).thenReturn(password); Repository repo = mock(Repository.class); when(repo.getId()).thenReturn(1); when(repo.getScmId()).thenReturn(GitScm.ID); hook.postUpdate(buildContext(), new RepositoryPushHookRequest.Builder(repo).build()); verify(repo).getId(); verify(repo).getScmId(); verify(bucketedExecutor).schedule(requestCaptor.capture(), eq(5L), same(TimeUnit.SECONDS)); MirrorRequest request = requestCaptor.getValue(); assertEquals(1, request.getRepositoryId()); } @Test public void testPostUpdateForHgRepository() { Repository repo = mock(Repository.class); when(repo.getScmId()).thenReturn("hg"); hook.postUpdate(buildContext(), new RepositoryPushHookRequest.Builder(repo).build()); verifyZeroInteractions(bucketedExecutor); } @Test public void testPostUpdateUnconfigured() { Repository repo = mock(Repository.class); when(repo.getScmId()).thenReturn(GitScm.ID); Settings settings = mock(Settings.class); when(settings.asMap()).thenReturn(Collections.emptyMap()); PostRepositoryHookContext context = mock(PostRepositoryHookContext.class); when(context.getSettings()).thenReturn(settings); hook.postUpdate(context, new RepositoryPushHookRequest.Builder(repo).build()); verifyZeroInteractions(bucketedExecutor); } @Test public void testUnwantedEventsIgnored() { Repository repo = mock(Repository.class); hook.postUpdate(buildContext(), buildRequest(StandardRepositoryHookTrigger.UNKNOWN, repo)); verify(bucketedExecutor, never()).submit(any()); } @Test public void testValidate() { Settings settings = mock(Settings.class); Map<String, Object> map = new HashMap<>(); map.put(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL + "0", ""); when(settings.asMap()).thenReturn(map); when(settings.getString(eq(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL + "0"), eq(""))) .thenThrow(new RuntimeException("Intentional unit test exception")) .thenReturn("") .thenReturn(mirrorRepoUrlHttp) .thenReturn("invalid uri") .thenReturn("http://should-not:[email protected]/scm/test/test.git") .thenReturn("ssh://[email protected]/scm/test/test.git") .thenReturn(mirrorRepoUrlSsh) .thenReturn(mirrorRepoUrlHttp); when(settings.getString(eq(MirrorRepositoryHook.SETTING_USERNAME + "0"), eq(""))) .thenReturn("") .thenReturn("") .thenReturn("") .thenReturn(username); when(settings.getString(eq(MirrorRepositoryHook.SETTING_PASSWORD + "0"), eq(""))) .thenReturn("") .thenReturn("") .thenReturn("") .thenReturn(password); when(settings.getString(eq(MirrorRepositoryHook.SETTING_REFSPEC + "0"), eq(""))) .thenReturn("??") .thenReturn("+refs/heads/master:refs/heads/master") .thenReturn(""); Repository repo = mock(Repository.class); Scope scope = Scopes.repository(repo); SettingsValidationErrors errors; errors = mock(SettingsValidationErrors.class); hook.validate(settings, errors, scope); verify(errors, times(1)).addFormError(anyString()); verify(errors, never()).addFieldError(anyString(), anyString()); errors = mock(SettingsValidationErrors.class); hook.validate(settings, errors, scope); verify(errors, never()).addFormError(anyString()); verify(errors).addFieldError(eq(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL + "0"), anyString()); verify(errors, never()).addFieldError(eq(MirrorRepositoryHook.SETTING_USERNAME + "0"), anyString()); verify(errors, never()).addFieldError(eq(MirrorRepositoryHook.SETTING_PASSWORD + "0"), anyString()); verify(errors).addFieldError(eq(MirrorRepositoryHook.SETTING_REFSPEC + "0"), anyString()); errors = mock(SettingsValidationErrors.class); hook.validate(settings, errors, scope); verify(errors, never()).addFormError(anyString()); verify(errors, never()).addFieldError(eq(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL + "0"), anyString()); verify(errors).addFieldError(eq(MirrorRepositoryHook.SETTING_USERNAME + "0"), anyString()); verify(errors).addFieldError(eq(MirrorRepositoryHook.SETTING_PASSWORD + "0"), anyString()); errors = mock(SettingsValidationErrors.class); hook.validate(settings, errors, scope); verify(errors, never()).addFormError(anyString()); verify(errors, never()).addFieldError(eq(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL + "0"), anyString()); verify(errors, never()).addFieldError(anyString(), anyString()); errors = mock(SettingsValidationErrors.class); hook.validate(settings, errors, scope); verify(errors, never()).addFormError(anyString()); verify(errors).addFieldError(eq(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL + "0"), anyString()); verify(errors, never()).addFieldError(eq(MirrorRepositoryHook.SETTING_USERNAME + "0"), anyString()); verify(errors, never()).addFieldError(eq(MirrorRepositoryHook.SETTING_PASSWORD + "0"), anyString()); errors = mock(SettingsValidationErrors.class); hook.validate(settings, errors, scope); verify(errors, never()).addFormError(anyString()); verify(errors, never()).addFieldError(eq(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL + "0"), anyString()); verify(errors, never()).addFieldError(eq(MirrorRepositoryHook.SETTING_USERNAME + "0"), anyString()); verify(errors, never()).addFieldError(eq(MirrorRepositoryHook.SETTING_PASSWORD + "0"), anyString()); errors = mock(SettingsValidationErrors.class); hook.validate(settings, errors, scope); verify(errors, never()).addFormError(anyString()); verify(errors, never()).addFieldError(anyString(), anyString()); errors = mock(SettingsValidationErrors.class); hook.validate(settings, errors, scope); verify(errors, never()).addFormError(anyString()); verify(errors, never()).addFieldError(anyString(), anyString()); } @Test public void testValidateForGlobal() { SettingsValidationErrors errors = mock(SettingsValidationErrors.class); Settings settings = mock(Settings.class); hook.validate(settings, errors, Scopes.global()); verifyZeroInteractions(bucketedExecutor, errors, settings); } @Test public void testValidateForProject() { SettingsValidationErrors errors = mock(SettingsValidationErrors.class); Project project = mock(Project.class); Settings settings = mock(Settings.class); hook.validate(settings, errors, Scopes.project(project)); verifyZeroInteractions(bucketedExecutor, errors, settings); } private PostRepositoryHookContext buildContext() { Settings settings = defaultSettings(); PostRepositoryHookContext context = mock(PostRepositoryHookContext.class); when(context.getSettings()).thenReturn(settings); return context; } private RepositoryHookRequest buildRequest(RepositoryHookTrigger trigger, Repository repo) { RepositoryHookRequest request = mock(RepositoryHookRequest.class); when(request.getTrigger()).thenReturn(trigger); when(request.getRepository()).thenReturn(repo); return request; } private Settings defaultSettings() { Map<String, Object> map = new HashMap<>(); map.put(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL, ""); Settings settings = mock(Settings.class); when(settings.asMap()).thenReturn(map); when(settings.getString(eq(MirrorRepositoryHook.SETTING_MIRROR_REPO_URL), eq(""))).thenReturn(mirrorRepoUrlHttp); when(settings.getString(eq(MirrorRepositoryHook.SETTING_USERNAME), eq(""))).thenReturn(username); when(settings.getString(eq(MirrorRepositoryHook.SETTING_PASSWORD), eq(""))).thenReturn(password); when(settings.getString(eq(MirrorRepositoryHook.SETTING_REFSPEC), eq(""))).thenReturn(refspec); when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_TAGS), eq(true))).thenReturn(true); when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_NOTES), eq(true))).thenReturn(true); when(settings.getBoolean(eq(MirrorRepositoryHook.SETTING_ATOMIC), eq(true))).thenReturn(true); return settings; } }