View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.http.remote;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.apache.juneau.http.HttpParts.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import java.math.*;
24  import java.util.*;
25  import java.util.concurrent.atomic.*;
26  
27  import org.apache.http.*;
28  import org.apache.http.client.config.*;
29  import org.apache.juneau.*;
30  import org.apache.juneau.annotation.*;
31  import org.apache.juneau.collections.*;
32  import org.apache.juneau.http.annotation.*;
33  import org.apache.juneau.http.part.*;
34  import org.apache.juneau.internal.*;
35  import org.apache.juneau.rest.RestRequest;
36  import org.apache.juneau.rest.annotation.*;
37  import org.apache.juneau.rest.client.*;
38  import org.apache.juneau.rest.mock.*;
39  import org.apache.juneau.uon.*;
40  import org.apache.juneau.utest.utils.*;
41  import org.junit.jupiter.api.*;
42  
43  class Remote_PathAnnotation_Test extends TestBase {
44  
45  	public static class Bean {
46  		public int x;
47  
48  		public static Bean create() {
49  			var b = new Bean();
50  			b.x = 1;
51  			return b;
52  		}
53  	}
54  
55  	//-----------------------------------------------------------------------------------------------------------------
56  	// Basic tests
57  	//-----------------------------------------------------------------------------------------------------------------
58  
59  	@Rest
60  	public static class A {
61  		@RestGet(path="/a/{x}")
62  		public String a(@Path("x") Object x) {
63  			return x.toString();
64  		}
65  	}
66  
67  	@Remote
68  	public interface A1 {
69  		@RemoteOp(path="a/{x}") String getX1(@Path("x") int b);
70  		@RemoteOp(path="a/{x}") String getX2(@Path("x") float b);
71  		@RemoteOp(path="a/{x}") String getX3(@Path("x") Bean b);
72  		@RemoteOp(path="a/{x}") String getX4(@Path("*") Bean b);
73  		@RemoteOp(path="a/{x}") String getX5(@Path Bean b);
74  		@RemoteOp(path="a/{x}") String getX6(@Path("x") Bean[] b);
75  		@RemoteOp(path="a/{x}") String getX7(@Path("x") @Schema(cf="uon") Bean[] b);
76  		@RemoteOp(path="a/{x}") String getX8(@Path("x") List<Bean> b);
77  		@RemoteOp(path="a/{x}") String getX9(@Path("x") @Schema(cf="uon") List<Bean> b);
78  		@RemoteOp(path="a/{x}") String getX10(@Path("x") Map<String,Bean> b);
79  		@RemoteOp(path="a/{x}") String getX11(@Path("*") Map<String,Bean> b);
80  		@RemoteOp(path="a/{x}") String getX12(@Path Map<String,Bean> b);
81  		@RemoteOp(path="a/{x}") String getX13(@Path("x") @Schema(cf="uon") Map<String,Bean> b);
82  		@RemoteOp(path="a/{x}") String getX14(@Path @Schema(f="uon") Map<String,Bean> b);
83  		@RemoteOp(path="a/{x}") String getX15(@Path("*") PartList b);
84  		@RemoteOp(path="a/{x}") String getX16(@Path PartList b);
85  		@RemoteOp(path="a/{x}") String getX17(@Path(name="x",serializer=UonSerializer.class) Map<String,Bean> b);
86  		@RemoteOp(path="a/{x}") String getX18(@Path("*") NameValuePair b);
87  		@RemoteOp(path="a/{x}") String getX19(@Path NameValuePair b);
88  		@RemoteOp(path="a/{x}") String getX20(@Path NameValuePair[] b);
89  		@RemoteOp(path="a/{x}") String getX21(@Path String b);
90  		@RemoteOp(path="a/{x}") String getX22(@Path List<NameValuePair> b);
91  	}
92  
93  	@Test void a01_path_objectTypes() {
94  		var x = remote(A.class,A1.class);
95  		assertEquals("1",x.getX1(1));
96  		assertEquals("1.0",x.getX2(1));
97  		assertEquals("x=1",x.getX3(Bean.create()));
98  		assertEquals("1",x.getX4(Bean.create()));
99  		assertEquals("1",x.getX5(Bean.create()));
100 		assertEquals("x=1,x=1",x.getX6(new Bean[]{Bean.create(),Bean.create()}));
101 		assertEquals("@((x=1),(x=1))",x.getX7(new Bean[]{Bean.create(),Bean.create()}));
102 		assertEquals("x=1,x=1",x.getX8(alist(Bean.create(),Bean.create())));
103 		assertEquals("@((x=1),(x=1))",x.getX9(alist(Bean.create(),Bean.create())));
104 		assertEquals("x=x\\=1",x.getX10(map("x",Bean.create())));
105 		assertEquals("x=1",x.getX11(map("x",Bean.create())));
106 		assertEquals("x=1",x.getX12(map("x",Bean.create())));
107 		assertEquals("(x=(x=1))",x.getX13(map("x",Bean.create())));
108 		assertEquals("x=1",x.getX14(map("x",Bean.create())));
109 		assertEquals("bar",x.getX15(parts("x","bar")));
110 		assertEquals("bar",x.getX16(parts("x","bar")));
111 		assertEquals("(x=(x=1))",x.getX17(map("x",Bean.create())));
112 		assertEquals("bar",x.getX18(part("x","bar")));
113 		assertEquals("bar",x.getX19(part("x","bar")));
114 		assertEquals("bar",x.getX20(new NameValuePair[]{part("x","bar")}));
115 		assertEquals("{x}",x.getX20(null));
116 		assertThrowsWithMessage(Exception.class, "Invalid value type for path arg 'null': java.lang.String", ()->x.getX21("foo"));
117 		assertEquals("bar",x.getX22(alist(part("x","bar"))));
118 	}
119 
120 	//-----------------------------------------------------------------------------------------------------------------
121 	// @Query(collectionFormat)
122 	//-----------------------------------------------------------------------------------------------------------------
123 
124 	@Rest
125 	public static class B {
126 		@RestGet(path="/a/{x}")
127 		public String a(@Path("x") Object x) {
128 			return x.toString();
129 		}
130 	}
131 
132 	@Remote
133 	public interface B1 {
134 		@RemoteOp(path="/a/{x}") String getX1(@Path("x") String...b);
135 		@RemoteOp(path="/a/{x}") String getX2(@Path("x") @Schema(cf="csv") String...b);
136 		@RemoteOp(path="/a/{x}") String getX3(@Path("x") @Schema(cf="ssv") String...b);
137 		@RemoteOp(path="/a/{x}") String getX4(@Path("x") @Schema(cf="tsv") String...b);
138 		@RemoteOp(path="/a/{x}") String getX5(@Path("x") @Schema(cf="pipes") String...b);
139 		@RemoteOp(path="/a/{x}") String getX6(@Path("x") @Schema(cf="multi") String...b); // Not supported,but should be treated as csv.
140 		@RemoteOp(path="/a/{x}") String getX7(@Path("x") @Schema(cf="uon") String...b);
141 	}
142 
143 	@Test void b01_path_collectionFormat() {
144 		var x = remote(B.class,B1.class);
145 		assertEquals("foo,bar",x.getX1("foo","bar"));
146 		assertEquals("foo,bar",x.getX2("foo","bar"));
147 		assertEquals("foo bar",x.getX3("foo","bar"));
148 		assertEquals("foo\tbar",x.getX4("foo","bar"));
149 		assertEquals("foo|bar",x.getX5("foo","bar"));
150 		assertEquals("foo,bar",x.getX6("foo","bar"));
151 		assertEquals("@(foo,bar)",x.getX7("foo","bar"));
152 	}
153 
154 	//-----------------------------------------------------------------------------------------------------------------
155 	// @Path(maximum,exclusiveMaximum,minimum,exclusiveMinimum)
156 	//-----------------------------------------------------------------------------------------------------------------
157 
158 	@Rest
159 	public static class C {
160 		@RestOp(path="/a/{x}")
161 		public String get(@Path("*") JsonMap m) {
162 			m.removeAll("/*","/**");
163 			return m.toString();
164 		}
165 	}
166 
167 	@Remote
168 	public interface C1 {
169 		@RemoteOp(path="/a/{x}") String getX1(@Path("x") @Schema(min="1",max="10") int b);
170 		@RemoteOp(path="/a/{x}") String getX2(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) int b);
171 		@RemoteOp(path="/a/{x}") String getX3(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) int b);
172 		@RemoteOp(path="/a/{x}") String getX4(@Path("x") @Schema(min="1",max="10") short b);
173 		@RemoteOp(path="/a/{x}") String getX5(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) short b);
174 		@RemoteOp(path="/a/{x}") String getX6(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) short b);
175 		@RemoteOp(path="/a/{x}") String getX7(@Path("x") @Schema(min="1",max="10") long b);
176 		@RemoteOp(path="/a/{x}") String getX8(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) long b);
177 		@RemoteOp(path="/a/{x}") String getX9(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) long b);
178 		@RemoteOp(path="/a/{x}") String getX10(@Path("x") @Schema(min="1",max="10") float b);
179 		@RemoteOp(path="/a/{x}") String getX11(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) float b);
180 		@RemoteOp(path="/a/{x}") String getX12(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) float b);
181 		@RemoteOp(path="/a/{x}") String getX13(@Path("x") @Schema(min="1",max="10") double b);
182 		@RemoteOp(path="/a/{x}") String getX14(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) double b);
183 		@RemoteOp(path="/a/{x}") String getX15(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) double b);
184 		@RemoteOp(path="/a/{x}") String getX16(@Path("x") @Schema(min="1",max="10") byte b);
185 		@RemoteOp(path="/a/{x}") String getX17(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) byte b);
186 		@RemoteOp(path="/a/{x}") String getX18(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) byte b);
187 		@RemoteOp(path="/a/{x}") String getX19(@Path("x") @Schema(min="1",max="10") AtomicInteger b);
188 		@RemoteOp(path="/a/{x}") String getX20(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) AtomicInteger b);
189 		@RemoteOp(path="/a/{x}") String getX21(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) AtomicInteger b);
190 		@RemoteOp(path="/a/{x}") String getX22(@Path("x") @Schema(min="1",max="10") BigDecimal b);
191 		@RemoteOp(path="/a/{x}") String getX23(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) BigDecimal b);
192 		@RemoteOp(path="/a/{x}") String getX24(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) BigDecimal b);
193 		@RemoteOp(path="/a/{x}") String getX25(@Path("x") @Schema(min="1",max="10") Integer b);
194 		@RemoteOp(path="/a/{x}") String getX26(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) Integer b);
195 		@RemoteOp(path="/a/{x}") String getX27(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) Integer b);
196 		@RemoteOp(path="/a/{x}") String getX28(@Path("x") @Schema(min="1",max="10") Short b);
197 		@RemoteOp(path="/a/{x}") String getX29(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) Short b);
198 		@RemoteOp(path="/a/{x}") String getX30(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) Short b);
199 		@RemoteOp(path="/a/{x}") String getX31(@Path("x") @Schema(min="1",max="10") Long b);
200 		@RemoteOp(path="/a/{x}") String getX32(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) Long b);
201 		@RemoteOp(path="/a/{x}") String getX33(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) Long b);
202 		@RemoteOp(path="/a/{x}") String getX34(@Path("x") @Schema(min="1",max="10") Float b);
203 		@RemoteOp(path="/a/{x}") String getX35(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) Float b);
204 		@RemoteOp(path="/a/{x}") String getX36(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) Float b);
205 		@RemoteOp(path="/a/{x}") String getX37(@Path("x") @Schema(min="1",max="10") Double b);
206 		@RemoteOp(path="/a/{x}") String getX38(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) Double b);
207 		@RemoteOp(path="/a/{x}") String getX39(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) Double b);
208 		@RemoteOp(path="/a/{x}") String getX40(@Path("x") @Schema(min="1",max="10") Byte b);
209 		@RemoteOp(path="/a/{x}") String getX41(@Path("x") @Schema(min="1",max="10",emin=false,emax=false) Byte b);
210 		@RemoteOp(path="/a/{x}") String getX42(@Path("x") @Schema(min="1",max="10",emin=true,emax=true) Byte b);
211 	}
212 
213 	@Test void c01_path_min_max_emin_emax() {
214 		var x = remote(C.class,C1.class);
215 		assertEquals("{x:'1'}",x.getX1(1));
216 		assertEquals("{x:'10'}",x.getX1(10));
217 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX1(0));
218 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX1(11));
219 		assertEquals("{x:'1'}",x.getX2(1));
220 		assertEquals("{x:'10'}",x.getX2(10));
221 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX2(0));
222 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX2(11));
223 		assertEquals("{x:'2'}",x.getX3(2));
224 		assertEquals("{x:'9'}",x.getX3(9));
225 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX3(1));
226 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX3(10));
227 		assertEquals("{x:'1'}",x.getX4((short)1));
228 		assertEquals("{x:'10'}",x.getX4((short)10));
229 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX4((short)0));
230 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX4((short)11));
231 		assertEquals("{x:'1'}",x.getX5((short)1));
232 		assertEquals("{x:'10'}",x.getX5((short)10));
233 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX5((short)0));
234 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX5((short)11));
235 		assertEquals("{x:'2'}",x.getX6((short)2));
236 		assertEquals("{x:'9'}",x.getX6((short)9));
237 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX6((short)1));
238 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX6((short)10));
239 		assertEquals("{x:'1'}",x.getX7(1L));
240 		assertEquals("{x:'10'}",x.getX7(10L));
241 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX7(0L));
242 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX7(11L));
243 		assertEquals("{x:'1'}",x.getX8(1L));
244 		assertEquals("{x:'10'}",x.getX8(10L));
245 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX8(0L));
246 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX8(11L));
247 		assertEquals("{x:'2'}",x.getX9(2L));
248 		assertEquals("{x:'9'}",x.getX9(9L));
249 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX9(1L));
250 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX9(10L));
251 		assertEquals("{x:'1.0'}",x.getX10(1f));
252 		assertEquals("{x:'10.0'}",x.getX10(10f));
253 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX10(0.9f));
254 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX10(10.1f));
255 		assertEquals("{x:'1.0'}",x.getX11(1f));
256 		assertEquals("{x:'10.0'}",x.getX11(10f));
257 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX11(0.9f));
258 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX11(10.1f));
259 		assertEquals("{x:'1.1'}",x.getX12(1.1f));
260 		assertEquals("{x:'9.9'}",x.getX12(9.9f));
261 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX12(1f));
262 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX12(10f));
263 		assertEquals("{x:'1.0'}",x.getX13(1d));
264 		assertEquals("{x:'10.0'}",x.getX13(10d));
265 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX13(0.9d));
266 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX13(10.1d));
267 		assertEquals("{x:'1.0'}",x.getX14(1d));
268 		assertEquals("{x:'10.0'}",x.getX14(10d));
269 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX14(0.9d));
270 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX14(10.1d));
271 		assertEquals("{x:'1.1'}",x.getX15(1.1d));
272 		assertEquals("{x:'9.9'}",x.getX15(9.9d));
273 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX15(1d));
274 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX15(10d));
275 		assertEquals("{x:'1'}",x.getX16((byte)1));
276 		assertEquals("{x:'10'}",x.getX16((byte)10));
277 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX16((byte)0));
278 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX16((byte)11));
279 		assertEquals("{x:'1'}",x.getX17((byte)1));
280 		assertEquals("{x:'10'}",x.getX17((byte)10));
281 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX17((byte)0));
282 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX17((byte)11));
283 		assertEquals("{x:'2'}",x.getX18((byte)2));
284 		assertEquals("{x:'9'}",x.getX18((byte)9));
285 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX18((byte)1));
286 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX18((byte)10));
287 		assertEquals("{x:'1'}",x.getX19(new AtomicInteger(1)));
288 		assertEquals("{x:'10'}",x.getX19(new AtomicInteger(10)));
289 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX19(new AtomicInteger(0)));
290 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX19(new AtomicInteger(11)));
291 		assertEquals("{x:'1'}",x.getX20(new AtomicInteger(1)));
292 		assertEquals("{x:'10'}",x.getX20(new AtomicInteger(10)));
293 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX20(new AtomicInteger(0)));
294 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX20(new AtomicInteger(11)));
295 		assertEquals("{x:'2'}",x.getX21(new AtomicInteger(2)));
296 		assertEquals("{x:'9'}",x.getX21(new AtomicInteger(9)));
297 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX21(new AtomicInteger(1)));
298 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX21(new AtomicInteger(10)));
299 		assertEquals("{x:'1'}",x.getX22(new BigDecimal(1)));
300 		assertEquals("{x:'10'}",x.getX22(new BigDecimal(10)));
301 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX22(new BigDecimal(0)));
302 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX22(new BigDecimal(11)));
303 		assertEquals("{x:'1'}",x.getX23(new BigDecimal(1)));
304 		assertEquals("{x:'10'}",x.getX23(new BigDecimal(10)));
305 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX23(new BigDecimal(0)));
306 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX23(new BigDecimal(11)));
307 		assertEquals("{x:'2'}",x.getX24(new BigDecimal(2)));
308 		assertEquals("{x:'9'}",x.getX24(new BigDecimal(9)));
309 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX24(new BigDecimal(1)));
310 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX24(new BigDecimal(10)));
311 		assertEquals("{x:'1'}",x.getX25(1));
312 		assertEquals("{x:'10'}",x.getX25(10));
313 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX25(0));
314 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX25(11));
315 		assertEquals("{x:'1'}",x.getX26(1));
316 		assertEquals("{x:'10'}",x.getX26(10));
317 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX26(0));
318 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX26(11));
319 		assertEquals("{x:'2'}",x.getX27(2));
320 		assertEquals("{x:'9'}",x.getX27(9));
321 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX27(1));
322 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX27(10));
323 		assertEquals("{x:'1'}",x.getX28((short)1));
324 		assertEquals("{x:'10'}",x.getX28((short)10));
325 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX28((short)0));
326 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX28((short)11));
327 		assertEquals("{x:'1'}",x.getX29((short)1));
328 		assertEquals("{x:'10'}",x.getX29((short)10));
329 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX29((short)0));
330 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX29((short)11));
331 		assertEquals("{x:'2'}",x.getX30((short)2));
332 		assertEquals("{x:'9'}",x.getX30((short)9));
333 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX30((short)1));
334 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX30((short)10));
335 		assertEquals("{x:'1'}",x.getX31(1L));
336 		assertEquals("{x:'10'}",x.getX31(10L));
337 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX31(0L));
338 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX31(11L));
339 		assertEquals("{x:'1'}",x.getX32(1L));
340 		assertEquals("{x:'10'}",x.getX32(10L));
341 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX32(0L));
342 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX32(11L));
343 		assertEquals("{x:'2'}",x.getX33(2L));
344 		assertEquals("{x:'9'}",x.getX33(9L));
345 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX33(1L));
346 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX33(10L));
347 		assertEquals("{x:'1.0'}",x.getX34(1f));
348 		assertEquals("{x:'10.0'}",x.getX34(10f));
349 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX34(0.9f));
350 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX34(10.1f));
351 		assertEquals("{x:'1.0'}",x.getX35(1f));
352 		assertEquals("{x:'10.0'}",x.getX35(10f));
353 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX35(0.9f));
354 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX35(10.1f));
355 		assertEquals("{x:'1.1'}",x.getX36(1.1f));
356 		assertEquals("{x:'9.9'}",x.getX36(9.9f));
357 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX36(1f));
358 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX36(10f));
359 		assertEquals("{x:'1.0'}",x.getX37(1d));
360 		assertEquals("{x:'10.0'}",x.getX37(10d));
361 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX37(0.9d));
362 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX37(10.1d));
363 		assertEquals("{x:'1.0'}",x.getX38(1d));
364 		assertEquals("{x:'10.0'}",x.getX38(10d));
365 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX38(0.9d));
366 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX38(10.1d));
367 		assertEquals("{x:'1.1'}",x.getX39(1.1d));
368 		assertEquals("{x:'9.9'}",x.getX39(9.9d));
369 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX39(1d));
370 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX39(10d));
371 		assertEquals("{x:'1'}",x.getX40((byte)1));
372 		assertEquals("{x:'10'}",x.getX40((byte)10));
373 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX40((byte)0));
374 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX40((byte)11));
375 		assertEquals("{x:'1'}",x.getX41((byte)1));
376 		assertEquals("{x:'10'}",x.getX41((byte)10));
377 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX41((byte)0));
378 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX41((byte)11));
379 		assertEquals("{x:'2'}",x.getX42((byte)2));
380 		assertEquals("{x:'9'}",x.getX42((byte)9));
381 		assertThrowsWithMessage(Exception.class, "Minimum value not met.", ()->x.getX42((byte)1));
382 		assertThrowsWithMessage(Exception.class, "Maximum value exceeded.", ()->x.getX42((byte)10));
383 	}
384 
385 	//-----------------------------------------------------------------------------------------------------------------
386 	// @Path(maxItems,minItems,uniqueItems)
387 	//-----------------------------------------------------------------------------------------------------------------
388 
389 	@Rest
390 	public static class D {
391 		@RestOp(path="/{x}")
392 		public String get(@Path("*") JsonMap m) {
393 			m.removeAll("/*","/**");
394 			return m.toString();
395 		}
396 	}
397 
398 	@Remote
399 	public interface D1 {
400 		@RemoteOp(path="/{x}") String getX1(@Path("x") @Schema(cf="pipes",mini=1,maxi=2) String...b);
401 		@RemoteOp(path="/{x}") String getX2(@Path("x") @Schema(items=@Items(cf="pipes",mini=1,maxi=2)) String[]...b);
402 		@RemoteOp(path="/{x}") String getX3(@Path("x") @Schema(cf="pipes",ui=false) String...b);
403 		@RemoteOp(path="/{x}") String getX4(@Path("x") @Schema(items=@Items(cf="pipes",ui=false)) String[]...b);
404 		@RemoteOp(path="/{x}") String getX5(@Path("x") @Schema(cf="pipes",ui=true) String...b);
405 		@RemoteOp(path="/{x}") String getX6(@Path("x") @Schema(items=@Items(cf="pipes",ui=true)) String[]...b);
406 	}
407 
408 	@Test void d01_path_miniMaxi() {
409 		var x = remote(D.class,D1.class);
410 		assertEquals("{x:'1'}",x.getX1("1"));
411 		assertEquals("{x:'1|2'}",x.getX1("1","2"));
412 		assertThrowsWithMessage(Exception.class, "Minimum number of items not met.", x::getX1);
413 		assertThrowsWithMessage(Exception.class, "Maximum number of items exceeded.", ()->x.getX1("1","2","3"));
414 		assertEquals("{x:'null'}",x.getX1((String)null));
415 		assertEquals("{x:'1'}",x.getX2(new String[]{"1"}));
416 		assertEquals("{x:'1|2'}",x.getX2(new String[]{"1","2"}));
417 		assertThrowsWithMessage(Exception.class, "Minimum number of items not met.", ()->x.getX2(new String[]{}));
418 		assertThrowsWithMessage(Exception.class, "Maximum number of items exceeded.", ()->x.getX2(new String[]{"1","2","3"}));
419 		assertEquals("{x:'null'}",x.getX2(new String[]{null}));
420 		assertEquals("{x:'1|1'}",x.getX3("1","1"));
421 		assertEquals("{x:'1|1'}",x.getX4(new String[]{"1","1"}));
422 		assertEquals("{x:'1|2'}",x.getX5("1","2"));
423 		assertThrowsWithMessage(Exception.class, "Duplicate items not allowed.", ()->x.getX5("1","1"));
424 		assertEquals("{x:'1|2'}",x.getX6(new String[]{"1","2"}));
425 		assertThrowsWithMessage(Exception.class, "Duplicate items not allowed.", ()->x.getX6(new String[]{"1","1"}));
426 	}
427 
428 	//-----------------------------------------------------------------------------------------------------------------
429 	// @Path(maxLength,minLength,enum)
430 	//-----------------------------------------------------------------------------------------------------------------
431 
432 	@Rest
433 	public static class E {
434 		@RestOp(path="/{x}")
435 		public String get(@Path("*") JsonMap m) {
436 			m.removeAll("/*","/**");
437 			return m.toString();
438 		}
439 	}
440 
441 	@Remote
442 	public interface E1 {
443 		@RemoteOp(path="/{x}") String getX1(@Path("x") @Schema(minl=2,maxl=3) String b);
444 		@RemoteOp(path="/{x}") String getX2(@Path("x") @Schema(cf="pipes",items=@Items(minl=2,maxl=3)) String...b);
445 		@RemoteOp(path="/{x}") String getX3(@Path("x") @Schema(e={"foo"}) String b);
446 		@RemoteOp(path="/{x}") String getX4(@Path("x") @Schema(cf="pipes",items=@Items(e={"foo"})) String...b);
447 		@RemoteOp(path="/{x}") String getX5(@Path("x") @Schema(p="foo\\d{1,3}") String b);
448 		@RemoteOp(path="/{x}") String getX6(@Path("x") @Schema(cf="pipes",items=@Items(p="foo\\d{1,3}")) String...b);
449 	}
450 
451 	@Test void e01_path_minLength_maxLength() {
452 		var x = remote(E.class,E1.class);
453 		assertEquals("{x:'12'}",x.getX1("12"));
454 		assertEquals("{x:'123'}",x.getX1("123"));
455 		assertThrowsWithMessage(Exception.class, "Minimum length of value not met.", ()->x.getX1("1"));
456 		assertThrowsWithMessage(Exception.class, "Maximum length of value exceeded.", ()->x.getX1("1234"));
457 		assertEquals("{x:'12|34'}",x.getX2("12","34"));
458 		assertEquals("{x:'123|456'}",x.getX2("123","456"));
459 		assertThrowsWithMessage(Exception.class, "Minimum length of value not met.", ()->x.getX2("1","2"));
460 		assertThrowsWithMessage(Exception.class, "Maximum length of value exceeded.", ()->x.getX2("1234","5678"));
461 		assertEquals("{x:'12|null'}",x.getX2("12",null));
462 		assertEquals("{x:'foo'}",x.getX3("foo"));
463 		assertThrowsWithMessage(Exception.class, "Value does not match one of the expected values.  Must be one of the following:  foo", ()->x.getX3("bar"));
464 		assertEquals("{x:'foo'}",x.getX4("foo"));
465 		assertThrowsWithMessage(Exception.class, "Value does not match one of the expected values.  Must be one of the following:  foo", ()->x.getX4("bar"));
466 		assertEquals("{x:'null'}",x.getX4((String)null));
467 		assertEquals("{x:'foo123'}",x.getX5("foo123"));
468 		assertThrowsWithMessage(Exception.class, "Value does not match expected pattern.  Must match pattern: foo\\d{1,3}", ()->x.getX5("bar"));
469 		assertEquals("{x:'foo123'}",x.getX6("foo123"));
470 		assertThrowsWithMessage(Exception.class, "Value does not match expected pattern.  Must match pattern: foo\\d{1,3}", ()->x.getX6("foo"));
471 		assertEquals("{x:'null'}",x.getX6((String)null));
472 	}
473 
474 	//-----------------------------------------------------------------------------------------------------------------
475 	// @Path(multipleOf)
476 	//-----------------------------------------------------------------------------------------------------------------
477 
478 	@Rest
479 	public static class F {
480 		@RestOp(path="/{x}")
481 		public String get(@Path("*") JsonMap m) {
482 			m.removeAll("/*","/**");
483 			return m.toString();
484 		}
485 	}
486 
487 	@Remote
488 	public interface F1 {
489 		@RemoteOp(path="/{x}") String getX1(@Path("x") @Schema(mo="2") int b);
490 		@RemoteOp(path="/{x}") String getX2(@Path("x") @Schema(mo="2") short b);
491 		@RemoteOp(path="/{x}") String getX3(@Path("x") @Schema(mo="2") long b);
492 		@RemoteOp(path="/{x}") String getX4(@Path("x") @Schema(mo="2") float b);
493 		@RemoteOp(path="/{x}") String getX5(@Path("x") @Schema(mo="2") double b);
494 		@RemoteOp(path="/{x}") String getX6(@Path("x") @Schema(mo="2") byte b);
495 		@RemoteOp(path="/{x}") String getX7(@Path("x") @Schema(mo="2") AtomicInteger b);
496 		@RemoteOp(path="/{x}") String getX8(@Path("x") @Schema(mo="2") BigDecimal b);
497 		@RemoteOp(path="/{x}") String getX9(@Path("x") @Schema(mo="2") Integer b);
498 		@RemoteOp(path="/{x}") String getX10(@Path("x") @Schema(mo="2") Short b);
499 		@RemoteOp(path="/{x}") String getX11(@Path("x") @Schema(mo="2") Long b);
500 		@RemoteOp(path="/{x}") String getX12(@Path("x") @Schema(mo="2") Float b);
501 		@RemoteOp(path="/{x}") String getX13(@Path("x") @Schema(mo="2") Double b);
502 		@RemoteOp(path="/{x}") String getX14(@Path("x") @Schema(mo="2") Byte b);
503 	}
504 
505 	@Test void f01_path_multipleOf() {
506 		var x = remote(F.class,F1.class);
507 		assertEquals("{x:'4'}",x.getX1(4));
508 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX1(5));
509 		assertEquals("{x:'4'}",x.getX2((short)4));
510 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX2((short)5));
511 		assertEquals("{x:'4'}",x.getX3(4));
512 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX3(5));
513 		assertEquals("{x:'4.0'}",x.getX4(4));
514 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX4(5));
515 		assertEquals("{x:'4.0'}",x.getX5(4));
516 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX5(5));
517 		assertEquals("{x:'4'}",x.getX6((byte)4));
518 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX6((byte)5));
519 		assertEquals("{x:'4'}",x.getX7(new AtomicInteger(4)));
520 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX7(new AtomicInteger(5)));
521 		assertEquals("{x:'4'}",x.getX8(new BigDecimal(4)));
522 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX8(new BigDecimal(5)));
523 		assertEquals("{x:'4'}",x.getX9(4));
524 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX9(5));
525 		assertEquals("{x:'4'}",x.getX10((short)4));
526 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX10((short)5));
527 		assertEquals("{x:'4'}",x.getX11(4L));
528 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX11(5L));
529 		assertEquals("{x:'4.0'}",x.getX12(4f));
530 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX12(5f));
531 		assertEquals("{x:'4.0'}",x.getX13(4d));
532 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX13(5d));
533 		assertEquals("{x:'4'}",x.getX14((byte)4));
534 		assertThrowsWithMessage(Exception.class, "Multiple-of not met.", ()->x.getX14((byte)5));
535 	}
536 
537 	//-----------------------------------------------------------------------------------------------------------------
538 	// @Path(required)
539 	//-----------------------------------------------------------------------------------------------------------------
540 
541 	@Rest
542 	public static class G {
543 	}
544 
545 	@Remote
546 	public interface G1 {
547 		@RemoteOp(path="/{x}") String getX1(@Path("x") String b);
548 	}
549 
550 	@Test void h01_path_required() {
551 		var x = remote(G.class,G1.class);
552 		assertThrowsWithMessage(Exception.class, "Required value not provided.", ()->x.getX1(null));
553 	}
554 
555 	//-----------------------------------------------------------------------------------------------------------------
556 	// @Path(serializer)
557 	//-----------------------------------------------------------------------------------------------------------------
558 
559 	@Rest
560 	public static class H {
561 		@RestOp(path="/{x}")
562 		public String get(@Path("*") JsonMap m) {
563 			m.removeAll("/*","/**");
564 			return m.toString();
565 		}
566 	}
567 
568 	@Remote
569 	public interface H1 {
570 		@RemoteOp(path="/{x}") String getX1(@Path(name="x",serializer=FakeWriterSerializer.X.class) String b);
571 	}
572 
573 	@Test void h01_path_serializer() {
574 		var x = remote(H.class,H1.class);
575 		assertEquals("{x:'xXx'}",x.getX1("X"));
576 	}
577 
578 	//-----------------------------------------------------------------------------------------------------------------
579 	// RequstBean @Path
580 	//-----------------------------------------------------------------------------------------------------------------
581 
582 	@Rest
583 	public static class K  {
584 		@RestOp(path="/*")
585 		public String get(RestRequest req) {
586 			return req.getPathParams().getRemainder().orElse(null);
587 		}
588 	}
589 
590 	//-----------------------------------------------------------------------------------------------------------------
591 	// RequstBean @Path, Simple values
592 	//-----------------------------------------------------------------------------------------------------------------
593 
594 	@Remote(path="/")
595 	public interface K1 {
596 		@RemoteOp(path="/{a}/{b}/{c}/{e}/{g}/{h}") String getX1(@Request K1a rb);
597 		@RemoteOp(path="/{a}/{b}/{c}/{e}/{g}/{h}") String getX2(@Request(serializer=FakeWriterSerializer.X.class) K1a rb);
598 	}
599 
600 	public static class K1a {
601 		@Path public String getA() { return "a1"; }
602 		@Path("b") public String getX1() { return "b1"; }
603 		@Path("c") public String getX2() { return "c1"; }
604 		@Path("e") @Schema(aev=true) public String getX4() { return ""; }
605 		@Path("g") public String getX6() { return "true"; }
606 		@Path("h") public String getX7() { return "123"; }
607 	}
608 
609 	@Test void k01_requestBean_simpleVals() {
610 		var x1 = remote(K.class,K1.class);
611 		var x2 = client(K.class).partSerializer(UonSerializer.class).build().getRemote(K1.class);
612 		assertEquals("a1/b1/c1//true/123",x1.getX1(new K1a()));
613 		assertEquals("a1/b1/c1//'true'/'123'",x2.getX1(new K1a()));
614 		assertEquals("xa1x/xb1x/xc1x/xx/xtruex/x123x",x2.getX2(new K1a()));
615 	}
616 
617 	//-----------------------------------------------------------------------------------------------------------------
618 	// RequstBean @Path, Maps
619 	//-----------------------------------------------------------------------------------------------------------------
620 
621 	@Remote(path="/")
622 	public interface K2 {
623 		@RemoteOp(path="/{a1}/{a2}/{a3}/{a4}/{b1}/{b2}/{b3}/{c1}/{c2}/{c3}/{c4}") String getX1(@Request K2a rb);
624 		@RemoteOp(path="/{a1}/{a2}/{a3}/{a4}/{b1}/{b2}/{b3}/{c1}/{c2}/{c3}/{c4}") String getX2(@Request(serializer=FakeWriterSerializer.X.class) K2a rb);
625 	}
626 
627 	public static class K2a {
628 		@Path("*") @Schema(aev=true) public Map<String,Object> getA() { return CollectionUtils.mapBuilder(String.class,Object.class).add("a1","v1").add("a2",123).add("a3",null).add("a4","").build(); }
629 		@Path("*") public Map<String,Object> getB() { return map("b1","true","b2","123","b3","null"); }
630 		@Path("*") @Schema(aev=true) public Map<String,Object> getC() { return CollectionUtils.mapBuilder(String.class,Object.class).add("c1","v1").add("c2",123).add("c3",null).add("c4","").build(); }
631 		@Path("*")
632 		public Map<String,Object> getD() { return null; }
633 	}
634 
635 	@Test void k02_reauestBean_maps() {
636 		var x1 = remote(K.class,K2.class);
637 		var x2 = client(K.class).partSerializer(UonSerializer.class).build().getRemote(K2.class);
638 		assertEquals("v1/123/null//true/123/null/v1/123/null/",x1.getX1(new K2a()));
639 		assertEquals("v1/123/null//'true'/'123'/'null'/v1/123/null/",x2.getX1(new K2a()));
640 		assertEquals("xv1x/x123x/null/xx/xtruex/x123x/xnullx/xv1x/x123x/null/xx",x2.getX2(new K2a()));
641 	}
642 
643 	//-----------------------------------------------------------------------------------------------------------------
644 	// RequstBean @Path, NameValuePairs
645 	//-----------------------------------------------------------------------------------------------------------------
646 
647 	@Remote(path="/")
648 	public interface K3 {
649 		@RemoteOp(path="/{a1}/{a2}/{a3}/{a4}/{b1}/{b2}/{b3}/{c1}/{c2}/{c3}/{c4}/{e1}/{e2}/{e3}/{e4}") String getX1(@Request K3a rb);
650 		@RemoteOp(path="/{a1}/{a2}/{a3}/{a4}/{b1}/{b2}/{b3}/{c1}/{c2}/{c3}/{c4}/{e1}/{e2}/{e3}/{e4}") String getX2(@Request(serializer=FakeWriterSerializer.X.class) K3a rb);
651 	}
652 
653 	public static class K3a {
654 		@Path("*") @Schema(aev=true) public PartList getA() { return parts("a1","v1","a2","123","a3",null,"a4",""); }
655 		@Path("/*") public PartList getB() { return parts("b1","true","b2","123","b3","null"); }
656 		@Path("*") @Schema(aev=true) public PartList getC() { return parts("c1","v1","c2","123","c3",null,"c4",""); }
657 		@Path("/*")
658 		public PartList getD() { return null; }
659 		@Path @Schema(aev=true) public NameValuePair[] getE() { return parts("e1","v1","e2","123","e3",null,"e4","").getAll(); }
660 	}
661 
662 	@Test void k03_requestBean_nameValuePairs() {
663 		var x1 = remote(K.class,K3.class);
664 		var x2 = client(K.class).partSerializer(UonSerializer.class).build().getRemote(K3.class);
665 		assertEquals("v1/123/null//true/123/null/v1/123/null//v1/123/null/",x1.getX1(new K3a()));
666 		assertEquals("v1/123/null//true/123/null/v1/123/null//v1/123/null/",x2.getX1(new K3a()));
667 		assertEquals("v1/123/null//true/123/null/v1/123/null//v1/123/null/",x2.getX2(new K3a()));
668 	}
669 
670 	//-----------------------------------------------------------------------------------------------------------------
671 	// RequstBean @Path, Collections
672 	//-----------------------------------------------------------------------------------------------------------------
673 
674 	@Remote(path="/")
675 	public interface K4 {
676 		@RemoteOp(path="/{a}/{b}/{c}/{d}/{f}/{g}/{h}") String getX1(@Request K4a rb);
677 		@RemoteOp(path="/{a}/{b}/{c}/{d}/{f}/{g}/{h}") String getX2(@Request(serializer=FakeWriterSerializer.X.class) K4a rb);
678 	}
679 
680 	public static class K4a {
681 		@Path public List<Object> getA() { return alist("foo","","true","123","null",true,123,null); }
682 		@Path("b") public List<Object> getX1() { return alist("foo","","true","123","null",true,123,null); }
683 		@Path(name="c",serializer=FakeWriterSerializer.X.class) public List<Object> getX2() { return alist("foo","","true","123","null",true,123,null); }
684 		@Path("d") @Schema(aev=true) public List<Object> getX3() { return alist(); }
685 		@Path("f") public Object[] getX5() { return new Object[]{"foo","","true","123","null",true,123,null}; }
686 		@Path(name="g",serializer=FakeWriterSerializer.X.class) public Object[] getX6() { return new Object[]{"foo","","true","123","null",true,123,null}; }
687 		@Path("h") @Schema(aev=true)
688 		public Object[] getX7() {
689 			return new Object[]{};
690 		}
691 	}
692 
693 	@Test void k04_requestBean_collections() {
694 		var x1 = remote(K.class,K4.class);
695 		var x2 = client(K.class).partSerializer(UonSerializer.class).build().getRemote(K4.class);
696 		assertEquals("foo,,true,123,null,true,123,null/foo,,true,123,null,true,123,null/xfoo||true|123|null|true|123|nullx//foo,,true,123,null,true,123,null/xfoo||true|123|null|true|123|nullx/", x1.getX1(new K4a()));
697 		assertEquals("@(foo,'','true','123','null',true,123,null)/@(foo,'','true','123','null',true,123,null)/xfoo||true|123|null|true|123|nullx/@()/@(foo,'','true','123','null',true,123,null)/xfoo||true|123|null|true|123|nullx/@()", x2.getX1(new K4a()));
698 		assertEquals("xfoo||true|123|null|true|123|nullx/xfoo||true|123|null|true|123|nullx/xfoo||true|123|null|true|123|nullx/xx/xfoo||true|123|null|true|123|nullx/xfoo||true|123|null|true|123|nullx/xx", x2.getX2(new K4a()));
699 	}
700 
701 	//------------------------------------------------------------------------------------------------------------------
702 	// Helper methods.
703 	//------------------------------------------------------------------------------------------------------------------
704 
705 	private static PartList parts(String...pairs) {
706 		return partList(pairs);
707 	}
708 
709 	private static NameValuePair part(String key, Object val) {
710 		return basicPart(key, val);
711 	}
712 
713 	private static RestClient.Builder client(Class<?> c) {
714 		return MockRestClient.create(c).defaultRequestConfig(RequestConfig.custom().setNormalizeUri(false).build());
715 	}
716 
717 	private static <T> T remote(Class<?> rest, Class<T> t) {
718 		return MockRestClient.create(rest).defaultRequestConfig(RequestConfig.custom().setNormalizeUri(false).build()).build().getRemote(t);
719 	}
720 }