2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3 * Copyright (c) 2023 PANTHEON.tech, s.r.o.
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
9 package org.opendaylight.restconf.nb.jaxrs;
11 import static org.junit.jupiter.api.Assertions.assertEquals;
12 import static org.mockito.Mockito.doReturn;
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;
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"));
57 private YangTextSourceExtension sourceProvider;
59 private DOMSchemaService schemaService;
62 EffectiveModelContext modelContext() {
67 * Positive test of getting <code>SchemaExportContext</code>. Expected module name, revision and namespace are
71 void toSchemaExportContextFromIdentifierTest() {
74 namespace test:module;
82 """, assertYang(null, TEST_MODULE_NAME, TEST_MODULE_REVISION));
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.
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());
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
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());
111 * Positive test of getting <code>SchemaExportContext</code> for module behind mount point.
112 * Expected module name, revision and namespace are verified.
115 void toSchemaExportContextFromIdentifierMountPointTest() {
118 final var content = assertYang(MOUNT_POINT_IDENT, TEST_MODULE_NAME, TEST_MODULE_REVISION);
121 namespace test:module;
124 revision 2016-06-02 {
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.
137 void toSchemaExportContextFromIdentifierMountPointNotFoundTest() {
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());
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.
153 void toSchemaExportContextFromIdentifierMountPointInvalidIdentifierNegativeTest() {
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());
164 void toSchemaExportContextFromIdentifierNullSchemaContextBehindMountPointNegativeTest() {
165 doReturn(Optional.of(schemaService)).when(mountPoint).getService(DOMSchemaService.class);
166 doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(MOUNT_IID);
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());
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);
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.
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());
200 * Negative test of module revision validation when supplied revision is not parsable as revision. Test fails
201 * catching <code>RestconfDocumentedException</code>.
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());
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.
214 void validateAndGetModulNameNotSuppliedTest() {
215 final var error = assertInvalidValue(null, null);
216 assertEquals("Module name must be supplied", error.getErrorMessage());
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.
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());
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.
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());
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.
246 void validateAndGetModuleNameEmptyTest() {
247 final var error = assertInvalidValue("", null);
248 assertEquals("Identifier must start with character from set 'a-zA-Z_", error.getErrorMessage());
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);
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());