spring-cloud-gateway icon indicating copy to clipboard operation
spring-cloud-gateway copied to clipboard

fixing undeterministic behavior in keepOriginalEncodingOfQueryParameter()

Open rjuare8 opened this issue 1 month ago • 0 comments

Deflake keepOriginalEncodingOfQueryParameter by relaxing URI ordering assertion

While running the Spring Cloud Gateway MVC tests (including with tools like NonDex), I observed non-deterministic failures in ProxyExchangeHandlerFunctionTest.keepOriginalEncodingOfQueryParameter().

The original test asserted that the entire URI, including the precise ordering of query parameters, exactly matched a hard-coded value:

assertThat(uri).hasToString(
    "http://localhost:8080/%C3%A9?foo=value1%20value2&bar=value3%3D&qux=value4%2B"
)
.hasPath("/é")
.hasParameter("foo", "value1 value2")
.hasParameter("bar", "value3=")
.hasParameter("qux", "value4+");

However, the order of query parameters is not guaranteed. When the iteration order of parameters changes (for example, under NonDex’s randomized iteration), the resulting URI may still contain exactly the same parameters and encodings but in a different order. In that case, the hasToString assertion fails even though the behavior of ProxyExchangeHandlerFunction is correct.

This is a classic implementation-dependent (ID) flaky test pattern: the test is asserting on concrete implementation details (a specific parameter ordering) rather than the actual contract (preserving the encoded path and parameter values).

This PR updates the test to verify that the URI contains the expected path and query fragments independently of their order, while still asserting on the decoded path and parameter values:

URI uri = proxyExchange.getRequest().getUri();


assertThat(uri.toString()).contains(
    "http://localhost:8080/%C3%A9?",
    "foo=value1%20value2",
    "bar=value3%3D",
    "qux=value4%2B"
);

assertThat(uri).hasPath("/é")
    .hasParameter("foo", "value1 value2")
    .hasParameter("bar", "value3=")
    .hasParameter("qux", "value4+");

Summary of changes

spring-cloud-gateway-server-webmvc/src/test/java/org/springframework/cloud/gateway/server/mvc/handler/ProxyExchangeHandlerFunctionTest.java:

Before:

URI uri = proxyExchange.getRequest().getUri();

assertThat(uri).hasToString(
    "http://localhost:8080/%C3%A9?foo=value1%20value2&bar=value3%3D&qux=value4%2B"
)
.hasPath("/é")
.hasParameter("foo", "value1 value2")
.hasParameter("bar", "value3=")
.hasParameter("qux", "value4+");

After:

URI uri = proxyExchange.getRequest().getUri();

assertThat(uri.toString()).contains(
    "http://localhost:8080/%C3%A9?",
    "foo=value1%20value2",
    "bar=value3%3D",
    "qux=value4%2B"
);

assertThat(uri).hasPath("/é")
    .hasParameter("foo", "value1 value2")
    .hasParameter("bar", "value3=")
    .hasParameter("qux", "value4+");

rjuare8 avatar Nov 30 '25 01:11 rjuare8