Bitbucket中用于同步git仓库的插件,支持http(s)和ssh协议。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

292 lines
12 KiB

package com.englishtown.bitbucket.hook;
import com.atlassian.bitbucket.hook.repository.RepositoryHookContext;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.repository.RepositoryService;
import com.atlassian.bitbucket.scm.CommandErrorHandler;
import com.atlassian.bitbucket.scm.CommandExitHandler;
import com.atlassian.bitbucket.scm.CommandOutputHandler;
import com.atlassian.bitbucket.scm.ScmService;
import com.atlassian.bitbucket.scm.git.command.GitCommand;
import com.atlassian.bitbucket.scm.git.command.GitScmCommandBuilder;
import com.atlassian.bitbucket.setting.Settings;
import com.atlassian.bitbucket.setting.SettingsValidationErrors;
import com.atlassian.sal.api.pluginsettings.PluginSettings;
import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
/**
* Unit tests for {@link MirrorRepositoryHook}
*/
public class MirrorRepositoryHookTest {
private MirrorRepositoryHook hook;
private GitScmCommandBuilder builder;
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock
private ScmService scmService;
@Mock
private GitCommand<String> cmd;
@Mock
private ScheduledExecutorService executor;
@Mock
private PasswordEncryptor passwordEncryptor;
@Mock
private SettingsReflectionHelper settingsReflectionHelper;
@Mock
private PluginSettingsFactory pluginSettingsFactory;
@Mock
private PluginSettings pluginSettings;
@Mock
private RepositoryService repositoryService;
private final String mirrorRepoUrlHttp = "https://bitbucket-mirror.englishtown.com/scm/test/test.git";
private final String mirrorRepoUrlSsh = "ssh://git@bitbucket-mirror.englishtown.com/scm/test/test.git";
private final String username = "test-user";
private final String password = "test-password";
private final String repository = "https://test-user:test-password@bitbucket-mirror.englishtown.com/scm/test/test.git";
private final String refspec = "+refs/heads/master:refs/heads/master +refs/heads/develop:refs/heads/develop";
@Captor
ArgumentCaptor<Runnable> argumentCaptor;
@Before
public void setup() {
builder = mock(GitScmCommandBuilder.class);
when(builder.command(anyString())).thenReturn(builder);
when(builder.argument(anyString())).thenReturn(builder);
when(builder.errorHandler(any(CommandErrorHandler.class))).thenReturn(builder);
when(builder.exitHandler(any(CommandExitHandler.class))).thenReturn(builder);
when(builder.<String>build(any(CommandOutputHandler.class))).thenReturn(cmd);
doReturn(builder).when(scmService).createBuilder(any());
when(pluginSettingsFactory.createSettingsForKey(anyString())).thenReturn(pluginSettings);
hook = new MirrorRepositoryHook(scmService, mock(I18nService.class), executor, passwordEncryptor
, settingsReflectionHelper, pluginSettingsFactory, repositoryService);
}
@Test
public void testPostReceive() throws Exception {
when(passwordEncryptor.decrypt(anyString())).thenReturn(password);
Repository repo = mock(Repository.class);
hook.postReceive(buildContext(repo), new ArrayList<>());
verifyExecutor();
}
@Test
public void testEmptyRepositoriesNotMirrored() {
Repository repo = mock(Repository.class);
when(repositoryService.isEmpty(repo)).thenReturn(true);
hook.postReceive(buildContext(repo), new ArrayList<>());
verify(executor, never()).submit(ArgumentMatchers.<Runnable>any());
}
@Test
public void testRunMirrorCommand_Retries() throws Exception {
when(scmService.createBuilder(any())).thenThrow(new RuntimeException("Intentional unit test exception"));
MirrorRepositoryHook hook = new MirrorRepositoryHook(scmService, mock(I18nService.class), executor,
passwordEncryptor, settingsReflectionHelper, pluginSettingsFactory, repositoryService);
MirrorRepositoryHook.MirrorSettings ms = new MirrorRepositoryHook.MirrorSettings();
ms.mirrorRepoUrl = mirrorRepoUrlHttp;
ms.username = username;
ms.password = password;
hook.runMirrorCommand(ms, mock(Repository.class));
verify(executor).submit(argumentCaptor.capture());
Runnable runnable = argumentCaptor.getValue();
runnable.run();
verify(executor, times(1)).schedule(argumentCaptor.capture(), anyLong(), any(TimeUnit.class));
runnable = argumentCaptor.getValue();
runnable.run();
verify(executor, times(2)).schedule(argumentCaptor.capture(), anyLong(), any(TimeUnit.class));
runnable = argumentCaptor.getValue();
runnable.run();
verify(executor, times(3)).schedule(argumentCaptor.capture(), anyLong(), any(TimeUnit.class));
runnable = argumentCaptor.getValue();
runnable.run();
verify(executor, times(4)).schedule(argumentCaptor.capture(), anyLong(), any(TimeUnit.class));
runnable = argumentCaptor.getValue();
runnable.run();
// Make sure it is only called 5 times
runnable.run();
runnable.run();
runnable.run();
verify(executor, times(4)).schedule(argumentCaptor.capture(), anyLong(), any(TimeUnit.class));
}
private void verifyExecutor() throws Exception {
verify(executor).submit(argumentCaptor.capture());
Runnable runnable = argumentCaptor.getValue();
runnable.run();
verify(builder, times(1)).command(eq("push"));
verify(builder, times(1)).argument(eq("--prune"));
verify(builder, times(1)).argument(eq("--atomic"));
verify(builder, times(1)).argument(eq(repository));
verify(builder, times(1)).argument(eq("--force"));
verify(builder, times(1)).argument(eq("+refs/tags/*:refs/tags/*"));
verify(builder, times(1)).argument(eq("+refs/notes/*:refs/notes/*"));
verify(builder, times(1)).argument(eq("+refs/heads/master:refs/heads/master"));
verify(builder, times(1)).argument(eq("+refs/heads/develop:refs/heads/develop"));
verify(cmd, times(1)).call();
}
@Test
public void testGetAuthenticatedUrl() throws Exception {
String result = hook.getAuthenticatedUrl(mirrorRepoUrlHttp, username, password);
assertEquals(repository, result);
}
@Test
public void testValidate() throws Exception {
Settings settings = mock(Settings.class);
Map<String, Object> map = new HashMap<String, Object>();
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:have-user@bitbucket-mirror.englishtown.com/scm/test/test.git")
.thenReturn("ssh://user@bitbucket-mirror.englishtown.com/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);
SettingsValidationErrors errors;
errors = mock(SettingsValidationErrors.class);
hook.validate(settings, errors, repo);
verify(errors, times(1)).addFormError(anyString());
verify(errors, never()).addFieldError(anyString(), anyString());
errors = mock(SettingsValidationErrors.class);
hook.validate(settings, errors, repo);
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, repo);
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, repo);
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, repo);
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, repo);
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, repo);
verify(errors, never()).addFormError(anyString());
verify(errors, never()).addFieldError(anyString(), anyString());
errors = mock(SettingsValidationErrors.class);
hook.validate(settings, errors, repo);
verify(errors, never()).addFormError(anyString());
verify(errors, never()).addFieldError(anyString(), anyString());
}
private RepositoryHookContext buildContext(Repository repo) {
RepositoryHookContext context = mock(RepositoryHookContext.class);
Settings settings = defaultSettings();
when(context.getSettings()).thenReturn(settings);
when(context.getRepository()).thenReturn(repo);
return context;
}
private Settings defaultSettings() {
Map<String, Object> map = new HashMap<String, Object>();
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);
return settings;
}
}