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.HttpHeaders.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import java.util.concurrent.*;
24  
25  import org.apache.juneau.*;
26  import org.apache.juneau.http.*;
27  import org.apache.juneau.http.annotation.*;
28  import org.apache.juneau.http.header.*;
29  import org.apache.juneau.marshaller.*;
30  import org.apache.juneau.rest.annotation.*;
31  import org.apache.juneau.rest.client.*;
32  import org.apache.juneau.rest.client.remote.*;
33  import org.apache.juneau.rest.config.*;
34  import org.apache.juneau.rest.httppart.*;
35  import org.apache.juneau.rest.mock.*;
36  import org.apache.juneau.rest.servlet.*;
37  import org.junit.jupiter.api.*;
38  
39  class Remote_Test extends TestBase {
40  
41  	//-----------------------------------------------------------------------------------------------------------------
42  	// @Remote(path), relative paths
43  	//-----------------------------------------------------------------------------------------------------------------
44  
45  	@Rest
46  	public static class A {
47  		@RestGet
48  		public String x1() {
49  			return "foo";
50  		}
51  		@RestGet(path="/A/x2")
52  		public String x2() {
53  			return "foo";
54  		}
55  		@RestGet(path="/A/A/x3")
56  		public String x3() {
57  			return "foo";
58  		}
59  	}
60  
61  	@Remote
62  	public interface A1 {
63  		String x1();
64  		@RemoteOp(path="x1") String x1a();
65  		@RemoteOp(path="/x1/") String x1b();
66  	}
67  
68  	@Test void a01_noPath() {
69  		var x = plainRemote(A.class,A1.class);
70  		assertEquals("foo",x.x1());
71  		assertEquals("foo",x.x1a());
72  		assertEquals("foo",x.x1b());
73  	}
74  
75  	@Remote(path="A")
76  	public interface A2 {
77  		String x2();
78  		@RemoteOp(path="x2") String x2a();
79  		@RemoteOp(path="/x2/") String x2b();
80  	}
81  
82  	@Test void a02_normalPath() {
83  		var x = plainRemote(A.class,A2.class);
84  		assertEquals("foo",x.x2());
85  		assertEquals("foo",x.x2a());
86  		assertEquals("foo",x.x2b());
87  	}
88  
89  	@Remote(path="/A/")
90  	public interface A3 {
91  		String x2();
92  		@RemoteOp(path="x2") String x2a();
93  		@RemoteOp(path="/x2/") String x2b();
94  	}
95  
96  	@Test void a03_normalPathWithSlashes() {
97  		var x = plainRemote(A.class,A3.class);
98  		assertEquals("foo",x.x2());
99  		assertEquals("foo",x.x2a());
100 		assertEquals("foo",x.x2b());
101 	}
102 
103 	@Remote
104 	public interface A4 {
105 		String x2();
106 		@RemoteOp(path="x2") String x2a();
107 		@RemoteOp(path="/x2/") String x2b();
108 	}
109 
110 	@Test void a04_pathOnClient() {
111 		var x = plainRemote(A.class,A4.class,"http://localhost/A");
112 		assertEquals("foo",x.x2());
113 		assertEquals("foo",x.x2a());
114 		assertEquals("foo",x.x2b());
115 	}
116 
117 	@Remote(path="A/A")
118 	public interface A5 {
119 		String x3();
120 		@RemoteOp(path="x3") String x3a();
121 		@RemoteOp(path="/x3/") String x3b();
122 	}
123 
124 	@Test void a05_normalPath() {
125 		var x = plainRemote(A.class,A5.class);
126 		assertEquals("foo",x.x3());
127 		assertEquals("foo",x.x3a());
128 		assertEquals("foo",x.x3b());
129 	}
130 
131 	@Remote(path="/A/A/")
132 	public interface A6 {
133 		String x3();
134 		@RemoteOp(path="x3") String x3a();
135 		@RemoteOp(path="/x3/") String x3b();
136 	}
137 
138 	@Test void a06_normalPathWithSlashes() {
139 		var x = plainRemote(A.class,A6.class);
140 		assertEquals("foo",x.x3());
141 		assertEquals("foo",x.x3a());
142 		assertEquals("foo",x.x3b());
143 	}
144 
145 	@Remote(path="A")
146 	public interface A7 {
147 		String x3();
148 		@RemoteOp(path="x3") String x3a();
149 		@RemoteOp(path="/x3/") String x3b();
150 	}
151 
152 	@Test void a07_partialPath() {
153 		var x = plainRemote(A.class,A7.class,"http://localhost/A");
154 		assertEquals("foo",x.x3());
155 		assertEquals("foo",x.x3a());
156 		assertEquals("foo",x.x3b());
157 	}
158 
159 	@Remote(path="/A/")
160 	public interface A8 {
161 		String x3();
162 		@RemoteOp(path="x3") String x3a();
163 		@RemoteOp(path="/x3/") String x3b();
164 	}
165 
166 	@Test void a08_partialPathExtraSlashes() {
167 		var x = plainRemote(A.class,A8.class,"http://localhost/A/");
168 		assertEquals("foo",x.x3());
169 		assertEquals("foo",x.x3a());
170 		assertEquals("foo",x.x3b());
171 	}
172 
173 	//-----------------------------------------------------------------------------------------------------------------
174 	// @RemoteResource(path), absolute paths
175 	//-----------------------------------------------------------------------------------------------------------------
176 
177 	@Rest
178 	public static class B {
179 		@RestGet(path="B/x1")
180 		public String x1() {
181 			return "foo";
182 		}
183 	}
184 
185 	@Remote
186 	public interface B1 {
187 		String x1();
188 		@RemoteOp(path="x1") String x1a();
189 		@RemoteOp(path="/x1/") String x1b();
190 	}
191 
192 	@Test void b01_noPath() {
193 		var x = plainRemote(B.class,B1.class,"http://localhost/B");
194 		assertEquals("foo",x.x1());
195 		assertEquals("foo",x.x1a());
196 		assertEquals("foo",x.x1b());
197 	}
198 
199 	@Remote(path="http://localhost/B")
200 	public interface B2 {
201 		String x1();
202 		@RemoteOp(path="x1") String x1a();
203 		@RemoteOp(path="/x1/") String x1b();
204 	}
205 
206 	@Test void b02_absolutePathOnClass() {
207 		var x = plainRemote(B.class,B2.class,"http://localhost/B");
208 		assertEquals("foo",x.x1());
209 		assertEquals("foo",x.x1a());
210 		assertEquals("foo",x.x1b());
211 	}
212 
213 	@Remote
214 	public interface B3 {
215 		String x1();
216 		@RemoteOp(path="http://localhost/B/x1") String x1a();
217 		@RemoteOp(path="http://localhost/B/x1/") String x1b();
218 	}
219 
220 	@Test void b03_absolutePathsOnMethods() {
221 		var x = plainRemote(B.class,B3.class,"http://localhost/B");
222 		assertEquals("foo",x.x1());
223 		assertEquals("foo",x.x1a());
224 		assertEquals("foo",x.x1b());
225 	}
226 
227 	//-----------------------------------------------------------------------------------------------------------------
228 	// Other tests
229 	//-----------------------------------------------------------------------------------------------------------------
230 
231 	@Rest(path="/C1")
232 	public static class C implements BasicJson5Config {
233 		@RestOp
234 		public String x1() {
235 			return "foo";
236 		}
237 		@RestOp("GET")
238 		public String x2() {
239 			return "bar";
240 		}
241 		@RestOp("GET /x3")
242 		public String x3x() {
243 			return "baz";
244 		}
245 		@RestGet
246 		public String x4() {
247 			return "qux";
248 		}
249 		@RestGet("/x5")
250 		public String x5x() {
251 			return "quux";
252 		}
253 	}
254 
255 	@Remote(path="/")
256 	public interface C1 {
257 		String x1();
258 		@RemoteOp("GET") String x2();
259 		@RemoteOp("GET /x3") String x3x();
260 		@RemoteOp("GET /x4") String x4();
261 		@RemoteOp("GET /x5") String x5x();
262 	}
263 
264 	@Test void c01_overriddenRootUrl() {
265 		var x = client(C.class).build().getRemote(C1.class,"http://localhost/C1");
266 		assertEquals("foo",x.x1());
267 		assertEquals("bar",x.x2());
268 		assertEquals("baz",x.x3x());
269 		assertEquals("qux",x.x4());
270 		assertEquals("quux",x.x5x());
271 	}
272 
273 	@Rest(path="/C3")
274 	public static class C3a implements BasicJson5Config {
275 		@RestOp
276 		public String x1() {
277 			return "bar";
278 		}
279 		@RestOp
280 		public String getX2() {
281 			return "baz";
282 		}
283 		@RestGet
284 		public String x3() {
285 			return "baz";
286 		}
287 		@RestGet
288 		public String getX4() {
289 			return "qux";
290 		}
291 	}
292 
293 	@Remote(path="/")
294 	public interface C3b {
295 		String x1();
296 		String getX2();
297 		String x3();
298 		String getX4();
299 	}
300 
301 	@Test void c03_methodNotAnnotated() {
302 		var x = remote(C3a.class,C3b.class);
303 		assertEquals("bar",x.x1());
304 		assertEquals("baz",x.getX2());
305 		assertEquals("baz",x.x3());
306 		assertEquals("qux",x.getX4());
307 	}
308 
309 	@Rest(path="/C4")
310 	@SuppressWarnings("unused")
311 	public static class C4a implements BasicJson5Config {
312 		@RestOp
313 		public String x1() throws C4c {
314 			throw new C4c("foo");
315 		}
316 		@RestOp
317 		public String x2() throws C4c {
318 			throw new RuntimeException("foo");
319 		}
320 		@RestOp
321 		public String x3() {
322 			throw new AssertionError("foo");
323 		}
324 		@RestGet
325 		public String x4() throws C4c {
326 			throw new C4c("foo");
327 		}
328 		@RestGet
329 		public String x5() throws C4c {
330 			throw new RuntimeException("foo");
331 		}
332 		@RestGet
333 		public String x6() {
334 			throw new AssertionError("foo");
335 		}
336 	}
337 
338 	@Remote
339 	public interface C4b {
340 		String x1() throws C4c;
341 		@RemoteOp(path="x1") Future<String> x1a() throws C4c;
342 		@RemoteOp(path="x1") CompletableFuture<String> x1b() throws C4c;
343 		String x2() throws C4c;
344 		Future<String> x3() throws AssertionError;
345 		String x4() throws C4c;
346 		@RemoteOp(path="x1") Future<String> x4a() throws C4c;
347 		@RemoteOp(path="x1") CompletableFuture<String> x4b() throws C4c;
348 		String x5() throws C4c;
349 		Future<String> x6() throws AssertionError;
350 	}
351 
352 	@SuppressWarnings("serial")
353 	public static class C4c extends Exception {
354 		public C4c(String msg) {
355 			super(msg);
356 		}
357 	}
358 
359 	@Test void c04_rethrownExceptions() {
360 		var x = remote(C4a.class,C4b.class);
361 		assertThrowsWithMessage(Remote_Test.C4c.class, "foo", x::x1);
362 		assertThrowsWithMessage(Exception.class, "foo", ()->x.x1a().get());
363 		assertThrowsWithMessage(Exception.class, "foo", ()->x.x1b().get());
364 		assertThrowsWithMessage(RuntimeException.class, "foo", x::x2);
365 		assertThrowsWithMessage(Exception.class, "foo", ()->x.x3().get());
366 		assertThrowsWithMessage(Remote_Test.C4c.class, "foo", x::x4);
367 		assertThrowsWithMessage(Exception.class, "foo", ()->x.x4a().get());
368 		assertThrowsWithMessage(Exception.class, "foo", ()->x.x4b().get());
369 		assertThrowsWithMessage(RuntimeException.class, "foo", x::x5);
370 		assertThrowsWithMessage(Exception.class, "foo", ()->x.x6().get());
371 	}
372 
373 	//-----------------------------------------------------------------------------------------------------------------
374 	// Status return type
375 	//-----------------------------------------------------------------------------------------------------------------
376 
377 	@Rest
378 	public static class D1 implements BasicJson5Config {
379 		@RestGet
380 		public void r202(org.apache.juneau.rest.RestResponse res) {
381 			res.setStatus(202);
382 		}
383 		@RestGet
384 		public void r400(org.apache.juneau.rest.RestResponse res) {
385 			res.setStatus(400);
386 		}
387 	}
388 
389 	@Remote
390 	public interface D1a {
391 		@RemoteOp(path="/r202",returns=RemoteReturn.STATUS) int x1() throws AssertionError;
392 		@RemoteOp(path="/r202",returns=RemoteReturn.STATUS) Integer x2() throws AssertionError;
393 		@RemoteOp(path="/r202",returns=RemoteReturn.STATUS) boolean x3() throws AssertionError;
394 		@RemoteOp(path="/r202",returns=RemoteReturn.STATUS) Boolean x4() throws AssertionError;
395 		@RemoteOp(path="/r202",returns=RemoteReturn.STATUS) String x5() throws AssertionError;
396 		@RemoteOp(path="/r400",returns=RemoteReturn.STATUS) int x6() throws AssertionError;
397 		@RemoteOp(path="/r400",returns=RemoteReturn.STATUS) Integer x7() throws AssertionError;
398 		@RemoteOp(path="/r400",returns=RemoteReturn.STATUS) boolean x8() throws AssertionError;
399 		@RemoteOp(path="/r400",returns=RemoteReturn.STATUS) Boolean x9() throws AssertionError;
400 		@RemoteOp(path="/r400",returns=RemoteReturn.STATUS) String x10() throws AssertionError;
401 	}
402 
403 	@Test void d01_statusReturnType() {
404 		var x = client(D1.class).ignoreErrors().build().getRemote(D1a.class);
405 		assertEquals(202,x.x1());
406 		assertEquals(202,x.x2().intValue());
407 		assertEquals(true,x.x3());
408 		assertEquals(true,x.x4());
409 		assertEquals(400,x.x6());
410 		assertEquals(400,x.x7().intValue());
411 		assertEquals(false,x.x8());
412 		assertEquals(false,x.x9());
413 		assertThrowsWithMessage(Exception.class, "Only integer and booleans types are valid.", x::x5);
414 		assertThrowsWithMessage(Exception.class, "Only integer and booleans types are valid.", x::x10);
415 	}
416 
417 	@Rest
418 	public static class D2 implements BasicJson5Config {
419 		@RestGet
420 		public Integer x1() {
421 			return null;
422 		}
423 		@RestGet
424 		public Integer x2() {
425 			return 1;
426 		}
427 	}
428 
429 	@Remote
430 	public interface D2a {
431 		int x1() throws AssertionError;
432 		int x2() throws AssertionError;
433 		@RemoteOp(path="x1") Integer x1a() throws AssertionError;
434 	}
435 
436 	@Test void d02_primitiveReturns() {
437 		var x = client(D2.class).ignoreErrors().build().getRemote(D2a.class);
438 		assertEquals(0,x.x1());
439 		assertEquals(1,x.x2());
440 		assertNull(x.x1a());
441 	}
442 
443 	//-----------------------------------------------------------------------------------------------------------------
444 	// RRPC interfaces
445 	//-----------------------------------------------------------------------------------------------------------------
446 
447 	@Rest
448 	public static class E implements BasicJson5Config {
449 		@RestOp(method=HttpMethod.RRPC)
450 		public E1 proxy() {
451 			return body -> body;
452 		}
453 	}
454 
455 	public interface E1 {
456 		String echo(String body);
457 	}
458 
459 	@Test void e01_rrpcBasic() {
460 		var x = client(E.class).rootUrl("http://localhost/proxy").build().getRrpcInterface(E1.class);
461 
462 		assertEquals("foo",x.echo("foo"));
463 	}
464 
465 	@Remote(path="/proxy")
466 	public interface E3 {
467 		String echo(String body);
468 	}
469 
470 	@Test void e03_rrpc_noRestUrl() {
471 		var x = client(E.class).rootUrl("http://localhost").build().getRrpcInterface(E3.class);
472 		assertEquals("foo",x.echo("foo"));
473 	}
474 
475 	@Remote(path="http://localhost/proxy")
476 	public interface E4 {
477 		String echo(String body);
478 	}
479 
480 	@Test void e04_rrpc_fullPathOnRemotePath() {
481 		var x = client(E.class).rootUrl("").build().getRrpcInterface(E4.class);
482 		assertEquals("foo",x.echo("foo"));
483 	}
484 
485 	@Rest
486 	public static class E5 implements BasicJson5Config {
487 		@RestOp(method=HttpMethod.RRPC)
488 		public E5b proxy() {
489 			return body -> {
490 				throw new E5a("foobar");
491 			};
492 		}
493 	}
494 
495 	@SuppressWarnings("serial")
496 	public static class E5a extends Exception {
497 		public E5a(String msg) {
498 			super(msg);
499 		}
500 	}
501 
502 	public interface E5b {
503 		String echo(String body) throws E5a;
504 	}
505 
506 	@Test void e05_rrpc_rethrownCheckedException() {
507 		var x = client(E5.class).build();
508 		assertThrowsWithMessage(Remote_Test.E5a.class, "foobar", ()->x.getRrpcInterface(E5b.class,"/proxy").echo("foo"));
509 	}
510 
511 	@Rest
512 	public static class E6 implements BasicJson5Config {
513 		@RestOp(method=HttpMethod.RRPC)
514 		public E5b proxy() {
515 			return body -> {
516 				throw new AssertionError("foobar");
517 			};
518 		}
519 	}
520 
521 	@Test void e06_rrpc_rethrownUncheckedException() {
522 		var x = client(E6.class).build();
523 		assertThrowsWithMessage(Exception.class, "foobar", ()->x.getRrpcInterface(E5b.class,"/proxy").echo("foo"));
524 	}
525 
526 	//-----------------------------------------------------------------------------------------------------------------
527 	// @Remote headers
528 	//-----------------------------------------------------------------------------------------------------------------
529 
530 	@Rest
531 	public static class F extends BasicRestObject {
532 		@RestGet
533 		public String[] headers(org.apache.juneau.rest.RestRequest req) {
534 			return req.getHeaders().getAll(req.getHeaderParam("Check").orElse(null)).stream().map(RequestHeader::getValue).toArray(String[]::new);
535 		}
536 	}
537 
538 	@Remote(headers="Foo:bar",headerList=F1b.class,version="1.2.3")
539 	public interface F1a {
540 		String[] getHeaders();
541 	}
542 
543 	@SuppressWarnings("serial")
544 	public static class F1b extends HeaderList {
545 		public F1b() {
546 			super(
547 				create()
548 				.append(basicHeader("Foo","baz"))
549 				.append(HeaderList.create().append(basicHeader("Foo",()->"qux")).getAll())
550 			);
551 		}
552 	}
553 
554 	@Test void f01_headers() throws Exception {
555 		var x = client(F.class).header("Check","Foo").build().getRemote(F1a.class);
556 		assertEquals("['bar','baz','qux']",Json5.of(x.getHeaders()));
557 		x = client(F.class).header("Check","Client-Version").build().getRemote(F1a.class);
558 		assertEquals("['1.2.3']",Json5.of(x.getHeaders()));
559 	}
560 
561 	@Remote(headerList=F2b.class)
562 	public interface F2a {
563 		String[] getHeaders();
564 	}
565 
566 	@SuppressWarnings("serial")
567 	public static class F2b extends HeaderList {
568 		public F2b() {
569 			throw new NullPointerException("foo");
570 		}
571 	}
572 
573 	@Test void f02_headers_badSupplier() {
574 		assertThrowsWithMessage(Exception.class, "foo", ()->client(F.class).build().getRemote(F2a.class));
575 	}
576 
577 	//-----------------------------------------------------------------------------------------------------------------
578 	// Other
579 	//-----------------------------------------------------------------------------------------------------------------
580 
581 	@Rest
582 	public static class G extends BasicRestObject {}
583 
584 	@Remote
585 	public interface G1 {
586 		@RemoteOp(method="FOO")
587 		String[] getHeaders();
588 	}
589 
590 	@Test void g01_badMethodName() {
591 		assertThrowsWithMessage(RemoteMetadataException.class, "Invalid value", ()->client(G.class).header("Check","Foo").build().getRemote(G1.class));
592 	}
593 
594 	//-----------------------------------------------------------------------------------------------------------------
595 	// Method detection
596 	//-----------------------------------------------------------------------------------------------------------------
597 
598 	@Rest
599 	public static class H extends BasicRestObject {
600 		@RestOp(method="*", path="/*")
601 		public String echoMethod(@Method String method, @Path("/*") String path) {
602 			return method + " " + path;
603 		}
604 	}
605 
606 	@Remote
607 	public interface H1 {
608 		@RemoteOp(method="get") String a1();
609 		@RemoteOp(method="put") String a2();
610 		@RemoteOp(method="post") String a3();
611 		@RemoteOp(method="patch") String a4();
612 		@RemoteOp(method="delete") String a5();
613 		@RemoteOp(method="options") String a6();
614 		@RemoteGet String a11();
615 		@RemotePut String a12();
616 		@RemotePost String a13();
617 		@RemotePatch String a14();
618 		@RemoteDelete String a15();
619 		@RemoteOp String getA21();
620 		@RemoteOp String putA22();
621 		@RemoteOp String postA23();
622 		@RemoteOp String patchA24();
623 		@RemoteOp String deleteA25();
624 		@RemoteOp String optionsA26();
625 		@RemoteGet("/a31x") String a31();
626 		@RemotePut("/a32x") String a32();
627 		@RemotePost("/a33x") String a33();
628 		@RemotePatch("/a34x") String a34();
629 		@RemoteDelete("/a35x") String a35();
630 		@RemoteOp("GET /a41x") String a41();
631 		@RemoteOp("PUT /a42x") String a42();
632 		@RemoteOp("POST /a43x") String a43();
633 		@RemoteOp("PATCH /a44x") String a44();
634 		@RemoteOp("DELETE /a45x") String a45();
635 		@RemoteOp("OPTIONS /a46x") String a46();
636 		@RemoteGet("a51x") String a51();
637 		@RemotePut("a52x") String a52();
638 		@RemotePost("a53x") String a53();
639 		@RemotePatch("a54x") String a54();
640 		@RemoteDelete("a55x") String a55();
641 		@RemoteOp("GET a61x") String a61();
642 		@RemoteOp("PUT a62x") String a62();
643 		@RemoteOp("POST a63x") String a63();
644 		@RemoteOp("PATCH a64x") String a64();
645 		@RemoteOp("DELETE a65x") String a65();
646 		@RemoteOp("OPTIONS a66x") String a66();
647 	}
648 
649 
650 	@Test void h01_methodDetection() {
651 
652 		var x = client(H.class).build().getRemote(H1.class);
653 		assertEquals("GET a1", x.a1());
654 		assertEquals("PUT a2", x.a2());
655 		assertEquals("POST a3", x.a3());
656 		assertEquals("PATCH a4", x.a4());
657 		assertEquals("DELETE a5", x.a5());
658 		assertEquals("OPTIONS a6", x.a6());
659 		assertEquals("GET a11", x.a11());
660 		assertEquals("PUT a12", x.a12());
661 		assertEquals("POST a13", x.a13());
662 		assertEquals("PATCH a14", x.a14());
663 		assertEquals("DELETE a15", x.a15());
664 		assertEquals("GET a21", x.getA21());
665 		assertEquals("PUT a22", x.putA22());
666 		assertEquals("POST a23", x.postA23());
667 		assertEquals("PATCH a24", x.patchA24());
668 		assertEquals("DELETE a25", x.deleteA25());
669 		assertEquals("OPTIONS a26", x.optionsA26());
670 		assertEquals("GET a31x", x.a31());
671 		assertEquals("PUT a32x", x.a32());
672 		assertEquals("POST a33x", x.a33());
673 		assertEquals("PATCH a34x", x.a34());
674 		assertEquals("DELETE a35x", x.a35());
675 		assertEquals("GET a41x", x.a41());
676 		assertEquals("PUT a42x", x.a42());
677 		assertEquals("POST a43x", x.a43());
678 		assertEquals("PATCH a44x", x.a44());
679 		assertEquals("DELETE a45x", x.a45());
680 		assertEquals("OPTIONS a46x", x.a46());
681 		assertEquals("GET a51x", x.a51());
682 		assertEquals("PUT a52x", x.a52());
683 		assertEquals("POST a53x", x.a53());
684 		assertEquals("PATCH a54x", x.a54());
685 		assertEquals("DELETE a55x", x.a55());
686 		assertEquals("GET a61x", x.a61());
687 		assertEquals("PUT a62x", x.a62());
688 		assertEquals("POST a63x", x.a63());
689 		assertEquals("PATCH a64x", x.a64());
690 		assertEquals("DELETE a65x", x.a65());
691 		assertEquals("OPTIONS a66x", x.a66());
692 	}
693 
694 	//-----------------------------------------------------------------------------------------------------------------
695 	// Helper methods.
696 	//-----------------------------------------------------------------------------------------------------------------
697 
698 	private static RestClient.Builder client(Class<?> c) {
699 		return MockRestClient.create(c).noTrace().json5();
700 	}
701 
702 	private static <T> T remote(Class<?> c, Class<T> r) {
703 		return MockRestClient.create(c).noTrace().json5().build().getRemote(r);
704 	}
705 
706 	private static <T> T plainRemote(Class<?> c, Class<T> r) {
707 		return MockRestClient.create(c).build().getRemote(r);
708 	}
709 
710 	private static <T> T plainRemote(Class<?> c, Class<T> r, String rootUrl) {
711 		return MockRestClient.create(c).rootUrl(rootUrl).build().getRemote(r);
712 	}
713 }