|
|
|
|
@ -16,7 +16,7 @@
@@ -16,7 +16,7 @@
|
|
|
|
|
|
|
|
|
|
package org.springframework.util; |
|
|
|
|
|
|
|
|
|
import java.util.Properties; |
|
|
|
|
import java.util.Map; |
|
|
|
|
import java.util.stream.Stream; |
|
|
|
|
|
|
|
|
|
import org.junit.jupiter.api.Nested; |
|
|
|
|
@ -36,13 +36,13 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -36,13 +36,13 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
|
|
|
import static org.mockito.BDDMockito.given; |
|
|
|
|
import static org.mockito.Mockito.inOrder; |
|
|
|
|
import static org.mockito.Mockito.mock; |
|
|
|
|
import static org.mockito.Mockito.verify; |
|
|
|
|
import static org.mockito.Mockito.verifyNoMoreInteractions; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Tests for {@link PlaceholderParser}. |
|
|
|
|
* |
|
|
|
|
* @author Stephane Nicoll |
|
|
|
|
* @author Sam Brannen |
|
|
|
|
*/ |
|
|
|
|
class PlaceholderParserTests { |
|
|
|
|
|
|
|
|
|
@ -54,11 +54,11 @@ class PlaceholderParserTests {
@@ -54,11 +54,11 @@ class PlaceholderParserTests {
|
|
|
|
|
@ParameterizedTest(name = "{0} -> {1}") |
|
|
|
|
@MethodSource("placeholders") |
|
|
|
|
void placeholderIsReplaced(String text, String expected) { |
|
|
|
|
Properties properties = new Properties(); |
|
|
|
|
properties.setProperty("firstName", "John"); |
|
|
|
|
properties.setProperty("nested0", "first"); |
|
|
|
|
properties.setProperty("nested1", "Name"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected); |
|
|
|
|
Map<String, String> properties = Map.of( |
|
|
|
|
"firstName", "John", |
|
|
|
|
"nested0", "first", |
|
|
|
|
"nested1", "Name"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Stream<Arguments> placeholders() { |
|
|
|
|
@ -79,13 +79,13 @@ class PlaceholderParserTests {
@@ -79,13 +79,13 @@ class PlaceholderParserTests {
|
|
|
|
|
@ParameterizedTest(name = "{0} -> {1}") |
|
|
|
|
@MethodSource("nestedPlaceholders") |
|
|
|
|
void nestedPlaceholdersAreReplaced(String text, String expected) { |
|
|
|
|
Properties properties = new Properties(); |
|
|
|
|
properties.setProperty("p1", "v1"); |
|
|
|
|
properties.setProperty("p2", "v2"); |
|
|
|
|
properties.setProperty("p3", "${p1}:${p2}"); // nested placeholders
|
|
|
|
|
properties.setProperty("p4", "${p3}"); // deeply nested placeholders
|
|
|
|
|
properties.setProperty("p5", "${p1}:${p2}:${bogus}"); // unresolvable placeholder
|
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected); |
|
|
|
|
Map<String, String> properties = Map.of( |
|
|
|
|
"p1", "v1", |
|
|
|
|
"p2", "v2", |
|
|
|
|
"p3", "${p1}:${p2}", // nested placeholders
|
|
|
|
|
"p4", "${p3}", // deeply nested placeholders
|
|
|
|
|
"p5", "${p1}:${p2}:${bogus}"); // unresolvable placeholder
|
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Stream<Arguments> nestedPlaceholders() { |
|
|
|
|
@ -101,19 +101,15 @@ class PlaceholderParserTests {
@@ -101,19 +101,15 @@ class PlaceholderParserTests {
|
|
|
|
|
@Test |
|
|
|
|
void parseWithSinglePlaceholder() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${firstName}", resolver)) |
|
|
|
|
.isEqualTo("John"); |
|
|
|
|
verify(resolver).resolvePlaceholder("firstName"); |
|
|
|
|
verifyNoMoreInteractions(resolver); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${firstName}", resolver)).isEqualTo("John"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "firstName"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void parseWithPlaceholderAndPrefixText() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("This is ${firstName}", resolver)) |
|
|
|
|
.isEqualTo("This is John"); |
|
|
|
|
verify(resolver).resolvePlaceholder("firstName"); |
|
|
|
|
verifyNoMoreInteractions(resolver); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("This is ${firstName}", resolver)).isEqualTo("This is John"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "firstName"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
@ -121,31 +117,25 @@ class PlaceholderParserTests {
@@ -121,31 +117,25 @@ class PlaceholderParserTests {
|
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "John", "lastName", "Smith"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("User: ${firstName} - ${lastName}.", resolver)) |
|
|
|
|
.isEqualTo("User: John - Smith."); |
|
|
|
|
verify(resolver).resolvePlaceholder("firstName"); |
|
|
|
|
verify(resolver).resolvePlaceholder("lastName"); |
|
|
|
|
verifyNoMoreInteractions(resolver); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "firstName", "lastName"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void parseWithNestedPlaceholderInKey() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver( |
|
|
|
|
"nested", "Name", "firstName", "John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${first${nested}}", resolver)) |
|
|
|
|
.isEqualTo("John"); |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("nested", "Name", "firstName", "John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${first${nested}}", resolver)).isEqualTo("John"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "nested", "firstName"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void parseWithMultipleNestedPlaceholdersInKey() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver( |
|
|
|
|
"nested0", "first", "nested1", "Name", "firstName", "John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${${nested0}${nested1}}", resolver)) |
|
|
|
|
.isEqualTo("John"); |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("nested0", "first", "nested1", "Name", "firstName", "John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${${nested0}${nested1}}", resolver)).isEqualTo("John"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "nested0", "nested1", "firstName"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void placeholdersWithSeparatorAreHandledAsIs() { |
|
|
|
|
void placeholderValueContainingSeparatorIsHandledAsIs() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("my:test", "value"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${my:test}", resolver)).isEqualTo("value"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "my:test"); |
|
|
|
|
@ -153,17 +143,20 @@ class PlaceholderParserTests {
@@ -153,17 +143,20 @@ class PlaceholderParserTests {
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void placeholdersWithoutEscapeCharAreNotEscaped() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("test", "value"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("\\${test}", resolver)).isEqualTo("\\value"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "test"); |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("p1", "v1", "p2", "v2", "p3", "v3", "p4", "v4"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("\\${p1}", resolver)).isEqualTo("\\v1"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("\\\\${p2}", resolver)).isEqualTo("\\\\v2"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("\\${p3}\\", resolver)).isEqualTo("\\v3\\"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("a\\${p4}\\z", resolver)).isEqualTo("a\\v4\\z"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "p1", "p2", "p3", "p4"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void textWithInvalidPlaceholderIsMerged() { |
|
|
|
|
void textWithInvalidPlaceholderSyntaxIsMerged() { |
|
|
|
|
String text = "test${of${with${and${"; |
|
|
|
|
ParsedValue parsedValue = this.parser.parse(text); |
|
|
|
|
assertThat(parsedValue.parts()).singleElement().isInstanceOfSatisfying( |
|
|
|
|
TextPart.class, textPart -> assertThat(textPart.text()).isEqualTo(text)); |
|
|
|
|
assertThat(parsedValue.parts()).singleElement().isInstanceOfSatisfying(TextPart.class, |
|
|
|
|
textPart -> assertThat(textPart.text()).isEqualTo(text)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
@ -176,11 +169,11 @@ class PlaceholderParserTests {
@@ -176,11 +169,11 @@ class PlaceholderParserTests {
|
|
|
|
|
@ParameterizedTest(name = "{0} -> {1}") |
|
|
|
|
@MethodSource("placeholders") |
|
|
|
|
void placeholderIsReplaced(String text, String expected) { |
|
|
|
|
Properties properties = new Properties(); |
|
|
|
|
properties.setProperty("firstName", "John"); |
|
|
|
|
properties.setProperty("nested0", "first"); |
|
|
|
|
properties.setProperty("nested1", "Name"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected); |
|
|
|
|
Map<String, String> properties = Map.of( |
|
|
|
|
"firstName", "John", |
|
|
|
|
"nested0", "first", |
|
|
|
|
"nested1", "Name"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Stream<Arguments> placeholders() { |
|
|
|
|
@ -199,14 +192,14 @@ class PlaceholderParserTests {
@@ -199,14 +192,14 @@ class PlaceholderParserTests {
|
|
|
|
|
@ParameterizedTest(name = "{0} -> {1}") |
|
|
|
|
@MethodSource("nestedPlaceholders") |
|
|
|
|
void nestedPlaceholdersAreReplaced(String text, String expected) { |
|
|
|
|
Properties properties = new Properties(); |
|
|
|
|
properties.setProperty("p1", "v1"); |
|
|
|
|
properties.setProperty("p2", "v2"); |
|
|
|
|
properties.setProperty("p3", "${p1}:${p2}"); // nested placeholders
|
|
|
|
|
properties.setProperty("p4", "${p3}"); // deeply nested placeholders
|
|
|
|
|
properties.setProperty("p5", "${p1}:${p2}:${bogus}"); // unresolvable placeholder
|
|
|
|
|
properties.setProperty("p6", "${p1}:${p2}:${bogus:def}"); // unresolvable w/ default
|
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected); |
|
|
|
|
Map<String, String> properties = Map.of( |
|
|
|
|
"p1", "v1", |
|
|
|
|
"p2", "v2", |
|
|
|
|
"p3", "${p1}:${p2}", // nested placeholders
|
|
|
|
|
"p4", "${p3}", // deeply nested placeholders
|
|
|
|
|
"p5", "${p1}:${p2}:${bogus}", // unresolvable placeholder
|
|
|
|
|
"p6", "${p1}:${p2}:${bogus:def}"); // unresolvable w/ default
|
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Stream<Arguments> nestedPlaceholders() { |
|
|
|
|
@ -225,11 +218,11 @@ class PlaceholderParserTests {
@@ -225,11 +218,11 @@ class PlaceholderParserTests {
|
|
|
|
|
@ParameterizedTest(name = "{0} -> {1}") |
|
|
|
|
@MethodSource("exactMatchPlaceholders") |
|
|
|
|
void placeholdersWithExactMatchAreConsidered(String text, String expected) { |
|
|
|
|
Properties properties = new Properties(); |
|
|
|
|
properties.setProperty("prefix://my-service", "example-service"); |
|
|
|
|
properties.setProperty("px", "prefix"); |
|
|
|
|
properties.setProperty("p1", "${prefix://my-service}"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected); |
|
|
|
|
Map<String, String> properties = Map.of( |
|
|
|
|
"prefix://my-service", "example-service", |
|
|
|
|
"px", "prefix", |
|
|
|
|
"p1", "${prefix://my-service}"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Stream<Arguments> exactMatchPlaceholders() { |
|
|
|
|
@ -242,74 +235,55 @@ class PlaceholderParserTests {
@@ -242,74 +235,55 @@ class PlaceholderParserTests {
|
|
|
|
|
@Test |
|
|
|
|
void parseWithKeyEqualsToText() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "Steve"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${firstName}", resolver)) |
|
|
|
|
.isEqualTo("Steve"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${firstName}", resolver)).isEqualTo("Steve"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "firstName"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void parseWithHardcodedFallback() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver(); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${firstName:Steve}", resolver)) |
|
|
|
|
.isEqualTo("Steve"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${firstName:Steve}", resolver)).isEqualTo("Steve"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "firstName:Steve", "firstName"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void parseWithNestedPlaceholderInKeyUsingFallback() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${first${invalid:Name}}", resolver)) |
|
|
|
|
.isEqualTo("John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${first${invalid:Name}}", resolver)).isEqualTo("John"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "invalid:Name", "invalid", "firstName"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void parseWithFallbackUsingPlaceholder() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("firstName", "John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${invalid:${firstName}}", resolver)) |
|
|
|
|
.isEqualTo("John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders("${invalid:${firstName}}", resolver)).isEqualTo("John"); |
|
|
|
|
verifyPlaceholderResolutions(resolver, "invalid", "firstName"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Nested // Tests with the use of the escape character
|
|
|
|
|
/** |
|
|
|
|
* Tests that use the escape character. |
|
|
|
|
*/ |
|
|
|
|
@Nested |
|
|
|
|
class EscapedTests { |
|
|
|
|
|
|
|
|
|
private final PlaceholderParser parser = new PlaceholderParser("${", "}", ":", '\\', true); |
|
|
|
|
|
|
|
|
|
@ParameterizedTest(name = "{0} -> {1}") |
|
|
|
|
@MethodSource("escapedInNestedPlaceholders") |
|
|
|
|
void escapedSeparatorInNestedPlaceholder(String text, String expected) { |
|
|
|
|
Properties properties = new Properties(); |
|
|
|
|
properties.setProperty("app.environment", "qa"); |
|
|
|
|
properties.setProperty("app.service", "protocol"); |
|
|
|
|
properties.setProperty("protocol://host/qa/name", "protocol://example.com/qa/name"); |
|
|
|
|
properties.setProperty("service/host/qa/name", "https://example.com/qa/name"); |
|
|
|
|
properties.setProperty("service/host/qa/name:value", "https://example.com/qa/name-value"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Stream<Arguments> escapedInNestedPlaceholders() { |
|
|
|
|
return Stream.of( |
|
|
|
|
Arguments.of("${protocol\\://host/${app.environment}/name}", "protocol://example.com/qa/name"), |
|
|
|
|
Arguments.of("${${app.service}\\://host/${app.environment}/name}", "protocol://example.com/qa/name"), |
|
|
|
|
Arguments.of("${service/host/${app.environment}/name:\\value}", "https://example.com/qa/name"), |
|
|
|
|
Arguments.of("${service/host/${name\\:value}/}", "${service/host/${name:value}/}")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ParameterizedTest(name = "{0} -> {1}") |
|
|
|
|
@MethodSource("escapedPlaceholders") |
|
|
|
|
void escapedPlaceholderIsNotReplaced(String text, String expected) { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver( |
|
|
|
|
"firstName", "John", "nested0", "first", "nested1", "Name", |
|
|
|
|
Map<String, String> properties = Map.of( |
|
|
|
|
"firstName", "John", |
|
|
|
|
"${test}", "John", |
|
|
|
|
"p1", "v1", "p2", "\\${p1:default}", "p3", "${p2}", |
|
|
|
|
"p1", "v1", |
|
|
|
|
"p2", "\\${p1:default}", |
|
|
|
|
"p3", "${p2}", |
|
|
|
|
"p4", "adc${p0:\\${p1}}", |
|
|
|
|
"p5", "adc${\\${p0}:${p1}}", |
|
|
|
|
"p6", "adc${p0:def\\${p1}}", |
|
|
|
|
"p7", "adc\\${"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, resolver)).isEqualTo(expected); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Stream<Arguments> escapedPlaceholders() { |
|
|
|
|
@ -324,18 +298,21 @@ class PlaceholderParserTests {
@@ -324,18 +298,21 @@ class PlaceholderParserTests {
|
|
|
|
|
Arguments.of("${p4}", "adc${p1}"), |
|
|
|
|
Arguments.of("${p5}", "adcv1"), |
|
|
|
|
Arguments.of("${p6}", "adcdef${p1}"), |
|
|
|
|
Arguments.of("${p7}", "adc\\${")); |
|
|
|
|
|
|
|
|
|
Arguments.of("${p7}", "adc\\${"), |
|
|
|
|
// Double backslash
|
|
|
|
|
Arguments.of("DOMAIN\\\\${user.name}", "DOMAIN\\${user.name}"), |
|
|
|
|
// Triple backslash
|
|
|
|
|
Arguments.of("triple\\\\\\${backslash}", "triple\\\\${backslash}"), |
|
|
|
|
// Multiple escaped placeholders
|
|
|
|
|
Arguments.of("start\\${prop1}middle\\${prop2}end", "start${prop1}middle${prop2}end") |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ParameterizedTest(name = "{0} -> {1}") |
|
|
|
|
@MethodSource("escapedSeparators") |
|
|
|
|
void escapedSeparatorIsNotReplaced(String text, String expected) { |
|
|
|
|
Properties properties = new Properties(); |
|
|
|
|
properties.setProperty("first:Name", "John"); |
|
|
|
|
properties.setProperty("nested0", "first"); |
|
|
|
|
properties.setProperty("nested1", "Name"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::getProperty)).isEqualTo(expected); |
|
|
|
|
Map<String, String> properties = Map.of("first:Name", "John"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Stream<Arguments> escapedSeparators() { |
|
|
|
|
@ -345,6 +322,26 @@ class PlaceholderParserTests {
@@ -345,6 +322,26 @@ class PlaceholderParserTests {
|
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ParameterizedTest(name = "{0} -> {1}") |
|
|
|
|
@MethodSource("escapedSeparatorsInNestedPlaceholders") |
|
|
|
|
void escapedSeparatorInNestedPlaceholderIsNotReplaced(String text, String expected) { |
|
|
|
|
Map<String, String> properties = Map.of( |
|
|
|
|
"app.environment", "qa", |
|
|
|
|
"app.service", "protocol", |
|
|
|
|
"protocol://host/qa/name", "protocol://example.com/qa/name", |
|
|
|
|
"service/host/qa/name", "https://example.com/qa/name", |
|
|
|
|
"service/host/qa/name:value", "https://example.com/qa/name-value"); |
|
|
|
|
assertThat(this.parser.replacePlaceholders(text, properties::get)).isEqualTo(expected); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Stream<Arguments> escapedSeparatorsInNestedPlaceholders() { |
|
|
|
|
return Stream.of( |
|
|
|
|
Arguments.of("${protocol\\://host/${app.environment}/name}", "protocol://example.com/qa/name"), |
|
|
|
|
Arguments.of("${${app.service}\\://host/${app.environment}/name}", "protocol://example.com/qa/name"), |
|
|
|
|
Arguments.of("${service/host/${app.environment}/name:\\value}", "https://example.com/qa/name"), |
|
|
|
|
Arguments.of("${service/host/${name\\:value}/}", "${service/host/${name:value}/}")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Nested |
|
|
|
|
@ -354,34 +351,38 @@ class PlaceholderParserTests {
@@ -354,34 +351,38 @@ class PlaceholderParserTests {
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void textWithCircularReference() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("pL", "${pR}", "pR", "${pL}"); |
|
|
|
|
assertThatThrownBy(() -> this.parser.replacePlaceholders("${pL}", resolver)) |
|
|
|
|
Map<String, String> properties = Map.of( |
|
|
|
|
"pL", "${pR}", |
|
|
|
|
"pR", "${pL}"); |
|
|
|
|
assertThatThrownBy(() -> this.parser.replacePlaceholders("${pL}", properties::get)) |
|
|
|
|
.isInstanceOf(PlaceholderResolutionException.class) |
|
|
|
|
.hasMessage("Circular placeholder reference 'pL' in value \"${pL}\" <-- \"${pR}\" <-- \"${pL}\""); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void unresolvablePlaceholderIsReported() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver(); |
|
|
|
|
assertThatExceptionOfType(PlaceholderResolutionException.class) |
|
|
|
|
.isThrownBy(() -> this.parser.replacePlaceholders("${bogus}", resolver)) |
|
|
|
|
.withMessage("Could not resolve placeholder 'bogus' in value \"${bogus}\"") |
|
|
|
|
.isThrownBy(() -> this.parser.replacePlaceholders("X${bogus}Z", placeholderName -> null)) |
|
|
|
|
.withMessage("Could not resolve placeholder 'bogus' in value \"X${bogus}Z\"") |
|
|
|
|
.withNoCause(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void unresolvablePlaceholderInNestedPlaceholderIsReportedWithChain() { |
|
|
|
|
PlaceholderResolver resolver = mockPlaceholderResolver("p1", "v1", "p2", "v2", |
|
|
|
|
Map<String, String> properties = Map.of( |
|
|
|
|
"p1", "v1", |
|
|
|
|
"p2", "v2", |
|
|
|
|
"p3", "${p1}:${p2}:${bogus}"); |
|
|
|
|
assertThatExceptionOfType(PlaceholderResolutionException.class) |
|
|
|
|
.isThrownBy(() -> this.parser.replacePlaceholders("${p3}", resolver)) |
|
|
|
|
.isThrownBy(() -> this.parser.replacePlaceholders("${p3}", properties::get)) |
|
|
|
|
.withMessage("Could not resolve placeholder 'bogus' in value \"${p1}:${p2}:${bogus}\" <-- \"${p3}\"") |
|
|
|
|
.withNoCause(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
PlaceholderResolver mockPlaceholderResolver(String... pairs) { |
|
|
|
|
|
|
|
|
|
private static PlaceholderResolver mockPlaceholderResolver(String... pairs) { |
|
|
|
|
if (pairs.length % 2 == 1) { |
|
|
|
|
throw new IllegalArgumentException("size must be even, it is a set of key=value pairs"); |
|
|
|
|
} |
|
|
|
|
@ -394,7 +395,7 @@ class PlaceholderParserTests {
@@ -394,7 +395,7 @@ class PlaceholderParserTests {
|
|
|
|
|
return resolver; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void verifyPlaceholderResolutions(PlaceholderResolver mock, String... placeholders) { |
|
|
|
|
private static void verifyPlaceholderResolutions(PlaceholderResolver mock, String... placeholders) { |
|
|
|
|
InOrder ordered = inOrder(mock); |
|
|
|
|
for (String placeholder : placeholders) { |
|
|
|
|
ordered.verify(mock).resolvePlaceholder(placeholder); |
|
|
|
|
|