Add rfc8040.ApiPath
[netconf.git] / restconf / restconf-nb-rfc8040 / src / test / java / org / opendaylight / restconf / nb / rfc8040 / ApiPathTest.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.nb.rfc8040;
9
10 import static org.hamcrest.CoreMatchers.instanceOf;
11 import static org.hamcrest.MatcherAssert.assertThat;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertThrows;
14
15 import java.text.ParseException;
16 import java.util.Arrays;
17 import java.util.List;
18 import org.junit.Test;
19 import org.opendaylight.restconf.nb.rfc8040.ApiPath.ApiIdentifier;
20 import org.opendaylight.restconf.nb.rfc8040.ApiPath.ListInstance;
21 import org.opendaylight.restconf.nb.rfc8040.ApiPath.Step;
22 import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
23
24 public class ApiPathTest {
25     @Test
26     public void testNull() {
27         assertThrows(NullPointerException.class, () -> ApiPath.parse(null));
28     }
29
30     @Test
31     public void testEmpty() {
32         assertEquals(List.of(), parse("/"));
33     }
34
35     @Test
36     public void testSingleSlash() throws ParseException {
37         final var ex = assertThrows(ParseException.class, () -> ApiPath.parse("/"));
38         assertEquals("Identifier may not be empty", ex.getMessage());
39         assertEquals(0, ex.getErrorOffset());
40
41         assertEquals(ApiPath.empty(), ApiPath.parseUrl("/"));
42     }
43
44     @Test
45     public void testTrailingSlash() throws ParseException {
46         final var ex = assertThrows(ParseException.class, () -> ApiPath.parse("foo/"));
47         assertEquals("Identifier may not be empty", ex.getMessage());
48         assertEquals(4, ex.getErrorOffset());
49
50         final var path = ApiPath.parseUrl("foo/").steps();
51         assertEquals(1, path.size());
52         assertApiIdentifier(path.get(0), null, "foo");
53     }
54
55     @Test
56     public void testExample1() {
57         final var path = parse("/example-top:top/list1=key1,key2,key3/list2=key4,key5/X");
58         assertEquals(4, path.size());
59         assertApiIdentifier(path.get(0), "example-top", "top");
60         assertListInstance(path.get(1), null, "list1", "key1", "key2", "key3");
61         assertListInstance(path.get(2), null, "list2", "key4", "key5");
62         assertApiIdentifier(path.get(3), null, "X");
63     }
64
65     @Test
66     public void testExample2() {
67         final var path = parse("/example-top:top/Y=instance-value");
68         assertEquals(2, path.size());
69         assertApiIdentifier(path.get(0), "example-top", "top");
70         assertListInstance(path.get(1), null, "Y", "instance-value");
71     }
72
73     @Test
74     public void testExample3() {
75         final var path = parse("/example-top:top/list1=%2C%27\"%3A\"%20%2F,,foo");
76         assertEquals(2, path.size());
77         assertApiIdentifier(path.get(0), "example-top", "top");
78         assertListInstance(path.get(1), null, "list1", ",'\":\" /", "", "foo");
79     }
80
81     @Test
82     public void testEscapedColon() {
83         final var path = parse("/foo%3Afoo");
84         assertEquals(1, path.size());
85         assertApiIdentifier(path.get(0), "foo", "foo");
86     }
87
88     @Test
89     public void nonAsciiFirstIdentifier() {
90         final var ex = assertThrows(ParseException.class, () -> ApiPath.parse("a%80"));
91         assertEquals("Expecting %00-%7F, not %80", ex.getMessage());
92         assertEquals(1, ex.getErrorOffset());
93     }
94
95     @Test
96     public void nonAsciiSecondIdentifier() {
97         final var ex = assertThrows(ParseException.class, () -> ApiPath.parse("foo:a%80"));
98         assertEquals("Expecting %00-%7F, not %80", ex.getMessage());
99         assertEquals(5, ex.getErrorOffset());
100     }
101
102     @Test
103     public void testIllegalEscape() {
104         final var ex = assertThrows(ParseException.class, () -> ApiPath.parse("foo:foo=%41%FF%42%FF%43"));
105         assertEquals("Invalid UTF-8 sequence 'A�B�C': Input length = 1", ex.getMessage());
106         assertEquals(8, ex.getErrorOffset());
107     }
108
109     private static void assertApiIdentifier(final Step step, final String module, final String identifier) {
110         assertThat(step, instanceOf(ApiIdentifier.class));
111         assertEquals(module, step.module());
112         assertEquals(UnqualifiedQName.of(identifier), step.identifier());
113     }
114
115     private static void assertListInstance(final Step step, final String module, final String identifier,
116             final String... keyValues) {
117         assertThat(step, instanceOf(ListInstance.class));
118         assertEquals(module, step.module());
119         assertEquals(UnqualifiedQName.of(identifier), step.identifier());
120         assertEquals(Arrays.asList(keyValues), ((ListInstance) step).keyValues());
121     }
122
123     private static List<Step> parse(final String str) {
124         final String toParse = str.substring(1);
125         try {
126             return ApiPath.parse(toParse).steps();
127         } catch (ParseException e) {
128             throw new AssertionError("Failed to parse \"" + toParse + "\"", e);
129         }
130     }
131 }