From 0c5a0c70b143733191d3439946f742fbec18d210 Mon Sep 17 00:00:00 2001 From: David Edwards Date: Tue, 7 Apr 2020 09:13:03 +0100 Subject: [PATCH] ATS-702/ATS-675 Add POC for test inheritance (Imagemagick) Currently tests do not pass on windows due to path error reading resources. --- .../alfresco-transform-core-aio-boot/pom.xml | 8 +- .../AllInOneControllerImageMagickTestPOC.java | 101 +++++ .../pom.xml | 12 + .../ImageMagickControllerTestBasePOC.java | 375 ++++++++++++++++++ .../ImageMagickControllerTestPOC.java | 75 ++++ 5 files changed, 568 insertions(+), 3 deletions(-) create mode 100644 alfresco-transform-core-aio/alfresco-transform-core-aio-boot/src/test/java/org/alfresco/transformer/AllInOneControllerImageMagickTestPOC.java create mode 100644 alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/src/test/java/org/alfresco/transformer/ImageMagickControllerTestBasePOC.java create mode 100644 alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/src/test/java/org/alfresco/transformer/ImageMagickControllerTestPOC.java diff --git a/alfresco-transform-core-aio/alfresco-transform-core-aio-boot/pom.xml b/alfresco-transform-core-aio/alfresco-transform-core-aio-boot/pom.xml index a8e2f48a..f479539c 100644 --- a/alfresco-transform-core-aio/alfresco-transform-core-aio-boot/pom.xml +++ b/alfresco-transform-core-aio/alfresco-transform-core-aio-boot/pom.xml @@ -38,12 +38,14 @@ test-jar test - + tests + test-jar + test + org.springframework.boot diff --git a/alfresco-transform-core-aio/alfresco-transform-core-aio-boot/src/test/java/org/alfresco/transformer/AllInOneControllerImageMagickTestPOC.java b/alfresco-transform-core-aio/alfresco-transform-core-aio-boot/src/test/java/org/alfresco/transformer/AllInOneControllerImageMagickTestPOC.java new file mode 100644 index 00000000..bb6afb7e --- /dev/null +++ b/alfresco-transform-core-aio/alfresco-transform-core-aio-boot/src/test/java/org/alfresco/transformer/AllInOneControllerImageMagickTestPOC.java @@ -0,0 +1,101 @@ +/* + * #%L + * Alfresco Transform Core + * %% + * Copyright (C) 2005 - 2020 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import java.io.IOException; +import java.util.Map; + +import org.alfresco.transformer.transformers.AllInOneTransformer; +import org.alfresco.transformer.transformers.ImageMagickAdapter; +import org.alfresco.transformer.transformers.Transformer; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(SpringRunner.class) +@WebMvcTest(AllInOneController.class) +@Import(AllInOneCustomConfig.class) +public class AllInOneControllerImageMagickTestPOC extends ImageMagickControllerTestBasePOC +{ + + static ImageMagickAdapter adapter; + + @Autowired + AllInOneTransformer transformer; + + @SpyBean + AllInOneController controller; + + @BeforeClass + public static void beforeClass() throws Exception + { + adapter = new ImageMagickAdapter(); + } + + @Before @SuppressWarnings("unchecked") + public void before() throws IOException + { + ReflectionTestUtils.setField(commandExecutor, "transformCommand", mockTransformCommand); + ReflectionTestUtils.setField(commandExecutor, "checkCommand", mockCheckCommand); + ReflectionTestUtils.setField(adapter, "commandExecutor", commandExecutor); + //Need to wire in the mocked adpater into the controller... + if (ReflectionTestUtils.getField(transformer,"transformerTransformMapping") instanceof Map) + { + Map transformers = (Map)ReflectionTestUtils.getField(transformer,"transformerTransformMapping"); + transformers.replace("imagemagick", adapter); + ReflectionTestUtils.setField(transformer, "transformerTransformMapping", transformers); + } + + ReflectionTestUtils.setField(controller, "transformer",transformer); + + mockTransformCommand("jpg", "png", "image/jpg", true); + } + + @Override + protected AbstractTransformerController getController() + { + return controller; + } + + //There is currently a bug in the version of the surefire plugin that junit4 uses + //which means that inherited tests are not detected and run if there are no tests + //present in the super class, the below is a workaround. + @Test + public void emptyTest() + { + controller.info(); + } + + +} \ No newline at end of file diff --git a/alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/pom.xml b/alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/pom.xml index f37250d5..6d7c1b47 100644 --- a/alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/pom.xml +++ b/alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/pom.xml @@ -86,6 +86,18 @@ org.apache.maven.plugins maven-failsafe-plugin + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + test-jar + + + + diff --git a/alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/src/test/java/org/alfresco/transformer/ImageMagickControllerTestBasePOC.java b/alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/src/test/java/org/alfresco/transformer/ImageMagickControllerTestBasePOC.java new file mode 100644 index 00000000..46ba7408 --- /dev/null +++ b/alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/src/test/java/org/alfresco/transformer/ImageMagickControllerTestBasePOC.java @@ -0,0 +1,375 @@ +/* + * #%L + * Alfresco Transform Core + * %% + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import static org.alfresco.transformer.executors.RuntimeExec.ExecutionResult; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; +import static org.springframework.http.HttpHeaders.ACCEPT; +import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.CREATED; +import static org.springframework.http.HttpStatus.OK; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.http.MediaType.IMAGE_PNG_VALUE; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.util.StringUtils.getFilenameExtension; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.alfresco.transform.client.model.TransformReply; +import org.alfresco.transform.client.model.TransformRequest; +import org.alfresco.transformer.executors.ImageMagickCommandExecutor; +import org.alfresco.transformer.executors.RuntimeExec; +import org.alfresco.transformer.model.FileRefEntity; +import org.alfresco.transformer.model.FileRefResponse; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.stubbing.Answer; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +/** + * Test the ImageMagickController without a server. + * Super class includes tests for the AbstractTransformerController. + */ +@RunWith(SpringRunner.class) +@WebMvcTest(ImageMagickController.class) +public abstract class ImageMagickControllerTestBasePOC extends AbstractTransformerControllerTest +{ + private static final String ENGINE_CONFIG_NAME = "imagemagick_engine_config.json"; + + @Mock + protected ExecutionResult mockExecutionResult; + + @Mock + protected RuntimeExec mockTransformCommand; + + @Mock + protected RuntimeExec mockCheckCommand; + + protected ImageMagickCommandExecutor commandExecutor = new ImageMagickCommandExecutor(); + + + @Override + public String getEngineConfigName() + { + return ENGINE_CONFIG_NAME; + } + + @Override + protected void mockTransformCommand(String sourceExtension, + String targetExtension, String sourceMimetype, + boolean readTargetFileBytes) throws IOException + { + this.sourceExtension = sourceExtension; + this.targetExtension = targetExtension; + this.sourceMimetype = sourceMimetype; + + expectedOptions = null; + expectedSourceSuffix = null; + expectedSourceFileBytes = readTestFile(sourceExtension); + expectedTargetFileBytes = readTargetFileBytes ? readTestFile(targetExtension) : null; + sourceFile = new MockMultipartFile("file", "quick." + sourceExtension, sourceMimetype, + expectedSourceFileBytes); + + when(mockTransformCommand.execute(any(), anyLong())).thenAnswer( + (Answer) invocation -> { + Map actualProperties = invocation.getArgument(0); + assertEquals("There should be 3 properties", 3, actualProperties.size()); + + String actualOptions = actualProperties.get("options"); + String actualSource = actualProperties.get("source"); + String actualTarget = actualProperties.get("target"); + String actualTargetExtension = getFilenameExtension(actualTarget); + + assertNotNull(actualSource); + assertNotNull(actualTarget); + if (expectedSourceSuffix != null) + { + assertTrue( + "The source file \"" + actualSource + "\" should have ended in \"" + expectedSourceSuffix + "\"", + actualSource.endsWith(expectedSourceSuffix)); + actualSource = actualSource.substring(0, + actualSource.length() - expectedSourceSuffix.length()); + } + + assertNotNull(actualOptions); + if (expectedOptions != null) + { + assertEquals("expectedOptions", expectedOptions, actualOptions); + } + + Long actualTimeout = invocation.getArgument(1); + assertNotNull(actualTimeout); + if (expectedTimeout != null) + { + assertEquals("expectedTimeout", expectedTimeout, actualTimeout); + } + + // Copy a test file into the target file location if it exists + int i = actualTarget.lastIndexOf('_'); + if (i >= 0) + { + String testFilename = actualTarget.substring(i + 1); + File testFile = getTestFile(testFilename, false); + File targetFile = new File(actualTarget); + generateTargetFileFromResourceFile(actualTargetExtension, testFile, + targetFile); + } + + // Check the supplied source file has not been changed. + byte[] actualSourceFileBytes = Files.readAllBytes(new File(actualSource).toPath()); + assertTrue("Source file is not the same", + Arrays.equals(expectedSourceFileBytes, actualSourceFileBytes)); + + return mockExecutionResult; + }); + + when(mockExecutionResult.getExitValue()).thenReturn(0); + when(mockExecutionResult.getStdErr()).thenReturn("STDERROR"); + when(mockExecutionResult.getStdOut()).thenReturn("STDOUT"); + } + + + @Test + public void cropGravityGoodTest() throws Exception + { + for (String value : new String[]{"North", "NorthEast", "East", "SouthEast", "South", "SouthWest", "West", "NorthWest", "Center"}) + { + expectedOptions = "-gravity " + value + " +repage"; + mockMvc + .perform(MockMvcRequestBuilders + .multipart("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension) + .param("cropGravity", value)) + .andExpect(status().is(OK.value())) + .andExpect(content().bytes(expectedTargetFileBytes)) + .andExpect(header().string("Content-Disposition", + "attachment; filename*= UTF-8''quick." + targetExtension)); + } + } + + @Test + public void cropGravityBadTest() throws Exception + { + mockMvc + .perform(MockMvcRequestBuilders + .multipart("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension) + .param("cropGravity", "badValue")) + .andExpect(status().is(BAD_REQUEST.value())); + } + + @Test + public void optionsTest() throws Exception + { + expectedOptions = "-alpha remove -gravity SouthEast -crop 123x456%+90+12 +repage -thumbnail 321x654%!"; + expectedSourceSuffix = "[2-3]"; + mockMvc + .perform(MockMvcRequestBuilders + .multipart("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension) + + .param("startPage", "2") + .param("endPage", "3") + + .param("alphaRemove", "true") + .param("autoOrient", "false") + + .param("cropGravity", "SouthEast") + .param("cropWidth", "123") + .param("cropHeight", "456") + .param("cropPercentage", "true") + .param("cropXOffset", "90") + .param("cropYOffset", "12") + + .param("thumbnail", "true") + .param("resizeWidth", "321") + .param("resizeHeight", "654") + .param("resizePercentage", "true") + .param("allowEnlargement", "true") + .param("maintainAspectRatio", "false")) + .andExpect(status().is(OK.value())) + .andExpect(content().bytes(expectedTargetFileBytes)) + .andExpect(header().string("Content-Disposition", + "attachment; filename*= UTF-8''quick." + targetExtension)); + } + + @Test + public void optionsNegateBooleansTest() throws Exception + { + expectedOptions = "-auto-orient -gravity SouthEast -crop 123x456+90+12 +repage -resize 321x654>"; + expectedSourceSuffix = "[2-3]"; + mockMvc + .perform(MockMvcRequestBuilders + .multipart("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension) + + .param("startPage", "2") + .param("endPage", "3") + + .param("alphaRemove", "false") + .param("autoOrient", "true") + + .param("cropGravity", "SouthEast") + .param("cropWidth", "123") + .param("cropHeight", "456") + .param("cropPercentage", "false") + .param("cropXOffset", "90") + .param("cropYOffset", "12") + + .param("thumbnail", "false") + .param("resizeWidth", "321") + .param("resizeHeight", "654") + .param("resizePercentage", "false") + .param("allowEnlargement", "false") + .param("maintainAspectRatio", "true")) + .andExpect(status().is(OK.value())) + .andExpect(content().bytes(expectedTargetFileBytes)) + .andExpect(header().string("Content-Disposition", + "attachment; filename*= UTF-8''quick." + targetExtension)); + } + + @Test + public void deprecatedCommandOptionsTest() throws Exception + { + // Example of why the commandOptions parameter is a bad idea. + expectedOptions = "( horrible command / ); -resize 321x654>"; + mockMvc + .perform(MockMvcRequestBuilders + .multipart("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension) + .param("thumbnail", "false") + .param("resizeWidth", "321") + .param("resizeHeight", "654") + .param("commandOptions", "( horrible command / );")) + .andExpect(status().is(OK.value())) + .andExpect(content().bytes(expectedTargetFileBytes)) + .andExpect(header().string("Content-Disposition", + "attachment; filename*= UTF-8''quick." + targetExtension)); + } + + @Override + protected void updateTransformRequestWithSpecificOptions(TransformRequest transformRequest) + { + transformRequest.setSourceExtension("png"); + transformRequest.setTargetExtension("png"); + transformRequest.setSourceMediaType(IMAGE_PNG_VALUE); + transformRequest.setTargetMediaType(IMAGE_PNG_VALUE); + } + + @Test + public void badExitCodeTest() throws Exception + { + when(mockExecutionResult.getExitValue()).thenReturn(1); + + mockMvc.perform(mockMvcRequest("/transform", sourceFile, "targetExtension", "xxx")) + .andExpect(status().is(BAD_REQUEST.value())) + .andExpect( + status().reason(containsString("Transformer exit code was not 0: \nSTDERR"))); + } + + @Test + public void testPojoTransform() throws Exception + { + // Files + String sourceFileRef = UUID.randomUUID().toString(); + File sourceFile = getTestFile("quick." + sourceExtension, true); + String targetFileRef = UUID.randomUUID().toString(); + + // Transformation Request POJO + TransformRequest transformRequest = new TransformRequest(); + transformRequest.setRequestId("1"); + transformRequest.setSchema(1); + transformRequest.setClientData("Alfresco Digital Business Platform"); + transformRequest.setTransformRequestOptions(new HashMap<>()); + transformRequest.setSourceReference(sourceFileRef); + transformRequest.setSourceExtension(sourceExtension); + transformRequest.setSourceSize(sourceFile.length()); + transformRequest.setTargetExtension(targetExtension); + + // HTTP Request + HttpHeaders headers = new HttpHeaders(); + headers.set(CONTENT_DISPOSITION, "attachment; filename=quick." + sourceExtension); + ResponseEntity response = new ResponseEntity<>(new FileSystemResource( + sourceFile), headers, OK); + + when(alfrescoSharedFileStoreClient.retrieveFile(sourceFileRef)).thenReturn(response); + when(alfrescoSharedFileStoreClient.saveFile(any())) + .thenReturn(new FileRefResponse(new FileRefEntity(targetFileRef))); + when(mockExecutionResult.getExitValue()).thenReturn(0); + + // Update the Transformation Request with any specific params before sending it + updateTransformRequestWithSpecificOptions(transformRequest); + + // Serialize and call the transformer + String tr = objectMapper.writeValueAsString(transformRequest); + String transformationReplyAsString = mockMvc + .perform(MockMvcRequestBuilders + .post("/transform") + .header(ACCEPT, APPLICATION_JSON_VALUE) + .header(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .content(tr)) + .andExpect(status().is(CREATED.value())) + .andReturn().getResponse().getContentAsString(); + + TransformReply transformReply = objectMapper.readValue(transformationReplyAsString, + TransformReply.class); + + // Assert the reply + assertEquals(transformRequest.getRequestId(), transformReply.getRequestId()); + assertEquals(transformRequest.getClientData(), transformReply.getClientData()); + assertEquals(transformRequest.getSchema(), transformReply.getSchema()); + } +} diff --git a/alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/src/test/java/org/alfresco/transformer/ImageMagickControllerTestPOC.java b/alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/src/test/java/org/alfresco/transformer/ImageMagickControllerTestPOC.java new file mode 100644 index 00000000..c45195e3 --- /dev/null +++ b/alfresco-transform-imagemagick/alfresco-transform-imagemagick-boot/src/test/java/org/alfresco/transformer/ImageMagickControllerTestPOC.java @@ -0,0 +1,75 @@ +/* + * #%L + * Alfresco Transform Core + * %% + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import java.io.IOException; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.util.ReflectionTestUtils; + +/** + * Test the ImageMagickController without a server. + * Super class includes tests for the AbstractTransformerController. + */ +@RunWith(SpringRunner.class) +@WebMvcTest(ImageMagickController.class) +public class ImageMagickControllerTestPOC extends ImageMagickControllerTestBasePOC +{ + + @SpyBean + private ImageMagickController controller; + + @Before + public void before() throws IOException + { + ReflectionTestUtils.setField(commandExecutor, "transformCommand", mockTransformCommand); + ReflectionTestUtils.setField(commandExecutor, "checkCommand", mockCheckCommand); + ReflectionTestUtils.setField(controller, "commandExecutor", commandExecutor); + + mockTransformCommand("jpg", "png", "image/jpg", true); + } + + @Override + protected AbstractTransformerController getController() + { + return controller; + } + + //There is currently a bug in the version of the surefire plugin that junit4 uses + //which means that inherited tests are not detected and run if there are no tests + //present in the super class, the below is a workaround. + @Test + public void emptyTest() + { + controller.info(); + } +}