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.anyObject;
27 import static org.mockito.ArgumentMatchers.eq;
28 import static org.mockito.Mockito.anyString;
29 import static org.mockito.Mockito.doNothing;
30 import static org.mockito.Mockito.mock;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
34 import ch.qos.logback.classic.Logger;
35 import ch.qos.logback.classic.spi.ILoggingEvent;
36 import ch.qos.logback.core.read.ListAppender;
38 import java.io.IOException;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Collections;
42 import java.util.Enumeration;
43 import java.util.List;
44 import javax.servlet.http.HttpServletRequest;
45 import javax.servlet.http.HttpServletResponse;
46 import org.apache.commons.lang3.reflect.FieldUtils;
47 import org.junit.AfterClass;
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 import org.mockito.Mock;
52 import org.powermock.api.mockito.PowerMockito;
53 import org.powermock.core.classloader.annotations.PowerMockIgnore;
54 import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
55 import org.powermock.modules.junit4.PowerMockRunner;
56 import org.slf4j.LoggerFactory;
58 @RunWith(PowerMockRunner.class)
59 @SuppressStaticInitializationFor("org.onap.dmaap.datarouter.node.NodeConfigManager")
60 @PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.*"})
61 public class NodeServletTest {
63 private NodeServlet nodeServlet;
64 private Delivery delivery;
67 private HttpServletRequest request;
70 private HttpServletResponse response;
72 private ListAppender<ILoggingEvent> listAppender;
74 private NodeConfigManager config = mock(NodeConfigManager.class);
77 public void setUp() throws Exception {
78 listAppender = setTestLogger();
79 setBehalfHeader("Stub_Value");
80 when(request.getPathInfo()).thenReturn("2");
81 when(request.isSecure()).thenReturn(true);
82 createFilesAndDirectories();
84 setUpNodeMainDelivery();
85 delivery = mock(Delivery.class);
86 when(delivery.markTaskSuccess("spool/s/0/1", "dmaap-dr-node.1234567")).thenReturn(true);
87 nodeServlet = new NodeServlet(delivery);
88 when(request.getHeader("Authorization")).thenReturn("User1");
89 when(request.getHeader("X-DMAAP-DR-PUBLISH-ID")).thenReturn("User1");
93 public static void tearDown() {
94 deleteCreatedDirectories();
98 public void Given_Request_Is_HTTP_GET_And_Config_Is_Down_Then_Service_Unavailable_Response_Is_Generated() throws Exception {
99 setNodeConfigManagerIsConfiguredToReturnFalse();
100 nodeServlet.doGet(request, response);
101 verify(response).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));
102 verifyEnteringExitCalled(listAppender);
106 public void Given_Request_Is_HTTP_GET_And_Endpoint_Is_Internal_FetchProv_Then_No_Content_Response_Is_Generated() {
107 when(request.getPathInfo()).thenReturn("/internal/fetchProv");
108 nodeServlet.doGet(request, response);
109 verify(response).setStatus(eq(HttpServletResponse.SC_NO_CONTENT));
110 verifyEnteringExitCalled(listAppender);
114 public void Given_Request_Is_HTTP_GET_And_Endpoint_Is_ResetSubscription_Then_No_Content_Response_Is_Generated() {
115 when(request.getPathInfo()).thenReturn("/internal/resetSubscription/1");
116 nodeServlet.doGet(request, response);
117 verify(response).setStatus(eq(HttpServletResponse.SC_NO_CONTENT));
118 verifyEnteringExitCalled(listAppender);
122 public void Given_Request_Is_HTTP_GET_To_Invalid_Endpoint_Then_Not_Found_Response_Is_Generated() throws Exception {
123 when(request.getPathInfo()).thenReturn("/incorrect");
124 nodeServlet.doGet(request, response);
125 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND));
126 verifyEnteringExitCalled(listAppender);
130 public void Given_Request_Is_HTTP_PUT_And_Config_Is_Down_Then_Service_Unavailable_Response_Is_Generated() throws Exception {
131 setNodeConfigManagerIsConfiguredToReturnFalse();
132 nodeServlet.doPut(request, response);
133 verify(response).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));
134 verifyEnteringExitCalled(listAppender);
138 public void Given_Request_Is_HTTP_PUT_And_Endpoint_Is_Incorrect_Then_Not_Found_Response_Is_Generated() throws Exception {
139 when(request.getPathInfo()).thenReturn("/incorrect/");
140 nodeServlet.doPut(request, response);
141 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
142 verifyEnteringExitCalled(listAppender);
146 public void Given_Request_Is_HTTP_PUT_And_Request_Is_Not_Secure_And_TLS_Enabled_Then_Forbidden_Response_Is_Generated() throws Exception {
147 when(request.isSecure()).thenReturn(false);
148 when(config.isTlsEnabled()).thenReturn(true);
149 nodeServlet.doPut(request, response);
150 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
151 verifyEnteringExitCalled(listAppender);
155 public void Given_Request_Is_HTTP_PUT_And_File_Id_Is_Null_Then_Not_Found_Response_Is_Generated() throws Exception {
156 when(request.getPathInfo()).thenReturn(null);
157 nodeServlet.doPut(request, response);
158 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
159 verifyEnteringExitCalled(listAppender);
163 public void Given_Request_Is_HTTP_PUT_And_Authorization_Is_Null_Then_Forbidden_Response_Is_Generated() throws Exception {
164 when(request.getHeader("Authorization")).thenReturn(null);
165 nodeServlet.doPut(request, response);
166 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
167 verifyEnteringExitCalled(listAppender);
171 public void Given_Request_Is_HTTP_PUT_And_Publish_Does_Not_Include_File_Id_Then_Not_Found_Response_Is_Generated() throws Exception {
172 when(request.getPathInfo()).thenReturn("/publish/");
173 nodeServlet.doPut(request, response);
174 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
175 verifyEnteringExitCalled(listAppender);
179 public void Given_Request_Is_HTTP_PUT_And_Publish_Not_Permitted_Then_Forbidden_Response_Is_Generated() throws Exception {
180 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
181 when(request.getRemoteAddr()).thenReturn("1.2.3.4");
182 setNodeConfigManagerIsPublishPermittedToReturnAReason();
183 nodeServlet.doPut(request, response);
184 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
185 verifyEnteringExitCalled(listAppender);
189 public void Given_Request_Is_HTTP_PUT_And_Internal_Publish_On_Same_Node_Then_Forbidden_Response_Is_Generated() throws Exception {
190 when(request.getPathInfo()).thenReturn("/internal/publish/1/fileName");
191 setNodeConfigManagerIsPublishPermittedToReturnAReason();
192 nodeServlet.doPut(request, response);
193 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN));
194 verifyEnteringExitCalled(listAppender);
198 public void Given_Request_Is_HTTP_PUT_And_Internal_Publish_But_Invalid_File_Id_Then_Not_Found_Response_Is_Generated() throws Exception {
199 when(request.getPathInfo()).thenReturn("/internal/publish/1/blah");
200 when(request.getRemoteAddr()).thenReturn("1.2.3.4");
201 when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
202 nodeServlet.doPut(request, response);
203 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
204 verifyEnteringExitCalled(listAppender);
208 public void Given_Request_Is_HTTP_PUT_On_Publish_And_Ingress_Node_Is_Provided_Then_Request_Is_Redirected() throws Exception {
209 setNodeConfigManagerToAllowRedirectOnIngressNode();
210 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
211 when(request.getRemoteAddr()).thenReturn("1.2.3.4");
212 nodeServlet.doPut(request, response);
213 verify(response).sendRedirect(anyString());
214 verifyEnteringExitCalled(listAppender);
218 public void Given_Request_Is_HTTP_PUT_On_Publish_With_Meta_Data_Too_Long_Then_Bad_Request_Response_Is_Generated() throws Exception {
219 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
220 setHeadersForValidRequest(true);
221 nodeServlet.doPut(request, response);
222 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), anyString());
226 public void Given_Request_Is_HTTP_PUT_On_Publish_With_Meta_Data_Malformed_Then_Bad_Request_Response_Is_Generated() throws Exception {
227 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
228 setHeadersForValidRequest(false);
229 nodeServlet.doPut(request, response);
230 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), anyString());
234 public void Given_Request_Is_HTTP_PUT_On_Publish_On_AAF_Feed_And_Cadi_Enabled_And_No_Permissions_Then_Forbidden_Response_Is_Generated() throws Exception {
235 when(config.getCadiEnabled()).thenReturn(true);
236 when(config.getAafInstance("1")).thenReturn("*");
237 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
238 setHeadersForValidRequest(true);
239 nodeServlet.doPut(request, response);
240 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
241 verifyEnteringExitCalled(listAppender);
245 public void Given_Request_Is_HTTP_DELETE_On_Publish_With_Meta_Data_Malformed_Then_Bad_Request_Response_Is_Generated() throws Exception {
246 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
247 setHeadersForValidRequest(false);
248 nodeServlet.doDelete(request, response);
249 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), anyString());
253 public void Given_Request_Is_HTTP_DELETE_File_With_Invalid_Endpoint_Then_Not_Found_Response_Is_Generated() throws Exception {
254 when(request.getPathInfo()).thenReturn("/delete/1");
255 nodeServlet.doDelete(request, response);
256 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
257 verifyEnteringExitCalled(listAppender);
261 public void Given_Request_Is_HTTP_DELETE_File_And_Is_Not_Privileged_Subscription_Then_Not_Found_Response_Is_Generated() throws Exception {
262 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
263 setUpConfigToReturnUnprivilegedSubscriber();
264 nodeServlet.doDelete(request, response);
265 verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED));
266 verifyEnteringExitCalled(listAppender);
270 public void Given_Request_Is_HTTP_DELETE_File_And_Subscription_Does_Not_Exist_Then_Not_Found_Response_Is_Generated() throws Exception {
271 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
272 setUpConfigToReturnNullOnIsDeletePermitted();
273 nodeServlet.doDelete(request, response);
274 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND));
275 verifyEnteringExitCalled(listAppender);
279 public void Given_Request_Is_HTTP_DELETE_File_Then_Request_Succeeds() throws Exception {
280 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
281 createFilesAndDirectories();
282 nodeServlet.doDelete(request, response);
283 verify(response).setStatus(eq(HttpServletResponse.SC_OK));
284 verifyEnteringExitCalled(listAppender);
288 public void Given_Request_Is_HTTP_DELETE_File_And_Request_Is_Not_Secure_But_TLS_Disabled_Then_Request_Succeeds() throws Exception {
289 when(request.isSecure()).thenReturn(false);
290 when(config.isTlsEnabled()).thenReturn(false);
291 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
292 createFilesAndDirectories();
293 nodeServlet.doDelete(request, response);
294 verify(response).setStatus(eq(HttpServletResponse.SC_OK));
295 verifyEnteringExitCalled(listAppender);
299 public void Given_Request_Is_HTTP_DELETE_File_And_File_Does_Not_Exist_Then_Not_Found_Response_Is_Generated() throws IOException {
300 when(request.getPathInfo()).thenReturn("/delete/1/nonExistingFile");
301 nodeServlet.doDelete(request, response);
302 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
303 verifyEnteringExitCalled(listAppender);
306 private void setBehalfHeader(String headerValue) {
307 when(request.getHeader("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(headerValue);
310 private ListAppender<ILoggingEvent> setTestLogger() {
311 Logger Logger = (Logger) LoggerFactory.getLogger(NodeServlet.class);
312 ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
313 listAppender.start();
314 Logger.addAppender(listAppender);
318 private void verifyEnteringExitCalled(ListAppender<ILoggingEvent> listAppender) {
319 assertEquals("EELF0004I Entering data router node component with RequestId and InvocationId", listAppender.list.get(0).getMessage());
320 assertEquals("EELF0005I Exiting data router node component with RequestId and InvocationId", listAppender.list.get(listAppender.list.size() -1).getMessage());
323 private void setUpConfig() throws IllegalAccessException {
324 PowerMockito.mockStatic(NodeConfigManager.class);
325 when(config.isShutdown()).thenReturn(false);
326 when(config.isConfigured()).thenReturn(true);
327 when(config.getSpoolDir()).thenReturn("spool/f");
328 when(config.getSpoolBase()).thenReturn("spool");
329 when(config.getLogDir()).thenReturn("log/dir");
330 when(config.getPublishId()).thenReturn("User1");
331 when(config.isAnotherNode(anyString(), anyString())).thenReturn(false);
332 when(config.getEventLogInterval()).thenReturn("40");
333 when(config.isDeletePermitted("1")).thenReturn(true);
334 when(config.getAllDests()).thenReturn(new DestInfo[0]);
335 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
336 FieldUtils.writeDeclaredStaticField(NodeRunner.class, "nodeConfigManager", config, true);
337 PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
340 private void setUpConfigToReturnUnprivilegedSubscriber() throws IllegalAccessException {
341 NodeConfigManager config = mock(NodeConfigManager.class);
342 PowerMockito.mockStatic(NodeConfigManager.class);
343 when(config.isShutdown()).thenReturn(false);
344 when(config.isConfigured()).thenReturn(true);
345 when(config.isDeletePermitted("1")).thenReturn(false);
346 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
347 FieldUtils.writeDeclaredStaticField(NodeRunner.class, "nodeConfigManager", config, true);
348 PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
351 private void setUpConfigToReturnNullOnIsDeletePermitted() throws IllegalAccessException {
352 NodeConfigManager config = mock(NodeConfigManager.class);
353 PowerMockito.mockStatic(NodeConfigManager.class);
354 when(config.isShutdown()).thenReturn(false);
355 when(config.isConfigured()).thenReturn(true);
356 when(config.isDeletePermitted("1")).thenThrow(new NullPointerException());
357 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
358 FieldUtils.writeDeclaredStaticField(NodeRunner.class, "nodeConfigManager", config, true);
359 PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
362 private void setUpNodeMainDelivery() throws IllegalAccessException{
363 Delivery delivery = mock(Delivery.class);
364 doNothing().when(delivery).resetQueue(anyObject());
365 FieldUtils.writeDeclaredStaticField(NodeServer.class, "delivery", delivery, true);
368 private void setNodeConfigManagerIsConfiguredToReturnFalse() throws IllegalAccessException{
369 NodeConfigManager config = mock(NodeConfigManager.class);
370 when(config.isConfigured()).thenReturn(false);
371 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
374 private void setNodeConfigManagerIsPublishPermittedToReturnAReason() throws IllegalAccessException{
375 NodeConfigManager config = mock(NodeConfigManager.class);
376 when(config.isShutdown()).thenReturn(false);
377 when(config.getMyName()).thenReturn("dmaap-dr-node");
378 when(config.isConfigured()).thenReturn(true);
379 when(config.getSpoolDir()).thenReturn("spool/dir");
380 when(config.getLogDir()).thenReturn("log/dir");
381 when(config.isPublishPermitted(anyString(), anyString(), anyString())).thenReturn("Publisher not permitted for this feed");
382 when(config.isAnotherNode(anyString(), anyString())).thenReturn(false);
383 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
386 private void setNodeConfigManagerToAllowRedirectOnIngressNode() throws IllegalAccessException{
387 NodeConfigManager config = mock(NodeConfigManager.class);
388 when(config.isShutdown()).thenReturn(false);
389 when(config.isConfigured()).thenReturn(true);
390 when(config.getSpoolDir()).thenReturn("spool/dir");
391 when(config.getLogDir()).thenReturn("log/dir");
392 when(config.getPublishId()).thenReturn("User1");
393 when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
394 when(config.getAuthUser(anyString(), anyString())).thenReturn("User1");
395 when(config.getIngressNode(anyString(), anyString(), anyString())).thenReturn("NewNode");
396 when(config.getExtHttpsPort()).thenReturn(8080);
397 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
400 private String createLargeMetaDataString() {
401 StringBuilder myString = new StringBuilder("meta");
402 for (int i = 0; i <= 4098; ++i) {
403 myString.append('x');
405 return myString.toString();
408 private void setHeadersForValidRequest(boolean isMetaTooLong) {
409 String metaDataString;
411 metaDataString = createLargeMetaDataString();
413 metaDataString = "?#@><";
415 List<String> headers = new ArrayList<>();
416 headers.add("Content-Type");
417 headers.add("X-DMAAP-DR-ON-BEHALF-OF");
418 headers.add("X-DMAAP-DR-META");
419 Enumeration<String> headerNames = Collections.enumeration(headers);
420 when(request.getHeaderNames()).thenReturn(headerNames);
421 Enumeration<String> contentTypeHeader = Collections.enumeration(Arrays.asList("text/plain"));
422 Enumeration<String> behalfHeader = Collections.enumeration(Arrays.asList("User1"));
423 Enumeration<String> metaDataHeader = Collections.enumeration(Arrays.asList(metaDataString));
424 when(request.getHeaders("Content-Type")).thenReturn(contentTypeHeader);
425 when(request.getHeaders("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(behalfHeader);
426 when(request.getHeaders("X-DMAAP-DR-META")).thenReturn(metaDataHeader);
429 private void createFilesAndDirectories() throws IOException {
430 File nodeDir = new File("spool/n/172.0.0.1");
431 File spoolDir = new File("spool/s/0/1");
432 File dataFile = new File("spool/s/0/1/dmaap-dr-node.1234567");
433 File metaDataFile = new File("spool/s/0/1/dmaap-dr-node.1234567.M");
436 dataFile.createNewFile();
437 metaDataFile.createNewFile();
440 private static void deleteCreatedDirectories() {
441 File spoolDir = new File("spool");
445 private static void delete(File file) {
446 if (file.isDirectory()) {
447 for (File f: file.listFiles()) {
451 if (!file.delete()) {
452 System.out.println("Failed to delete: " + file);