Simplify and revise PlaceholderParserTests for consistency
This commit is contained in:
parent
5a2cbc1ab3
commit
90453643cc
|
|
@ -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;
|
|||
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 {
|
|||
@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 {
|
|||
@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 {
|
|||
@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 {
|
|||
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,17 @@ 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");
|
||||
assertThat(this.parser.replacePlaceholders("\\${p1}", resolver)).isEqualTo("\\v1");
|
||||
verifyPlaceholderResolutions(resolver, "p1");
|
||||
}
|
||||
|
||||
@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 +166,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 +189,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 +215,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 +232,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 +295,15 @@ class PlaceholderParserTests {
|
|||
Arguments.of("${p4}", "adc${p1}"),
|
||||
Arguments.of("${p5}", "adcv1"),
|
||||
Arguments.of("${p6}", "adcdef${p1}"),
|
||||
Arguments.of("${p7}", "adc\\${"));
|
||||
|
||||
Arguments.of("${p7}", "adc\\${")
|
||||
);
|
||||
}
|
||||
|
||||
@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 +313,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 +342,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 +386,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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue