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.rest.util;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.apache.juneau.rest.util.UrlPathMatcher.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import java.util.*;
24  
25  import org.apache.juneau.*;
26  import org.apache.juneau.json.*;
27  import org.junit.jupiter.api.*;
28  
29  class UrlPathMatcher_Test extends TestBase {
30  
31  	private void check(UrlPathMatcher p, String path, String expected) {
32  		assertString(expected, p.match(UrlPath.of(path)));
33  	}
34  
35  	private void shouldNotMatch(UrlPathMatcher p, String...paths) {
36  		for (String path : paths)
37  			assertNull(p.match(UrlPath.of(path)));
38  	}
39  
40  	//------------------------------------------------------------------------------------------------------------------
41  	// Comparison
42  	//------------------------------------------------------------------------------------------------------------------
43  
44  	@Test void a01_comparision() {
45  		var l = new LinkedList<UrlPathMatcher>();
46  
47  		l.add(of(""));
48  		l.add(of("*"));
49  		l.add(of("/"));
50  		l.add(of("/*"));
51  		l.add(of("/foo"));
52  		l.add(of("/foo/*"));
53  		l.add(of("/foo/bar"));
54  		l.add(of("/foo/bar/*"));
55  		l.add(of("/foo/{id}"));
56  		l.add(of("/foo/{id}/*"));
57  		l.add(of("/foo/{id}/bar"));
58  		l.add(of("/foo/{id}/bar/*"));
59  		l.add(of("foo.txt"));
60  		l.add(of("*.txt"));
61  		l.add(of("foo.*"));
62  
63  		Collections.sort(l);
64  		assertEquals("['foo.txt','foo.*','*.txt','/foo/bar','/foo/bar/*','/foo/{id}/bar','/foo/{id}/bar/*','/foo/{id}','/foo/{id}/*','/foo','/foo/*','/','/*','','*']", Json5Serializer.DEFAULT.toString(l));
65  	}
66  
67  	@Test void a02_comparision() {
68  		var l = new LinkedList<UrlPathMatcher>();
69  
70  		l.add(of("foo.txt"));
71  		l.add(of("*.txt"));
72  		l.add(of("foo.*"));
73  		l.add(of("/foo/{id}/bar/*"));
74  		l.add(of("/foo/{id}/bar"));
75  		l.add(of("/foo/{id}/*"));
76  		l.add(of("/foo/{id}"));
77  		l.add(of("/foo/bar/*"));
78  		l.add(of("/foo/bar"));
79  		l.add(of("/foo/*"));
80  		l.add(of("/foo"));
81  		l.add(of("/*"));
82  		l.add(of("/"));
83  		l.add(of("*"));
84  		l.add(of(""));
85  
86  		Collections.sort(l);
87  		assertEquals("['foo.txt','foo.*','*.txt','/foo/bar','/foo/bar/*','/foo/{id}/bar','/foo/{id}/bar/*','/foo/{id}','/foo/{id}/*','/foo','/foo/*','/','/*','','*']", Json5Serializer.DEFAULT.toString(l));
88  	}
89  
90  	@Test void a03_comparision() {
91  		var l = new LinkedList<UrlPathMatcher>();
92  
93  		l.add(of("/foo"));
94  		l.add(of("/foo"));
95  
96  		Collections.sort(l);
97  		assertEquals("['/foo','/foo']", Json5Serializer.DEFAULT.toString(l));
98  	}
99  
100 	//------------------------------------------------------------------------------------------------------------------
101 	// Simple pattern matching
102 	//------------------------------------------------------------------------------------------------------------------
103 
104 	@Test void b01_simple_match() {
105 		var p = of("/foo");
106 		check(p, "/foo", "{}");
107 		check(p, "/foo/", "{r:''}");
108 	}
109 
110 	@Test void b02_simple_noMatch() {
111 		var p = of("/foo");
112 		shouldNotMatch(p, "/fooo", "/fo", "/fooo/", "/", "/foo/bar");
113 	}
114 
115 	@Test void b03_simple_match_2parts() {
116 		var p = of("/foo/bar");
117 		check(p, "/foo/bar", "{}");
118 		check(p, "/foo/bar/", "{r:''}");
119 	}
120 
121 	@Test void b04_simple_noMatch_2parts() {
122 		var p = of("/foo/bar");
123 		shouldNotMatch(p, "/foo", "/foo/baz", "/foo/barr", "/foo/bar/baz");
124 	}
125 
126 	@Test void b05_simple_match_0parts() {
127 		var p = of("/");
128 		check(p, "/", "{r:''}");
129 	}
130 
131 	@Test void b06_simple_noMatch_0parts() {
132 		var p = of("/");
133 		shouldNotMatch(p, "/foo", "/foo/bar");
134 	}
135 
136 	@Test void b07_simple_match_blank() {
137 		var p = of("");
138 		check(p, "/", "{r:''}");
139 	}
140 
141 	@Test void b08_simple_noMatch_blank() {
142 		var p = of("");
143 		shouldNotMatch(p, "/foo", "/foo/bar");
144 	}
145 
146 	//------------------------------------------------------------------------------------------------------------------
147 	// Simple pattern matching with remainder
148 	//------------------------------------------------------------------------------------------------------------------
149 
150 	@Test void c01_simple_withRemainder_match() {
151 		var p = of("/foo/*");
152 		check(p, "/foo", "{}");
153 		check(p, "/foo/", "{r:''}");
154 		check(p, "/foo/bar", "{r:'bar'}");
155 		check(p, "/foo/bar/", "{r:'bar/'}");
156 		check(p, "/foo/bar/baz", "{r:'bar/baz'}");
157 	}
158 
159 	@Test void c02_simple_withRemainder_noMatch() {
160 		var p = of("/foo/*");
161 		shouldNotMatch(p, "/fooo", "/fo", "/fooo/", "/", "/fooo/bar");
162 	}
163 
164 	@Test void c03_simple_withRemainder_match_2parts() {
165 		var p = of("/foo/bar/*");
166 		check(p, "/foo/bar", "{}");
167 		check(p, "/foo/bar/baz", "{r:'baz'}");
168 		check(p, "/foo/bar/baz/", "{r:'baz/'}");
169 	}
170 
171 	@Test void c04_simple_withRemainder_noMatch_2parts() {
172 		var p = of("/foo/bar/*");
173 		shouldNotMatch(p, "/", "/foo", "/foo/baz", "/foo/barr", "/foo/barr/");
174 	}
175 
176 	@Test void c05_simple_withRemainder_match_0parts() {
177 		var p = of("/*");
178 		check(p, "/", "{r:''}");
179 		check(p, "/foo", "{r:'foo'}");
180 		check(p, "/foo/bar", "{r:'foo/bar'}");
181 	}
182 
183 	@Test void c06_simple_withRemainder_match_blank() {
184 		var p = of("*");
185 		check(p, "/", "{r:''}");
186 		check(p, "/foo", "{r:'foo'}");
187 		check(p, "/foo/bar", "{r:'foo/bar'}");
188 	}
189 
190 	//------------------------------------------------------------------------------------------------------------------
191 	// Pattern with variables
192 	//------------------------------------------------------------------------------------------------------------------
193 
194 	@Test void d01_1part1vars_match() {
195 		var p = of("/{foo}");
196 		check(p, "/bar", "{v:{foo:'bar'}}");
197 		check(p, "/bar/", "{v:{foo:'bar'},r:''}");
198 	}
199 
200 	@Test void d02_1part1var_noMatch() {
201 		var p = of("/{foo}");
202 		shouldNotMatch(p, "/foo/bar", "/");
203 	}
204 
205 	@Test void d03_2parts1var_match() {
206 		var p = of("/foo/{bar}");
207 		check(p, "/foo/baz", "{v:{bar:'baz'}}");
208 		check(p, "/foo/baz/", "{v:{bar:'baz'},r:''}");
209 	}
210 
211 	@Test void d04_2parts1var_noMatch() {
212 		var p = of("/foo/{bar}");
213 		shouldNotMatch(p, "/fooo/baz", "/fo/baz", "/foo", "/");
214 	}
215 
216 	@Test void d05_3vars_match() {
217 		var p = of("/{a}/{b}/{c}");
218 		check(p, "/A/B/C", "{v:{a:'A',b:'B',c:'C'}}");
219 		check(p, "/A/B/C/", "{v:{a:'A',b:'B',c:'C'},r:''}");
220 	}
221 
222 	@Test void d06_3vars_noMatch() {
223 		var p = of("/{a}/{b}/{c}");
224 		shouldNotMatch(p, "/A/B", "/A/B/C/D", "/");
225 	}
226 
227 	@Test void d07_7parts3vars_match() {
228 		var p = of("/a/{a}/b/{b}/c/{c}/d");
229 		check(p, "/a/A/b/B/c/C/d", "{v:{a:'A',b:'B',c:'C'}}");
230 		check(p, "/a/A/b/B/c/C/d/", "{v:{a:'A',b:'B',c:'C'},r:''}");
231 	}
232 
233 	@Test void d08_6parts3vars_noMatch() {
234 		var p = of("/a/{a}/b/{b}/c/{c}/d");
235 		shouldNotMatch(p, "/a/A/a/B/c/C/d", "/a/A/b/B/c/C/dd", "/a/b/c/d");
236 	}
237 
238 	//------------------------------------------------------------------------------------------------------------------
239 	// Pattern with variables and remainder
240 	//------------------------------------------------------------------------------------------------------------------
241 
242 	@Test void e01_1part1vars_withRemainder_match() {
243 		var p = of("/{foo}/*");
244 		check(p, "/bar", "{v:{foo:'bar'}}");
245 		check(p, "/bar/", "{v:{foo:'bar'},r:''}");
246 		check(p, "/bar/baz", "{v:{foo:'bar'},r:'baz'}");
247 		check(p, "/bar/baz/qux", "{v:{foo:'bar'},r:'baz/qux'}");
248 	}
249 
250 	@Test void e02_1part1var_withRemainder_noMatch() {
251 		var p = of("/{foo}/*");
252 		shouldNotMatch(p, "/");
253 	}
254 
255 	@Test void e03_2parts1var_withRemainder_match() {
256 		var p = of("/foo/{bar}/*");
257 		check(p, "/foo/baz", "{v:{bar:'baz'}}");
258 		check(p, "/foo/baz/", "{v:{bar:'baz'},r:''}");
259 		check(p, "/foo/baz/qux/", "{v:{bar:'baz'},r:'qux/'}");
260 	}
261 
262 	@Test void e04_2parts1var_withRemainder_noMatch() {
263 		var p = of("/foo/{bar}/*");
264 		shouldNotMatch(p, "/fooo/baz", "/fo/baz", "/foo", "/");
265 	}
266 
267 	//------------------------------------------------------------------------------------------------------------------
268 	// Pattern with inner meta
269 	//------------------------------------------------------------------------------------------------------------------
270 
271 	@Test void f01_innerMeta_withRemainder_match() {
272 		var p = of("/*/*");
273 		check(p, "/bar", "{}");
274 		check(p, "/bar/", "{r:''}");
275 		check(p, "/bar/baz", "{r:'baz'}");
276 		check(p, "/bar/baz/qux", "{r:'baz/qux'}");
277 	}
278 
279 	@Test void f02_innerMeta_withRemainder_noMatch() {
280 		var p = of("/*/*");
281 		shouldNotMatch(p, "/");
282 	}
283 
284 	@Test void f03_innerMeta_withRemainder_match() {
285 		var p = of("/foo/*/bar/*");
286 		check(p, "/foo/baz/bar", "{}");
287 		check(p, "/foo/baz/bar/", "{r:''}");
288 		check(p, "/foo/baz/bar/qux/", "{r:'qux/'}");
289 	}
290 
291 	@Test void f04_innerMeta_withRemainder_noMatch() {
292 		var p = of("/foo/*/bar/*");
293 		shouldNotMatch(p, "/fooo/baz/bar", "/fo/baz/bar", "/foo/bar", "/");
294 	}
295 
296 	//------------------------------------------------------------------------------------------------------------------
297 	// Paths with encoded vars
298 	//------------------------------------------------------------------------------------------------------------------
299 
300 	@Test void g01_encodedVars() {
301 		var p = of("/foo/{bar}/*");
302 		check(p, "/foo/baz%2Fqux", "{v:{bar:'baz/qux'}}");
303 		check(p, "/foo/baz+qux", "{v:{bar:'baz qux'}}");
304 		check(p, "/foo/baz%2Fqux/quux%2Fquuux", "{v:{bar:'baz/qux'},r:'quux%2Fquuux'}");
305 		check(p, "/foo/baz+qux/quux+quuux", "{v:{bar:'baz qux'},r:'quux+quuux'}");
306 	}
307 
308 	//------------------------------------------------------------------------------------------------------------------
309 	// Paths with not vars
310 	//------------------------------------------------------------------------------------------------------------------
311 
312 	@Test void h01_notVars() {
313 		var p = of("/foo/{bar/*");
314 		check(p, "/foo/{bar", "{}");
315 		check(p, "/foo/{bar/{baz", "{r:'{baz'}");
316 	}
317 
318 	@Test void h02_notVars() {
319 		var p = of("/foo/bar}/*");
320 		check(p, "/foo/bar}", "{}");
321 		check(p, "/foo/bar}/baz}", "{r:'baz}'}");
322 	}
323 
324 	@Test void h03_notVars() {
325 		var p = of("/foo/x{bar}x/*");
326 		check(p, "/foo/x{bar}x", "{}");
327 		check(p, "/foo/x{bar}x/x{baz}x", "{r:'x{baz}x'}");
328 	}
329 
330 	//------------------------------------------------------------------------------------------------------------------
331 	// Filename matches
332 	//------------------------------------------------------------------------------------------------------------------
333 
334 	@Test void i01_filenameMatcher() {
335 		var p = of("foo.bar");
336 		check(p, "/foo.bar", "{}");
337 		check(p, "/foo/foo.bar", "{}");
338 		shouldNotMatch(p, "/foo.baz", "/foo", "/foo.barx", "/foo", "/foo.*", "/*.bar", "/*.*", "/*", null);
339 
340 		p = of("*.bar");
341 		check(p, "/foo.bar", "{}");
342 		check(p, "/foo/foo.bar", "{}");
343 		check(p, "/*.bar", "{}");
344 		shouldNotMatch(p, "/foo.baz", "/foo", "/foo", "/foo.*", "/*.*", "/*", null);
345 
346 		p = of("foo.*");
347 		check(p, "/foo.bar", "{}");
348 		check(p, "/foo/foo.bar", "{}");
349 		check(p, "/foo.*", "{}");
350 		shouldNotMatch(p, "/foo", "/foo", "/*.*", "/*", null);
351 
352 		p = of("*.*");
353 		check(p, "/foo.bar", "{}");
354 		check(p, "/foo/foo.bar", "{}");
355 		check(p, "/foo.*", "{}");
356 		check(p, "/*.bar", "{}");
357 		check(p, "/*.*", "{}");
358 		shouldNotMatch(p, "/foo", "/foo", "/*", null);
359 	}
360 }