2 * Copyright (c) 2024 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.restconf.server;
10 import static org.mockito.ArgumentMatchers.any;
11 import static org.mockito.ArgumentMatchers.eq;
12 import static org.mockito.ArgumentMatchers.isNull;
13 import static org.mockito.Mockito.doAnswer;
14 import static org.mockito.Mockito.doReturn;
15 import static org.mockito.Mockito.doThrow;
16 import static org.opendaylight.restconf.server.PathParameters.MODULES;
17 import static org.opendaylight.restconf.server.PathParameters.YANG_LIBRARY_VERSION;
18 import static org.opendaylight.restconf.server.RestconfRequestDispatcher.MISSING_FILENAME_ERROR;
19 import static org.opendaylight.restconf.server.RestconfRequestDispatcher.REVISION;
20 import static org.opendaylight.restconf.server.RestconfRequestDispatcher.SOURCE_READ_FAILURE_ERROR;
21 import static org.opendaylight.restconf.server.TestUtils.answerCompleteWith;
22 import static org.opendaylight.restconf.server.TestUtils.assertErrorResponse;
23 import static org.opendaylight.restconf.server.TestUtils.assertOptionsResponse;
24 import static org.opendaylight.restconf.server.TestUtils.assertResponse;
25 import static org.opendaylight.restconf.server.TestUtils.buildRequest;
26 import static org.opendaylight.restconf.server.TestUtils.charSource;
27 import static org.opendaylight.restconf.server.TestUtils.formattableBody;
29 import com.google.common.io.ByteSource;
30 import com.google.common.io.CharSource;
31 import io.netty.handler.codec.http.DefaultFullHttpRequest;
32 import io.netty.handler.codec.http.HttpMethod;
33 import io.netty.handler.codec.http.HttpResponseStatus;
34 import io.netty.handler.codec.http.HttpVersion;
35 import java.io.IOException;
36 import java.util.stream.Stream;
37 import org.junit.jupiter.params.ParameterizedTest;
38 import org.junit.jupiter.params.provider.Arguments;
39 import org.junit.jupiter.params.provider.MethodSource;
40 import org.junit.jupiter.params.provider.ValueSource;
41 import org.mockito.Mock;
42 import org.opendaylight.restconf.server.TestUtils.TestEncoding;
43 import org.opendaylight.restconf.server.api.ModulesGetResult;
44 import org.opendaylight.yangtools.yang.common.ErrorTag;
46 class ModulesRequestProcessorTest extends AbstractRequestProcessorTest {
47 private static final String YANG_LIBRARY_VERSION_URI = BASE_PATH + YANG_LIBRARY_VERSION;
48 private static final String MODULES_PATH = BASE_PATH + MODULES + "/";
49 private static final String MODULE_FILENAME = "module-filename";
50 private static final String MODULE_URI = MODULES_PATH + MODULE_FILENAME;
51 private static final String MODULE_URI_WITH_MOUNT = MODULES_PATH + MOUNT_PATH + "/" + MODULE_FILENAME;
52 private static final String REVISION_VALUE = "revision-value";
53 private static final String REVISION_PARAM = "?" + REVISION + "=" + REVISION_VALUE;
54 private static final String YANG_CONTENT = "yang-content";
55 private static final String YIN_CONTENT = "yin-content";
58 private CharSource source;
60 private ByteSource byteSource;
63 @ValueSource(strings = {YANG_LIBRARY_VERSION_URI, MODULES_PATH, MODULE_URI, MODULE_URI_WITH_MOUNT})
64 void options(final String uri) {
65 final var request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.OPTIONS, uri);
66 assertOptionsResponse(dispatch(request), "GET, HEAD, OPTIONS");
70 @MethodSource("encodings")
71 void getYangLibraryVersion(final TestEncoding encoding, final String content) {
72 final var result = formattableBody(encoding, content);
73 doAnswer(answerCompleteWith(result)).when(server).yangLibraryVersionGET(any());
75 final var request = buildRequest(HttpMethod.GET, YANG_LIBRARY_VERSION_URI, encoding, null);
76 final var response = dispatch(request);
77 assertResponse(response, HttpResponseStatus.OK, encoding.responseType, content);
81 @MethodSource("modules")
82 void getModule(final TestEncoding encoding, final String content, final boolean hasRevision) {
83 final var result = new ModulesGetResult(charSource(content));
84 final var revision = hasRevision ? REVISION_VALUE : null;
85 if (encoding.isYin()) {
86 doAnswer(answerCompleteWith(result)).when(server).modulesYinGET(any(), eq(MODULE_FILENAME), eq(revision));
88 doAnswer(answerCompleteWith(result)).when(server).modulesYangGET(any(), eq(MODULE_FILENAME), eq(revision));
91 final var uri = MODULE_URI + (hasRevision ? REVISION_PARAM : "");
92 final var request = buildRequest(HttpMethod.GET, uri, encoding, null);
93 final var response = dispatch(request);
94 assertResponse(response, HttpResponseStatus.OK, encoding.responseType, content);
98 @MethodSource("modules")
99 void getModuleWithMountPath(final TestEncoding encoding, final String content, final boolean hasRevision) {
100 final var result = new ModulesGetResult(charSource(content));
101 final var revision = hasRevision ? REVISION_VALUE : null;
102 if (encoding.isYin()) {
103 doAnswer(answerCompleteWith(result)).when(server)
104 .modulesYinGET(any(), eq(MOUNT_API_PATH), eq(MODULE_FILENAME), eq(revision));
106 doAnswer(answerCompleteWith(result)).when(server)
107 .modulesYangGET(any(), eq(MOUNT_API_PATH), eq(MODULE_FILENAME), eq(revision));
109 final var uri = MODULE_URI_WITH_MOUNT + (hasRevision ? REVISION_PARAM : "");
110 final var request = buildRequest(HttpMethod.GET, uri, encoding, null);
111 final var response = dispatch(request);
112 assertResponse(response, HttpResponseStatus.OK, encoding.responseType, content);
115 private static Stream<Arguments> modules() {
117 // encoding, content, hasRevision
118 Arguments.of(TestEncoding.YANG, YANG_CONTENT, true),
119 Arguments.of(TestEncoding.YANG, YANG_CONTENT, false),
120 Arguments.of(TestEncoding.YIN, YIN_CONTENT, true),
121 Arguments.of(TestEncoding.YIN, YIN_CONTENT, false)
126 @MethodSource("moduleErrorEncodings")
127 void noFilenameError(final TestEncoding encoding, final TestEncoding errorEncoding) {
128 final var request = buildRequest(HttpMethod.GET, MODULES_PATH, encoding, null);
129 final var response = dispatch(request);
130 assertErrorResponse(response, errorEncoding, ErrorTag.MISSING_ELEMENT, MISSING_FILENAME_ERROR);
134 @MethodSource("moduleErrorEncodings")
135 void sourceReadFailure(final TestEncoding encoding, final TestEncoding errorEncoding) throws IOException {
136 final var errorMessage = "source-read-failure";
137 doReturn(byteSource).when(source).asByteSource(any());
138 doThrow(new IOException(errorMessage)).when(byteSource).read();
140 final var result = new ModulesGetResult(source);
141 if (encoding.isYin()) {
142 doAnswer(answerCompleteWith(result)).when(server).modulesYinGET(any(), eq(MODULE_FILENAME), isNull());
144 doAnswer(answerCompleteWith(result)).when(server).modulesYangGET(any(), eq(MODULE_FILENAME), isNull());
147 final var request = buildRequest(HttpMethod.GET, MODULE_URI, encoding, null);
148 final var response = dispatch(request);
149 assertErrorResponse(response, errorEncoding, ErrorTag.OPERATION_FAILED,
150 SOURCE_READ_FAILURE_ERROR + errorMessage);
153 private static Stream<Arguments> moduleErrorEncodings() {
155 // request accept encoding, error response encoding
156 Arguments.of(TestEncoding.YANG, DEFAULT_ENCODING),
157 Arguments.of(TestEncoding.YIN, TestEncoding.XML)