11package com .sap .cloud .sdk .datamodel .odata .client .request ;
22
3+ import static com .github .tomakehurst .wiremock .client .WireMock .get ;
4+ import static com .github .tomakehurst .wiremock .client .WireMock .getRequestedFor ;
5+ import static com .github .tomakehurst .wiremock .client .WireMock .okJson ;
6+ import static com .github .tomakehurst .wiremock .client .WireMock .post ;
7+ import static com .github .tomakehurst .wiremock .client .WireMock .urlEqualTo ;
8+ import static com .github .tomakehurst .wiremock .client .WireMock .verify ;
9+ import static com .github .tomakehurst .wiremock .core .WireMockConfiguration .wireMockConfig ;
310import static org .assertj .core .api .Assertions .assertThat ;
411import static org .mockito .Mockito .mock ;
512import static org .mockito .Mockito .when ;
613
14+ import com .github .tomakehurst .wiremock .WireMockServer ;
15+ import com .github .tomakehurst .wiremock .core .WireMockConfiguration ;
16+ import io .vavr .control .Try ;
17+ import lombok .AllArgsConstructor ;
18+ import lombok .Builder ;
19+ import lombok .RequiredArgsConstructor ;
20+ import lombok .Setter ;
21+ import lombok .experimental .Accessors ;
722import org .apache .http .HttpResponse ;
823import org .apache .http .HttpVersion ;
924import org .apache .http .entity .ContentType ;
1227import org .junit .jupiter .api .Test ;
1328
1429import com .sap .cloud .sdk .datamodel .odata .client .ODataProtocol ;
30+ import org .junit .jupiter .params .ParameterizedTest ;
31+ import org .junit .jupiter .params .provider .FieldSource ;
32+
33+ import javax .annotation .Nonnull ;
1534
1635class ODataNextLinkTest
1736{
@@ -24,6 +43,102 @@ class ODataNextLinkTest
2443 }
2544 """ ;
2645
46+ @ RequiredArgsConstructor ( staticName = "named" )
47+ @ AllArgsConstructor
48+ @ Accessors ( fluent = true )
49+ @ Setter
50+ static class QueryParameterCase
51+ {
52+ final String label ;
53+ Setup with ;
54+ Expectation expects ;
55+
56+ @ Builder
57+ static class Setup
58+ {
59+ String destinationQuery ;
60+ String propertiesQuery ;
61+ String initialQuery ;
62+ String nextLinkQuery ;
63+ }
64+
65+ @ Builder
66+ static class Expectation
67+ {
68+ String initialQuerySent ;
69+ String nextLinkQueryParsed ;
70+ String nextLinkQuerySent ;
71+ }
72+ }
73+
74+ static QueryParameterCase [] QUERY_PARAMETERS_CASES =
75+ {
76+ // case 1: query-parameters from destination uri, destination properties, next-link and odata-request are distinct
77+ QueryParameterCase
78+ .named ("DISTINCT" )
79+ .with (
80+ QueryParameterCase .Setup
81+ .builder ()
82+ .destinationQuery ("dest1=one&dest2=two" )
83+ .propertiesQuery ("prop1=one&prop2=two" )
84+ .initialQuery ("odata1=one&odata2=two" )
85+ .nextLinkQuery ("next1=one&next2=two" )
86+ .build ())
87+ .expects (
88+ QueryParameterCase .Expectation
89+ .builder ()
90+ .initialQuerySent ("dest1=one&dest2=two&odata1=one&odata2=two&prop1=one&prop2=two" )
91+ .nextLinkQueryParsed ("$skiptoken=42&next1=one&next2=two" )
92+ .nextLinkQuerySent ("dest1=one&dest2=two&$skiptoken=42&next1=one&next2=two&prop1=one&prop2=two" )
93+ .build ()) };
94+
95+ @ ParameterizedTest
96+ @ FieldSource ( "QUERY_PARAMETERS_CASES" )
97+ void testRemoveDuplicateQueryArguments1 ( @ Nonnull final QueryParameterCase testCase )
98+ {
99+ final WireMockConfiguration wiremockConfig = wireMockConfig ().dynamicPort ();
100+ final WireMockServer wiremock = new WireMockServer (wiremockConfig );
101+ wiremock .start ();
102+
103+ // TEST SETUP: Mock first request and response
104+ final String initialRequest = "/v1/%s/endpoint?%s" .formatted (testCase .label , testCase .expects .initialQuerySent );
105+ final String initialResponse =
106+ "{\" d\" :{\" results\" :[],\" __next\" :\" /v1/%s/endpoint?$skiptoken=42&%s\" }}"
107+ .formatted (testCase .label , testCase .with .nextLinkQuery );
108+ wiremock .stubFor (get (urlEqualTo (initialRequest )).willReturn (okJson (initialResponse )));
109+
110+ // TEST SETUP: Mock second request and response
111+ final String secondRequest = "/v1/%s/endpoint?%s" .formatted (testCase .label , testCase .expects .nextLinkQuerySent );
112+ final String secondResponse = "{\" d\" :{\" results\" :[]}}" ;
113+ wiremock .stubFor (get (urlEqualTo (secondRequest )).willReturn (okJson (secondResponse )));
114+
115+ // TEST SETUP: construct destination and HttpClient
116+ final String destinationUrl = wiremock .baseUrl () + "/?" + testCase .with .destinationQuery ;
117+ final DefaultHttpDestination .Builder destinationBuilder = DefaultHttpDestination .builder (destinationUrl );
118+ for (final String queryArg : testCase .with .propertiesQuery .split ("&" )) {
119+ final String [] queryArgParts = queryArg .split ("=" , 2 );
120+ destinationBuilder .property ("URL.queries." + queryArgParts [0 ], queryArgParts [1 ]);
121+ }
122+ final HttpClient client = HttpClientAccessor .getHttpClient (destinationBuilder .build ());
123+
124+ // TEST EXECUTION: Run OData request on behalf of HttpClient
125+ final ODataRequestRead request =
126+ new ODataRequestRead ("/v1/" + testCase .label , "endpoint" , testCase .with .initialQuery , ODataProtocol .V2 );
127+ final ODataRequestResultGeneric resultFirst = request .execute (client );
128+
129+ // TEST VALIDATION: Validate first actual request uri
130+ assertThat (resultFirst .getNextLink ())
131+ .contains ("/v1/%s/endpoint?%s" .formatted (testCase .label , testCase .expects .nextLinkQueryParsed ));
132+ wiremock .verify (getRequestedFor (urlEqualTo (initialRequest )));
133+
134+ // TEST VALIDATION: Validate second actual request uri
135+ final Try <ODataRequestResultGeneric > resultSecond = resultFirst .tryGetNextPage ();
136+ assertThat (resultSecond ).isNotEmpty ();
137+ wiremock .verify (getRequestedFor (urlEqualTo (secondRequest )));
138+
139+ wiremock .shutdown ();
140+ }
141+
27142 @ Test
28143 void testNotParsedNextLinkV4 ()
29144 {
0 commit comments