a638f261fcc30ae886214c2b784e16397df116d4
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / rfc8040 / databind / jaxrs / QueryParamsTest.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.databind.jaxrs;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertNull;
13 import static org.junit.Assert.assertThrows;
14 import static org.mockito.Mockito.doReturn;
15 import static org.mockito.Mockito.mock;
16 import static org.mockito.Mockito.withSettings;
17
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21 import java.util.Set;
22 import java.util.function.Function;
23 import javax.ws.rs.core.MultivaluedHashMap;
24 import javax.ws.rs.core.MultivaluedMap;
25 import javax.ws.rs.core.UriInfo;
26 import org.junit.Test;
27 import org.junit.runner.RunWith;
28 import org.mockito.junit.MockitoJUnitRunner;
29 import org.opendaylight.restconf.api.query.ContentParam;
30 import org.opendaylight.restconf.api.query.DepthParam;
31 import org.opendaylight.restconf.api.query.InsertParam;
32 import org.opendaylight.restconf.api.query.RestconfQueryParam;
33 import org.opendaylight.restconf.api.query.WithDefaultsParam;
34 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
35 import org.opendaylight.restconf.common.errors.RestconfError;
36 import org.opendaylight.restconf.nb.rfc8040.legacy.InstanceIdentifierContext;
37 import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
38 import org.opendaylight.yangtools.yang.common.ErrorTag;
39 import org.opendaylight.yangtools.yang.common.ErrorType;
40 import org.opendaylight.yangtools.yang.common.QName;
41 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
43 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
45 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
46 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
47
48 @RunWith(MockitoJUnitRunner.StrictStubs.class)
49 public class QueryParamsTest {
50     /**
51      * Test when parameter is present at most once.
52      */
53     @Test
54     public void optionalParamTest() {
55         assertEquals("all", QueryParams.optionalParam(ContentParam.uriName, List.of("all")));
56     }
57
58     /**
59      * Test when parameter is present more than once.
60      */
61     @Test
62     public void optionalParamMultipleTest() {
63         final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
64             () -> QueryParams.optionalParam(ContentParam.uriName, List.of("config", "nonconfig", "all")));
65         final List<RestconfError> errors = ex.getErrors();
66         assertEquals(1, errors.size());
67
68         final RestconfError error = errors.get(0);
69         assertEquals("Error type is not correct", ErrorType.PROTOCOL, error.getErrorType());
70         assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, error.getErrorTag());
71     }
72
73     /**
74      * Test when not allowed parameter type is used.
75      */
76     @Test
77     public void checkParametersTypesNegativeTest() {
78         assertUnknownParam(QueryParams::newNotificationQueryParams);
79         assertUnknownParam(QueryParams::newReadDataParams);
80         assertUnknownParam(QueryParams::parseInsert);
81
82         assertInvalidParam(QueryParams::newNotificationQueryParams, ContentParam.ALL);
83         assertInvalidParam(QueryParams::newReadDataParams, InsertParam.LAST);
84         assertInvalidParam(QueryParams::parseInsert, ContentParam.ALL);
85     }
86
87     /**
88      * Test of parsing default parameters from URI request.
89      */
90     @Test
91     public void parseUriParametersDefaultTest() {
92         // no parameters, default values should be used
93         final var params = assertParams(QueryParams::newReadDataParams, new MultivaluedHashMap<>());
94         assertEquals(ContentParam.ALL, params.content());
95         assertNull(params.depth());
96         assertNull(params.fields());
97     }
98
99     @Test
100     public void testInvalidValueReadDataParams() {
101         assertInvalidValue(QueryParams::newReadDataParams, ContentParam.uriName);
102         assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName);
103         assertInvalidValue(QueryParams::newReadDataParams, WithDefaultsParam.uriName);
104
105         // inserted value is too high
106         assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName, "65536");
107         // inserted value is too low
108         assertInvalidValue(QueryParams::newReadDataParams, DepthParam.uriName, "0");
109     }
110
111     /**
112      * Testing parsing of with-defaults parameter which value matches 'report-all-tagged' setting.
113      */
114     @Test
115     public void parseUriParametersWithDefaultAndTaggedTest() {
116         final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "report-all-tagged");
117         assertEquals(WithDefaultsParam.REPORT_ALL_TAGGED, params.withDefaults());
118     }
119
120     /**
121      * Testing parsing of with-defaults parameter which value matches 'report-all' setting.
122      */
123     @Test
124     public void parseUriParametersWithDefaultAndReportAllTest() {
125         final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "report-all");
126         assertEquals(WithDefaultsParam.REPORT_ALL, params.withDefaults());
127     }
128
129     /**
130      * Testing parsing of with-defaults parameter which value doesn't match report-all or report-all-tagged patterns
131      * - non-reporting setting.
132      */
133     @Test
134     public void parseUriParametersWithDefaultAndNonTaggedTest() {
135         final var params = assertParams(QueryParams::newReadDataParams, WithDefaultsParam.uriName, "explicit");
136         assertEquals(WithDefaultsParam.EXPLICIT, params.withDefaults());
137     }
138
139     /**
140      * Test of parsing user defined parameters from URI request.
141      */
142     @Test
143     public void parseUriParametersUserDefinedTest() {
144         final QName containerChild = QName.create("ns", "container-child");
145
146         final var parameters = new MultivaluedHashMap<String, String>();
147         parameters.putSingle("content", "config");
148         parameters.putSingle("depth", "10");
149         parameters.putSingle("fields", "container-child");
150
151         final var params = assertParams(QueryParams::newReadDataParams, parameters);
152         // content
153         assertEquals(ContentParam.CONFIG, params.content());
154
155         // depth
156         final DepthParam depth = params.depth();
157         assertNotNull(depth);
158         assertEquals(10, depth.value());
159
160         // fields
161         assertNotNull(params.fields());
162
163         // fields for write filtering
164         final var containerSchema = mock(ContainerSchemaNode.class,
165             withSettings().extraInterfaces(ContainerEffectiveStatement.class));
166         final var containerQName = QName.create(containerChild, "container");
167         doReturn(containerQName).when(containerSchema).getQName();
168         final var containerChildSchema = mock(LeafSchemaNode.class);
169         doReturn(containerChild).when(containerChildSchema).getQName();
170         doReturn(containerChildSchema).when(containerSchema).dataChildByName(containerChild);
171
172         final var module = mock(ModuleEffectiveStatement.class);
173         doReturn(Optional.of(containerSchema)).when(module).findSchemaTreeNode(containerQName);
174         final var context = mock(EffectiveModelContext.class);
175         doReturn(Map.of(containerQName.getModule(), module)).when(context).getModuleStatements();
176
177         final var stack = SchemaInferenceStack.of(context);
178         stack.enterSchemaTree(containerQName);
179
180         final QueryParameters queryParameters = QueryParams.newQueryParameters(params,
181             InstanceIdentifierContext.ofStack(stack));
182         final List<Set<QName>> fields = queryParameters.fields();
183         assertNotNull(fields);
184         assertEquals(1, fields.size());
185         assertEquals(Set.of(containerChild), fields.get(0));
186     }
187
188     private static void assertInvalidParam(final Function<UriInfo, ?> paramsMethod, final RestconfQueryParam<?> param) {
189         final var params = new MultivaluedHashMap<String, String>();
190         params.putSingle(param.paramName(), "odl-test-value");
191         assertParamsThrows(ErrorTag.MALFORMED_MESSAGE, paramsMethod, params);
192     }
193
194     private static void assertUnknownParam(final Function<UriInfo, ?> paramsMethod) {
195         final var params = new MultivaluedHashMap<String, String>();
196         params.putSingle("odl-unknown-param", "odl-test-value");
197         assertParamsThrows(ErrorTag.UNKNOWN_ATTRIBUTE, paramsMethod, params);
198     }
199
200     private static void assertInvalidValue(final Function<UriInfo, ?> paramsMethod, final String name) {
201         assertInvalidValue(paramsMethod, name, "odl-invalid-value");
202     }
203
204     private static void assertInvalidValue(final Function<UriInfo, ?> paramsMethod, final String name,
205             final String value) {
206         final var params = new MultivaluedHashMap<String, String>();
207         params.putSingle(name, value);
208         assertParamsThrows(ErrorTag.INVALID_VALUE, paramsMethod, params);
209     }
210
211     private static void assertParamsThrows(final ErrorTag expectedTag, final Function<UriInfo, ?> paramsMethod,
212             final MultivaluedMap<String, String> params) {
213         final var uriInfo = mock(UriInfo.class);
214         doReturn(params).when(uriInfo).getQueryParameters();
215
216         final var ex = assertThrows(RestconfDocumentedException.class,  () -> paramsMethod.apply(uriInfo));
217         final var errors = ex.getErrors();
218         assertEquals(1, errors.size());
219
220         final var error = errors.get(0);
221         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
222         assertEquals(expectedTag, error.getErrorTag());
223     }
224
225     private static <T> T assertParams(final Function<UriInfo, T> paramsMethod, final String name,
226             final String value) {
227         final var params = new MultivaluedHashMap<String, String>();
228         params.putSingle(name, value);
229         return assertParams(paramsMethod, params);
230     }
231
232     private static <T> T assertParams(final Function<UriInfo, T> paramsMethod,
233             final MultivaluedMap<String, String> params) {
234         final var uriInfo = mock(UriInfo.class);
235         doReturn(params).when(uriInfo).getQueryParameters();
236         return paramsMethod.apply(uriInfo);
237     }
238 }