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 ch.qos.logback.classic.spi.ILoggingEvent;
26 import ch.qos.logback.core.read.ListAppender;
27 import ch.qos.logback.classic.Logger;
28 import org.apache.commons.lang3.reflect.FieldUtils;
30 import org.junit.runner.RunWith;
31 import org.mockito.Mock;
32 import org.powermock.api.mockito.PowerMockito;
33 import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
34 import org.powermock.modules.junit4.PowerMockRunner;
35 import org.slf4j.LoggerFactory;
37 import javax.servlet.http.HttpServletRequest;
38 import javax.servlet.http.HttpServletResponse;
41 import java.io.IOException;
44 import static org.hamcrest.Matchers.notNullValue;
45 import static org.junit.Assert.assertEquals;
46 import static org.mockito.Matchers.eq;
47 import static org.mockito.Mockito.*;
49 @RunWith(PowerMockRunner.class)
50 @SuppressStaticInitializationFor("org.onap.dmaap.datarouter.node.NodeConfigManager")
51 public class NodeServletTest {
53 private NodeServlet nodeServlet;
54 private Delivery delivery;
57 private HttpServletRequest request;
60 private HttpServletResponse response;
62 ListAppender<ILoggingEvent> listAppender;
65 public void setUp() throws Exception {
66 listAppender = setTestLogger();
67 setBehalfHeader("Stub_Value");
68 when(request.getPathInfo()).thenReturn("2");
69 when(request.isSecure()).thenReturn(true);
70 createFilesAndDirectories();
72 setUpNodeMainDelivery();
73 delivery = mock(Delivery.class);
74 when(delivery.markTaskSuccess("spool/s/0/1", "dmaap-dr-node.1234567")).thenReturn(true);
75 nodeServlet = new NodeServlet(delivery);
76 when(request.getHeader("Authorization")).thenReturn("User1");
77 when(request.getHeader("X-DMAAP-DR-PUBLISH-ID")).thenReturn("User1");
81 public static void tearDown() {
82 deleteCreatedDirectories();
86 public void Given_Request_Is_HTTP_GET_And_Config_Is_Down_Then_Service_Unavailable_Response_Is_Generated() throws Exception {
87 setNodeConfigManagerIsConfiguredToReturnFalse();
88 nodeServlet.doGet(request, response);
89 verify(response).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));
90 verifyEnteringExitCalled(listAppender);
94 public void Given_Request_Is_HTTP_GET_And_Endpoint_Is_Internal_FetchProv_Then_No_Content_Response_Is_Generated() {
95 when(request.getPathInfo()).thenReturn("/internal/fetchProv");
96 nodeServlet.doGet(request, response);
97 verify(response).setStatus(eq(HttpServletResponse.SC_NO_CONTENT));
98 verifyEnteringExitCalled(listAppender);
102 public void Given_Request_Is_HTTP_GET_And_Endpoint_Is_ResetSubscription_Then_No_Content_Response_Is_Generated() {
103 when(request.getPathInfo()).thenReturn("/internal/resetSubscription/1");
104 nodeServlet.doGet(request, response);
105 verify(response).setStatus(eq(HttpServletResponse.SC_NO_CONTENT));
106 verifyEnteringExitCalled(listAppender);
110 public void Given_Request_Is_HTTP_GET_To_Invalid_Endpoint_Then_Not_Found_Response_Is_Generated() throws Exception {
111 when(request.getPathInfo()).thenReturn("/incorrect");
112 nodeServlet.doGet(request, response);
113 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND));
114 verifyEnteringExitCalled(listAppender);
118 public void Given_Request_Is_HTTP_PUT_And_Config_Is_Down_Then_Service_Unavailable_Response_Is_Generated() throws Exception {
119 setNodeConfigManagerIsConfiguredToReturnFalse();
120 nodeServlet.doPut(request, response);
121 verify(response).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));
122 verifyEnteringExitCalled(listAppender);
126 public void Given_Request_Is_HTTP_PUT_And_Endpoint_Is_Incorrect_Then_Not_Found_Response_Is_Generated() throws Exception {
127 when(request.getPathInfo()).thenReturn("/incorrect/");
128 nodeServlet.doPut(request, response);
129 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
130 verifyEnteringExitCalled(listAppender);
134 public void Given_Request_Is_HTTP_PUT_And_Request_Is_Not_Secure_Then_Forbidden_Response_Is_Generated() throws Exception {
135 when(request.isSecure()).thenReturn(false);
136 nodeServlet.doPut(request, response);
137 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), argThat(notNullValue(String.class)));
138 verifyEnteringExitCalled(listAppender);
142 public void Given_Request_Is_HTTP_PUT_And_File_Id_Is_Null_Then_Not_Found_Response_Is_Generated() throws Exception {
143 when(request.getPathInfo()).thenReturn(null);
144 nodeServlet.doPut(request, response);
145 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
146 verifyEnteringExitCalled(listAppender);
150 public void Given_Request_Is_HTTP_PUT_And_Authorization_Is_Null_Then_Forbidden_Response_Is_Generated() throws Exception {
151 when(request.getHeader("Authorization")).thenReturn(null);
152 nodeServlet.doPut(request, response);
153 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), argThat(notNullValue(String.class)));
154 verifyEnteringExitCalled(listAppender);
158 public void Given_Request_Is_HTTP_PUT_And_Publish_Does_Not_Include_File_Id_Then_Not_Found_Response_Is_Generated() throws Exception {
159 when(request.getPathInfo()).thenReturn("/publish/");
160 nodeServlet.doPut(request, response);
161 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
162 verifyEnteringExitCalled(listAppender);
166 public void Given_Request_Is_HTTP_PUT_And_Publish_Not_Permitted_Then_Forbidden_Response_Is_Generated() throws Exception {
167 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
168 setNodeConfigManagerIsPublishPermittedToReturnAReason();
169 nodeServlet.doPut(request, response);
170 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), argThat(notNullValue(String.class)));
171 verifyEnteringExitCalled(listAppender);
175 public void Given_Request_Is_HTTP_PUT_And_Internal_Publish_On_Same_Node_Then_Forbidden_Response_Is_Generated() throws Exception {
176 when(request.getPathInfo()).thenReturn("/internal/publish/1/fileName");
177 setNodeConfigManagerIsPublishPermittedToReturnAReason();
178 nodeServlet.doPut(request, response);
179 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN));
180 verifyEnteringExitCalled(listAppender);
184 public void Given_Request_Is_HTTP_PUT_And_Internal_Publish_But_Invalid_File_Id_Then_Not_Found_Response_Is_Generated() throws Exception {
185 when(request.getPathInfo()).thenReturn("/internal/publish/1/");
186 nodeServlet.doPut(request, response);
187 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
188 verifyEnteringExitCalled(listAppender);
192 public void Given_Request_Is_HTTP_PUT_On_Publish_And_Ingress_Node_Is_Provided_Then_Request_Is_Redirected() throws Exception {
193 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
194 setNodeConfigManagerToAllowRedirectOnIngressNode();
195 nodeServlet.doPut(request, response);
196 verify(response).sendRedirect(anyString());
197 verifyEnteringExitCalled(listAppender);
201 public void Given_Request_Is_HTTP_PUT_On_Publish_With_Meta_Data_Too_Long_Then_Bad_Request_Response_Is_Generated() throws Exception {
202 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
203 setHeadersForValidRequest(true);
204 nodeServlet.doPut(request, response);
205 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), argThat(notNullValue(String.class)));
206 verifyEnteringExitCalled(listAppender);
210 public void Given_Request_Is_HTTP_PUT_On_Publish_With_Meta_Data_Malformed_Then_Bad_Request_Response_Is_Generated() throws Exception {
211 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
212 setHeadersForValidRequest(false);
213 nodeServlet.doPut(request, response);
214 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), argThat(notNullValue(String.class)));
215 verifyEnteringExitCalled(listAppender);
219 public void Given_Request_Is_HTTP_DELETE_On_Publish_With_Meta_Data_Malformed_Then_Bad_Request_Response_Is_Generated() throws Exception {
220 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
221 setHeadersForValidRequest(false);
222 nodeServlet.doDelete(request, response);
223 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), argThat(notNullValue(String.class)));
224 verifyEnteringExitCalled(listAppender);
228 public void Given_Request_Is_HTTP_DELETE_File_With_Invalid_Endpoint_Then_Not_Found_Response_Is_Generated() throws Exception {
229 when(request.getPathInfo()).thenReturn("/delete/1");
230 nodeServlet.doDelete(request, response);
231 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
232 verifyEnteringExitCalled(listAppender);
236 public void Given_Request_Is_HTTP_DELETE_File_And_Is_Not_Privileged_Subscription_Then_Not_Found_Response_Is_Generated() throws Exception {
237 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
238 setUpConfigToReturnUnprivilegedSubscriber();
239 nodeServlet.doDelete(request, response);
240 verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED));
241 verifyEnteringExitCalled(listAppender);
245 public void Given_Request_Is_HTTP_DELETE_File_And_Subscription_Does_Not_Exist_Then_Not_Found_Response_Is_Generated() throws Exception {
246 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
247 setUpConfigToReturnNullOnIsDeletePermitted();
248 nodeServlet.doDelete(request, response);
249 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND));
250 verifyEnteringExitCalled(listAppender);
254 public void Given_Request_Is_HTTP_DELETE_File_Then_Request_Succeeds() throws Exception {
255 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
256 createFilesAndDirectories();
257 nodeServlet.doDelete(request, response);
258 verify(response).setStatus(eq(HttpServletResponse.SC_OK));
259 verifyEnteringExitCalled(listAppender);
263 public void Given_Request_Is_HTTP_DELETE_File_And_File_Does_Not_Exist_Then_Not_Found_Response_Is_Generated() throws IOException {
264 when(request.getPathInfo()).thenReturn("/delete/1/nonExistingFile");
265 nodeServlet.doDelete(request, response);
266 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
267 verifyEnteringExitCalled(listAppender);
270 private void setBehalfHeader(String headerValue) {
271 when(request.getHeader("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(headerValue);
274 private ListAppender<ILoggingEvent> setTestLogger() {
275 Logger Logger = (Logger) LoggerFactory.getLogger(NodeServlet.class);
276 ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
277 listAppender.start();
278 Logger.addAppender(listAppender);
282 private void verifyEnteringExitCalled(ListAppender<ILoggingEvent> listAppender) {
283 assertEquals("EELF0004I Entering data router node component with RequestId and InvocationId", listAppender.list.get(0).getMessage());
284 assertEquals("EELF0005I Exiting data router node component with RequestId and InvocationId", listAppender.list.get(2).getMessage());
285 assertEquals(3, listAppender.list.size());
288 private void setUpConfig() throws IllegalAccessException {
289 NodeConfigManager config = mock(NodeConfigManager.class);
290 PowerMockito.mockStatic(NodeConfigManager.class);
291 when(config.isShutdown()).thenReturn(false);
292 when(config.isConfigured()).thenReturn(true);
293 when(config.getSpoolDir()).thenReturn("spool/f");
294 when(config.getSpoolBase()).thenReturn("spool");
295 when(config.getLogDir()).thenReturn("log/dir");
296 when(config.getPublishId()).thenReturn("User1");
297 when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
298 when(config.getEventLogInterval()).thenReturn("40");
299 when(config.isDeletePermitted("1")).thenReturn(true);
300 when(config.getAllDests()).thenReturn(new DestInfo[0]);
301 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
302 FieldUtils.writeDeclaredStaticField(NodeMain.class, "nodeConfigManager", config, true);
303 PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
306 private void setUpConfigToReturnUnprivilegedSubscriber() throws IllegalAccessException {
307 NodeConfigManager config = mock(NodeConfigManager.class);
308 PowerMockito.mockStatic(NodeConfigManager.class);
309 when(config.isShutdown()).thenReturn(false);
310 when(config.isConfigured()).thenReturn(true);
311 when(config.isDeletePermitted("1")).thenReturn(false);
312 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
313 FieldUtils.writeDeclaredStaticField(NodeMain.class, "nodeConfigManager", config, true);
314 PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
317 private void setUpConfigToReturnNullOnIsDeletePermitted() throws IllegalAccessException {
318 NodeConfigManager config = mock(NodeConfigManager.class);
319 PowerMockito.mockStatic(NodeConfigManager.class);
320 when(config.isShutdown()).thenReturn(false);
321 when(config.isConfigured()).thenReturn(true);
322 when(config.isDeletePermitted("1")).thenThrow(new NullPointerException());
323 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
324 FieldUtils.writeDeclaredStaticField(NodeMain.class, "nodeConfigManager", config, true);
325 PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
328 private void setUpNodeMainDelivery() throws IllegalAccessException{
329 Delivery delivery = mock(Delivery.class);
330 doNothing().when(delivery).resetQueue(anyObject());
331 FieldUtils.writeDeclaredStaticField(NodeMain.class, "delivery", delivery, true);
334 private void setNodeConfigManagerIsConfiguredToReturnFalse() throws IllegalAccessException{
335 NodeConfigManager config = mock(NodeConfigManager.class);
336 when(config.isConfigured()).thenReturn(false);
337 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
340 private void setNodeConfigManagerIsPublishPermittedToReturnAReason() throws IllegalAccessException{
341 NodeConfigManager config = mock(NodeConfigManager.class);
342 when(config.isShutdown()).thenReturn(false);
343 when(config.isConfigured()).thenReturn(true);
344 when(config.getSpoolDir()).thenReturn("spool/dir");
345 when(config.getLogDir()).thenReturn("log/dir");
346 when(config.isPublishPermitted(anyString(), anyString(), anyString())).thenReturn("Not Permitted");
347 when(config.isAnotherNode(anyString(), anyString())).thenReturn(false);
348 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
351 private void setNodeConfigManagerToAllowRedirectOnIngressNode() throws IllegalAccessException{
352 NodeConfigManager config = mock(NodeConfigManager.class);
353 when(config.isShutdown()).thenReturn(false);
354 when(config.isConfigured()).thenReturn(true);
355 when(config.getSpoolDir()).thenReturn("spool/dir");
356 when(config.getLogDir()).thenReturn("log/dir");
357 when(config.getPublishId()).thenReturn("User1");
358 when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
359 when(config.getAuthUser(anyString(), anyString())).thenReturn("User1");
360 when(config.getIngressNode(anyString(), anyString(), anyString())).thenReturn("NewNode");
361 when(config.getExtHttpsPort()).thenReturn(8080);
362 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
365 private String createLargeMetaDataString() {
366 StringBuilder myString = new StringBuilder("meta");
367 for (int i = 0; i <= 4098; ++i) {
368 myString.append('x');
370 return myString.toString();
373 private void setHeadersForValidRequest(boolean isMetaTooLong) {
374 String metaDataString;
376 metaDataString = createLargeMetaDataString();
378 metaDataString = "?#@><";
380 List<String> headers = new ArrayList<>();
381 headers.add("Content-Type");
382 headers.add("X-DMAAP-DR-ON-BEHALF-OF");
383 headers.add("X-DMAAP-DR-META");
384 Enumeration<String> headerNames = Collections.enumeration(headers);
385 when(request.getHeaderNames()).thenReturn(headerNames);
386 Enumeration<String> contentTypeHeader = Collections.enumeration(Arrays.asList("text/plain"));
387 Enumeration<String> behalfHeader = Collections.enumeration(Arrays.asList("User1"));
388 Enumeration<String> metaDataHeader = Collections.enumeration(Arrays.asList(metaDataString));
389 when(request.getHeaders("Content-Type")).thenReturn(contentTypeHeader);
390 when(request.getHeaders("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(behalfHeader);
391 when(request.getHeaders("X-DMAAP-DR-META")).thenReturn(metaDataHeader);
394 private void createFilesAndDirectories() throws IOException {
395 File nodeDir = new File("spool/n/172.0.0.1");
396 File spoolDir = new File("spool/s/0/1");
397 File dataFile = new File("spool/s/0/1/dmaap-dr-node.1234567");
398 File metaDataFile = new File("spool/s/0/1/dmaap-dr-node.1234567.M");
401 dataFile.createNewFile();
402 metaDataFile.createNewFile();
405 private static void deleteCreatedDirectories() {
406 File spoolDir = new File("spool");
410 private static void delete(File file) {
411 if (file.isDirectory()) {
412 for (File f: file.listFiles()) {
416 if (!file.delete()) {
417 System.out.println("Failed to delete: " + file);