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 private ListAppender<ILoggingEvent> listAppender;
64 private NodeConfigManager config = mock(NodeConfigManager.class);
67 public void setUp() throws Exception {
68 listAppender = setTestLogger();
69 setBehalfHeader("Stub_Value");
70 when(request.getPathInfo()).thenReturn("2");
71 when(request.isSecure()).thenReturn(true);
72 createFilesAndDirectories();
74 setUpNodeMainDelivery();
75 delivery = mock(Delivery.class);
76 when(delivery.markTaskSuccess("spool/s/0/1", "dmaap-dr-node.1234567")).thenReturn(true);
77 nodeServlet = new NodeServlet(delivery);
78 when(request.getHeader("Authorization")).thenReturn("User1");
79 when(request.getHeader("X-DMAAP-DR-PUBLISH-ID")).thenReturn("User1");
83 public static void tearDown() {
84 deleteCreatedDirectories();
88 public void Given_Request_Is_HTTP_GET_And_Config_Is_Down_Then_Service_Unavailable_Response_Is_Generated() throws Exception {
89 setNodeConfigManagerIsConfiguredToReturnFalse();
90 nodeServlet.doGet(request, response);
91 verify(response).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));
92 verifyEnteringExitCalled(listAppender);
96 public void Given_Request_Is_HTTP_GET_And_Endpoint_Is_Internal_FetchProv_Then_No_Content_Response_Is_Generated() {
97 when(request.getPathInfo()).thenReturn("/internal/fetchProv");
98 nodeServlet.doGet(request, response);
99 verify(response).setStatus(eq(HttpServletResponse.SC_NO_CONTENT));
100 verifyEnteringExitCalled(listAppender);
104 public void Given_Request_Is_HTTP_GET_And_Endpoint_Is_ResetSubscription_Then_No_Content_Response_Is_Generated() {
105 when(request.getPathInfo()).thenReturn("/internal/resetSubscription/1");
106 nodeServlet.doGet(request, response);
107 verify(response).setStatus(eq(HttpServletResponse.SC_NO_CONTENT));
108 verifyEnteringExitCalled(listAppender);
112 public void Given_Request_Is_HTTP_GET_To_Invalid_Endpoint_Then_Not_Found_Response_Is_Generated() throws Exception {
113 when(request.getPathInfo()).thenReturn("/incorrect");
114 nodeServlet.doGet(request, response);
115 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND));
116 verifyEnteringExitCalled(listAppender);
120 public void Given_Request_Is_HTTP_PUT_And_Config_Is_Down_Then_Service_Unavailable_Response_Is_Generated() throws Exception {
121 setNodeConfigManagerIsConfiguredToReturnFalse();
122 nodeServlet.doPut(request, response);
123 verify(response).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));
124 verifyEnteringExitCalled(listAppender);
128 public void Given_Request_Is_HTTP_PUT_And_Endpoint_Is_Incorrect_Then_Not_Found_Response_Is_Generated() throws Exception {
129 when(request.getPathInfo()).thenReturn("/incorrect/");
130 nodeServlet.doPut(request, response);
131 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
132 verifyEnteringExitCalled(listAppender);
136 public void Given_Request_Is_HTTP_PUT_And_Request_Is_Not_Secure_Then_Forbidden_Response_Is_Generated() throws Exception {
137 when(request.isSecure()).thenReturn(false);
138 nodeServlet.doPut(request, response);
139 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), argThat(notNullValue(String.class)));
140 verifyEnteringExitCalled(listAppender);
144 public void Given_Request_Is_HTTP_PUT_And_File_Id_Is_Null_Then_Not_Found_Response_Is_Generated() throws Exception {
145 when(request.getPathInfo()).thenReturn(null);
146 nodeServlet.doPut(request, response);
147 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
148 verifyEnteringExitCalled(listAppender);
152 public void Given_Request_Is_HTTP_PUT_And_Authorization_Is_Null_Then_Forbidden_Response_Is_Generated() throws Exception {
153 when(request.getHeader("Authorization")).thenReturn(null);
154 nodeServlet.doPut(request, response);
155 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), argThat(notNullValue(String.class)));
156 verifyEnteringExitCalled(listAppender);
160 public void Given_Request_Is_HTTP_PUT_And_Publish_Does_Not_Include_File_Id_Then_Not_Found_Response_Is_Generated() throws Exception {
161 when(request.getPathInfo()).thenReturn("/publish/");
162 nodeServlet.doPut(request, response);
163 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
164 verifyEnteringExitCalled(listAppender);
168 public void Given_Request_Is_HTTP_PUT_And_Publish_Not_Permitted_Then_Forbidden_Response_Is_Generated() throws Exception {
169 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
170 setNodeConfigManagerIsPublishPermittedToReturnAReason();
171 nodeServlet.doPut(request, response);
172 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), argThat(notNullValue(String.class)));
173 verifyEnteringExitCalled(listAppender);
177 public void Given_Request_Is_HTTP_PUT_And_Internal_Publish_On_Same_Node_Then_Forbidden_Response_Is_Generated() throws Exception {
178 when(request.getPathInfo()).thenReturn("/internal/publish/1/fileName");
179 setNodeConfigManagerIsPublishPermittedToReturnAReason();
180 nodeServlet.doPut(request, response);
181 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN));
182 verifyEnteringExitCalled(listAppender);
186 public void Given_Request_Is_HTTP_PUT_And_Internal_Publish_But_Invalid_File_Id_Then_Not_Found_Response_Is_Generated() throws Exception {
187 when(request.getPathInfo()).thenReturn("/internal/publish/1/");
188 nodeServlet.doPut(request, response);
189 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
190 verifyEnteringExitCalled(listAppender);
194 public void Given_Request_Is_HTTP_PUT_On_Publish_And_Ingress_Node_Is_Provided_Then_Request_Is_Redirected() throws Exception {
195 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
196 setNodeConfigManagerToAllowRedirectOnIngressNode();
197 nodeServlet.doPut(request, response);
198 verify(response).sendRedirect(anyString());
199 verifyEnteringExitCalled(listAppender);
203 public void Given_Request_Is_HTTP_PUT_On_Publish_With_Meta_Data_Too_Long_Then_Bad_Request_Response_Is_Generated() throws Exception {
204 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
205 setHeadersForValidRequest(true);
206 nodeServlet.doPut(request, response);
207 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), argThat(notNullValue(String.class)));
208 verifyEnteringExitCalled(listAppender);
212 public void Given_Request_Is_HTTP_PUT_On_Publish_With_Meta_Data_Malformed_Then_Bad_Request_Response_Is_Generated() throws Exception {
213 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
214 setHeadersForValidRequest(false);
215 nodeServlet.doPut(request, response);
216 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), argThat(notNullValue(String.class)));
217 verifyEnteringExitCalled(listAppender);
221 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 {
222 when(config.getCadiEnabeld()).thenReturn(true);
223 when(config.getAafInstance("1")).thenReturn("*");
224 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
225 setHeadersForValidRequest(true);
226 nodeServlet.doPut(request, response);
227 verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), argThat(notNullValue(String.class)));
228 verifyEnteringExitCalled(listAppender);
232 public void Given_Request_Is_HTTP_DELETE_On_Publish_With_Meta_Data_Malformed_Then_Bad_Request_Response_Is_Generated() throws Exception {
233 when(request.getPathInfo()).thenReturn("/publish/1/fileName");
234 setHeadersForValidRequest(false);
235 nodeServlet.doDelete(request, response);
236 verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), argThat(notNullValue(String.class)));
237 verifyEnteringExitCalled(listAppender);
241 public void Given_Request_Is_HTTP_DELETE_File_With_Invalid_Endpoint_Then_Not_Found_Response_Is_Generated() throws Exception {
242 when(request.getPathInfo()).thenReturn("/delete/1");
243 nodeServlet.doDelete(request, response);
244 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
245 verifyEnteringExitCalled(listAppender);
249 public void Given_Request_Is_HTTP_DELETE_File_And_Is_Not_Privileged_Subscription_Then_Not_Found_Response_Is_Generated() throws Exception {
250 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
251 setUpConfigToReturnUnprivilegedSubscriber();
252 nodeServlet.doDelete(request, response);
253 verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED));
254 verifyEnteringExitCalled(listAppender);
258 public void Given_Request_Is_HTTP_DELETE_File_And_Subscription_Does_Not_Exist_Then_Not_Found_Response_Is_Generated() throws Exception {
259 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
260 setUpConfigToReturnNullOnIsDeletePermitted();
261 nodeServlet.doDelete(request, response);
262 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND));
263 verifyEnteringExitCalled(listAppender);
267 public void Given_Request_Is_HTTP_DELETE_File_Then_Request_Succeeds() throws Exception {
268 when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
269 createFilesAndDirectories();
270 nodeServlet.doDelete(request, response);
271 verify(response).setStatus(eq(HttpServletResponse.SC_OK));
272 verifyEnteringExitCalled(listAppender);
276 public void Given_Request_Is_HTTP_DELETE_File_And_File_Does_Not_Exist_Then_Not_Found_Response_Is_Generated() throws IOException {
277 when(request.getPathInfo()).thenReturn("/delete/1/nonExistingFile");
278 nodeServlet.doDelete(request, response);
279 verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), argThat(notNullValue(String.class)));
280 verifyEnteringExitCalled(listAppender);
283 private void setBehalfHeader(String headerValue) {
284 when(request.getHeader("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(headerValue);
287 private ListAppender<ILoggingEvent> setTestLogger() {
288 Logger Logger = (Logger) LoggerFactory.getLogger(NodeServlet.class);
289 ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
290 listAppender.start();
291 Logger.addAppender(listAppender);
295 private void verifyEnteringExitCalled(ListAppender<ILoggingEvent> listAppender) {
296 assertEquals("EELF0004I Entering data router node component with RequestId and InvocationId", listAppender.list.get(0).getMessage());
297 assertEquals("EELF0005I Exiting data router node component with RequestId and InvocationId", listAppender.list.get(2).getMessage());
298 assertEquals(3, listAppender.list.size());
301 private void setUpConfig() throws IllegalAccessException {
302 PowerMockito.mockStatic(NodeConfigManager.class);
303 when(config.isShutdown()).thenReturn(false);
304 when(config.isConfigured()).thenReturn(true);
305 when(config.getSpoolDir()).thenReturn("spool/f");
306 when(config.getSpoolBase()).thenReturn("spool");
307 when(config.getLogDir()).thenReturn("log/dir");
308 when(config.getPublishId()).thenReturn("User1");
309 when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
310 when(config.getEventLogInterval()).thenReturn("40");
311 when(config.isDeletePermitted("1")).thenReturn(true);
312 when(config.getAllDests()).thenReturn(new DestInfo[0]);
313 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
314 FieldUtils.writeDeclaredStaticField(NodeMain.class, "nodeConfigManager", config, true);
315 PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
318 private void setUpConfigToReturnUnprivilegedSubscriber() throws IllegalAccessException {
319 NodeConfigManager config = mock(NodeConfigManager.class);
320 PowerMockito.mockStatic(NodeConfigManager.class);
321 when(config.isShutdown()).thenReturn(false);
322 when(config.isConfigured()).thenReturn(true);
323 when(config.isDeletePermitted("1")).thenReturn(false);
324 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
325 FieldUtils.writeDeclaredStaticField(NodeMain.class, "nodeConfigManager", config, true);
326 PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
329 private void setUpConfigToReturnNullOnIsDeletePermitted() throws IllegalAccessException {
330 NodeConfigManager config = mock(NodeConfigManager.class);
331 PowerMockito.mockStatic(NodeConfigManager.class);
332 when(config.isShutdown()).thenReturn(false);
333 when(config.isConfigured()).thenReturn(true);
334 when(config.isDeletePermitted("1")).thenThrow(new NullPointerException());
335 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
336 FieldUtils.writeDeclaredStaticField(NodeMain.class, "nodeConfigManager", config, true);
337 PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
340 private void setUpNodeMainDelivery() throws IllegalAccessException{
341 Delivery delivery = mock(Delivery.class);
342 doNothing().when(delivery).resetQueue(anyObject());
343 FieldUtils.writeDeclaredStaticField(NodeMain.class, "delivery", delivery, true);
346 private void setNodeConfigManagerIsConfiguredToReturnFalse() throws IllegalAccessException{
347 NodeConfigManager config = mock(NodeConfigManager.class);
348 when(config.isConfigured()).thenReturn(false);
349 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
352 private void setNodeConfigManagerIsPublishPermittedToReturnAReason() throws IllegalAccessException{
353 NodeConfigManager config = mock(NodeConfigManager.class);
354 when(config.isShutdown()).thenReturn(false);
355 when(config.isConfigured()).thenReturn(true);
356 when(config.getSpoolDir()).thenReturn("spool/dir");
357 when(config.getLogDir()).thenReturn("log/dir");
358 when(config.isPublishPermitted(anyString(), anyString(), anyString())).thenReturn("Not Permitted");
359 when(config.isAnotherNode(anyString(), anyString())).thenReturn(false);
360 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
363 private void setNodeConfigManagerToAllowRedirectOnIngressNode() throws IllegalAccessException{
364 NodeConfigManager config = mock(NodeConfigManager.class);
365 when(config.isShutdown()).thenReturn(false);
366 when(config.isConfigured()).thenReturn(true);
367 when(config.getSpoolDir()).thenReturn("spool/dir");
368 when(config.getLogDir()).thenReturn("log/dir");
369 when(config.getPublishId()).thenReturn("User1");
370 when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
371 when(config.getAuthUser(anyString(), anyString())).thenReturn("User1");
372 when(config.getIngressNode(anyString(), anyString(), anyString())).thenReturn("NewNode");
373 when(config.getExtHttpsPort()).thenReturn(8080);
374 FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
377 private String createLargeMetaDataString() {
378 StringBuilder myString = new StringBuilder("meta");
379 for (int i = 0; i <= 4098; ++i) {
380 myString.append('x');
382 return myString.toString();
385 private void setHeadersForValidRequest(boolean isMetaTooLong) {
386 String metaDataString;
388 metaDataString = createLargeMetaDataString();
390 metaDataString = "?#@><";
392 List<String> headers = new ArrayList<>();
393 headers.add("Content-Type");
394 headers.add("X-DMAAP-DR-ON-BEHALF-OF");
395 headers.add("X-DMAAP-DR-META");
396 Enumeration<String> headerNames = Collections.enumeration(headers);
397 when(request.getHeaderNames()).thenReturn(headerNames);
398 Enumeration<String> contentTypeHeader = Collections.enumeration(Arrays.asList("text/plain"));
399 Enumeration<String> behalfHeader = Collections.enumeration(Arrays.asList("User1"));
400 Enumeration<String> metaDataHeader = Collections.enumeration(Arrays.asList(metaDataString));
401 when(request.getHeaders("Content-Type")).thenReturn(contentTypeHeader);
402 when(request.getHeaders("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(behalfHeader);
403 when(request.getHeaders("X-DMAAP-DR-META")).thenReturn(metaDataHeader);
406 private void createFilesAndDirectories() throws IOException {
407 File nodeDir = new File("spool/n/172.0.0.1");
408 File spoolDir = new File("spool/s/0/1");
409 File dataFile = new File("spool/s/0/1/dmaap-dr-node.1234567");
410 File metaDataFile = new File("spool/s/0/1/dmaap-dr-node.1234567.M");
413 dataFile.createNewFile();
414 metaDataFile.createNewFile();
417 private static void deleteCreatedDirectories() {
418 File spoolDir = new File("spool");
422 private static void delete(File file) {
423 if (file.isDirectory()) {
424 for (File f: file.listFiles()) {
428 if (!file.delete()) {
429 System.out.println("Failed to delete: " + file);