/*******************************************************************************
* (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.runtime.bindings;
import io.cloudslang.dependency.api.services.DependencyService;
import io.cloudslang.dependency.api.services.MavenConfig;
import io.cloudslang.dependency.impl.services.DependencyServiceImpl;
import io.cloudslang.dependency.impl.services.MavenConfigImpl;
import io.cloudslang.lang.entities.SystemProperty;
import io.cloudslang.lang.entities.bindings.Input;
import io.cloudslang.lang.entities.bindings.values.Value;
import io.cloudslang.lang.entities.bindings.values.ValueFactory;
import io.cloudslang.lang.runtime.bindings.scripts.ScriptEvaluator;
import io.cloudslang.runtime.api.python.PythonRuntimeService;
import io.cloudslang.runtime.impl.python.PythonExecutionCachedEngine;
import io.cloudslang.runtime.impl.python.PythonExecutionEngine;
import io.cloudslang.runtime.impl.python.PythonRuntimeServiceImpl;
import io.cloudslang.score.events.EventBus;
import io.cloudslang.score.events.EventBusImpl;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = InputsBindingTest.Config.class)
public class InputsBindingTest {
@Autowired
private InputsBinding inputsBinding;
@Rule
public ExpectedException exception = ExpectedException.none();
@Test
public void testEmptyBindInputs() throws Exception {
List<Input> inputs = Collections.emptyList();
Map<String, Value> result = bindInputs(inputs);
Assert.assertTrue(result.isEmpty());
}
@Test
public void testDefaultValue() {
List<Input> inputs = Collections.singletonList(new Input.InputBuilder("input1", "value").build());
Map<String, Value> result = bindInputs(inputs);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("value", result.get("input1").get());
}
@Ignore("Remove when types are supported")
@Test
public void testDefaultValueInt() {
List<Input> inputs = Collections.singletonList(new Input.InputBuilder("input1", 2).build());
Map<String, Value> result = bindInputs(inputs);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals(2, result.get("input1").get());
}
@Ignore("Remove when types are supported")
@Test
public void testDefaultValueBoolean() {
List<Input> inputs = Arrays.asList(
new Input.InputBuilder("input1", true).build(),
new Input.InputBuilder("input2", false).build(),
new Input.InputBuilder("input3", "${ str('phrase containing true and false') }").build()
);
Map<String, Value> result = bindInputs(inputs);
Assert.assertTrue((boolean) result.get("input1").get());
Assert.assertFalse((boolean) result.get("input2").get());
Assert.assertEquals("phrase containing true and false", result.get("input3").get());
}
@Test
public void testTwoInputs() {
List<Input> inputs = Arrays.asList(
new Input.InputBuilder("input2", "yyy").build(),
new Input.InputBuilder("input1", "zzz").build()
);
Map<String, Value> result = bindInputs(inputs);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("zzz", result.get("input1").get());
Assert.assertTrue(result.containsKey("input2"));
Assert.assertEquals("yyy", result.get("input2").get());
}
@Test
public void testAssignFromInput() {
Input input1 = new Input.InputBuilder("input1", "val1", false)
.withRequired(false)
.withPrivateInput(true)
.build();
Input input2 = new Input.InputBuilder("input2", "${ input1 }", false)
.withRequired(false)
.withPrivateInput(true)
.build();
List<Input> inputs = Arrays.asList(input1, input2);
Map<String, Value> result = bindInputs(inputs);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("val1", result.get("input1").get());
Assert.assertTrue(result.containsKey("input2"));
Assert.assertEquals("val1", result.get("input2").get());
}
@Test
public void testPrivateInputMissingInContext() {
Input input1 = new Input.InputBuilder("input1", "${ input1 }")
.withRequired(false)
.withPrivateInput(true)
.build();
List<Input> inputs = Collections.singletonList(input1);
exception.expect(RuntimeException.class);
exception.expectMessage(new BaseMatcher<String>() {
public void describeTo(Description description) {
}
public boolean matches(Object o) {
String message = o.toString();
return message.contains("Error binding input: 'input1'") &&
message.contains("Error is: Error in running script expression: 'input1'") &&
message.contains("Exception is: name 'input1' is not defined");
}
});
bindInputs(inputs);
}
@Test
public void testInputMissingInContext() {
Input input1 = new Input.InputBuilder("input1", "${ input1 }")
.withRequired(false)
.withPrivateInput(false)
.build();
List<Input> inputs = Collections.singletonList(input1);
exception.expect(RuntimeException.class);
exception.expectMessage(new BaseMatcher<String>() {
public void describeTo(Description description) {
}
public boolean matches(Object o) {
String message = o.toString();
return message.contains("Error binding input: 'input1'") &&
message.contains("Error is: Error in running script expression: 'input1'") &&
message.contains("Exception is: name 'input1' is not defined");
}
});
bindInputs(inputs);
}
@Test
public void testInputMissing() {
Input input1 = new Input.InputBuilder("input1", null)
.withRequired(true)
.withPrivateInput(false)
.build();
List<Input> inputs = Collections.singletonList(input1);
exception.expect(RuntimeException.class);
exception.expectMessage("Input with name: 'input1' is Required, but value is empty");
bindInputs(inputs);
}
@Test
public void testInputWithDefaultValueNull() {
Input input1 = new Input.InputBuilder("input1", null)
.withRequired(false)
.withPrivateInput(false)
.build();
List<Input> inputs = Collections.singletonList(input1);
Map<String, Value> result = bindInputs(inputs);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals(null, result.get("input1").get());
}
@Test
public void testInputRef() {
Map<String, Value> context = new HashMap<>();
context.put("inputX", ValueFactory.create("xxx"));
List<Input> inputs = Collections.singletonList(new Input.InputBuilder("input1", "${ str(inputX) }").build());
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("xxx", result.get("input1").get());
Assert.assertEquals(1, context.size());
}
@Test
public void testInputScriptEval() {
Map<String, Value> context = new HashMap<>();
context.put("valX", ValueFactory.create("5"));
Input scriptInput = new Input.InputBuilder("input1", "${ \"3\" + valX }").build();
List<Input> inputs = Collections.singletonList(scriptInput);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("35", result.get("input1").get());
Assert.assertEquals(1, context.size());
}
@Test
public void testInputScriptEval2() {
Map<String, Value> context = new HashMap<>();
context.put("valB", ValueFactory.create("b"));
context.put("valC", ValueFactory.create("c"));
Input scriptInput = new Input.InputBuilder("input1", "${ 'a' + valB + valC }").build();
List<Input> inputs = Collections.singletonList(scriptInput);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("abc", result.get("input1").get());
}
@Test
public void testDefaultValueVsEmptyRef() {
Map<String, Value> context = new HashMap<>();
Input refInput = new Input.InputBuilder("input1", "${ str('val') }").build();
List<Input> inputs = Collections.singletonList(refInput);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("val", result.get("input1").get());
Assert.assertTrue(context.isEmpty());
}
@Test
public void testAssignFromAndExpr() {
Map<String, Value> context = new HashMap<>();
context.put("input1", ValueFactory.create("3"));
Input input = new Input.InputBuilder("input1", "${ 5+7 }").build();
List<Input> inputs = Collections.singletonList(input);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("3", result.get("input1").get());
Assert.assertEquals(1, context.size());
Assert.assertEquals("3", context.get("input1").get());
}
@Test
public void testAssignFromAndConst() {
Map<String, Value> context = new HashMap<>();
context.put("input1", ValueFactory.create("3"));
Input input = new Input.InputBuilder("input1", 5).build();
List<Input> inputs = Collections.singletonList(input);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("3", result.get("input1").get());
}
@Test
public void testComplexExpr() {
Map<String, Value> context = new HashMap<>();
context.put("input1", ValueFactory.create("3"));
Input input = new Input.InputBuilder("input2", "${ input1 + \"3 * 2\" }").build();
List<Input> inputs = Collections.singletonList(input);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input2"));
Assert.assertEquals("33 * 2", result.get("input2").get());
Assert.assertEquals(1, result.size());
}
@Test
public void testAssignFromVsRef() {
Map<String, Value> context = new HashMap<>();
context.put("input2", ValueFactory.create(3));
context.put("input1", ValueFactory.create("5"));
Input input = new Input.InputBuilder("input1", "${ input2 }").build();
List<Input> inputs = Collections.singletonList(input);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("5", result.get("input1").get());
Assert.assertEquals(1, result.size());
}
@Test
public void testOverrideAssignFrom() {
Map<String, Value> context = new HashMap<>();
context.put("input2", ValueFactory.create("3"));
context.put("input1", ValueFactory.create("5"));
Input input = new Input.InputBuilder("input1", "${ input2 }", false)
.withRequired(false)
.withPrivateInput(true)
.build();
List<Input> inputs = Collections.singletonList(input);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("3", result.get("input1").get());
Assert.assertEquals(1, result.size());
Assert.assertEquals(2, context.size());
}
@Test
public void testOverrideAssignFrom2() {
Map<String, Value> context = new HashMap<>();
context.put("input1", ValueFactory.create(5));
Input input = new Input.InputBuilder("input1", "3", false)
.withRequired(false)
.withPrivateInput(true)
.build();
List<Input> inputs = Collections.singletonList(input);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("3", result.get("input1").get());
Assert.assertEquals(1, result.size());
}
@Test
public void testOverrideAssignFrom3() {
Map<String, Value> context = new HashMap<>();
context.put("input1", ValueFactory.create(5));
Input input = new Input.InputBuilder("input1", null, false)
.withRequired(false)
.withPrivateInput(true)
.build();
List<Input> inputs = Collections.singletonList(input);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("'not private' disables the assignFrom func...", null, result.get("input1").get());
Assert.assertEquals(1, result.size());
}
@Ignore("Remove when types are supported")
@Test
public void testOverrideFalse() {
Map<String, Value> context = new HashMap<>();
context.put("input1", ValueFactory.create(5));
Input input = new Input.InputBuilder("input1", 6).build();
List<Input> inputs = Collections.singletonList(input);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals(5, result.get("input1").get());
Assert.assertEquals(1, result.size());
}
@Test(expected = RuntimeException.class)
public void testExpressionWithWrongRef() {
Map<String, Value> context = new HashMap<>();
Input input = new Input.InputBuilder("input1", "${ input2 }", false)
.withRequired(false)
.withPrivateInput(true)
.build();
List<Input> inputs = Collections.singletonList(input);
bindInputs(inputs, context);
}
@Test
public void testInputAssignFromAnotherInput() {
Map<String, Value> context = new HashMap<>();
Input input1 = new Input.InputBuilder("input1", "5").build();
Input input2 = new Input.InputBuilder("input2", "${ input1 }").build();
List<Input> inputs = Arrays.asList(input1, input2);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("5", result.get("input1").get());
Assert.assertTrue(result.containsKey("input2"));
Assert.assertEquals("5", result.get("input2").get());
Assert.assertEquals(2, result.size());
Assert.assertTrue("orig context should not change", context.isEmpty());
}
@Test
public void testComplexExpressionInput() {
Map<String, Value> context = new HashMap<>();
context.put("varX", ValueFactory.create("5"));
Input input1 = new Input.InputBuilder("input1", "5").build();
Input input2 = new Input.InputBuilder("input2", "${ input1 + \"5\" + varX }").build();
List<Input> inputs = Arrays.asList(input1, input2);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("5", result.get("input1").get());
Assert.assertTrue(result.containsKey("input2"));
Assert.assertEquals("555", result.get("input2").get());
Assert.assertEquals(2, result.size());
Assert.assertEquals("orig context should not change", 1, context.size());
}
@Test
public void testComplexExpression2Input() {
Map<String, Value> context = new HashMap<>();
context.put("varX", ValueFactory.create("roles"));
Input input1 = new Input.InputBuilder("input1", "${ 'mighty' + ' max ' + varX }").build();
List<Input> inputs = Collections.singletonList(input1);
Map<String, Value> result = bindInputs(inputs, context);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.containsKey("input1"));
Assert.assertEquals("mighty max roles", result.get("input1").get());
Assert.assertEquals(1, result.size());
Assert.assertEquals("orig context should not change", 1, context.size());
}
private Map<String, Value> bindInputs(List<Input> inputs, Map<String, Value> context,
Set<SystemProperty> systemProperties) {
return inputsBinding.bindInputs(inputs, context, systemProperties);
}
private Map<String, Value> bindInputs(List<Input> inputs, Map<String, Value> context) {
return bindInputs(inputs, context, null);
}
private Map<String, Value> bindInputs(List<Input> inputs) {
return bindInputs(inputs, new HashMap<String, Value>());
}
@Configuration
static class Config {
@Bean
public InputsBinding inputsBinding() {
return new InputsBinding();
}
@Bean
public ScriptEvaluator scriptEvaluator() {
return new ScriptEvaluator();
}
@Bean
public DependencyService mavenRepositoryService() {
return new DependencyServiceImpl();
}
@Bean
public MavenConfig mavenConfig() {
return new MavenConfigImpl();
}
@Bean
public PythonRuntimeService pythonRuntimeService() {
return new PythonRuntimeServiceImpl();
}
@Bean
public PythonExecutionEngine pythonExecutionEngine() {
return new PythonExecutionCachedEngine();
}
@Bean
public EventBus eventBus() {
return new EventBusImpl();
}
}
}