1 /*******************************************************************************
2 * ============LICENSE_START==================================================
4 * * ===========================================================================
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6 * * ===========================================================================
7 * * Licensed under the Apache License, Version 2.0 (the "License");
8 * * you may not use this file except in compliance with the License.
9 * * You may obtain a copy of the License at
11 * * http://www.apache.org/licenses/LICENSE-2.0
13 * * Unless required by applicable law or agreed to in writing, software
14 * * distributed under the License is distributed on an "AS IS" BASIS,
15 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * * See the License for the specific language governing permissions and
17 * * limitations under the License.
18 * * ============LICENSE_END====================================================
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 ******************************************************************************/
23 package org.onap.dmaap.datarouter.node;
25 import static org.junit.Assert.assertEquals;
26 import static org.mockito.ArgumentMatchers.any;
27 import static org.mockito.ArgumentMatchers.anyObject;
28 import static org.mockito.ArgumentMatchers.eq;
29 import static org.mockito.Mockito.anyString;
30 import static org.mockito.Mockito.doNothing;
31 import static org.mockito.Mockito.mock;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.when;
35 import ch.qos.logback.classic.Logger;
36 import ch.qos.logback.classic.spi.ILoggingEvent;
37 import ch.qos.logback.core.read.ListAppender;
39 import java.io.IOException;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.Enumeration;
44 import java.util.List;
45 import jakarta.servlet.http.HttpServletRequest;
46 import jakarta.servlet.http.HttpServletResponse;
47 import org.apache.commons.lang3.reflect.FieldUtils;
48 import org.junit.AfterClass;
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.mockito.Mock;
53 import org.onap.dmaap.datarouter.node.delivery.Delivery;
54 import org.powermock.api.mockito.PowerMockito;
55 import org.powermock.core.classloader.annotations.PowerMockIgnore;
56 import org.powermock.core.classloader.annotations.PrepareForTest;
57 import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
58 import org.powermock.modules.junit4.PowerMockRunner;
59 import org.slf4j.LoggerFactory;
61 @RunWith(PowerMockRunner.class)
62 @SuppressStaticInitializationFor("org.onap.dmaap.datarouter.node.NodeConfigManager")
63 @PrepareForTest(NodeServer.class)
64 @PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.*"})
65 public class NodeServletTest {
67 private NodeServlet nodeServlet;
68 private Delivery delivery;
71 private HttpServletRequest request;
74 private HttpServletResponse response;
76 private ListAppender<ILoggingEvent> listAppender;
78 private NodeConfigManager config = mock(NodeConfigManager.class);
81 public void setUp() throws Exception {
82 listAppender = setTestLogger();
83 setBehalfHeader("Stub_Value");
84 when(request.getPathInfo()).thenReturn("2");
85 when(request.isSecure()).thenReturn(true);
86 createFilesAndDirectories();
88 setUpNodeMainDelivery();
89 delivery = mock(Delivery.class);
90 when(delivery.markTaskSuccess("spool/s/0/1", "dmaap-dr-node.1234567")).thenReturn(true);
91 PowerMockito.mockStatic(NodeServer.class);
92 nodeServlet = new NodeServlet(delivery, config);
93 when(request.getHeader("Authorization")).thenReturn("User1");
94 when(request.getHeader("X-DMAAP-DR-PUBLISH-ID")).thenReturn("User1");
98 public static void tearDown() {
99 deleteCreatedDirectories();
103 public void Given_Request_Is_HTTP_GET_And_Config_Is_Down_Then_Service_Unavailable_Response_Is_Generated() throws Exception {
104 setNodeConfigManagerIsConfiguredToReturnFalse();
105 nodeServlet.doGet(request, response);
106 verify(response).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));
107 verifyEnteringExitCalled(listAppender);
111 public void Given_Request_Is_HTTP_GET_And_Endpoint_Is_Internal_FetchProv_Then_No_Content_Response_Is_Generated() {
112 when(request.getPathInfo()).thenReturn("/internal/fetchProv");
113 nodeServlet.doGet(request, response);
114 verify(response).setStatus(eq(HttpServletResponse.SC_NO_CONTENT));
115 verifyEnteringExitCalled(listAppender);
119 public void Given_Request_Is_HTTP_GET_And_Endpoint_Is_ResetSubscription_Then_No_Content_Response_Is_Generated() {
120 when(request.getPathInfo()).thenReturn("/internal/resetSubscription/1");
121 nodeServlet.doGet(request, response);
122 verify(response).setStatus(eq(HttpServletResponse.SC_NO_CONTENT));
123 verifyEnteringExitCalled(listAppender);
127 public void Given_Request_Is_HTTP_GET_To_Invalid_Endpoint_Then_Not_Found_Response_Is_Generated() throws Exception {
128 when(request.getPathInfo()).thenReturn("/incorrect");
129 nodeServlet.doGet(request, response);
130 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND));
131 verifyEnteringExitCalled(listAppender);
135 public void Given_Request_Is_HTTP_PUT_And_Config_Is_Down_Then_Service_Unavailable_Response_Is_Generated() throws Exception {
136 setNodeConfigManagerIsConfiguredToReturnFalse();
137 nodeServlet.doPut(request, response);
138 verify(response).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));
139 verifyEnteringExitCalled(listAppender);
143 public void Given_Request_Is_HTTP_PUT_And_Endpoint_Is_Incorrect_Then_Not_Found_Response_Is_Generated() throws Exception {
144 when(request.getPathInfo()).thenReturn("/incorrect/");
145 nodeServlet.doPut(request, response);
146 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
147 verifyEnteringExitCalled(listAppender);
151 public void Given_Request_Is_HTTP_PUT_And_Request_Is_Not_Secure_And_TLS_Enabled_Then_Forbidden_Response_Is_Generated() throws Exception {
152 when(request.isSecure()).thenReturn(false);
153 when(config.isTlsEnabled()).thenReturn(true);
154 nodeServlet.doPut(request, response);
155 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
156 verifyEnteringExitCalled(listAppender);
160 public void Given_Request_Is_HTTP_PUT_And_File_Id_Is_Null_Then_Not_Found_Response_Is_Generated() throws Exception {
161 when(request.getPathInfo()).thenReturn(null);
162 nodeServlet.doPut(request, response);
163 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
164 verifyEnteringExitCalled(listAppender);
168 public void Given_Request_Is_HTTP_PUT_And_Authorization_Is_Null_Then_Forbidden_Response_Is_Generated() throws Exception {
169 when(request.getHeader("Authorization")).thenReturn(null);
170 nodeServlet.doPut(request, response);
171 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
172 verifyEnteringExitCalled(listAppender);
176 public void Given_Request_Is_HTTP_PUT_And_Publish_Does_Not_Include_File_Id_Then_Not_Found_Response_Is_Generated() throws Exception {
177 when(request.getPathInfo()).thenReturn("/publish/");
178 nodeServlet.doPut(request, response);
179 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
180 verifyEnteringExitCalled(listAppender);
184 public void Given_Request_Is_HTTP_PUT_And_Publish_Not_Permitted_Then_Forbidden_Response_Is_Generated() throws Exception {
185 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
186 when(request.getRemoteAddr()).thenReturn("1.2.3.4");
187 setNodeConfigManagerIsPublishPermittedToReturnAReason();
188 nodeServlet.doPut(request, response);
189 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
190 verifyEnteringExitCalled(listAppender);
194 public void Given_Request_Is_HTTP_PUT_And_Internal_Publish_On_Same_Node_Then_Forbidden_Response_Is_Generated() throws Exception {
195 when(request.getPathInfo()).thenReturn("/internal/publish/1/fileName");
196 setNodeConfigManagerIsPublishPermittedToReturnAReason();
197 nodeServlet.doPut(request, response);
198 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN));
199 verifyEnteringExitCalled(listAppender);
203 public void Given_Request_Is_HTTP_PUT_And_Internal_Publish_But_Invalid_File_Id_Then_Not_Found_Response_Is_Generated() throws Exception {
204 when(request.getPathInfo()).thenReturn("/internal/publish/1/blah");
205 when(request.getRemoteAddr()).thenReturn("1.2.3.4");
206 when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
207 nodeServlet.doPut(request, response);
208 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
209 verifyEnteringExitCalled(listAppender);
213 public void Given_Request_Is_HTTP_PUT_On_Publish_And_Ingress_Node_Is_Provided_Then_Request_Is_Redirected() throws Exception {
214 setNodeConfigManagerToAllowRedirectOnIngressNode();
215 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
216 when(request.getRemoteAddr()).thenReturn("1.2.3.4");
217 nodeServlet.doPut(request, response);
218 verify(response).sendRedirect(anyString());
219 verifyEnteringExitCalled(listAppender);
223 public void Given_Request_Is_HTTP_PUT_On_Publish_With_Meta_Data_Too_Long_Then_Bad_Request_Response_Is_Generated() throws Exception {
224 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
225 setHeadersForValidRequest(true);
226 nodeServlet.doPut(request, response);
227 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), anyString());
231 public void Given_Request_Is_HTTP_PUT_On_Publish_With_Meta_Data_Malformed_Then_Bad_Request_Response_Is_Generated() throws Exception {
232 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
233 setHeadersForValidRequest(false);
234 nodeServlet.doPut(request, response);
235 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), anyString());
240 public void Given_Request_Is_HTTP_DELETE_On_Publish_With_Meta_Data_Malformed_Then_Bad_Request_Response_Is_Generated() throws Exception {
241 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
242 setHeadersForValidRequest(false);
243 nodeServlet.doDelete(request, response);
244 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), anyString());
248 public void Given_Request_Is_HTTP_DELETE_File_With_Invalid_Endpoint_Then_Not_Found_Response_Is_Generated() throws Exception {
249 when(request.getPathInfo()).thenReturn("/delete/1");
250 nodeServlet.doDelete(request, response);
251 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
252 verifyEnteringExitCalled(listAppender);
256 public void Given_Request_Is_HTTP_DELETE_File_And_Is_Not_Privileged_Subscription_Then_Not_Found_Response_Is_Generated() throws Exception {
257 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
258 setUpConfigToReturnUnprivilegedSubscriber();
259 nodeServlet.doDelete(request, response);
260 verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED));
261 verifyEnteringExitCalled(listAppender);
265 public void Given_Request_Is_HTTP_DELETE_File_And_Subscription_Does_Not_Exist_Then_Not_Found_Response_Is_Generated() throws Exception {
266 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
267 setUpConfigToReturnNullOnIsDeletePermitted();
268 nodeServlet.doDelete(request, response);
269 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND));
270 verifyEnteringExitCalled(listAppender);
274 public void Given_Request_Is_HTTP_DELETE_File_Then_Request_Succeeds() throws Exception {
275 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
276 createFilesAndDirectories();
277 nodeServlet.doDelete(request, response);
278 verify(response).setStatus(eq(HttpServletResponse.SC_OK));
279 verifyEnteringExitCalled(listAppender);
283 public void Given_Request_Is_HTTP_DELETE_File_And_Request_Is_Not_Secure_But_TLS_Disabled_Then_Request_Succeeds() throws Exception {
284 when(request.isSecure()).thenReturn(false);
285 when(config.isTlsEnabled()).thenReturn(false);
286 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
287 createFilesAndDirectories();
288 nodeServlet.doDelete(request, response);
289 verify(response).setStatus(eq(HttpServletResponse.SC_OK));
290 verifyEnteringExitCalled(listAppender);
294 public void Given_Request_Is_HTTP_DELETE_File_And_File_Does_Not_Exist_Then_Not_Found_Response_Is_Generated() throws IOException {
295 when(request.getPathInfo()).thenReturn("/delete/1/nonExistingFile");
296 nodeServlet.doDelete(request, response);
297 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
298 verifyEnteringExitCalled(listAppender);
301 private void setBehalfHeader(String headerValue) {
302 when(request.getHeader("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(headerValue);
305 private ListAppender<ILoggingEvent> setTestLogger() {
306 Logger Logger = (Logger) LoggerFactory.getLogger(NodeServlet.class);
307 ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
308 listAppender.start();
309 Logger.addAppender(listAppender);
313 private void verifyEnteringExitCalled(ListAppender<ILoggingEvent> listAppender) {
314 assertEquals("EELF0004I Entering data router node component with RequestId and InvocationId", listAppender.list.get(0).getMessage());
315 assertEquals("EELF0005I Exiting data router node component with RequestId and InvocationId", listAppender.list.get(listAppender.list.size() -1).getMessage());
318 private void setUpConfig() throws IllegalAccessException {
319 PowerMockito.mockStatic(NodeConfigManager.class);
320 when(config.isShutdown()).thenReturn(false);
321 when(config.isConfigured()).thenReturn(true);
322 when(config.getSpoolDir()).thenReturn("spool/f");
323 when(config.getSpoolBase()).thenReturn("spool");
324 when(config.getLogDir()).thenReturn("log/dir");
325 when(config.getPublishId()).thenReturn("User1");
326 when(config.isAnotherNode(anyString(), anyString())).thenReturn(false);
327 when(config.getEventLogInterval()).thenReturn("40");
328 when(config.isDeletePermitted("1")).thenReturn(true);
329 when(config.getAllDests()).thenReturn(new DestInfo[0]);
330 FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
333 private void setUpConfigToReturnUnprivilegedSubscriber() throws IllegalAccessException {
334 PowerMockito.mockStatic(NodeConfigManager.class);
335 when(config.isShutdown()).thenReturn(false);
336 when(config.isConfigured()).thenReturn(true);
337 when(config.isDeletePermitted("1")).thenReturn(false);
338 FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
341 private void setUpConfigToReturnNullOnIsDeletePermitted() throws IllegalAccessException {
342 PowerMockito.mockStatic(NodeConfigManager.class);
343 when(config.isShutdown()).thenReturn(false);
344 when(config.isConfigured()).thenReturn(true);
345 when(config.isDeletePermitted("1")).thenThrow(new NullPointerException());
346 FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
349 private void setUpNodeMainDelivery() throws IllegalAccessException{
350 Delivery delivery = mock(Delivery.class);
351 doNothing().when(delivery).resetQueue(anyObject());
352 FieldUtils.writeDeclaredStaticField(NodeServer.class, "delivery", delivery, true);
355 private void setNodeConfigManagerIsConfiguredToReturnFalse() throws IllegalAccessException {
356 when(config.isConfigured()).thenReturn(false);
357 FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
360 private void setNodeConfigManagerIsPublishPermittedToReturnAReason() throws IllegalAccessException{
361 when(config.isShutdown()).thenReturn(false);
362 when(config.getMyName()).thenReturn("dmaap-dr-node");
363 when(config.isConfigured()).thenReturn(true);
364 when(config.getSpoolDir()).thenReturn("spool/dir");
365 when(config.getLogDir()).thenReturn("log/dir");
366 when(config.isPublishPermitted(anyString(), anyString(), anyString())).thenReturn("Publisher not permitted for this feed");
367 when(config.isAnotherNode(anyString(), anyString())).thenReturn(false);
368 FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
371 private void setNodeConfigManagerToAllowRedirectOnIngressNode() {
372 when(config.isShutdown()).thenReturn(false);
373 when(config.isConfigured()).thenReturn(true);
374 when(config.getSpoolDir()).thenReturn("spool/dir");
375 when(config.getLogDir()).thenReturn("log/dir");
376 when(config.getPublishId()).thenReturn("User1");
377 when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
378 when(config.getAuthUser(anyString(), anyString())).thenReturn("User1");
379 when(config.getIngressNode(anyString(), anyString(), anyString())).thenReturn("NewNode");
380 when(config.getExtHttpsPort()).thenReturn(8080);
383 private String createLargeMetaDataString() {
384 StringBuilder myString = new StringBuilder("meta");
385 for (int i = 0; i <= 4098; ++i) {
386 myString.append('x');
388 return myString.toString();
391 private void setHeadersForValidRequest(boolean isMetaTooLong) {
392 String metaDataString;
394 metaDataString = createLargeMetaDataString();
396 metaDataString = "?#@><";
398 List<String> headers = new ArrayList<>();
399 headers.add("Content-Type");
400 headers.add("X-DMAAP-DR-ON-BEHALF-OF");
401 headers.add("X-DMAAP-DR-META");
402 Enumeration<String> headerNames = Collections.enumeration(headers);
403 when(request.getHeaderNames()).thenReturn(headerNames);
404 Enumeration<String> contentTypeHeader = Collections.enumeration(Arrays.asList("text/plain"));
405 Enumeration<String> behalfHeader = Collections.enumeration(Arrays.asList("User1"));
406 Enumeration<String> metaDataHeader = Collections.enumeration(Arrays.asList(metaDataString));
407 when(request.getHeaders("Content-Type")).thenReturn(contentTypeHeader);
408 when(request.getHeaders("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(behalfHeader);
409 when(request.getHeaders("X-DMAAP-DR-META")).thenReturn(metaDataHeader);
412 private void createFilesAndDirectories() throws IOException {
413 File nodeDir = new File("spool/n/172.0.0.1");
414 File spoolDir = new File("spool/s/0/1");
415 File dataFile = new File("spool/s/0/1/dmaap-dr-node.1234567");
416 File metaDataFile = new File("spool/s/0/1/dmaap-dr-node.1234567.M");
419 dataFile.createNewFile();
420 metaDataFile.createNewFile();
423 private static void deleteCreatedDirectories() {
424 File spoolDir = new File("spool");
428 private static void delete(File file) {
429 if (file.isDirectory()) {
430 for (File f: file.listFiles()) {
434 if (!file.delete()) {
435 System.out.println("Failed to delete: " + file);