/******************************************************************************* * (c) Copyright 2016 Hewlett-Packard Development Company, L.P. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Apache License v2.0 which accompany this distribution. * * The Apache License is available at * http://www.apache.org/licenses/LICENSE-2.0 * *******************************************************************************/ package io.cloudslang.lang.compiler.modeller; import io.cloudslang.lang.entities.SensitivityLevel; import io.cloudslang.lang.compiler.SlangTextualKeys; import io.cloudslang.lang.compiler.modeller.model.Executable; import io.cloudslang.lang.compiler.modeller.model.Flow; import io.cloudslang.lang.compiler.modeller.model.Operation; import io.cloudslang.lang.compiler.modeller.model.Step; import io.cloudslang.lang.compiler.modeller.result.ExecutableModellingResult; import io.cloudslang.lang.compiler.modeller.transformers.PublishTransformer; import io.cloudslang.lang.compiler.modeller.transformers.ResultsTransformer; import io.cloudslang.lang.compiler.modeller.transformers.Transformer; import io.cloudslang.lang.compiler.parser.model.ParsedSlang; import io.cloudslang.lang.compiler.validator.ExecutableValidator; import io.cloudslang.lang.compiler.validator.ExecutableValidatorImpl; import io.cloudslang.lang.compiler.validator.PreCompileValidator; import io.cloudslang.lang.compiler.validator.PreCompileValidatorImpl; import io.cloudslang.lang.compiler.validator.SystemPropertyValidator; import io.cloudslang.lang.compiler.validator.SystemPropertyValidatorImpl; import io.cloudslang.lang.entities.bindings.Result; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyList; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {ExecutableBuilderTest.Config.class}) public class ExecutableBuilderTest { @Rule public ExpectedException exception = ExpectedException.none(); @Autowired private ExecutableBuilder executableBuilder; @Autowired private Transformer transformer; @Autowired private TransformersHandler transformersHandler; @Autowired private PreCompileValidator preCompileValidator; private static final String FILE_NAME = "filename"; private static final String NAMESPACE = "io.cloudslang"; @Before public void resetMocks() { Mockito.reset(transformer); } private ParsedSlang mockFlowSlangFile() { ParsedSlang parsedSlang = mock(ParsedSlang.class); when(parsedSlang.getType()).thenReturn(ParsedSlang.Type.FLOW); when(parsedSlang.getName()).thenReturn(FILE_NAME); Map<String, String> imports = new HashMap<>(); imports.put("ops", "ops"); when(parsedSlang.getImports()).thenReturn(imports); when(parsedSlang.getNamespace()).thenReturn(NAMESPACE); List<Result> results = new ArrayList<>(); Map<String, Serializable> postExecutableActionData = new HashMap<>(); postExecutableActionData.put(SlangTextualKeys.RESULTS_KEY, (Serializable) results); when(transformersHandler.runTransformers(anyMap(), anyList(), anyList(), anyString(), any(SensitivityLevel.class))) .thenReturn(postExecutableActionData); return parsedSlang; } private ParsedSlang mockOperationsSlangFile() { ParsedSlang parsedSlang = mock(ParsedSlang.class); when(parsedSlang.getType()).thenReturn(ParsedSlang.Type.OPERATION); when(parsedSlang.getName()).thenReturn(FILE_NAME); return parsedSlang; } @Test public void emptyExecutableDataThrowsException() throws Exception { final ParsedSlang mockParsedSlang = mockFlowSlangFile(); Map<String, Object> executableRawData = new HashMap<>(); String flowName = "flow2"; executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, flowName); exception.expect(RuntimeException.class); exception.expectMessage(flowName); transformToExecutable(mockParsedSlang, executableRawData); } @Test public void emptyWorkFlowThrowsException() throws Exception { final ParsedSlang mockParsedSlang = mockFlowSlangFile(); Map<String, Object> executableRawData = new HashMap<>(); executableRawData.put(SlangTextualKeys.WORKFLOW_KEY, new LinkedHashMap<>()); String flowName = "flow2"; executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, flowName); exception.expect(RuntimeException.class); exception.expectMessage(flowName); transformToExecutable(mockParsedSlang, executableRawData); } @Test public void emptyStepThrowsException() throws Exception { final ParsedSlang mockParsedSlang = mockFlowSlangFile(); Map<String, Object> executableRawData = new HashMap<>(); List<Map<String, Object>> workFlowData = new ArrayList<>(); String stepName = "step1"; Map<String, Object> step = new HashMap<>(); step.put(stepName, new HashMap<>()); workFlowData.add(step); executableRawData.put(SlangTextualKeys.WORKFLOW_KEY, workFlowData); executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, "flow1"); exception.expect(RuntimeException.class); exception.expectMessage(stepName); transformToExecutable(mockParsedSlang, executableRawData); } @Test public void stepKeyThatHasNoTransformerThrowsException() throws Exception { final ParsedSlang mockParsedSlang = mockFlowSlangFile(); Map<String, Object> executableRawData = new HashMap<>(); LinkedHashMap<Object, Object> workFlowData = new LinkedHashMap<>(); String stepName = "step1"; Map<String, Object> stepRawData = new HashMap<>(); String keyword = "a"; stepRawData.put(keyword, 'b'); workFlowData.put(stepName, stepRawData); executableRawData.put(SlangTextualKeys.WORKFLOW_KEY, workFlowData); executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, "flow1"); exception.expect(RuntimeException.class); exception.expectMessage(keyword); transformToExecutable(mockParsedSlang, executableRawData); } @Test public void stepKeyThatHasTransformerNotInScopeThrowsException() throws Exception { String keyword = "a"; when(transformer.keyToTransform()).thenReturn(keyword); when(transformer.getScopes()).thenReturn(Collections.singletonList(Transformer.Scope.ACTION)); final ParsedSlang mockParsedSlang = mockFlowSlangFile(); Map<String, Object> executableRawData = new HashMap<>(); LinkedHashMap<Object, Object> workFlowData = new LinkedHashMap<>(); String stepName = "step1"; Map<String, Object> stepRawData = new HashMap<>(); stepRawData.put(keyword, 'b'); workFlowData.put(stepName, stepRawData); executableRawData.put(SlangTextualKeys.WORKFLOW_KEY, workFlowData); executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, "flow1"); exception.expect(RuntimeException.class); exception.expectMessage(keyword); transformToExecutable(mockParsedSlang, executableRawData); } @Test public void transformerThatCantCastTheDataThrowsException() throws Exception { String keyword = "a"; when(transformer.keyToTransform()).thenReturn(keyword); when(transformer.getScopes()) .thenReturn(Collections.singletonList(Transformer.Scope.BEFORE_EXECUTABLE)); when(transformer.transform(any())).thenThrow(ClassCastException.class); final ParsedSlang mockParsedSlang = mockFlowSlangFile(); Map<String, Object> executableRawData = new HashMap<>(); LinkedHashMap<Object, Object> workFlowData = new LinkedHashMap<>(); String stepName = "step1"; Map<String, Object> stepRawData = new HashMap<>(); stepRawData.put(keyword, 'b'); workFlowData.put(stepName, stepRawData); executableRawData.put(SlangTextualKeys.WORKFLOW_KEY, workFlowData); executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, "flow1"); exception.expect(RuntimeException.class); exception.expectMessage(keyword); transformToExecutable(mockParsedSlang, executableRawData); } @Test public void stepWithNoDoEntranceThrowsException() throws Exception { String keyword = "a"; when(transformer.keyToTransform()).thenReturn(keyword); when(transformer.getScopes()).thenReturn(Collections.singletonList(Transformer.Scope.BEFORE_STEP)); final ParsedSlang mockParsedSlang = mockFlowSlangFile(); final Map<String, Object> executableRawData = new HashMap<>(); List<Map<String, Object>> workFlowData = new ArrayList<>(); String stepName = "step1"; Map<String, Object> stepRawData = new HashMap<>(); stepRawData.put(keyword, 'b'); Map<String, Object> step = new HashMap<>(); step.put(stepName, stepRawData); workFlowData.add(step); executableRawData.put(SlangTextualKeys.WORKFLOW_KEY, workFlowData); executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, "flow1"); exception.expect(RuntimeException.class); exception.expectMessage(stepName); transformToExecutable(mockParsedSlang, executableRawData); } @Test public void stepWithEmptyDoEntranceThrowsException() throws Exception { final ParsedSlang mockParsedSlang = mockFlowSlangFile(); final Map<String, Object> executableRawData = new HashMap<>(); List<Map<String, Object>> workFlowData = new ArrayList<>(); Map<String, Object> stepRawData = new HashMap<>(); stepRawData.put(SlangTextualKeys.DO_KEY, new HashMap<>()); String stepName = "step1"; Map<String, Object> step = new HashMap<>(); step.put(stepName, stepRawData); workFlowData.add(step); executableRawData.put(SlangTextualKeys.WORKFLOW_KEY, workFlowData); executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, "flow1"); exception.expect(RuntimeException.class); exception.expectMessage(stepName); transformToExecutable(mockParsedSlang, executableRawData); } @Test public void simpleFlowDataIsValid() throws Exception { final ParsedSlang mockParsedSlang = mockFlowSlangFile(); final Map<String, Object> executableRawData = new HashMap<>(); final List<Map<String, Object>> workFlowData = new ArrayList<>(); Map<String, Object> stepRawData = new HashMap<>(); Map<String, Object> doRawData = new HashMap<>(); String refId = "ops.print"; doRawData.put(refId, new HashMap<>()); stepRawData.put(SlangTextualKeys.DO_KEY, doRawData); String stepName = "step1"; Map<String, Object> step = new HashMap<>(); step.put(stepName, stepRawData); workFlowData.add(step); executableRawData.put(SlangTextualKeys.WORKFLOW_KEY, workFlowData); String flowName = "flow1"; executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, flowName); Flow flow = (Flow) executableBuilder.transformToExecutable(mockParsedSlang, executableRawData, SensitivityLevel.ENCRYPTED).getExecutable(); Assert.assertEquals(SlangTextualKeys.FLOW_TYPE, flow.getType()); Assert.assertEquals(flowName, flow.getName()); Deque<Step> steps = flow.getWorkflow().getSteps(); Assert.assertEquals(1, steps.size()); Assert.assertEquals(stepName, steps.getFirst().getName()); Assert.assertEquals(refId, steps.getFirst().getRefId()); } @Test public void stepWithImplicitAlias() throws Exception { final ParsedSlang mockParsedSlang = mockFlowSlangFile(); final Map<String, Object> executableRawData = new HashMap<>(); final List<Map<String, Object>> workFlowData = new ArrayList<>(); Map<String, Object> stepRawData = new HashMap<>(); Map<String, Object> doRawData = new HashMap<>(); String refString = "print"; doRawData.put(refString, new HashMap<>()); stepRawData.put(SlangTextualKeys.DO_KEY, doRawData); String stepName = "step1"; Map<String, Object> step = new HashMap<>(); step.put(stepName, stepRawData); workFlowData.add(step); executableRawData.put(SlangTextualKeys.WORKFLOW_KEY, workFlowData); String flowName = "flow1"; executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, flowName); Flow flow = (Flow) executableBuilder.transformToExecutable(mockParsedSlang, executableRawData, SensitivityLevel.ENCRYPTED).getExecutable(); Assert.assertEquals(SlangTextualKeys.FLOW_TYPE, flow.getType()); Assert.assertEquals(flowName, flow.getName()); Deque<Step> steps = flow.getWorkflow().getSteps(); Assert.assertEquals(1, steps.size()); Assert.assertEquals(stepName, steps.getFirst().getName()); Assert.assertEquals(NAMESPACE + "." + refString, steps.getFirst().getRefId()); } @Test public void invalidKeyWordsInOperationThrowsException() throws Exception { final ParsedSlang mockParsedSlang = mockOperationsSlangFile(); Map<String, Object> executableRawData = new HashMap<>(); String key = "a"; executableRawData.put(key, "b"); String operationName = "op1"; executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, operationName); exception.expect(RuntimeException.class); exception.expectMessage(operationName); exception.expectMessage(key); Operation op = (Operation) transformToExecutable(mockParsedSlang, executableRawData); Assert.assertNotNull(op); } @Test public void operationWithEmptyActionDataThrowException() throws Exception { String keyword = "a"; when(transformer.keyToTransform()).thenReturn(keyword); when(transformer.getScopes()) .thenReturn(Collections.singletonList(Transformer.Scope.BEFORE_EXECUTABLE)); final ParsedSlang mockParsedSlang = mockOperationsSlangFile(); Map<String, Object> executableRawData = new HashMap<>(); executableRawData.put(keyword, "b"); String operationName = "op1"; executableRawData.put(SlangTextualKeys.EXECUTABLE_NAME_KEY, operationName); exception.expect(RuntimeException.class); exception.expectMessage(operationName); transformToExecutable(mockParsedSlang, executableRawData); } private Executable transformToExecutable(ParsedSlang mockParsedSlang, Map<String, Object> executableRawData) { ExecutableModellingResult modellingResult = executableBuilder.transformToExecutable(mockParsedSlang, executableRawData, SensitivityLevel.ENCRYPTED); if (modellingResult.getErrors().size() > 0) { throw modellingResult.getErrors().get(0); } return modellingResult.getExecutable(); } static class Config { @Bean public ExecutableBuilder executableBuilder() { ExecutableBuilder executableBuilder = new ExecutableBuilder(); executableBuilder.setTransformersHandler(transformersHandler()); executableBuilder.setDependenciesHelper(dependenciesHelper()); executableBuilder.setPreCompileValidator(preCompileValidator()); executableBuilder.setResultsTransformer(resultsTransformer()); executableBuilder.setExecutableValidator(executableValidator()); executableBuilder.initScopedTransformersAndKeys(); return executableBuilder; } @Bean public Transformer transformer() { return mock(Transformer.class); } @Bean public DependenciesHelper dependenciesHelper() { return mock(DependenciesHelper.class); } @Bean public PublishTransformer publishTransformer() { return mock(PublishTransformer.class); } @Bean public TransformersHandler transformersHandler() { return mock(TransformersHandler.class); } @Bean public PreCompileValidator preCompileValidator() { PreCompileValidatorImpl preCompileValidator = new PreCompileValidatorImpl(); preCompileValidator.setExecutableValidator(executableValidator()); return preCompileValidator; } @Bean public ResultsTransformer resultsTransformer() { return mock(ResultsTransformer.class); } @Bean public ExecutableValidator executableValidator() { return new ExecutableValidatorImpl(); } @Bean public SystemPropertyValidator systemPropertyValidator() { return new SystemPropertyValidatorImpl(); } } }