View Javadoc
1   /*
2    * Copyright (c) 2012-2023, jcabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.urn;
31  
32  import java.net.URI;
33  import java.net.URISyntaxException;
34  import java.util.Arrays;
35  import java.util.Collections;
36  import java.util.List;
37  import org.apache.commons.lang3.SerializationUtils;
38  import org.hamcrest.MatcherAssert;
39  import org.hamcrest.Matchers;
40  import org.junit.jupiter.api.Assertions;
41  import org.junit.jupiter.api.Disabled;
42  import org.junit.jupiter.api.Test;
43  
44  /**
45   * Uniform Resource Name (URN), tests.
46   *
47   * @since 0.6
48   * @checkstyle AbbreviationAsWordInNameCheck (500 lines)
49   */
50  @SuppressWarnings("PMD.TooManyMethods")
51  final class URNTest {
52  
53      /**
54       * URN can be instantiated from plain text.
55       * @throws Exception If there is some problem inside
56       */
57      @Test
58      void instantiatesFromText() throws Exception {
59          final URN urn = new URN("urn:jcabi:jeff%20lebowski%2540");
60          MatcherAssert.assertThat(urn.nid(), Matchers.equalTo("jcabi"));
61          MatcherAssert.assertThat(
62              urn.nss(),
63              Matchers.equalTo("jeff lebowski%40")
64          );
65      }
66  
67      /**
68       * URN can encode NSS properly.
69       */
70      @Test
71      void encodesNssAsRequiredByUrlSyntax() {
72          final URN urn = new URN("test", "walter sobchak!");
73          MatcherAssert.assertThat(
74              urn.toString(),
75              Matchers.equalTo("urn:test:walter%20sobchak%21")
76          );
77      }
78  
79      /**
80       * URN can throw exception when text is NULL.
81       */
82      @Test
83      void throwsExceptionWhenTextIsNull() {
84          Assertions.assertThrows(
85              IllegalArgumentException.class,
86              () -> new URN(null)
87          );
88      }
89  
90      /**
91       * URN can be instantiated from components.
92       */
93      @Test
94      void instantiatesFromComponents() {
95          final String nid = "foo";
96          final String nss = "\u8416 & \u8415 *&^%$#@!-~`\"'";
97          final URN urn = new URN(nid, nss);
98          MatcherAssert.assertThat(urn.nid(), Matchers.equalTo(nid));
99          MatcherAssert.assertThat(urn.nss(), Matchers.equalTo(nss));
100         MatcherAssert.assertThat(urn.toURI(), Matchers.instanceOf(URI.class));
101     }
102 
103     /**
104      * URN can throw exception when NID is NULL.
105      */
106     @Test
107     void throwsExceptionWhenNidIsNull() {
108         Assertions.assertThrows(
109             IllegalArgumentException.class,
110             () -> new URN(null, "some-test-nss")
111         );
112     }
113 
114     /**
115      * URN can throw exception when NSS is NULL.
116      */
117     @Test
118     void throwsExceptionWhenNssIsNull() {
119         Assertions.assertThrows(
120             IllegalArgumentException.class,
121             () -> new URN("namespace1", null)
122         );
123     }
124 
125     /**
126      * URN can be tested for equivalence of another URN.
127      * @throws Exception If there is some problem inside
128      */
129     @Test
130     void comparesForEquivalence() throws Exception {
131         final String text = "urn:foo:some-other-specific-string";
132         final URN first = new URN(text);
133         final URN second = new URN(text);
134         MatcherAssert.assertThat(first, Matchers.equalTo(second));
135     }
136 
137     /**
138      * URN can be tested for equivalence with another URI.
139      * @throws Exception If there is some problem inside
140      */
141     @Test
142     void comparesForEquivalenceWithUri() throws Exception {
143         final String text = "urn:foo:somespecificstring";
144         final URN first = new URN(text);
145         final URI second = new URI(text);
146         MatcherAssert.assertThat(first.equals(second), Matchers.is(false));
147     }
148 
149     /**
150      * URN can be tested for equivalence with string.
151      * @throws Exception If there is some problem inside
152      */
153     @Test
154     void comparesForEquivalenceWithString() throws Exception {
155         final String text = "urn:foo:sometextastext";
156         final URN first = new URN(text);
157         MatcherAssert.assertThat(first.equals(text), Matchers.is(false));
158     }
159 
160     /**
161      * URN can be converted to string.
162      * @throws Exception If there is some problem inside
163      */
164     @Test
165     void convertsToString() throws Exception {
166         final String text = "urn:foo:textofurn";
167         final URN urn = new URN(text);
168         MatcherAssert.assertThat(urn.toString(), Matchers.equalTo(text));
169     }
170 
171     /**
172      * URN can catch incorrect syntax.
173      */
174     @Test
175     void catchesIncorrectURNSyntax() {
176         Assertions.assertThrows(
177             URISyntaxException.class,
178             () -> new URN("some incorrect name")
179         );
180     }
181 
182     /**
183      * URN can pass correct syntax.
184      */
185     @Test
186     void passesCorrectURNSyntax() {
187         final String[] texts = {
188             "URN:hello:test",
189             "urn:foo:some%20text%20with%20spaces",
190             "urn:a:",
191             "urn:a:?alpha=50",
192             "urn:a:?boom",
193             "urn:a:test?123",
194             "urn:a:test?1a2b3c",
195             "urn:a:test?1A2B3C",
196             "urn:a:?alpha=abccde%20%45%4Fme",
197             "urn:woquo:ns:pa/procure/BalanceRecord?name=*",
198             "urn:a:?alpha=50&beta=u%20worksfine",
199             "urn:verylongnamespaceid:",
200             "urn:a:?alpha=50*",
201             "urn:a:b/c/d",
202         };
203         for (final String text : texts) {
204             final URN urn = URN.create(text);
205             MatcherAssert.assertThat(
206                 URN.create(urn.toString()),
207                 Matchers.equalTo(urn)
208             );
209             MatcherAssert.assertThat("is valid", URN.isValid(urn.toString()));
210         }
211     }
212 
213     /**
214      * URN can throw exception for incorrect syntax.
215      */
216     @Test
217     void throwsExceptionForIncorrectURNSyntax() {
218         final String[] texts = {
219             "abc",
220             "",
221             "urn::",
222             "urn:urn:hello",
223             "urn:incorrect namespace name with spaces:test",
224             "urn:abc+foo:test-me",
225             "urn:test:?abc?",
226             "urn:test:?abc=incorrect*value",
227             "urn:test:?abc=invalid-symbols:^%$#&@*()!-in-argument-value",
228             "urn:incorrect%20namespace:",
229             "urn:verylongnameofanamespaceverylongnameofanamespace:",
230             "urn:test:spaces are not allowed here",
231             "urn:test:unicode-has-to-be-encoded:\u8514",
232         };
233         for (final String text : texts) {
234             try {
235                 URN.create(text);
236                 MatcherAssert.assertThat(text, Matchers.nullValue());
237             } catch (final IllegalArgumentException ex) {
238                 assert ex != null;
239             }
240         }
241     }
242 
243     /**
244      * URN can be "empty".
245      */
246     @Test
247     void emptyURNIsAFirstClassCitizen() {
248         final URN urn = new URN();
249         MatcherAssert.assertThat(urn.isEmpty(), Matchers.equalTo(true));
250     }
251 
252     /**
253      * URN can be "empty" only in one form.
254      */
255     @Test
256     void emptyURNHasOnlyOneVariant() {
257         Assertions.assertThrows(
258             IllegalArgumentException.class,
259             () -> new URN("void", "it-is-impossible-to-have-any-NSS-here")
260         );
261     }
262 
263     /**
264      * URN can be "empty" only in one form, with from-text ctor.
265      */
266     @Test
267     void emptyURNHasOnlyOneVariantWithTextCtor() {
268         Assertions.assertThrows(
269             URISyntaxException.class,
270             () -> new URN("urn:void:it-is-impossible-to-have-any-NSS-here")
271         );
272     }
273 
274     /**
275      * URN can match a pattern.
276      * @throws Exception If there is some problem inside
277      */
278     @Test
279     void matchesPatternWithAnotherURN() throws Exception {
280         MatcherAssert.assertThat(
281             "matches",
282             new URN("urn:test:file").matches("urn:test:*")
283         );
284     }
285 
286     /**
287      * URN can add and retrieve params.
288      * @throws Exception If there is some problem inside
289      */
290     @Test
291     void addAndRetrievesParamsByName() throws Exception {
292         final String name = "crap";
293         final String value = "@!$#^\u0433iu**76\u0945";
294         final URN urn = new URN("urn:test:x?bb")
295             .param("bar", "\u8514 value?")
296             .param(name, value);
297         MatcherAssert.assertThat(
298             urn.toString(),
299             Matchers.containsString("bar=%E8%94%94%20value%3F")
300         );
301         MatcherAssert.assertThat(urn.param("bb"), Matchers.equalTo(""));
302         MatcherAssert.assertThat(urn.param(name), Matchers.equalTo(value));
303     }
304 
305     /**
306      * URN can fetch a pure part (without params) from itself.
307      * @throws Exception If there is some problem inside
308      */
309     @Test
310     void fetchesBodyWithoutParams() throws Exception {
311         MatcherAssert.assertThat(
312             new URN("urn:test:something?a=9&b=4").pure(),
313             Matchers.equalTo(new URN("urn:test:something"))
314         );
315     }
316 
317     /**
318      * URN can be serialized.
319      * @throws Exception If there is some problem inside
320      */
321     @Test
322     void serializesToBytes() throws Exception {
323         final URN urn = new URN("urn:test:some-data-to-serialize");
324         final byte[] bytes = SerializationUtils.serialize(urn);
325         MatcherAssert.assertThat(
326             ((URN) SerializationUtils.deserialize(bytes)).toString(),
327             Matchers.equalTo(urn.toString())
328         );
329     }
330 
331     /**
332      * URN can be persistent in params ordering.
333      * @throws Exception If there is some problem inside
334      */
335     @Test
336     void persistsOrderingOfParams() throws Exception {
337         final List<String> params = Arrays.asList(
338             "ft", "sec", "9", "123", "a1b2c3", "A", "B", "C"
339         );
340         URN first = new URN("urn:test:x");
341         URN second = first;
342         for (final String param : params) {
343             first = first.param(param, "");
344         }
345         Collections.shuffle(params);
346         for (final String param : params) {
347             second = second.param(param, "");
348         }
349         MatcherAssert.assertThat(first, Matchers.equalTo(second));
350     }
351 
352     /**
353      * URN can be mocked.
354      */
355     @Test
356     void mocksUrnWithAMocker() {
357         MatcherAssert.assertThat(
358             new URNMocker().mock(),
359             Matchers.not(Matchers.equalTo(new URNMocker().mock()))
360         );
361     }
362 
363     /**
364      * URN can be lexical equivalent according to RFC 2144, section 6.
365      * @see <a href="http://www.ietf.org/rfc/rfc2141.txt"/>
366      * @see <a href="https://github.com/jcabi/jcabi-urn/issues/8">issue</a>
367      */
368     @Test
369     @Disabled
370     void checkLexicalEquivalence() {
371         final String[] urns = {
372             "URN:foo:a123,456",
373             "urn:foo:a123,456",
374             "urn:FOO:a123,456",
375             "urn:foo:a123%2C456",
376             "URN:FOO:a123%2c456",
377         };
378         for (final String first : urns) {
379             for (final String second : urns) {
380                 MatcherAssert.assertThat(
381                     URN.create(first),
382                     Matchers.equalTo(URN.create(second))
383                 );
384             }
385         }
386     }
387 
388 }