diff --git a/src/main/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerBuilder.java b/src/main/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerBuilder.java index b6ab56a..7883b1b 100644 --- a/src/main/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerBuilder.java +++ b/src/main/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerBuilder.java @@ -39,6 +39,7 @@ import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.verb.POST; import java.io.IOException; import java.io.Serializable; @@ -254,17 +255,22 @@ public String getDisplayName() { return DISPLAY_NAME; } + @POST public FormValidation doCheckIncludes(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException { + project.checkPermission(Item.CONFIGURE); return FilePath.validateFileMask(project.getSomeWorkspace(), value); } + @POST public FormValidation doCheckExcludes(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException { + project.checkPermission(Item.CONFIGURE); if (value == null || value.trim().length() == 0) { return FormValidation.ok(); } return FilePath.validateFileMask(project.getSomeWorkspace(), value); } + @POST public FormValidation doCheckRemote(@QueryParameter String value) throws IOException { if (value == null || value.trim().length() == 0) { throw FormValidation.error("Remote directory is mandatory."); diff --git a/src/main/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerPublisher.java b/src/main/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerPublisher.java index 79f97ab..99af4f6 100644 --- a/src/main/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerPublisher.java +++ b/src/main/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerPublisher.java @@ -42,6 +42,7 @@ import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.verb.POST; import java.io.IOException; import java.io.Serializable; @@ -347,17 +348,22 @@ public String getDisplayName() { return DISPLAY_NAME; } + @POST public FormValidation doCheckIncludes(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException { + project.checkPermission(Item.CONFIGURE); return FilePath.validateFileMask(project.getSomeWorkspace(), value); } + @POST public FormValidation doCheckExcludes(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException { + project.checkPermission(Item.CONFIGURE); if (value == null || value.trim().length() == 0) { return FormValidation.ok(); } return FilePath.validateFileMask(project.getSomeWorkspace(), value); } + @POST public FormValidation doCheckRemote(@QueryParameter String value) throws IOException { if (value == null || value.trim().length() == 0) { return FormValidation.error("Remote directory is mandatory."); diff --git a/src/main/java/org/jenkinsci/plugins/artifactdeployer/DeployedArtifactsResult.java b/src/main/java/org/jenkinsci/plugins/artifactdeployer/DeployedArtifactsResult.java index 6208acb..f85e366 100644 --- a/src/main/java/org/jenkinsci/plugins/artifactdeployer/DeployedArtifactsResult.java +++ b/src/main/java/org/jenkinsci/plugins/artifactdeployer/DeployedArtifactsResult.java @@ -23,6 +23,7 @@ package org.jenkinsci.plugins.artifactdeployer; import hudson.model.AbstractBuild; +import hudson.model.Item; import org.kohsuke.stapler.StaplerRequest2; import org.kohsuke.stapler.StaplerResponse2; @@ -77,6 +78,7 @@ public int compare(ArtifactDeployerVO artifactDeployer1, ArtifactDeployerVO arti @SuppressWarnings("unused") public void doDownload(final StaplerRequest2 request, final StaplerResponse2 response) throws IOException, ServletException { + getOwner().checkPermission(Item.READ); String restOfPath = request.getRestOfPath(); if (restOfPath == null) { diff --git a/src/main/java/org/jenkinsci/plugins/artifactdeployer/service/ArtifactDeployerCopy.java b/src/main/java/org/jenkinsci/plugins/artifactdeployer/service/ArtifactDeployerCopy.java index 2f90da0..d451b22 100644 --- a/src/main/java/org/jenkinsci/plugins/artifactdeployer/service/ArtifactDeployerCopy.java +++ b/src/main/java/org/jenkinsci/plugins/artifactdeployer/service/ArtifactDeployerCopy.java @@ -26,6 +26,7 @@ import hudson.Util; import hudson.model.BuildListener; import hudson.remoting.VirtualChannel; +import jenkins.security.Roles; import org.apache.tools.ant.types.FileSet; import org.jenkinsci.plugins.artifactdeployer.ArtifactDeployerVO; import org.jenkinsci.remoting.RoleChecker; @@ -88,7 +89,7 @@ public List invoke(File localBasedir, VirtualChannel channel } @Override public void checkRoles(RoleChecker roleChecker) throws SecurityException { - //We don't require any roles to be checked? + roleChecker.check(this, Roles.SLAVE); } } diff --git a/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerBuildActionTest.java b/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerBuildActionTest.java new file mode 100644 index 0000000..ddaad2f --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerBuildActionTest.java @@ -0,0 +1,49 @@ +package org.jenkinsci.plugins.artifactdeployer; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +class ArtifactDeployerBuildActionTest { + + @Test + void metadataMethods_shouldReturnExpectedConstants() { + ArtifactDeployerBuildAction action = new ArtifactDeployerBuildAction(); + + assertEquals("package.gif", action.getIconFileName()); + assertEquals("Deployed Build Artifacts", action.getDisplayName()); + assertEquals("deployedArtifacts", action.getUrlName()); + } + + @Test + void setArtifactsInfoAndCount_shouldExposeStoredData() { + ArtifactDeployerBuildAction action = new ArtifactDeployerBuildAction(); + + Map> data = new HashMap>(); + data.put(1, Arrays.asList(new ArtifactDeployerVO(), new ArtifactDeployerVO())); + data.put(2, Collections.singletonList(new ArtifactDeployerVO())); + + action.setArtifactsInfo(null, data); + + assertSame(data.get(1), action.getDeployedArtifactsInfo().get(1)); + assertSame(data.get(2), action.getDeployedArtifactsInfo().get(2)); + assertEquals(3, action.getDeployedArtifactsCount()); + } + + @Test + void getTarget_shouldCreateDeployedArtifactsResult() { + ArtifactDeployerBuildAction action = new ArtifactDeployerBuildAction(); + Object target = action.getTarget(); + + assertNotNull(target); + assertEquals(DeployedArtifactsResult.class, target.getClass()); + } +} diff --git a/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerEntryTest.java b/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerEntryTest.java new file mode 100644 index 0000000..4b04719 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerEntryTest.java @@ -0,0 +1,102 @@ +package org.jenkinsci.plugins.artifactdeployer; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ArtifactDeployerEntryTest { + + @Test + void constructorAndGetters_shouldReturnConfiguredValues() { + ArtifactDeployerEntry entry = new ArtifactDeployerEntry( + "**/*.jar", + "target", + "**/*.tmp", + "/remote", + true, + true, + true, + true + ); + + assertEquals("**/*.jar", entry.getIncludes()); + assertEquals("target", entry.getBasedir()); + assertEquals("**/*.tmp", entry.getExcludes()); + assertEquals("/remote", entry.getRemote()); + assertTrue(entry.isFlatten()); + assertTrue(entry.isDeleteRemote()); + assertTrue(entry.isDeleteRemoteArtifacts()); + assertTrue(entry.isFailNoFilesDeploy()); + } + + @Test + void setters_shouldUpdateAllFields() { + ArtifactDeployerEntry entry = new ArtifactDeployerEntry(); + + entry.setIncludes("a"); + entry.setBasedir("b"); + entry.setExcludes("c"); + entry.setRemote("d"); + entry.setFlatten(true); + entry.setDeleteRemote(true); + entry.setDeleteRemoteArtifacts(true); + entry.setDeleteRemoteArtifactsByScript(true); + entry.setGroovyExpression("println 'x'"); + entry.setFailNoFilesDeploy(true); + + assertEquals("a", entry.getIncludes()); + assertEquals("b", entry.getBasedir()); + assertEquals("c", entry.getExcludes()); + assertEquals("d", entry.getRemote()); + assertTrue(entry.isFlatten()); + assertTrue(entry.isDeleteRemote()); + assertTrue(entry.isDeleteRemoteArtifacts()); + assertTrue(entry.isFailNoFilesDeploy()); + } + + @Test + void getUniqueId_shouldChangeWhenRelevantFieldChanges() { + ArtifactDeployerEntry entry1 = new ArtifactDeployerEntry("i", "b", "e", "r", true, false, false, false); + ArtifactDeployerEntry entry2 = new ArtifactDeployerEntry("i", "b", "e", "r", true, false, false, false); + + assertEquals(entry1.getUniqueId(), entry2.getUniqueId()); + + entry2.setFailNoFilesDeploy(true); + assertNotEquals(entry1.getUniqueId(), entry2.getUniqueId()); + } + + @Test + void readObject_shouldMapLegacyDeletingRemoteFieldToDeleteRemote() throws Exception { + ArtifactDeployerEntry entry = new ArtifactDeployerEntry(); + entry.setDeleteRemote(false); + + java.lang.reflect.Field deletingRemote = ArtifactDeployerEntry.class.getDeclaredField("deletingRemote"); + deletingRemote.setAccessible(true); + deletingRemote.setBoolean(entry, true); + + Object returned = entry.readObject(); + + assertSame(entry, returned); + assertTrue(entry.isDeleteRemote()); + } + + @Test + void readObject_shouldKeepDeleteRemoteWhenLegacyFieldFalse() throws Exception { + ArtifactDeployerEntry entry = new ArtifactDeployerEntry(); + entry.setDeleteRemote(true); + + java.lang.reflect.Field deletingRemote = ArtifactDeployerEntry.class.getDeclaredField("deletingRemote"); + deletingRemote.setAccessible(true); + deletingRemote.setBoolean(entry, false); + + entry.readObject(); + + assertTrue(entry.isDeleteRemote()); + entry.setDeleteRemote(false); + assertFalse(entry.isDeleteRemote()); + } +} diff --git a/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerExceptionTest.java b/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerExceptionTest.java new file mode 100644 index 0000000..c6cd05b --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerExceptionTest.java @@ -0,0 +1,42 @@ +package org.jenkinsci.plugins.artifactdeployer; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +class ArtifactDeployerExceptionTest { + + @Test + void defaultConstructor_shouldCreateExceptionWithoutMessageOrCause() { + ArtifactDeployerException ex = new ArtifactDeployerException(); + assertNull(ex.getMessage()); + assertNull(ex.getCause()); + } + + @Test + void messageConstructor_shouldStoreMessage() { + ArtifactDeployerException ex = new ArtifactDeployerException("boom"); + assertEquals("boom", ex.getMessage()); + assertNull(ex.getCause()); + } + + @Test + void messageAndCauseConstructor_shouldStoreBoth() { + RuntimeException cause = new RuntimeException("cause"); + ArtifactDeployerException ex = new ArtifactDeployerException("boom", cause); + + assertEquals("boom", ex.getMessage()); + assertSame(cause, ex.getCause()); + } + + @Test + void causeConstructor_shouldStoreCause() { + RuntimeException cause = new RuntimeException("cause"); + ArtifactDeployerException ex = new ArtifactDeployerException(cause); + + assertSame(cause, ex.getCause()); + assertEquals(cause.toString(), ex.getMessage()); + } +} diff --git a/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerProjectActionTest.java b/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerProjectActionTest.java new file mode 100644 index 0000000..036f82c --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerProjectActionTest.java @@ -0,0 +1,17 @@ +package org.jenkinsci.plugins.artifactdeployer; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ArtifactDeployerProjectActionTest { + + @Test + void metadataMethods_shouldReturnExpectedValues() { + ArtifactDeployerProjectAction action = new ArtifactDeployerProjectAction(null); + + assertEquals("package.gif", action.getIconFileName()); + assertEquals("Last Successful Deployed Artifacts", action.getDisplayName()); + assertEquals("lastSuccessfulBuild//deployedArtifacts", action.getUrlName()); + } +} diff --git a/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerVOTest.java b/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerVOTest.java new file mode 100644 index 0000000..92b8065 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/artifactdeployer/ArtifactDeployerVOTest.java @@ -0,0 +1,24 @@ +package org.jenkinsci.plugins.artifactdeployer; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ArtifactDeployerVOTest { + + @Test + void settersAndGetters_shouldRoundTripValues() { + ArtifactDeployerVO vo = new ArtifactDeployerVO(); + + vo.setId(42); + vo.setFileName("archive.zip"); + vo.setDeployed(true); + vo.setRemotePath("/tmp/archive.zip"); + + assertEquals(42, vo.getId()); + assertEquals("archive.zip", vo.getFileName()); + assertTrue(vo.isDeployed()); + assertEquals("/tmp/archive.zip", vo.getRemotePath()); + } +} diff --git a/src/test/java/org/jenkinsci/plugins/artifactdeployer/DeployedArtifactsTest.java b/src/test/java/org/jenkinsci/plugins/artifactdeployer/DeployedArtifactsTest.java new file mode 100644 index 0000000..d444355 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/artifactdeployer/DeployedArtifactsTest.java @@ -0,0 +1,53 @@ +package org.jenkinsci.plugins.artifactdeployer; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class DeployedArtifactsTest { + + @Test + void actionMetadata_shouldBeNullAsDeprecatedAction() { + DeployedArtifacts deployed = new DeployedArtifacts(); + + assertNull(deployed.getIconFileName()); + assertNull(deployed.getDisplayName()); + assertNull(deployed.getUrlName()); + } + + @Test + void deployedArtifactsInfo_shouldBeMutableMap() { + DeployedArtifacts deployed = new DeployedArtifacts(); + Map> info = deployed.getDeployedArtifactsInfo(); + + assertNotNull(info); + info.put(10, Collections.singletonList(new ArtifactDeployerVO())); + + assertTrue(deployed.getDeployedArtifactsInfo().containsKey(10)); + assertEquals(1, deployed.getDeployedArtifactsInfo().get(10).size()); + } + + @Test + void readResolve_shouldReturnArtifactDeployerBuildActionWithCopiedData() throws Exception { + DeployedArtifacts deployed = new DeployedArtifacts(); + ArtifactDeployerVO vo = new ArtifactDeployerVO(); + deployed.getDeployedArtifactsInfo().put(99, Collections.singletonList(vo)); + + Method readResolve = DeployedArtifacts.class.getDeclaredMethod("readResolve"); + readResolve.setAccessible(true); + Object resolved = readResolve.invoke(deployed); + + assertEquals(ArtifactDeployerBuildAction.class, resolved.getClass()); + ArtifactDeployerBuildAction action = (ArtifactDeployerBuildAction) resolved; + assertTrue(action.getDeployedArtifactsInfo().containsKey(99)); + assertEquals(1, action.getDeployedArtifactsInfo().get(99).size()); + } +} diff --git a/src/test/java/org/jenkinsci/plugins/artifactdeployer/service/ArtifactDeployerManagerTest.java b/src/test/java/org/jenkinsci/plugins/artifactdeployer/service/ArtifactDeployerManagerTest.java new file mode 100644 index 0000000..b426fcd --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/artifactdeployer/service/ArtifactDeployerManagerTest.java @@ -0,0 +1,53 @@ +package org.jenkinsci.plugins.artifactdeployer.service; + +import hudson.FilePath; +import org.jenkinsci.plugins.artifactdeployer.ArtifactDeployerException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ArtifactDeployerManagerTest { + + @TempDir + Path tempDir; + + @Test + void getBasedirFilePath_whenBasedirNull_shouldReturnWorkspace() { + ArtifactDeployerManager manager = new ArtifactDeployerManager(); + FilePath workspace = new FilePath(tempDir.toFile()); + + FilePath result = manager.getBasedirFilePath(workspace, null); + + assertSame(workspace, result); + } + + @Test + void getBasedirFilePath_whenChildExists_shouldReturnChild() throws Exception { + ArtifactDeployerManager manager = new ArtifactDeployerManager(); + FilePath workspace = new FilePath(tempDir.toFile()); + Files.createDirectories(tempDir.resolve("dist")); + + FilePath result = manager.getBasedirFilePath(workspace, "dist"); + + assertEquals(workspace.child("dist").getRemote(), result.getRemote()); + } + + @Test + void getBasedirFilePath_whenChildMissing_shouldThrowHelpfulException() { + ArtifactDeployerManager manager = new ArtifactDeployerManager(); + FilePath workspace = new FilePath(tempDir.toFile()); + + ArtifactDeployerException exception = assertThrows( + ArtifactDeployerException.class, + () -> manager.getBasedirFilePath(workspace, "missing") + ); + + assertEquals("The basedir path 'missing' from the workspace doesn't exist.", exception.getMessage()); + } +} \ No newline at end of file diff --git a/src/test/java/org/jenkinsci/plugins/artifactdeployer/service/LocalCopyTest.java b/src/test/java/org/jenkinsci/plugins/artifactdeployer/service/LocalCopyTest.java new file mode 100644 index 0000000..4565e19 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/artifactdeployer/service/LocalCopyTest.java @@ -0,0 +1,56 @@ +package org.jenkinsci.plugins.artifactdeployer.service; + +import hudson.Util; +import org.apache.tools.ant.types.FileSet; +import org.jenkinsci.plugins.artifactdeployer.ArtifactDeployerException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class LocalCopyTest { + + @TempDir + Path tempDir; + + @Test + void copyAndGetNumbers_shouldCopyMatchingFiles() throws Exception { + Path sourceDir = Files.createDirectories(tempDir.resolve("src")); + Path targetDir = Files.createDirectories(tempDir.resolve("target")); + Files.writeString(sourceDir.resolve("a.txt"), "alpha"); + Files.writeString(sourceDir.resolve("b.log"), "beta"); + + FileSet fileSet = Util.createFileSet(sourceDir.toFile(), "**/*.txt", null); + + LocalCopy localCopy = new LocalCopy(); + List copied = localCopy.copyAndGetNumbers(fileSet, false, targetDir.toFile()); + + assertEquals(1, copied.size()); + assertTrue(Files.exists(targetDir.resolve("a.txt"))); + assertEquals("a.txt", copied.get(0).getName()); + } + + @Test + void copyAndGetNumbers_whenTargetIsFile_shouldWrapBuildException() throws Exception { + Path sourceDir = Files.createDirectories(tempDir.resolve("src2")); + Path targetFile = Files.writeString(tempDir.resolve("target-file.txt"), "not a directory"); + Files.writeString(sourceDir.resolve("a.txt"), "alpha"); + + FileSet fileSet = Util.createFileSet(sourceDir.toFile(), "**/*.txt", null); + LocalCopy localCopy = new LocalCopy(); + + ArtifactDeployerException exception = assertThrows( + ArtifactDeployerException.class, + () -> localCopy.copyAndGetNumbers(fileSet, false, targetFile.toFile()) + ); + + assertEquals("Error on copying file.", exception.getMessage()); + } +} \ No newline at end of file