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