View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2012-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.jcabi.urn;
6   
7   import java.net.URI;
8   import java.net.URISyntaxException;
9   import java.util.Arrays;
10  import java.util.Collections;
11  import java.util.List;
12  import org.apache.commons.lang3.SerializationUtils;
13  import org.hamcrest.MatcherAssert;
14  import org.hamcrest.Matchers;
15  import org.junit.jupiter.api.Assertions;
16  import org.junit.jupiter.api.Disabled;
17  import org.junit.jupiter.api.Test;
18  
19  /**
20   * Uniform Resource Name (URN), tests.
21   * @since 0.6
22   * @checkstyle AbbreviationAsWordInNameCheck (500 lines)
23   */
24  @SuppressWarnings("PMD.TooManyMethods")
25  final class URNTest {
26  
27      /**
28       * URN can be instantiated from plain text and provides correct NID.
29       * @throws Exception If there is some problem inside
30       */
31      @Test
32      void instantiatesFromTextWithCorrectNid() throws Exception {
33          MatcherAssert.assertThat(
34              "should provide correct NID",
35              new URN("urn:jcabi:jeff%20lebowski%2540").nid(),
36              Matchers.equalTo("jcabi")
37          );
38      }
39  
40      /**
41       * URN can be instantiated from plain text and provides correct NSS.
42       * @throws Exception If there is some problem inside
43       */
44      @Test
45      void instantiatesFromTextWithCorrectNss() throws Exception {
46          MatcherAssert.assertThat(
47              "should provide correct NSS",
48              new URN("urn:jcabi:jeff%20lebowski%2540").nss(),
49              Matchers.equalTo("jeff lebowski%40")
50          );
51      }
52  
53      /**
54       * URN can encode NSS properly.
55       */
56      @Test
57      void encodesNssAsRequiredByUrlSyntax() {
58          MatcherAssert.assertThat(
59              "should encode NSS correctly",
60              new URN("test", "walter sobchak!").toString(),
61              Matchers.equalTo("urn:test:walter%20sobchak%21")
62          );
63      }
64  
65      /**
66       * URN can throw exception when text is NULL.
67       */
68      @Test
69      void throwsExceptionWhenTextIsNull() {
70          Assertions.assertThrows(
71              IllegalArgumentException.class,
72              () -> new URN(null)
73          );
74      }
75  
76      /**
77       * URN can be instantiated from components with correct NID.
78       */
79      @Test
80      void instantiatesFromComponentsWithCorrectNid() {
81          MatcherAssert.assertThat(
82              "should preserve NID",
83              new URN("foo", "\u8416 test").nid(),
84              Matchers.equalTo("foo")
85          );
86      }
87  
88      /**
89       * URN can be instantiated from components with correct NSS.
90       */
91      @Test
92      void instantiatesFromComponentsWithCorrectNss() {
93          MatcherAssert.assertThat(
94              "should preserve NSS",
95              new URN("bar", "\u8416 & \u8415 *&^%$#@!-~`\"'").nss(),
96              Matchers.equalTo("\u8416 & \u8415 *&^%$#@!-~`\"'")
97          );
98      }
99  
100     /**
101      * URN can be converted to URI.
102      */
103     @Test
104     void convertsToUri() {
105         MatcherAssert.assertThat(
106             "should convert to URI",
107             new URN("baz", "test").toURI(),
108             Matchers.instanceOf(URI.class)
109         );
110     }
111 
112     /**
113      * URN can throw exception when NID is NULL.
114      */
115     @Test
116     void throwsExceptionWhenNidIsNull() {
117         Assertions.assertThrows(
118             IllegalArgumentException.class,
119             () -> new URN(null, "some-test-nss")
120         );
121     }
122 
123     /**
124      * URN can throw exception when NSS is NULL.
125      */
126     @Test
127     void throwsExceptionWhenNssIsNull() {
128         Assertions.assertThrows(
129             IllegalArgumentException.class,
130             () -> new URN("namespace1", null)
131         );
132     }
133 
134     /**
135      * URN can be tested for equivalence of another URN.
136      * @throws Exception If there is some problem inside
137      */
138     @Test
139     void comparesForEquivalence() throws Exception {
140         MatcherAssert.assertThat(
141             "should be equal",
142             new URN("urn:foo:some-other-specific-string"),
143             Matchers.equalTo(new URN("urn:foo:some-other-specific-string"))
144         );
145     }
146 
147     /**
148      * URN can be tested for equivalence with another URI.
149      * @throws Exception If there is some problem inside
150      */
151     @Test
152     void comparesForEquivalenceWithUri() throws Exception {
153         MatcherAssert.assertThat(
154             "should not be equal to URI",
155             new URN("urn:foo:somespecificstring").equals(
156                 new URI("urn:foo:somespecificstring")
157             ),
158             Matchers.is(false)
159         );
160     }
161 
162     /**
163      * URN can be tested for equivalence with string.
164      * @throws Exception If there is some problem inside
165      */
166     @Test
167     void comparesForEquivalenceWithString() throws Exception {
168         MatcherAssert.assertThat(
169             "should not be equal to String",
170             new URN("urn:foo:sometextastext").equals("urn:foo:sometextastext"),
171             Matchers.is(false)
172         );
173     }
174 
175     /**
176      * URN can be converted to string.
177      * @throws Exception If there is some problem inside
178      */
179     @Test
180     void convertsToString() throws Exception {
181         MatcherAssert.assertThat(
182             "should convert to string",
183             new URN("urn:foo:textofurn").toString(),
184             Matchers.equalTo("urn:foo:textofurn")
185         );
186     }
187 
188     /**
189      * URN can catch incorrect syntax.
190      */
191     @Test
192     void catchesIncorrectURNSyntax() {
193         Assertions.assertThrows(
194             URISyntaxException.class,
195             () -> new URN("some incorrect name")
196         );
197     }
198 
199     /**
200      * URN can pass correct syntax and round-trip.
201      */
202     @Test
203     void passesCorrectURNSyntaxAndRoundTrips() {
204         MatcherAssert.assertThat(
205             "should round-trip all valid URNs",
206             Arrays.asList(
207                 "URN:hello:test",
208                 "urn:foo:some%20text%20with%20spaces",
209                 "urn:a:",
210                 "urn:a:?alpha=50",
211                 "urn:a:?boom",
212                 "urn:a:test?123",
213                 "urn:a:test?1a2b3c",
214                 "urn:a:test?1A2B3C",
215                 "urn:a:?alpha=abccde%20%45%4Fme",
216                 "urn:woquo:ns:pa/procure/BalanceRecord?name=*",
217                 "urn:a:?alpha=50&beta=u%20worksfine",
218                 "urn:verylongnamespaceid:",
219                 "urn:a:?alpha=50*",
220                 "urn:a:b/c/d"
221             ),
222             Matchers.everyItem(
223                 Matchers.is(
224                     new org.hamcrest.CustomTypeSafeMatcher<String>("a round-trippable URN") {
225                         @Override
226                         protected boolean matchesSafely(final String text) {
227                             return URN.isValid(text)
228                                 && URN.create(URN.create(text).toString())
229                                     .equals(URN.create(text));
230                         }
231                     }
232                 )
233             )
234         );
235     }
236 
237     /**
238      * URN can throw exception for incorrect syntax.
239      */
240     @Test
241     void throwsExceptionForIncorrectURNSyntax() {
242         final String[] texts = {
243             "abc",
244             "",
245             "urn::",
246             "urn:urn:hello",
247             "urn:incorrect namespace name with spaces:test",
248             "urn:abc+foo:test-me",
249             "urn:test:?abc?",
250             "urn:test:?abc=incorrect*value",
251             "urn:test:?abc=invalid-symbols:^%$#&@*()!-in-argument-value",
252             "urn:incorrect%20namespace:",
253             "urn:verylongnameofanamespaceverylongnameofanamespace:",
254             "urn:test:spaces are not allowed here",
255             "urn:test:unicode-has-to-be-encoded:\u8514",
256         };
257         for (final String text : texts) {
258             try {
259                 URN.create(text);
260                 MatcherAssert.assertThat(text, Matchers.nullValue());
261             } catch (final IllegalArgumentException ex) {
262                 assert ex != null;
263             }
264         }
265     }
266 
267     /**
268      * URN can be "empty".
269      */
270     @Test
271     void emptyURNIsAFirstClassCitizen() {
272         MatcherAssert.assertThat(
273             "should be empty",
274             new URN().isEmpty(),
275             Matchers.equalTo(true)
276         );
277     }
278 
279     /**
280      * URN can be "empty" only in one form.
281      */
282     @Test
283     void emptyURNHasOnlyOneVariant() {
284         Assertions.assertThrows(
285             IllegalArgumentException.class,
286             () -> new URN("void", "it-is-impossible-to-have-any-NSS-here")
287         );
288     }
289 
290     /**
291      * URN can be "empty" only in one form, with from-text ctor.
292      */
293     @Test
294     void emptyURNHasOnlyOneVariantWithTextCtor() {
295         Assertions.assertThrows(
296             URISyntaxException.class,
297             () -> new URN("urn:void:it-is-impossible-to-have-any-NSS-here")
298         );
299     }
300 
301     /**
302      * URN can match a pattern.
303      * @throws Exception If there is some problem inside
304      */
305     @Test
306     void matchesPatternWithAnotherURN() throws Exception {
307         MatcherAssert.assertThat(
308             "matches",
309             new URN("urn:test:file").matches("urn:test:*")
310         );
311     }
312 
313     /**
314      * URN can encode params in string representation.
315      * @throws Exception If there is some problem inside
316      */
317     @Test
318     void encodesParamsInStringRepresentation() throws Exception {
319         MatcherAssert.assertThat(
320             "should encode param correctly",
321             new URN("urn:test:x?bb")
322                 .param("bar", "\u8514 value?")
323                 .toString(),
324             Matchers.containsString("bar=%E8%94%94%20value%3F")
325         );
326     }
327 
328     /**
329      * URN can retrieve empty param value.
330      * @throws Exception If there is some problem inside
331      */
332     @Test
333     void retrievesEmptyParamValue() throws Exception {
334         MatcherAssert.assertThat(
335             "should retrieve empty param",
336             new URN("urn:test:x?bb").param("bb"),
337             Matchers.equalTo("")
338         );
339     }
340 
341     /**
342      * URN can retrieve unicode param value.
343      * @throws Exception If there is some problem inside
344      */
345     @Test
346     void retrievesUnicodeParamValue() throws Exception {
347         MatcherAssert.assertThat(
348             "should retrieve unicode param",
349             new URN("urn:test:x?bb")
350                 .param("crap", "@!$#^\u0433iu**76\u0945")
351                 .param("crap"),
352             Matchers.equalTo("@!$#^\u0433iu**76\u0945")
353         );
354     }
355 
356     /**
357      * URN can fetch a pure part (without params) from itself.
358      * @throws Exception If there is some problem inside
359      */
360     @Test
361     void fetchesBodyWithoutParams() throws Exception {
362         MatcherAssert.assertThat(
363             new URN("urn:test:something?a=9&b=4").pure(),
364             Matchers.equalTo(new URN("urn:test:something"))
365         );
366     }
367 
368     /**
369      * URN can be serialized.
370      * @throws Exception If there is some problem inside
371      */
372     @Test
373     void serializesToBytes() throws Exception {
374         MatcherAssert.assertThat(
375             "should deserialize to same value",
376             ((URN) SerializationUtils.deserialize(
377                 SerializationUtils.serialize(
378                     new URN("urn:test:some-data-to-serialize")
379                 )
380             )).toString(),
381             Matchers.equalTo("urn:test:some-data-to-serialize")
382         );
383     }
384 
385     /**
386      * URN can be persistent in params ordering.
387      * @throws Exception If there is some problem inside
388      */
389     @Test
390     void persistsOrderingOfParams() throws Exception {
391         final List<String> params = Arrays.asList(
392             "ft", "sec", "9", "123", "a1b2c3", "A", "B", "C"
393         );
394         URN first = new URN("urn:test:x");
395         URN second = first;
396         for (final String param : params) {
397             first = first.param(param, "");
398         }
399         Collections.shuffle(params);
400         for (final String param : params) {
401             second = second.param(param, "");
402         }
403         MatcherAssert.assertThat(first, Matchers.equalTo(second));
404     }
405 
406     /**
407      * URN can be mocked.
408      */
409     @Test
410     void mocksUrnWithAMocker() {
411         MatcherAssert.assertThat(
412             new URNMocker().mock(),
413             Matchers.not(Matchers.equalTo(new URNMocker().mock()))
414         );
415     }
416 
417     /**
418      * URN can be lexical equivalent according to RFC 2144, section 6.
419      * @see <a href="http://www.ietf.org/rfc/rfc2141.txt"/>
420      * @see <a href="https://github.com/jcabi/jcabi-urn/issues/8">issue</a>
421      */
422     @Test
423     @Disabled
424     void checkLexicalEquivalence() {
425         final String[] urns = {
426             "URN:foo:a123,456",
427             "urn:foo:a123,456",
428             "urn:FOO:a123,456",
429             "urn:foo:a123%2C456",
430             "URN:FOO:a123%2c456",
431         };
432         for (final String first : urns) {
433             for (final String second : urns) {
434                 MatcherAssert.assertThat(
435                     URN.create(first),
436                     Matchers.equalTo(URN.create(second))
437                 );
438             }
439         }
440     }
441 }