update link to upper-constraints.txt
[dmaap/datarouter.git] / datarouter-node / src / test / java / org / onap / dmaap / datarouter / node / NodeServletTest.java
1 /*******************************************************************************
2  * ============LICENSE_START==================================================
3  * * org.onap.dmaap
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
10  * *
11  *  *      http://www.apache.org/licenses/LICENSE-2.0
12  * *
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====================================================
19  * *
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  * *
22  ******************************************************************************/
23 package org.onap.dmaap.datarouter.node;
24
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;
34
35 import ch.qos.logback.classic.Logger;
36 import ch.qos.logback.classic.spi.ILoggingEvent;
37 import ch.qos.logback.core.read.ListAppender;
38 import java.io.File;
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;
60
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 {
66
67     private NodeServlet nodeServlet;
68     private Delivery delivery;
69
70     @Mock
71     private HttpServletRequest request;
72
73     @Mock
74     private HttpServletResponse response;
75
76     private ListAppender<ILoggingEvent> listAppender;
77
78     private NodeConfigManager config = mock(NodeConfigManager.class);
79
80     @Before
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();
87         setUpConfig();
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");
95     }
96
97     @AfterClass
98     public static void tearDown() {
99         deleteCreatedDirectories();
100     }
101
102     @Test
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);
108     }
109
110     @Test
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);
116     }
117
118     @Test
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);
124     }
125
126     @Test
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);
132     }
133
134     @Test
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);
140     }
141
142     @Test
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);
148     }
149
150     @Test
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);
157     }
158
159     @Test
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);
165     }
166
167     @Test
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);
173     }
174
175     @Test
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);
181     }
182
183     @Test
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);
191     }
192
193     @Test
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);
200     }
201
202     @Test
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);
210     }
211
212     @Test
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);
220     }
221
222     @Test
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());
228     }
229
230     @Test
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());
236     }
237
238
239     @Test
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());
245     }
246
247     @Test
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);
253     }
254
255     @Test
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);
262     }
263
264     @Test
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);
271     }
272
273     @Test
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);
280     }
281
282     @Test
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);
291     }
292
293     @Test
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);
299     }
300
301     private void setBehalfHeader(String headerValue) {
302         when(request.getHeader("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(headerValue);
303     }
304
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);
310         return listAppender;
311     }
312
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());
316     }
317
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);
331     }
332
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);
339     }
340
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);
347     }
348
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);
353     }
354
355     private void setNodeConfigManagerIsConfiguredToReturnFalse() throws IllegalAccessException {
356         when(config.isConfigured()).thenReturn(false);
357         FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
358     }
359
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);
369     }
370
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);
381     }
382
383     private String createLargeMetaDataString() {
384         StringBuilder myString = new StringBuilder("meta");
385         for (int i = 0; i <= 4098; ++i) {
386             myString.append('x');
387         }
388         return myString.toString();
389     }
390
391     private void setHeadersForValidRequest(boolean isMetaTooLong) {
392         String metaDataString;
393         if (isMetaTooLong) {
394             metaDataString = createLargeMetaDataString();
395         } else {
396             metaDataString = "?#@><";
397         }
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);
410     }
411
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");
417         nodeDir.mkdirs();
418         spoolDir.mkdirs();
419         dataFile.createNewFile();
420         metaDataFile.createNewFile();
421     }
422
423     private static void deleteCreatedDirectories() {
424         File spoolDir = new File("spool");
425         delete(spoolDir);
426     }
427
428     private static void delete(File file) {
429         if (file.isDirectory()) {
430             for (File f: file.listFiles()) {
431                 delete(f);
432             }
433         }
434         if (!file.delete()) {
435             System.out.println("Failed to delete: " + file);
436         }
437     }
438 }