From b6fd0cb4720e9d4e4ca26d0752582790b191bd68 Mon Sep 17 00:00:00 2001 From: Owen Berry Date: Wed, 23 Mar 2016 15:11:42 -0400 Subject: [PATCH] Fix 188: Fixes for configuration migration - Migrate multi-branch-project plugin template configuration from 1.8 format to 2.0 format. - Remove Slack job properties from Pipeline projects which do not have publishers to migrate to. - Refactor migration code to better handle the additional complexity. --- .../jenkins/plugins/slack/SlackNotifier.java | 132 +++++++++++------- .../config/AbstractProjectConfigMigrator.java | 98 +++++++++++++ .../slack/config/ItemConfigMigrator.java | 112 +++++++++++++++ .../ItemWithTemplateConfigMigrator.java | 100 +++++++++++++ .../slack/config/JobConfigMigrator.java | 51 +++++++ .../slack/config/ItemConfigMigratorTest.java | 111 +++++++++++++++ .../ItemWithTemplateConfigMigratorTest.java | 95 +++++++++++++ .../slack/config/JobConfigMigratorTest.java | 56 ++++++++ 8 files changed, 701 insertions(+), 54 deletions(-) create mode 100644 src/main/java/jenkins/plugins/slack/config/AbstractProjectConfigMigrator.java create mode 100644 src/main/java/jenkins/plugins/slack/config/ItemConfigMigrator.java create mode 100644 src/main/java/jenkins/plugins/slack/config/ItemWithTemplateConfigMigrator.java create mode 100644 src/main/java/jenkins/plugins/slack/config/JobConfigMigrator.java create mode 100644 src/test/java/jenkins/plugins/slack/config/ItemConfigMigratorTest.java create mode 100644 src/test/java/jenkins/plugins/slack/config/ItemWithTemplateConfigMigratorTest.java create mode 100644 src/test/java/jenkins/plugins/slack/config/JobConfigMigratorTest.java diff --git a/src/main/java/jenkins/plugins/slack/SlackNotifier.java b/src/main/java/jenkins/plugins/slack/SlackNotifier.java index a9628b31..d1a1e266 100755 --- a/src/main/java/jenkins/plugins/slack/SlackNotifier.java +++ b/src/main/java/jenkins/plugins/slack/SlackNotifier.java @@ -6,7 +6,9 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; +import hudson.model.Item; import hudson.model.Descriptor; +import hudson.model.Job; import hudson.model.listeners.ItemListener; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; @@ -15,14 +17,21 @@ import hudson.util.FormValidation; import jenkins.model.Jenkins; import jenkins.model.JenkinsLocationConfiguration; +import jenkins.plugins.slack.config.AbstractProjectConfigMigrator; +import jenkins.plugins.slack.config.ItemConfigMigrator; +import jenkins.plugins.slack.config.JobConfigMigrator; import net.sf.json.JSONObject; + import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.export.Exported; +import org.springframework.util.ReflectionUtils; import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -58,14 +67,26 @@ public String getTeamDomain() { return teamDomain; } + public void setTeamDomain(final String teamDomain) { + this.teamDomain = teamDomain; + } + public String getRoom() { return room; } + public void setRoom(String room) { + this.room = room; + } + public String getAuthToken() { return authToken; } + public void setAuthToken(String authToken) { + this.authToken = authToken; + } + public String getBuildServerUrl() { if(buildServerUrl == null || buildServerUrl == "") { JenkinsLocationConfiguration jenkinsConfig = new JenkinsLocationConfiguration(); @@ -128,6 +149,54 @@ public String getCustomMessage() { return customMessage; } + public void setStartNotification(boolean startNotification) { + this.startNotification = startNotification; + } + + public void setNotifySuccess(boolean notifySuccess) { + this.notifySuccess = notifySuccess; + } + + public void setCommitInfoChoice(CommitInfoChoice commitInfoChoice) { + this.commitInfoChoice = commitInfoChoice; + } + + public void setNotifyAborted(boolean notifyAborted) { + this.notifyAborted = notifyAborted; + } + + public void setNotifyFailure(boolean notifyFailure) { + this.notifyFailure = notifyFailure; + } + + public void setNotifyNotBuilt(boolean notifyNotBuilt) { + this.notifyNotBuilt = notifyNotBuilt; + } + + public void setNotifyUnstable(boolean notifyUnstable) { + this.notifyUnstable = notifyUnstable; + } + + public void setNotifyBackToNormal(boolean notifyBackToNormal) { + this.notifyBackToNormal = notifyBackToNormal; + } + + public void setIncludeTestSummary(boolean includeTestSummary) { + this.includeTestSummary = includeTestSummary; + } + + public void setNotifyRepeatedFailure(boolean notifyRepeatedFailure) { + this.notifyRepeatedFailure = notifyRepeatedFailure; + } + + public void setIncludeCustomMessage(boolean includeCustomMessage) { + this.includeCustomMessage = includeCustomMessage; + } + + public void setCustomMessage(String customMessage) { + this.customMessage = customMessage; + } + @DataBoundConstructor public SlackNotifier(final String teamDomain, final String authToken, final String room, final String buildServerUrl, final String sendAs, final boolean startNotification, final boolean notifyAborted, final boolean notifyFailure, @@ -465,66 +534,21 @@ public String getCustomMessage() { } @Extension public static final class Migrator extends ItemListener { - @SuppressWarnings("deprecation") @Override public void onLoaded() { logger.info("Starting Settings Migration Process"); - for (AbstractProject p : Jenkins.getInstance().getAllItems(AbstractProject.class)) { - logger.info("processing Job: " + p.getName()); - - final SlackJobProperty slackJobProperty = p.getProperty(SlackJobProperty.class); - - if (slackJobProperty == null) { - logger.info(String - .format("Configuration is already up to date for \"%s\", skipping migration", - p.getName())); + + ItemConfigMigrator migrator = new ItemConfigMigrator(); + + for (Item item : Jenkins.getInstance().getAllItems()) { + if (!migrator.migrate(item)) { + logger.info(String.format("Skipping job \"%s\" with type %s", item.getName(), + item.getClass().getName())); continue; } - - SlackNotifier slackNotifier = p.getPublishersList().get(SlackNotifier.class); - - if (slackNotifier == null) { - logger.info(String - .format("Configuration does not have a notifier for \"%s\", not migrating settings", - p.getName())); - } else { - - //map settings - if (StringUtils.isBlank(slackNotifier.teamDomain)) { - slackNotifier.teamDomain = slackJobProperty.getTeamDomain(); - } - if (StringUtils.isBlank(slackNotifier.authToken)) { - slackNotifier.authToken = slackJobProperty.getToken(); - } - if (StringUtils.isBlank(slackNotifier.room)) { - slackNotifier.room = slackJobProperty.getRoom(); - } - - slackNotifier.startNotification = slackJobProperty.getStartNotification(); - - slackNotifier.notifyAborted = slackJobProperty.getNotifyAborted(); - slackNotifier.notifyFailure = slackJobProperty.getNotifyFailure(); - slackNotifier.notifyNotBuilt = slackJobProperty.getNotifyNotBuilt(); - slackNotifier.notifySuccess = slackJobProperty.getNotifySuccess(); - slackNotifier.notifyUnstable = slackJobProperty.getNotifyUnstable(); - slackNotifier.notifyBackToNormal = slackJobProperty.getNotifyBackToNormal(); - slackNotifier.notifyRepeatedFailure = slackJobProperty.getNotifyRepeatedFailure(); - - slackNotifier.includeTestSummary = slackJobProperty.includeTestSummary(); - slackNotifier.commitInfoChoice = slackJobProperty.getShowCommitList() ? CommitInfoChoice.AUTHORS_AND_TITLES : CommitInfoChoice.NONE; - slackNotifier.includeCustomMessage = slackJobProperty.includeCustomMessage(); - slackNotifier.customMessage = slackJobProperty.getCustomMessage(); - } - - try { - //property section is not used anymore - remove - p.removeProperty(SlackJobProperty.class); - p.save(); - logger.info("Configuration updated successfully"); - } catch (IOException e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } } + + logger.info("Completed Settings Migration Process"); } } } diff --git a/src/main/java/jenkins/plugins/slack/config/AbstractProjectConfigMigrator.java b/src/main/java/jenkins/plugins/slack/config/AbstractProjectConfigMigrator.java new file mode 100644 index 00000000..a09bd16f --- /dev/null +++ b/src/main/java/jenkins/plugins/slack/config/AbstractProjectConfigMigrator.java @@ -0,0 +1,98 @@ +package jenkins.plugins.slack.config; + +import hudson.model.AbstractProject; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import jenkins.plugins.slack.CommitInfoChoice; +import jenkins.plugins.slack.SlackNotifier; +import jenkins.plugins.slack.SlackNotifier.SlackJobProperty; + +import org.apache.commons.lang.StringUtils; + +/** + * Configuration migrator for migrating the Slack plugin configuration for a + * {@link AbstractProject} from the 1.8 format to the 2.0 format. It does so by + * removing the SlackJobProperty from the job properties (if there is one) and + * moving the Slack notification settings to a {@link SlackNotifier} in the list + * of publishers (if there is one). + */ +@SuppressWarnings("deprecation") +public class AbstractProjectConfigMigrator { + + private static final Logger logger = Logger.getLogger(AbstractProjectConfigMigrator.class + .getName()); + + public void migrate(final AbstractProject project) { + + logger.info(String.format("Migrating project \"%s\" with type %s", project.getName(), + project.getClass().getName())); + + final SlackJobProperty slackJobProperty = project.getProperty(SlackJobProperty.class); + + if (slackJobProperty == null) { + logger.info(String.format( + "Configuration is already up to date for \"%s\", skipping migration", + project.getName())); + return; + } + + SlackNotifier slackNotifier = project.getPublishersList().get(SlackNotifier.class); + + if (slackNotifier == null) { + logger.info(String.format( + "Configuration does not have a notifier for \"%s\", not migrating settings", + project.getName())); + } else { + updateSlackNotifier(slackNotifier, slackJobProperty); + } + + try { + // property section is not used anymore - remove + project.removeProperty(SlackJobProperty.class); + project.save(); + logger.info("Configuration updated successfully"); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } + + private void updateSlackNotifier(final SlackNotifier slackNotifier, + final SlackJobProperty slackJobProperty) { + + if (StringUtils.isBlank(slackNotifier.getTeamDomain())) { + slackNotifier.setTeamDomain(slackJobProperty.getTeamDomain()); + } + if (StringUtils.isBlank(slackNotifier.getAuthToken())) { + slackNotifier.setAuthToken(slackJobProperty.getToken()); + } + if (StringUtils.isBlank(slackNotifier.getRoom())) { + slackNotifier.setRoom(slackJobProperty.getRoom()); + } + + slackNotifier.setStartNotification(slackJobProperty.getStartNotification()); + + slackNotifier.setNotifyAborted(slackJobProperty.getNotifyAborted()); + slackNotifier.setNotifyFailure(slackJobProperty.getNotifyFailure()); + slackNotifier.setNotifyNotBuilt(slackJobProperty.getNotifyNotBuilt()); + slackNotifier.setNotifySuccess(slackJobProperty.getNotifySuccess()); + slackNotifier.setNotifyUnstable(slackJobProperty.getNotifyUnstable()); + slackNotifier.setNotifyBackToNormal(slackJobProperty.getNotifyBackToNormal()); + slackNotifier.setNotifyRepeatedFailure(slackJobProperty.getNotifyRepeatedFailure()); + + slackNotifier.setIncludeTestSummary(slackJobProperty.includeTestSummary()); + slackNotifier.setCommitInfoChoice(getCommitInfoChoice(slackJobProperty)); + slackNotifier.setIncludeCustomMessage(slackJobProperty.includeCustomMessage()); + slackNotifier.setCustomMessage(slackJobProperty.getCustomMessage()); + } + + private CommitInfoChoice getCommitInfoChoice(final SlackJobProperty slackJobProperty) { + if (slackJobProperty.getShowCommitList()) { + return CommitInfoChoice.AUTHORS_AND_TITLES; + } else { + return CommitInfoChoice.NONE; + } + } +} diff --git a/src/main/java/jenkins/plugins/slack/config/ItemConfigMigrator.java b/src/main/java/jenkins/plugins/slack/config/ItemConfigMigrator.java new file mode 100644 index 00000000..327b9140 --- /dev/null +++ b/src/main/java/jenkins/plugins/slack/config/ItemConfigMigrator.java @@ -0,0 +1,112 @@ +package jenkins.plugins.slack.config; + +import java.lang.reflect.Method; + +import org.springframework.util.ReflectionUtils; + +import hudson.model.AbstractProject; +import hudson.model.Item; +import hudson.model.Job; +import hudson.util.DescribableList; + +/** + * Configuration migrator for migrating the Slack plugin configuration from the + * 1.8 format to the 2.0 format, for an item like a job or project. Mainly just + * decides what needs to be done and delegates the actual migration work to + * other migrators. + * + * @see AbstractProjectConfigMigrator + * @see JobConfigMigrator + * @see ItemWithTemplateConfigMigrator + */ +public class ItemConfigMigrator { + + private final AbstractProjectConfigMigrator projectMigrator; + private final JobConfigMigrator jobMigrator; + private final ItemWithTemplateConfigMigrator templateMigrator; + + public ItemConfigMigrator() { + projectMigrator = new AbstractProjectConfigMigrator(); + jobMigrator = new JobConfigMigrator(); + templateMigrator = new ItemWithTemplateConfigMigrator(projectMigrator); + } + + /** + * Constructor for injecting migrators for testing. + */ + protected ItemConfigMigrator(AbstractProjectConfigMigrator projectMigrator, + JobConfigMigrator jobMigrator, ItemWithTemplateConfigMigrator templateMigrator) { + this.projectMigrator = projectMigrator; + this.jobMigrator = jobMigrator; + this.templateMigrator = templateMigrator; + } + + /** + * Migrate configuration for a {@link Item} from the 1.8 format to the 2.0 + * format. This primarily removes job properties and adds them to a + * notifier. + * + * @param item + * Item to migrate + * @return true if migration was attempted for an expected scenario; false + * if migration was not attempted due to an unexpected data type or + * other reason + */ + public boolean migrate(Item item) { + + // Attempt migrations in priority order + return migrateAbstractProject(item) || migrateJobWithoutPublishersList(item) + || templateMigrator.migrate(item); + } + + /** + * Migrate an item if it is a subclass of AbstractProject. + * + * @param item + * Item to migrate + * @return true if migration was attempted + */ + private boolean migrateAbstractProject(Item item) { + + if (item instanceof AbstractProject) { + AbstractProject project = (AbstractProject) item; + projectMigrator.migrate(project); + return true; + } + return false; + } + + /** + * Migrate an item if it is subclass of Job. It might have Slack job + * properties that need to be removed in the migration, but does not handle + * the possibility of a Job that has publishers. That should theoretically + * not happen because then it should be a subclass of AbstractProject, but + * we want to avoid losing settings. So if it looks like it has publishers, + * migration is skipped. + * + * @param item + * Item to migrate + * @return true if migration was attempted + */ + private boolean migrateJobWithoutPublishersList(Item item) { + if (item instanceof Job) { + if (!hasMethodGetPublishersList(item)) { + Job job = (Job) item; + jobMigrator.migrate(job); + return true; + } + } + return false; + } + + private boolean hasMethodGetPublishersList(Item item) { + + Method method = ReflectionUtils.findMethod(item.getClass(), "getPublishersList"); + + if (method != null && method.getReturnType().equals(DescribableList.class)) { + return true; + } + + return false; + } +} diff --git a/src/main/java/jenkins/plugins/slack/config/ItemWithTemplateConfigMigrator.java b/src/main/java/jenkins/plugins/slack/config/ItemWithTemplateConfigMigrator.java new file mode 100644 index 00000000..b8773701 --- /dev/null +++ b/src/main/java/jenkins/plugins/slack/config/ItemWithTemplateConfigMigrator.java @@ -0,0 +1,100 @@ +package jenkins.plugins.slack.config; + +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.springframework.util.ReflectionUtils; + +import hudson.model.AbstractProject; +import hudson.model.Item; +import hudson.model.Job; + +/** + * Configuration migrator for migrating the Slack plugin configuration for an + * {@link AbstractProject} that belongs to an {@link Item} as a template, from + * the 1.8 format to the 2.0 format. + * + *

+ * This is a workaround for installations that use the multi-branch-project + * plugin, which uses a template to configure jobs for all branches in a repo. + * The template will be updated by an {@link AbstractProjectConfigMigrator}. + *

+ */ +public class ItemWithTemplateConfigMigrator { + + private static final Logger logger = Logger.getLogger(ItemWithTemplateConfigMigrator.class + .getName()); + + private AbstractProjectConfigMigrator projectMigrator; + + /** + * Default constructor. + * + * @param projectMigrator + * Migrator to be used for migrating the template + */ + public ItemWithTemplateConfigMigrator(final AbstractProjectConfigMigrator projectMigrator) { + this.projectMigrator = projectMigrator; + } + + /** + * Migrate an item if it has a template that is a subclass of + * AbstractProject. + * + * @param item + * Item to migrate + * @return true if migration was attempted on a template + */ + public boolean migrate(final Item item) { + AbstractProject project = getTemplateProject(item); + + if (project != null) { + projectMigrator.migrate(project); + return true; + } + + return false; + } + + /** + * Examine an Item to determine if it has a "getTemplate" method that + * returns an AbstractProject, and return the AbstractProject if it does. + * + * @param item + * Item to examine + * @return AbstractProject that is returned by item.getTemplate(), or null + */ + private AbstractProject getTemplateProject(final Item item) { + + logger.log(Level.FINE, + String.format("Checking \"%s\" for AbstractProject template", item.getName())); + + Method getTemplate = ReflectionUtils.findMethod(item.getClass(), "getTemplate"); + + if (getTemplate == null) { + logger.log(Level.FINE, "No template getter method found"); + return null; + } + + Object obj = null; + + try { + obj = getTemplate.invoke(item); + } catch (Exception e) { + logger.info("Error getting \"template\" value: " + e.getMessage()); + return null; + } + + if (obj == null) { + logger.log(Level.FINE, "Item has no template"); + } else if (obj instanceof AbstractProject) { + return (AbstractProject) obj; + } else { + logger.log(Level.FINE, "Template is not an AbstractProject; type is: " + + obj.getClass().getName()); + } + + return null; + } +} diff --git a/src/main/java/jenkins/plugins/slack/config/JobConfigMigrator.java b/src/main/java/jenkins/plugins/slack/config/JobConfigMigrator.java new file mode 100644 index 00000000..d6c8a589 --- /dev/null +++ b/src/main/java/jenkins/plugins/slack/config/JobConfigMigrator.java @@ -0,0 +1,51 @@ +package jenkins.plugins.slack.config; + +import hudson.model.Job; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import jenkins.plugins.slack.SlackNotifier.SlackJobProperty; + +/** + * Configuration migrator for migrating the Slack plugin configuration for a + * {@link Job} from the 1.8 format to the 2.0 format. It does so by removing the + * SlackJobProperty from the job properties (if there is one). + * + *

+ * SlackJobProperty settings are usually migrated to a publisher, but there are + * no publishers in a Job so the settings are lost. For this reason, be + * careful of how you use this migrator.. + *

+ */ +@SuppressWarnings("deprecation") +public class JobConfigMigrator { + + private static final Logger logger = Logger.getLogger(JobConfigMigrator.class.getName()); + + public void migrate(final Job job) { + + logger.info(String.format("Migrating job \"%s\" with type %s", job.getName(), job + .getClass().getName())); + + final SlackJobProperty slackJobProperty = job.getProperty(SlackJobProperty.class); + + if (slackJobProperty == null) { + logger.info(String.format( + "Configuration is already up to date for \"%s\", skipping migration", + job.getName())); + return; + } + + try { + // property section is not used anymore - remove + job.removeProperty(SlackJobProperty.class); + job.save(); + logger.info(String.format("Configuration for \"%s\" updated successfully", + job.getName())); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } +} diff --git a/src/test/java/jenkins/plugins/slack/config/ItemConfigMigratorTest.java b/src/test/java/jenkins/plugins/slack/config/ItemConfigMigratorTest.java new file mode 100644 index 00000000..e6c72c44 --- /dev/null +++ b/src/test/java/jenkins/plugins/slack/config/ItemConfigMigratorTest.java @@ -0,0 +1,111 @@ +package jenkins.plugins.slack.config; + +import jenkins.model.AbstractTopLevelItem; +import jenkins.model.Jenkins; +import hudson.model.Descriptor; +import hudson.model.Item; +import hudson.model.ItemGroup; +import hudson.model.ViewJob; +import hudson.model.AbstractProject; +import hudson.model.Job; +import hudson.tasks.Publisher; +import hudson.util.DescribableList; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +public class ItemConfigMigratorTest { + + private AbstractProjectConfigMigrator projectMigrator; + private JobConfigMigrator jobMigrator; + private ItemWithTemplateConfigMigrator templateMigrator; + private ItemConfigMigrator migrator; + + private Jenkins jenkins; + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Before + public void setUp() { + jenkins = j.getInstance(); + + projectMigrator = mock(AbstractProjectConfigMigrator.class); + jobMigrator = mock(JobConfigMigrator.class); + templateMigrator = mock(ItemWithTemplateConfigMigrator.class); + + migrator = new ItemConfigMigrator(projectMigrator, jobMigrator, templateMigrator); + } + + @Test + public void testMigrate_AbstractProject() { + AbstractProject item = mock(AbstractProject.class); + + assertTrue(migrator.migrate(item)); + + verify(projectMigrator).migrate(any(AbstractProject.class)); + verify(jobMigrator, never()).migrate(any(Job.class)); + verify(templateMigrator, never()).migrate(any(AbstractProject.class)); + } + + @Test + public void testMigrate_Job() { + Job item = mock(Job.class); + + assertTrue(migrator.migrate(item)); + + verify(projectMigrator, never()).migrate(any(AbstractProject.class)); + verify(jobMigrator).migrate(any(Job.class)); + verify(templateMigrator, never()).migrate(any(AbstractProject.class)); + } + + @Test + public void testMigrate_JobWithPublishersList() { + Job item = new JobWithPublishers(jenkins, "Random Name"); + + doReturn(false).when(templateMigrator).migrate(item); + + assertFalse(migrator.migrate(item)); + + verify(projectMigrator, never()).migrate(any(AbstractProject.class)); + verify(jobMigrator, never()).migrate(any(Job.class)); + verify(templateMigrator).migrate(any(AbstractProject.class)); + } + + @Test + public void testMigrate_ItemWithTemplate() { + + Item item = mock(Item.class); + + doReturn(true).when(templateMigrator).migrate(item); + + assertTrue(migrator.migrate(item)); + + verify(projectMigrator, never()).migrate(any(AbstractProject.class)); + verify(jobMigrator, never()).migrate(any(Job.class)); + verify(templateMigrator).migrate(eq(item)); + } + + @SuppressWarnings("rawtypes") + private static class JobWithPublishers extends ViewJob { + + public JobWithPublishers(ItemGroup parent, String name) { + super(parent, name); + } + + @Override + public void reload() { + } + + @SuppressWarnings("unused") + public DescribableList> getPublishersList() { + return null; + } + } +} diff --git a/src/test/java/jenkins/plugins/slack/config/ItemWithTemplateConfigMigratorTest.java b/src/test/java/jenkins/plugins/slack/config/ItemWithTemplateConfigMigratorTest.java new file mode 100644 index 00000000..5ce65104 --- /dev/null +++ b/src/test/java/jenkins/plugins/slack/config/ItemWithTemplateConfigMigratorTest.java @@ -0,0 +1,95 @@ +package jenkins.plugins.slack.config; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; +import hudson.model.ItemGroup; +import hudson.model.AbstractProject; +import hudson.model.FreeStyleProject; +import jenkins.model.AbstractTopLevelItem; +import jenkins.model.Jenkins; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +@SuppressWarnings("deprecation") +public class ItemWithTemplateConfigMigratorTest { + + private Jenkins jenkins; + private ItemWithTemplateConfigMigrator migrator; + private AbstractProjectConfigMigrator projectMigratorMock; + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Before + public void setUp() { + jenkins = j.getInstance(); + projectMigratorMock = mock(AbstractProjectConfigMigrator.class); + migrator = new ItemWithTemplateConfigMigrator(projectMigratorMock); + } + + @Test + public void testMigrate_WithNoTemplateMethod() { + FreeStyleProject project = new FreeStyleProject(jenkins, "Test_Slack_Plugin"); + assertFalse("Should not be able to find template project", migrator.migrate(project)); + verify(projectMigratorMock, never()).migrate(any(AbstractProject.class)); + } + + @Test + public void testMigrate_WithNoTemplate() { + ProjectWithTemplate project = new ProjectWithTemplate(jenkins, "Test", null); + assertFalse("Should be no template project", migrator.migrate(project)); + verify(projectMigratorMock, never()).migrate(any(AbstractProject.class)); + } + + @Test + public void testMigrate_WithTemplateWrongType() { + ProjectWithTemplate project = new ProjectWithTemplate(jenkins, "Test", "NOT A PROJECT"); + assertFalse("Template is not correct type", migrator.migrate(project)); + verify(projectMigratorMock, never()).migrate(any(AbstractProject.class)); + } + + @Test + public void testMigrate_WithTemplate() { + FreeStyleProject template = new FreeStyleProject(jenkins, "Test_Slack_Plugin"); + ProjectWithTemplate project = new ProjectWithTemplate(jenkins, "Test", template); + assertTrue("Template was migrated", migrator.migrate(project)); + verify(projectMigratorMock, times(1)).migrate(eq(template)); + } + + @Test + public void testMigrate_WithException() { + FreeStyleProject template = new FreeStyleProject(jenkins, "Test_Slack_Plugin"); + ProjectWithTemplate project = new ProjectWithTemplate(jenkins, "Test", template, true); + assertFalse(migrator.migrate(project)); + verify(projectMigratorMock, never()).migrate(any(AbstractProject.class)); + } + + private static class ProjectWithTemplate extends AbstractTopLevelItem { + private Object template; + private boolean doThrow = false; + + public ProjectWithTemplate(ItemGroup group, String name, Object template) { + super(group, name); + this.template = template; + } + + public ProjectWithTemplate(ItemGroup group, String name, Object template, boolean doThrow) { + super(group, name); + this.template = template; + this.doThrow = doThrow; + } + + @SuppressWarnings("unused") + public Object getTemplate() { + if (doThrow) { + throw new RuntimeException("Something bad happened"); + } + return template; + } + } +} diff --git a/src/test/java/jenkins/plugins/slack/config/JobConfigMigratorTest.java b/src/test/java/jenkins/plugins/slack/config/JobConfigMigratorTest.java new file mode 100644 index 00000000..19765bc1 --- /dev/null +++ b/src/test/java/jenkins/plugins/slack/config/JobConfigMigratorTest.java @@ -0,0 +1,56 @@ +package jenkins.plugins.slack.config; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import hudson.model.Job; + +import java.io.IOException; + +import jenkins.plugins.slack.SlackNotifier.SlackJobProperty; + +import org.junit.Before; +import org.junit.Test; + +@SuppressWarnings("deprecation") +public class JobConfigMigratorTest { + + private JobConfigMigrator migrator; + + @Before + public void setUp() { + migrator = new JobConfigMigrator(); + } + + @Test + public void testMigrate_WithNoSlackJobProperty() throws Exception { + Job project = mock(Job.class); + migrator.migrate(project); + verify(project).getProperty(eq(SlackJobProperty.class)); + verify(project, never()).removeProperty(eq(SlackJobProperty.class)); + } + + @Test + public void testMigrate_WithSlackJobProperty() throws Exception { + SlackJobProperty jobProperty = mock(SlackJobProperty.class); + + Job project = mock(Job.class); + when(project.getProperty(eq(SlackJobProperty.class))).thenReturn(jobProperty); + + migrator.migrate(project); + verify(project).getProperty(eq(SlackJobProperty.class)); + verify(project).removeProperty(eq(SlackJobProperty.class)); + } + + @Test + public void testMigrate_WithIOExceptionOnSave() throws Exception { + SlackJobProperty jobProperty = mock(SlackJobProperty.class); + + Job project = mock(Job.class); + when(project.getProperty(eq(SlackJobProperty.class))).thenReturn(jobProperty); + doThrow(new IOException("Something bad happened")).when(project).save(); + + migrator.migrate(project); + + // exception caught and nothing bad happened + } +}