diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/FunctionizerTestingToolController.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/FunctionizerTestingToolController.java index 24afed03152ad5f4ba06e90b1618fe7e60dfcf67..1c2183c56b288cb134c550cc3fadc3b99f5410b1 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/FunctionizerTestingToolController.java +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/FunctionizerTestingToolController.java @@ -1,7 +1,7 @@ package eu.functionizer.functionizertestingtool; -import eu.functionizer.functionizertestingtool.model.FunctionizerTestResult; +import eu.passage.upperware.commons.model.testing.FunctionizerTestResult; import eu.functionizer.functionizertestingtool.service.TestRunner; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.*; @AllArgsConstructor(onConstructor = @__(@Autowired)) public class FunctionizerTestingToolController { - private TestRunner testRunner; + private final TestRunner testRunner; @RequestMapping("/") @ResponseStatus(HttpStatus.OK) diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/ReportEntryKey.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/ReportEntryKey.java index 71b8d99953fd67899130c12ac356fc4087985b1a..14981724d3ad0e003603729377110fa199f59284 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/ReportEntryKey.java +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/ReportEntryKey.java @@ -2,7 +2,9 @@ package eu.functionizer.functionizertestingtool.model; public class ReportEntryKey { public final static String EVENT = "event"; - public final static String EXPECTED_OUTPUT = "expectedOutput"; + public final static String EXPECTED_VALUE = "expectedValue"; + public final static String CONDITION = "condition"; + public final static String ACTUAL_OUTPUT = "actualOutput"; public final static String IGNORED = "ignored"; public final static String ROOT = "root"; public final static String STAGE = "stage"; diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/TestRunner.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/TestRunner.java index 89a62839cc439f9f9c74e2d84c59754f730871bd..8cc143c73d900de2c3cf2af3f7e05b607d305cd3 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/TestRunner.java +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/TestRunner.java @@ -1,6 +1,6 @@ package eu.functionizer.functionizertestingtool.service; -import eu.functionizer.functionizertestingtool.model.FunctionizerTestResult; +import eu.passage.upperware.commons.model.testing.FunctionizerTestResult; import eu.functionizer.functionizertestingtool.service.test.FunctionizerReportData; import eu.functionizer.functionizertestingtool.service.test.FunctionizerTestListener; import eu.functionizer.functionizertestingtool.service.test.ServerlessFunctionTestFactory; diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AWSLambdaService.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AWSLambdaService.java index 9b2d7aa30b6be5db7586d711397910d543c6c72f..d7280b7a0564e4d8378502b50a8c4d455a899387 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AWSLambdaService.java +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AWSLambdaService.java @@ -9,6 +9,7 @@ import com.amazonaws.services.lambda.model.InvokeRequest; import com.amazonaws.services.lambda.model.InvokeResult; import eu.functionizer.functionizertestingtool.model.ReportEntryKey; import eu.functionizer.functionizertestingtool.service.test.Stage; +import eu.passage.upperware.commons.model.testing.Condition; import eu.passage.upperware.commons.model.testing.FunctionTestConfiguration; import eu.passage.upperware.commons.model.testing.TestCase; import lombok.extern.slf4j.Slf4j; @@ -21,7 +22,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.jupiter.api.DynamicTest.dynamicTest; @@ -87,39 +88,45 @@ public class AWSLambdaService extends TestPreparationService { TestCase testCase ) { log.debug( - "Creating test case: event={}, expected output={}", + "Creating test case: event={}, condition={}, expected value={}", testCase.getEvent(), - testCase.getExpectedOutput() + testCase.getCondition().name(), + testCase.getExpectedValue() ); String displayName = createTestCaseDisplayName( functionName, testCase.getEvent(), - testCase.getExpectedOutput() + testCase.getCondition().name(), + testCase.getExpectedValue() ); - Map reportEntry = createReportEntry(displayName); - reportEntry.put(ReportEntryKey.EVENT, testCase.getEvent()); - reportEntry.put(ReportEntryKey.EXPECTED_OUTPUT, testCase.getExpectedOutput()); - testReporter.publishEntry(reportEntry); - return dynamicTest( displayName, () -> executeAWSLambdaTestCase( + displayName, awsLambdaClient, awsLambdaFunctionName, testCase.getEvent(), - testCase.getExpectedOutput() + testCase.getCondition(), + testCase.getExpectedValue() ) ); } public static void executeAWSLambdaTestCase( + String displayName, AWSLambda awsLambda, String functionName, String event, - String expectedOutput + Condition condition, + String expectedValue ) { + Map reportEntry = createReportEntry(displayName); + reportEntry.put(ReportEntryKey.EVENT, event); + reportEntry.put(ReportEntryKey.CONDITION, condition.name()); + reportEntry.put(ReportEntryKey.EXPECTED_VALUE, expectedValue); + InvokeRequest invokeRequest = new InvokeRequest() .withFunctionName(functionName) .withPayload(event); @@ -128,8 +135,11 @@ public class AWSLambdaService extends TestPreparationService { InvokeResult result = awsLambda.invoke(invokeRequest); String resultString = new String(result.getPayload().array(), StandardCharsets.UTF_8); if (Arrays.asList(200, 202, 204).contains(result.getStatusCode())) { - assertEquals(expectedOutput, resultString); + reportEntry.put(ReportEntryKey.ACTUAL_OUTPUT, resultString); + testReporter.publishEntry(reportEntry); + assertTrue(condition.getMethod().apply(resultString, expectedValue)); } else { + testReporter.publishEntry(reportEntry); fail(String.format( "Received status code %s. Reason: %s", result.getStatusCode(), @@ -137,6 +147,7 @@ public class AWSLambdaService extends TestPreparationService { )); } } catch (Exception e) { + testReporter.publishEntry(reportEntry); fail(String.format( "An exception while fetching response from AWS Lambda Client occurred. Cause: %s", e.getMessage() diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AzureFunctionsService.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AzureFunctionsService.java index 34453b19876af9223098d2f30aea4483ec4377fb..932805f72f93baa5379086d7187c17c339e9fed6 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AzureFunctionsService.java +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/AzureFunctionsService.java @@ -5,15 +5,13 @@ import com.microsoft.azure.credentials.ApplicationTokenCredentials; import com.microsoft.azure.management.Azure; import com.microsoft.azure.management.appservice.FunctionApp; import eu.functionizer.functionizertestingtool.model.ReportEntryKey; -import eu.functionizer.functionizertestingtool.service.test.ServerlessFunctionTestFactory; import eu.functionizer.functionizertestingtool.service.test.Stage; +import eu.passage.upperware.commons.model.testing.Condition; import eu.passage.upperware.commons.model.testing.FunctionTestConfiguration; import eu.passage.upperware.commons.model.testing.TestCase; -import io.github.cloudiator.rest.model.Function; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.DynamicNode; import org.junit.jupiter.api.DynamicTest; -import org.junit.jupiter.api.TestReporter; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -27,7 +25,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.jupiter.api.DynamicTest.dynamicTest; @@ -134,8 +132,7 @@ public class AzureFunctionsService extends TestPreparationService { configuration.getFunctionName(), azureFunctionEndpoint, functionKey, - testCase, - testReporter + testCase )) .collect(Collectors.toList()); @@ -146,51 +143,57 @@ public class AzureFunctionsService extends TestPreparationService { String functionName, String azureFunctionEndpoint, String functionKey, - TestCase testCase, - TestReporter testReporter + TestCase testCase ) { log.debug( - "Creating test case: event={}, expected output={}", + "Creating test case: event={}, condition={} expected value={}", testCase.getEvent(), - testCase.getExpectedOutput() + testCase.getCondition().name(), + testCase.getExpectedValue() ); String displayName = createTestCaseDisplayName( functionName, testCase.getEvent(), - testCase.getExpectedOutput() + testCase.getCondition().name(), + testCase.getExpectedValue() ); - Map reportEntry = createReportEntry(displayName); - reportEntry.put(ReportEntryKey.EVENT, testCase.getEvent()); - reportEntry.put(ReportEntryKey.EXPECTED_OUTPUT, testCase.getExpectedOutput()); - testReporter.publishEntry(reportEntry); - return dynamicTest( displayName, () -> executeAzureTestCase( + displayName, azureFunctionEndpoint, functionKey, testCase.getEvent(), - testCase.getExpectedOutput() + testCase.getCondition(), + testCase.getExpectedValue() ) ); } private static void executeAzureTestCase( + String displayName, String endpoint, String functionKey, String event, - String expectedOutput + Condition condition, + String expectedValue ) { - Stage stage = Stage.TEST_EXECUTION; + Map reportEntry = createReportEntry(displayName); + reportEntry.put(ReportEntryKey.EVENT, event); + reportEntry.put(ReportEntryKey.CONDITION, condition.name()); + reportEntry.put(ReportEntryKey.EXPECTED_VALUE, expectedValue); try { String response = AzureFunctionsService.invokeFunction( endpoint, functionKey, event ); - assertEquals(expectedOutput, response); + reportEntry.put(ReportEntryKey.ACTUAL_OUTPUT, response); + testReporter.publishEntry(reportEntry); + assertTrue(condition.getMethod().apply(response, expectedValue)); } catch (Exception e) { + testReporter.publishEntry(reportEntry); fail(String.format( "An exception while fetching response from AWS Lambda Client occurred. Cause: %s", e.getMessage() diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/TestPreparationService.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/TestPreparationService.java index 6e7e26967acb159882bf66015e7dad6f3bc36a56..0effc0c8c11ff6daafa647828cec788d552308ee 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/TestPreparationService.java +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/provider/TestPreparationService.java @@ -22,13 +22,15 @@ public class TestPreparationService { static String createTestCaseDisplayName( String functionName, String input, - String expectedOutput + String condition, + String expected ) { return String.format( - "Function %s invoked with %s should return %s", + "Output of %s invoked with %s %s %s", functionName, input, - expectedOutput + condition, + expected ); } @@ -56,11 +58,13 @@ public class TestPreparationService { String testCaseDisplayName = createTestCaseDisplayName( configuration.getFunctionName(), testCase.getEvent(), - testCase.getExpectedOutput() + testCase.getCondition().name(), + testCase.getExpectedValue() ); Map reportEntry = createReportEntry(testCaseDisplayName); reportEntry.put(ReportEntryKey.EVENT, testCase.getEvent()); - reportEntry.put(ReportEntryKey.EXPECTED_OUTPUT, testCase.getExpectedOutput()); + reportEntry.put(ReportEntryKey.CONDITION, testCase.getCondition().name()); + reportEntry.put(ReportEntryKey.EXPECTED_VALUE, testCase.getExpectedValue()); reportEntry.put(ReportEntryKey.IGNORED, "true"); testReporter.publishEntry(reportEntry); functionNode.add(dynamicTest(testCaseDisplayName, () -> fail("Test case ignored"))); diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/FunctionizerReportData.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/FunctionizerReportData.java index 471105000b852e6db4de7b9778a74258971ba265..66b8b5e29944748134d2da7fe617a0f21a04c12e 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/FunctionizerReportData.java +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/FunctionizerReportData.java @@ -1,7 +1,10 @@ package eu.functionizer.functionizertestingtool.service.test; import eu.functionizer.functionizertestingtool.model.*; -import org.junit.ComparisonFailure; +import eu.passage.upperware.commons.model.testing.FunctionTestResult; +import eu.passage.upperware.commons.model.testing.FunctionizerTestResult; +import eu.passage.upperware.commons.model.testing.TestCaseResult; +import eu.passage.upperware.commons.model.testing.TestResultEnum; import org.junit.platform.commons.util.ExceptionUtils; import org.junit.platform.engine.TestExecutionResult; import org.junit.platform.engine.reporting.ReportEntry; @@ -194,18 +197,12 @@ public class FunctionizerReportData { testCaseResult.setResult(TestResultEnum.IGNORED); } testCaseResult.setEvent(reportEntry.get(ReportEntryKey.EVENT)); - testCaseResult.setExpectedOutput(reportEntry.get(ReportEntryKey.EXPECTED_OUTPUT)); - testCaseResult.setActualOutput(reportEntry.get(ReportEntryKey.EXPECTED_OUTPUT)); + testCaseResult.setExpectedValue(reportEntry.get(ReportEntryKey.EXPECTED_VALUE)); + testCaseResult.setCondition(reportEntry.get(ReportEntryKey.CONDITION)); + testCaseResult.setActualOutput(reportEntry.get(ReportEntryKey.ACTUAL_OUTPUT)); result.getThrowable().ifPresent(error -> { testCaseResult.setMessage(error.getMessage()); - if (error instanceof ComparisonFailure) { - ComparisonFailure comparisonFailure = (ComparisonFailure) error; - testCaseResult.setExpectedOutput(comparisonFailure.getExpected()); - testCaseResult.setActualOutput(comparisonFailure.getActual()); - } else { - testCaseResult.setActualOutput(null); - } }); functionTestResult.addTestCaseResult(testCaseResult); } diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/Stage.java b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/Stage.java index 25a12ea988e9e327816f956ba1387fbb667bc46a..0140236c2fea499575487bdab40afdc316aabd1b 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/Stage.java +++ b/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/service/test/Stage.java @@ -9,8 +9,6 @@ public enum Stage { BUILD_AZURE_CLIENT("BUILD AZURE CLIENT"), BUILD_AWS_LAMBDA_CLIENT("BUILD AWS LAMBDA CLIENT"), GET_AZURE_FUNCTION_KEY("GET AZURE FUNCTION KEY"), - TEST_EXECUTION("TEST CASE EXECUTION"), - AWS_INVOKE("INVOKE AWS LAMBDA FUNCTION"), END("COLLECTING TESTS FINISH"); private final String name; diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/ApplicationContext.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/ApplicationContext.java index 7b77406c2f579b4028d3610bca0d80e4154e177c..2b1a3da8f1aa35480d9f5442bd38532703243c85 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/ApplicationContext.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/ApplicationContext.java @@ -76,6 +76,14 @@ public class ApplicationContext { return new MonitoringApi(apiClient); } + @Bean + public QueueInspector queueInspector( + QueueApi queueApi, + CloudiatorProperties cloudiatorProperties + ) { + return new QueueInspector(queueApi, cloudiatorProperties); + } + @Bean public CloudiatorApi cloudiatorApi( CloudApi cloudApi, diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolApi.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolApi.java index d2bd68cff2608db29c10751a628fd91ece03ebb2..ad02a25c12d6752b640bbd4f07a06f814df8db2e 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolApi.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolApi.java @@ -1,5 +1,7 @@ package eu.melodic.upperware.guibackend.communication.testingtool; +import eu.passage.upperware.commons.model.testing.FunctionizerTestResult; + public interface FunctionizerTestingToolApi { - Object runTests(String token); + FunctionizerTestResult runTests(String token); } diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolClientApi.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolClientApi.java index 8d04ce47e74e5abe703028b991e257b48aea769b..3dd916578bf058f827021ed5fa9e93435c63a0e7 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolClientApi.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/communication/testingtool/FunctionizerTestingToolClientApi.java @@ -3,6 +3,7 @@ package eu.melodic.upperware.guibackend.communication.testingtool; import eu.melodic.upperware.guibackend.communication.commons.RestCommunicationService; import eu.melodic.upperware.guibackend.communication.commons.ServiceName; import eu.melodic.upperware.guibackend.properties.GuiBackendProperties; +import eu.passage.upperware.commons.model.testing.FunctionizerTestResult; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; @@ -22,13 +23,13 @@ public class FunctionizerTestingToolClientApi extends RestCommunicationService i } @Override - public Object runTests(String token) { + public FunctionizerTestResult runTests(String token) { String requestUrl = guiBackendProperties.getFunctionizerTestingTool().getUrl() + "/test"; - ParameterizedTypeReference responseType = - new ParameterizedTypeReference() { + ParameterizedTypeReference responseType = + new ParameterizedTypeReference() { }; HttpEntity request = createEmptyHttpEntityWithAuthorizationHeader(token); - ResponseEntity response = getResponse( + ResponseEntity response = getResponse( requestUrl, responseType, request, diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/TestingController.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/TestingController.java index db47da5f2a2c15a80ad29746d7e4224c8acd4fca..e1360c1b735d701b37b81d0caaf9fe2ff086012c 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/TestingController.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/TestingController.java @@ -1,8 +1,9 @@ package eu.melodic.upperware.guibackend.controller.testing; import eu.melodic.upperware.guibackend.communication.testingtool.FunctionizerTestingToolApi; -import eu.melodic.upperware.guibackend.controller.testing.response.UploadTestConfigResponse; +import eu.melodic.upperware.guibackend.controller.testing.response.TestConfigurationResponse; import eu.melodic.upperware.guibackend.service.testing.TestingService; +import eu.passage.upperware.commons.model.testing.FunctionizerTestResult; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; @@ -19,22 +20,35 @@ public class TestingController { TestingService testingService; FunctionizerTestingToolApi functionizerTestApi; - @PostMapping(value = "/upload", consumes = {"multipart/form-data"}) + @PostMapping(value = "/config", consumes = {"multipart/form-data"}) @ResponseStatus(HttpStatus.CREATED) - public UploadTestConfigResponse uploadTestConfigFile(@RequestParam("file") MultipartFile file) { + public TestConfigurationResponse uploadTestConfigFile(@RequestParam("file") MultipartFile file) { String originalName = file.getResource().getFilename(); log.info( "POST request for upload test configuration file with name: {}", originalName ); - String fileName = testingService.uploadTestConfig(file); - log.info("File {} successfully uploaded.", originalName); - return testingService.createUploadTestConfigResponse(fileName); + return testingService.uploadTestConfig(file); + } + + @GetMapping(value = "/config") + @ResponseStatus(HttpStatus.OK) + public TestConfigurationResponse getTestConfigResponse() { + log.info("GET request for the test configuration"); + return testingService.getTestConfiguration(); + } + + @DeleteMapping(value = "/config") + @ResponseStatus(HttpStatus.OK) + public void deleteTestConfigFile() { + log.info("DELETE request for deleting the test configuration file"); + testingService.removeTestConfigFile(); } @PostMapping(value = "/run") @ResponseStatus(HttpStatus.OK) - public Object runTests(@RequestHeader(value = HttpHeaders.AUTHORIZATION) String token) { + public FunctionizerTestResult runTests(@RequestHeader(value = HttpHeaders.AUTHORIZATION) String token) { + log.info("POST request for running the tests"); return functionizerTestApi.runTests(token); } } diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/response/TestConfigurationResponse.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/response/TestConfigurationResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..651df4997e8ba033abb5529ffde3d7884b90edcc --- /dev/null +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/response/TestConfigurationResponse.java @@ -0,0 +1,12 @@ +package eu.melodic.upperware.guibackend.controller.testing.response; + +import eu.passage.upperware.commons.model.testing.TestConfiguration; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class TestConfigurationResponse { + private String path; + private TestConfiguration configuration; +} diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/response/UploadTestConfigResponse.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/response/UploadTestConfigResponse.java deleted file mode 100644 index d16aa9cf15f01489fcd536156be68a77a2907a7a..0000000000000000000000000000000000000000 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/controller/testing/response/UploadTestConfigResponse.java +++ /dev/null @@ -1,12 +0,0 @@ -package eu.melodic.upperware.guibackend.controller.testing.response; - -import lombok.*; - -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class UploadTestConfigResponse { - private String testConfigFilePath; -} diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/cdo/CdoService.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/cdo/CdoService.java index c12acb8971c3eeb021b041962683ccc9ad0d954f..43858d6b8ce1e589cac9db0b836c7e7296ac09d2 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/cdo/CdoService.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/cdo/CdoService.java @@ -2,6 +2,7 @@ package eu.melodic.upperware.guibackend.service.cdo; import camel.core.CamelModel; import camel.core.NamedElement; +import camel.deployment.impl.DeploymentTypeModelImpl; import camel.requirement.OptimisationRequirement; import camel.requirement.RequirementModel; import camel.requirement.impl.OptimisationRequirementImpl; @@ -32,7 +33,9 @@ import org.springframework.web.server.ResponseStatusException; import java.io.File; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; @Service @@ -43,7 +46,7 @@ public class CdoService { private CpModelMapper cpModelMapper; private GuiBackendProperties guiBackendProperties; - public boolean storeFileInCdo(String cdoName, File file) { + public boolean storeFileInCdo(String cdoName, File file) throws Exception { log.info("Storing Model {} into CDO with validationEnabled = {}", cdoName, guiBackendProperties.getCdoUploader().isValidationEnabled()); EObject model = null; @@ -55,12 +58,47 @@ public class CdoService { return false; } + log.info("Checking model's software components name uniqueness"); + String duplicateName = validateServerlessComponentNameUniqueness(model); + if (duplicateName != null) { + client.closeSession(); + log.error("Software component's name '{}' " + + "is used in more than one component.", + duplicateName + ); + throw new Exception( + String.format( + "Software component's name '%s' is used in more than one component." + + " Modify the file to have unique software component names and try again.", + duplicateName + ) + ); + } + log.info("Validation passed"); + + boolean successfullyStored = client.storeModel(model, cdoName, guiBackendProperties.getCdoUploader().isValidationEnabled()); log.info("Successfully stored of model {} in CDO = {}", cdoName, successfullyStored); client.closeSession(); return successfullyStored; } + private String validateServerlessComponentNameUniqueness(EObject model) { + CamelModel camelModel = (CamelModel) model; + DeploymentTypeModelImpl depModel = (DeploymentTypeModelImpl) camelModel.getDeploymentModels().get(0); + List names = depModel.getSoftwareComponents() + .stream() + .map(NamedElement::getName) + .collect(Collectors.toList()); + Set uniqueNames = new HashSet<>(); + for (String name : names) { + if (!uniqueNames.add(name)) { + return name; + } + } + return null; + } + public boolean deleteXmi(String cdoName) { log.info("Deleting model {} from CDO", cdoName); CDOClient client = getCdoClient(); diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentService.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentService.java index 5e1ccdc969b931472b38dbfbc53aab1d3e8a91f0..8273738637306be43ede71a1e399fcd23d73b49f 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentService.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/deployment/DeploymentService.java @@ -91,6 +91,8 @@ public class DeploymentService { } catch (IllegalStateException e) { log.error("Error by uploading xmi file:", e); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, String.format("Problem by uploading your %s file. CDO repository is in pending state. Please try again.", uploadXmiRequest.getResource().getFilename())); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); } } diff --git a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/testing/TestingService.java b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/testing/TestingService.java index aa3e7a715ed763e2ee2be5b5fd050ada531838df..b759b4dccce35611466487e66ca06a5486de81de 100644 --- a/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/testing/TestingService.java +++ b/gui-backend/src/main/java/eu/melodic/upperware/guibackend/service/testing/TestingService.java @@ -1,13 +1,12 @@ package eu.melodic.upperware.guibackend.service.testing; -import eu.melodic.upperware.guibackend.controller.testing.response.UploadTestConfigResponse; +import eu.melodic.upperware.guibackend.controller.testing.response.TestConfigurationResponse; import eu.passage.upperware.commons.MelodicConstants; import eu.passage.upperware.commons.model.testing.FunctionTestConfiguration; import eu.passage.upperware.commons.model.testing.TestConfiguration; import eu.passage.upperware.commons.service.testing.TestConfigurationValidationService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.eclipse.net4j.connector.ConnectorException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @@ -17,7 +16,12 @@ import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.constructor.ConstructorException; -import java.io.*; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; import static eu.passage.upperware.commons.service.testing.TestConfigurationValidationService.checkFunctionNamesUniqueness; import static eu.passage.upperware.commons.service.testing.TestConfigurationValidationService.checkTestCasesUniqueness; @@ -29,7 +33,7 @@ import static eu.passage.upperware.commons.service.testing.TestConfigurationVali public class TestingService { private final static String CONFIG_FILE_PATH = MelodicConstants.TEST_CONFIG_FILE_DIR + "/tests.yml"; - public String uploadTestConfig(MultipartFile uploadFileRequest) { + public TestConfigurationResponse uploadTestConfig(MultipartFile uploadFileRequest) { try { if (uploadFileRequest.getOriginalFilename() == null) { throw new ResponseStatusException( @@ -42,15 +46,17 @@ public class TestingService { if (!newFile.exists()) { newFile.getParentFile().mkdirs(); } - validate(uploadFileRequest.getInputStream()); + TestConfiguration configuration = loadAndValidateTestConfiguration( + uploadFileRequest.getInputStream() + ); uploadFileRequest.transferTo(newFile); log.info( - "File {} will be stored under path: {}", + "File {} successfully stored under path: {}", uploadFileRequest.getResource().getFilename(), CONFIG_FILE_PATH ); - return CONFIG_FILE_PATH; + return createTestConfigResponse(configuration); } catch (IOException | IllegalStateException e) { log.error("Error by uploading test configuration file:", e); @@ -59,25 +65,62 @@ public class TestingService { String.format("Problem by uploading your %s file. Please try again.", uploadFileRequest.getResource().getFilename() )); - } catch (ConnectorException e) { - log.error("Error by uploading test configuration file:", e); + } + } + + public TestConfigurationResponse getTestConfiguration() { + File configFile = new File(CONFIG_FILE_PATH); + InputStream ymlFileInputStream; + try { + ymlFileInputStream = new FileInputStream(configFile); + } catch (FileNotFoundException e) { + throw new ResponseStatusException( + HttpStatus.NOT_FOUND, + "Test configuration file not found on server." + ); + } + TestConfiguration testConfiguration = loadTestConfiguration(ymlFileInputStream); + return createTestConfigResponse(testConfiguration); + } + + public void removeTestConfigFile() { + File configFile = new File(CONFIG_FILE_PATH); + if (!configFile.exists()) { + log.info("No file to delete. Ending"); + return; + } + if (configFile.delete()) { + log.info("Successfully removed file {}", configFile.getName()); + } else { + log.error("Error while deleting test configuration file."); throw new ResponseStatusException( HttpStatus.BAD_REQUEST, - String.format("Problem by uploading your %s file. CDO repository not working. Please try again.", - uploadFileRequest.getResource().getFilename() - )); + String.format("Could not remove file %s.", configFile.getName()) + ); } } - public UploadTestConfigResponse createUploadTestConfigResponse(String fileName) { - return new UploadTestConfigResponse(fileName); + private TestConfigurationResponse createTestConfigResponse(TestConfiguration configuration) { + return new TestConfigurationResponse(CONFIG_FILE_PATH, configuration); } - private void validate(InputStream ymlFileInputStream) throws ResponseStatusException { + private TestConfiguration loadTestConfiguration(InputStream ymlFileInputStream) { Yaml yaml = new Yaml(new Constructor(TestConfiguration.class)); + try { + return yaml.load(ymlFileInputStream); + } catch (ConstructorException e) { + String errorMessage = "The file has a bad structure and could not be parsed."; + log.error(errorMessage); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, errorMessage); + } + } + + private TestConfiguration loadAndValidateTestConfiguration( + InputStream ymlFileInputStream + ) throws ResponseStatusException { TestConfiguration configuration; try { - configuration = yaml.load(ymlFileInputStream); + configuration = loadTestConfiguration(ymlFileInputStream); log.info("Checking uniqueness of function names"); checkFunctionNamesUniqueness(configuration.getTests()); @@ -89,7 +132,7 @@ public class TestingService { ); } } catch (ConstructorException e) { - String errorMessage = "The file has a bad format and could not be parsed."; + String errorMessage = "The file has a bad structure and could not be parsed."; log.error(errorMessage); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, errorMessage); } catch ( @@ -98,5 +141,6 @@ public class TestingService { ) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); } + return configuration; } } diff --git a/melodic-commons/pom.xml b/melodic-commons/pom.xml index aa9322aae245d82b666d37ca4e2dd107f40438e2..736e3f6243058cecbe29bf248820f3403f0e23c9 100644 --- a/melodic-commons/pom.xml +++ b/melodic-commons/pom.xml @@ -84,6 +84,10 @@ 1.9.13 compile + + org.junit.platform + junit-platform-engine + diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/CloudiatorClientApi.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/CloudiatorClientApi.java index e4fda2b91fb10c03f463eb8cd2cbceae3576fe18..9606a5a13d45949a51aaf427c51980de40c92c3a 100644 --- a/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/CloudiatorClientApi.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/CloudiatorClientApi.java @@ -7,9 +7,7 @@ import io.github.cloudiator.rest.model.*; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.ListUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; import javax.ws.rs.NotFoundException; @@ -17,8 +15,7 @@ import java.util.List; import java.util.stream.Collectors; @Slf4j -@Service -@AllArgsConstructor(onConstructor = @__({@Autowired})) +@AllArgsConstructor public class CloudiatorClientApi implements CloudiatorApi { private CloudApi cloudApi; diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/QueueInspector.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/QueueInspector.java index 2fad9481d4abf65d94899e39ae0039582b64eb5f..6987ad94f29c08da3880c8a273bac8e4f822102c 100644 --- a/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/QueueInspector.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/cloudiator/QueueInspector.java @@ -1,20 +1,16 @@ package eu.passage.upperware.commons.cloudiator; -import eu.passage.upperware.commons.cloudiator.CloudiatorProperties; import io.github.cloudiator.rest.ApiException; import io.github.cloudiator.rest.api.QueueApi; import io.github.cloudiator.rest.model.Queue; import io.github.cloudiator.rest.model.QueueStatus; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; @Slf4j -@Service -@AllArgsConstructor(onConstructor = @__({@Autowired})) +@AllArgsConstructor public class QueueInspector { private QueueApi queueApi; diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/Condition.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/Condition.java new file mode 100644 index 0000000000000000000000000000000000000000..abb2c8b8439c2f39897bb467d7b10695f3f623d2 --- /dev/null +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/Condition.java @@ -0,0 +1,22 @@ +package eu.passage.upperware.commons.model.testing; + +import lombok.Getter; + +import java.util.function.BiFunction; + +@Getter +public enum Condition { + EQUALS(String::equals), + STARTS_WITH(String::startsWith), + ENDS_WITH(String::endsWith), + CONTAINS_SUBSTRING(String::contains), + EQUALS_IGNORE_CASE(String::equalsIgnoreCase), + MATCHES_REGEX(String::matches); + + private final BiFunction method; + + + Condition(BiFunction method) { + this.method = method; + } +} diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionTestResult.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/FunctionTestResult.java similarity index 94% rename from functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionTestResult.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/FunctionTestResult.java index 6e83036b065c2b77336278997e3e58f4d077f809..759b8293ece1f6707834a6a2a0fbf5acbd22aec2 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionTestResult.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/FunctionTestResult.java @@ -1,12 +1,14 @@ -package eu.functionizer.functionizertestingtool.model; +package eu.passage.upperware.commons.model.testing; import lombok.Data; +import lombok.NoArgsConstructor; import java.util.ArrayList; import java.util.List; @Data +@NoArgsConstructor public class FunctionTestResult { private String functionName; private double duration; diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionizerTestResult.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/FunctionizerTestResult.java similarity index 90% rename from functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionizerTestResult.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/FunctionizerTestResult.java index 8dbd9978d44a73af4dbdaac89043aec3d9a621b9..513b978c599ed0622f567acef83df61e3081d40e 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/FunctionizerTestResult.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/FunctionizerTestResult.java @@ -1,4 +1,4 @@ -package eu.functionizer.functionizertestingtool.model; +package eu.passage.upperware.commons.model.testing; import lombok.Data; diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestCase.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestCase.java index 29b15255571692595ea68478a448935ba04afad3..b2e014a368f0f60424e6f749a96a495db95cd1b5 100644 --- a/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestCase.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestCase.java @@ -6,6 +6,7 @@ import lombok.Data; public class TestCase { private String functionName; private String event; - private String expectedOutput; + private Condition condition; + private String expectedValue; private String region; } diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestCaseResult.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestCaseResult.java similarity index 58% rename from functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestCaseResult.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestCaseResult.java index ef3c65b9e4b6c903d3675c74798d3caa215cf0d7..09db7c6adca364a99c6a2d741cabc5a52abaff58 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestCaseResult.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestCaseResult.java @@ -1,13 +1,16 @@ -package eu.functionizer.functionizertestingtool.model; +package eu.passage.upperware.commons.model.testing; import lombok.Data; @Data public class TestCaseResult { private String event; - private String expectedOutput; private String actualOutput; + private String condition; + private String expectedValue; private String message; private TestResultEnum result; private double duration; + + public TestCaseResult() {} } diff --git a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestResultEnum.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestResultEnum.java similarity index 88% rename from functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestResultEnum.java rename to melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestResultEnum.java index 4a2100edb579ce09a6953576cdb6ff9019788939..3be5b6212f803cbbf99acf2c594bf66caf6fbf25 100644 --- a/functionizer-testing-tool/src/main/java/eu/functionizer/functionizertestingtool/model/TestResultEnum.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/model/testing/TestResultEnum.java @@ -1,4 +1,4 @@ -package eu.functionizer.functionizertestingtool.model; +package eu.passage.upperware.commons.model.testing; import org.junit.platform.engine.TestExecutionResult; diff --git a/melodic-commons/src/main/java/eu/passage/upperware/commons/service/testing/TestConfigurationValidationService.java b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/testing/TestConfigurationValidationService.java index e0b8bc0377c524b80ea4f37152fa11abfe46c786..cbe057d4a9ab80c565703f8400c1213c81feac17 100644 --- a/melodic-commons/src/main/java/eu/passage/upperware/commons/service/testing/TestConfigurationValidationService.java +++ b/melodic-commons/src/main/java/eu/passage/upperware/commons/service/testing/TestConfigurationValidationService.java @@ -1,10 +1,11 @@ package eu.passage.upperware.commons.service.testing; +import eu.passage.upperware.commons.model.testing.Condition; import eu.passage.upperware.commons.model.testing.FunctionTestConfiguration; import eu.passage.upperware.commons.model.testing.TestCase; import eu.passage.upperware.commons.model.testing.TestConfiguration; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; import java.util.HashSet; import java.util.List; @@ -35,19 +36,27 @@ public class TestConfigurationValidationService { public static class NotUniqueTestCaseException extends Exception { String functionName; String event; - String expectedOutput; + Condition condition; + String expected; String message; - NotUniqueTestCaseException(String functionName, String event, String expectedOutput) { + NotUniqueTestCaseException( + String functionName, + String event, + Condition condition, + String expected + ) { this.functionName = functionName; this.event = event; - this.expectedOutput = expectedOutput; + this.condition = condition; + this.expected = expected; this.message = String.format( - "The pair of event = '%s' and expected output = '%s'" + + "The triplet of event = '%s', condition = %s, expected value = '%s'" + " appears in more than one test case of function '%s'." + - " Please adjust the test cases to be unique", + " Please adjust the test cases to be unique.", this.event, - this.expectedOutput, + this.condition, + this.expected, this.functionName ); } @@ -89,23 +98,25 @@ public class TestConfigurationValidationService { public static void checkTestCasesUniqueness( List testCases, String functionName ) throws NotUniqueTestCaseException { - List> eventsExpectedOutputs = testCases + List> testCaseTriplets = testCases .stream() - .map(testCase -> Pair.of(testCase.getEvent(), testCase.getExpectedOutput())) + .map(testCase -> Triple.of(testCase.getEvent(), testCase.getCondition(), testCase.getExpectedValue())) .collect(Collectors.toList()); - Set> uniquePairs = new HashSet<>(); - for (Pair eventExpectedOutput : eventsExpectedOutputs) { - if (!uniquePairs.add(eventExpectedOutput)) { + Set> uniqueTriplets = new HashSet<>(); + for (Triple testCaseTriplet : testCaseTriplets) { + if (!uniqueTriplets.add(testCaseTriplet)) { log.error( - "Function '{}' has more than one test case with event '{}' and expected output '{}'", + "Function '{}' has more than one test case with the event-predicate-expected triplet: ('{}', {}, '{}').", functionName, - eventExpectedOutput.getKey(), - eventExpectedOutput.getValue() + testCaseTriplet.getLeft(), + testCaseTriplet.getMiddle(), + testCaseTriplet.getRight() ); throw new NotUniqueTestCaseException( functionName, - eventExpectedOutput.getKey(), - eventExpectedOutput.getValue() + testCaseTriplet.getLeft(), + testCaseTriplet.getMiddle(), + testCaseTriplet.getRight() ); } }