Bump upstreams
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / jaxrs / RestconfModulesGetTest.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  * Copyright (c) 2023 PANTHEON.tech, s.r.o.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9 package org.opendaylight.restconf.nb.jaxrs;
10
11 import static org.junit.jupiter.api.Assertions.assertEquals;
12 import static org.mockito.Mockito.doReturn;
13
14 import com.google.common.io.CharStreams;
15 import java.io.IOException;
16 import java.io.Reader;
17 import java.util.Optional;
18 import java.util.function.Consumer;
19 import javax.ws.rs.container.AsyncResponse;
20 import org.junit.jupiter.api.Test;
21 import org.junit.jupiter.api.extension.ExtendWith;
22 import org.mockito.Mock;
23 import org.mockito.junit.jupiter.MockitoExtension;
24 import org.opendaylight.mdsal.dom.api.DOMActionService;
25 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
26 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
27 import org.opendaylight.mdsal.dom.api.DOMRpcService;
28 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
29 import org.opendaylight.mdsal.dom.api.DOMSchemaService.YangTextSourceExtension;
30 import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
31 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
32 import org.opendaylight.restconf.api.ApiPath;
33 import org.opendaylight.restconf.common.errors.RestconfError;
34 import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
35 import org.opendaylight.yangtools.yang.common.ErrorTag;
36 import org.opendaylight.yangtools.yang.common.ErrorType;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
39 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
40 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
41
42 @ExtendWith(MockitoExtension.class)
43 class RestconfModulesGetTest extends AbstractRestconfTest {
44     private static final EffectiveModelContext MODEL_CONTEXT =
45         YangParserTestUtils.parseYangResourceDirectory("/parser-identifier");
46     private static final EffectiveModelContext MODEL_CONTEXT_ON_MOUNT_POINT =
47         YangParserTestUtils.parseYangResourceDirectory("/parser-identifier");
48     private static final String TEST_MODULE_NAME = "test-module";
49     private static final String TEST_MODULE_REVISION = "2016-06-02";
50     private static final String TEST_MODULE_NAMESPACE = "test:module";
51     private static final ApiPath MOUNT_POINT_IDENT = apiPath("mount-point:mount-container/point-number/yang-ext:mount");
52     private static final YangInstanceIdentifier MOUNT_IID = YangInstanceIdentifier.of(
53         QName.create("mount:point", "2016-06-02", "mount-container"),
54         QName.create("mount:point", "2016-06-02", "point-number"));
55
56     @Mock
57     private YangTextSourceExtension sourceProvider;
58     @Mock
59     private DOMSchemaService schemaService;
60
61     @Override
62     EffectiveModelContext modelContext() {
63         return MODEL_CONTEXT;
64     }
65
66     /**
67      * Positive test of getting <code>SchemaExportContext</code>. Expected module name, revision and namespace are
68      * verified.
69      */
70     @Test
71     void toSchemaExportContextFromIdentifierTest() {
72         assertEquals("""
73             module test-module {
74               namespace test:module;
75               prefix testm;
76               yang-version 1;
77               revision 2016-06-02 {
78                 description
79                   "Initial revision.";
80               }
81             }
82             """, assertYang(null, TEST_MODULE_NAME, TEST_MODULE_REVISION));
83     }
84
85     /**
86      * Test of getting <code>SchemaExportContext</code> when desired module is not found.
87      * <code>SchemaExportContext</code> should not be created and exception is thrown.
88      */
89     @Test
90     void toSchemaExportContextFromIdentifierNotFoundTest() {
91         final var error = assertError(ar -> restconf.modulesYinGET("not-existing-module", "2016-01-01", ar));
92         assertEquals("Source not-existing-module@2016-01-01 not found", error.getErrorMessage());
93         assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
94         assertEquals(ErrorType.APPLICATION, error.getErrorType());
95     }
96
97     /**
98      * Negative test trying to get <code>SchemaExportContext</code> with invalid identifier. Test is expected to fail
99      * with <code>RestconfDocumentedException</code> error type, error tag and error status code are compared to
100      * expected values.
101      */
102     @Test
103     void toSchemaExportContextFromIdentifierInvalidIdentifierNegativeTest() {
104         final var error = assertError(ar -> restconf.modulesYangGET(TEST_MODULE_REVISION, TEST_MODULE_NAME, ar));
105         assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
106         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
107         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
108     }
109
110     /**
111      * Positive test of getting <code>SchemaExportContext</code> for module behind mount point.
112      * Expected module name, revision and namespace are verified.
113      */
114     @Test
115     void toSchemaExportContextFromIdentifierMountPointTest() {
116         mockMountPoint();
117
118         final var content = assertYang(MOUNT_POINT_IDENT, TEST_MODULE_NAME, TEST_MODULE_REVISION);
119         assertEquals("""
120             module test-module {
121               namespace test:module;
122               prefix testm;
123               yang-version 1;
124               revision 2016-06-02 {
125                 description
126                   "Initial revision.";
127               }
128             }
129             """, content);
130     }
131
132     /**
133      * Negative test of getting <code>SchemaExportContext</code> when desired module is not found behind mount point.
134      * <code>SchemaExportContext</code> should not be created and exception is thrown.
135      */
136     @Test
137     void toSchemaExportContextFromIdentifierMountPointNotFoundTest() {
138         mockMountPoint();
139
140         final var error = assertError(
141             ar -> restconf.modulesYangGET(MOUNT_POINT_IDENT, "not-existing-module", "2016-01-01", ar));
142         assertEquals("Source not-existing-module@2016-01-01 not found", error.getErrorMessage());
143         assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
144         assertEquals(ErrorType.APPLICATION, error.getErrorType());
145     }
146
147     /**
148      * Negative test trying to get <code>SchemaExportContext</code> behind mount point with invalid identifier. Test is
149      * expected to fail with <code>RestconfDocumentedException</code> error type, error tag and error status code are
150      * compared to expected values.
151      */
152     @Test
153     void toSchemaExportContextFromIdentifierMountPointInvalidIdentifierNegativeTest() {
154         mockMountPoint();
155
156         final var error = assertError(
157             ar -> restconf.modulesYangGET(MOUNT_POINT_IDENT, TEST_MODULE_REVISION, TEST_MODULE_NAME, ar));
158         assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
159         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
160         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
161     }
162
163     @Test
164     void toSchemaExportContextFromIdentifierNullSchemaContextBehindMountPointNegativeTest() {
165         doReturn(Optional.of(schemaService)).when(mountPoint).getService(DOMSchemaService.class);
166         doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(MOUNT_IID);
167
168         final var error = assertError(
169             ar -> restconf.modulesYangGET(MOUNT_POINT_IDENT, TEST_MODULE_NAME, TEST_MODULE_REVISION, ar));
170         assertEquals("Mount point 'mount-point:mount-container/point-number' does not have any models",
171             error.getErrorMessage());
172         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
173         assertEquals(ErrorTags.RESOURCE_DENIED_TRANSPORT, error.getErrorTag());
174     }
175
176     private void mockMountPoint() {
177         doReturn(Optional.of(new FixedDOMSchemaService(MODEL_CONTEXT_ON_MOUNT_POINT))).when(mountPoint)
178             .getService(DOMSchemaService.class);
179         doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(MOUNT_IID);
180         doReturn(Optional.of(mountPointService)).when(mountPoint).getService(DOMMountPointService.class);
181         doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
182         doReturn(Optional.empty()).when(mountPoint).getService(DOMActionService.class);
183         doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
184         doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
185     }
186
187     /**
188      * Negative test of module revision validation when there is no revision. Test fails catching
189      * <code>RestconfDocumentedException</code> and checking for correct error type, error tag and error status code.
190      */
191     @Test
192     void validateAndGetRevisionNotSuppliedTest() {
193         final var error = assertError(ar -> restconf.modulesYangGET("module", null, ar));
194         assertEquals("Source module not found", error.getErrorMessage());
195         assertEquals(ErrorType.APPLICATION, error.getErrorType());
196         assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
197     }
198
199     /**
200      * Negative test of module revision validation when supplied revision is not parsable as revision. Test fails
201      * catching <code>RestconfDocumentedException</code>.
202      */
203     @Test
204     void validateAndGetRevisionNotParsableTest() {
205         final var error = assertInvalidValue("module", "not-parsable-as-date");
206         assertEquals("Supplied revision is not in expected date format YYYY-mm-dd", error.getErrorMessage());
207     }
208
209     /**
210      * Negative test of module name validation when there is no module name. Test fails catching
211      * <code>RestconfDocumentedException</code> and checking for correct error type, error tag and error status code.
212      */
213     @Test
214     void validateAndGetModulNameNotSuppliedTest() {
215         final var error = assertInvalidValue(null, null);
216         assertEquals("Module name must be supplied", error.getErrorMessage());
217     }
218
219     /**
220      * Negative test of module name validation when supplied name is not parsable as module name on the first
221      * character. Test fails catching <code>RestconfDocumentedException</code> and checking for correct error type,
222      * error tag and error status code.
223      */
224     @Test
225     void validateAndGetModuleNameNotParsableFirstTest() {
226         final var error = assertInvalidValue("01-not-parsable-as-name-on-first-char", null);
227         assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
228     }
229
230     /**
231      * Negative test of module name validation when supplied name is not parsable as module name on any of the
232      * characters after the first character. Test fails catching <code>RestconfDocumentedException</code> and checking
233      * for correct error type, error tag and error status code.
234      */
235     @Test
236     public void validateAndGetModuleNameNotParsableNextTest() {
237         final var error = assertInvalidValue("not-parsable-as-name-after-first-char*", null);
238         assertEquals("Supplied name has not expected identifier format", error.getErrorMessage());
239     }
240
241     /**
242      * Negative test of module name validation when supplied name is empty. Test fails catching
243      * <code>RestconfDocumentedException</code> and checking for correct error type, error tag and error status code.
244      */
245     @Test
246     void validateAndGetModuleNameEmptyTest() {
247         final var error = assertInvalidValue("", null);
248         assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
249     }
250
251     private String assertYang(final ApiPath mountPath, final String fileName, final String revision) {
252         final Consumer<AsyncResponse> invocation = mountPath != null
253             ? ar -> restconf.modulesYangGET(mountPath, fileName, revision, ar)
254                 : ar -> restconf.modulesYangGET(fileName, revision, ar);
255         try (var reader = assertEntity(Reader.class, 200, invocation)) {
256             return CharStreams.toString(reader);
257         } catch (IOException e) {
258             throw new AssertionError(e);
259         }
260     }
261
262     private RestconfError assertInvalidValue(final String fileName, final String revision) {
263         final var error = assertError(ar -> restconf.modulesYangGET(fileName, revision, ar));
264         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
265         assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
266         return error;
267     }
268 }