Remove major and minor code smells in dr-node
[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 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;
29 import org.junit.*;
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;
36
37 import javax.servlet.http.HttpServletRequest;
38 import javax.servlet.http.HttpServletResponse;
39
40 import java.io.File;
41 import java.io.IOException;
42 import java.util.*;
43
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.*;
48
49 @RunWith(PowerMockRunner.class)
50 @SuppressStaticInitializationFor("org.onap.dmaap.datarouter.node.NodeConfigManager")
51 public class NodeServletTest {
52
53     private NodeServlet nodeServlet;
54     private Delivery delivery;
55
56     @Mock
57     private HttpServletRequest request;
58
59     @Mock
60     private HttpServletResponse response;
61
62     private ListAppender<ILoggingEvent> listAppender;
63
64     private NodeConfigManager config = mock(NodeConfigManager.class);
65
66     @Before
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();
73         setUpConfig();
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");
80     }
81
82     @AfterClass
83     public static void tearDown() {
84         deleteCreatedDirectories();
85     }
86
87     @Test
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);
93     }
94
95     @Test
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);
101     }
102
103     @Test
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);
109     }
110
111     @Test
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);
117     }
118
119     @Test
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);
125     }
126
127     @Test
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);
133     }
134
135     @Test
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);
141     }
142
143     @Test
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);
149     }
150
151     @Test
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);
157     }
158
159     @Test
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);
165     }
166
167     @Test
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);
174     }
175
176     @Test
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);
183     }
184
185     @Test
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);
191     }
192
193     @Test
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);
200     }
201
202     @Test
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);
209     }
210
211     @Test
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);
218     }
219
220     @Test
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.getCadiEnabled()).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);
229     }
230
231     @Test
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);
238     }
239
240     @Test
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);
246     }
247
248     @Test
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);
255     }
256
257     @Test
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);
264     }
265
266     @Test
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);
273     }
274
275     @Test
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);
281     }
282
283     private void setBehalfHeader(String headerValue) {
284         when(request.getHeader("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(headerValue);
285     }
286
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);
292         return listAppender;
293     }
294
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(listAppender.list.size() -1).getMessage());
298     }
299
300     private void setUpConfig() throws IllegalAccessException {
301         PowerMockito.mockStatic(NodeConfigManager.class);
302         when(config.isShutdown()).thenReturn(false);
303         when(config.isConfigured()).thenReturn(true);
304         when(config.getSpoolDir()).thenReturn("spool/f");
305         when(config.getSpoolBase()).thenReturn("spool");
306         when(config.getLogDir()).thenReturn("log/dir");
307         when(config.getPublishId()).thenReturn("User1");
308         when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
309         when(config.getEventLogInterval()).thenReturn("40");
310         when(config.isDeletePermitted("1")).thenReturn(true);
311         when(config.getAllDests()).thenReturn(new DestInfo[0]);
312         FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
313         FieldUtils.writeDeclaredStaticField(NodeMain.class, "nodeConfigManager", config, true);
314         PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
315     }
316
317     private void setUpConfigToReturnUnprivilegedSubscriber() 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")).thenReturn(false);
323         FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
324         FieldUtils.writeDeclaredStaticField(NodeMain.class, "nodeConfigManager", config, true);
325         PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
326     }
327
328     private void setUpConfigToReturnNullOnIsDeletePermitted() throws IllegalAccessException {
329         NodeConfigManager config = mock(NodeConfigManager.class);
330         PowerMockito.mockStatic(NodeConfigManager.class);
331         when(config.isShutdown()).thenReturn(false);
332         when(config.isConfigured()).thenReturn(true);
333         when(config.isDeletePermitted("1")).thenThrow(new NullPointerException());
334         FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
335         FieldUtils.writeDeclaredStaticField(NodeMain.class, "nodeConfigManager", config, true);
336         PowerMockito.when(NodeConfigManager.getInstance()).thenReturn(config);
337     }
338
339     private void setUpNodeMainDelivery() throws IllegalAccessException{
340         Delivery delivery = mock(Delivery.class);
341         doNothing().when(delivery).resetQueue(anyObject());
342         FieldUtils.writeDeclaredStaticField(NodeMain.class, "delivery", delivery, true);
343     }
344
345     private void setNodeConfigManagerIsConfiguredToReturnFalse() throws IllegalAccessException{
346         NodeConfigManager config = mock(NodeConfigManager.class);
347         when(config.isConfigured()).thenReturn(false);
348         FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
349     }
350
351     private void setNodeConfigManagerIsPublishPermittedToReturnAReason() 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.isPublishPermitted(anyString(), anyString(), anyString())).thenReturn("Not Permitted");
358         when(config.isAnotherNode(anyString(), anyString())).thenReturn(false);
359         FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
360     }
361
362     private void setNodeConfigManagerToAllowRedirectOnIngressNode() throws IllegalAccessException{
363         NodeConfigManager config = mock(NodeConfigManager.class);
364         when(config.isShutdown()).thenReturn(false);
365         when(config.isConfigured()).thenReturn(true);
366         when(config.getSpoolDir()).thenReturn("spool/dir");
367         when(config.getLogDir()).thenReturn("log/dir");
368         when(config.getPublishId()).thenReturn("User1");
369         when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
370         when(config.getAuthUser(anyString(), anyString())).thenReturn("User1");
371         when(config.getIngressNode(anyString(), anyString(), anyString())).thenReturn("NewNode");
372         when(config.getExtHttpsPort()).thenReturn(8080);
373         FieldUtils.writeDeclaredStaticField(NodeServlet.class, "config", config, true);
374     }
375
376     private String createLargeMetaDataString() {
377         StringBuilder myString = new StringBuilder("meta");
378         for (int i = 0; i <= 4098; ++i) {
379             myString.append('x');
380         }
381         return myString.toString();
382     }
383
384     private void setHeadersForValidRequest(boolean isMetaTooLong) {
385         String metaDataString;
386         if (isMetaTooLong) {
387             metaDataString = createLargeMetaDataString();
388         } else {
389             metaDataString = "?#@><";
390         }
391         List<String> headers = new ArrayList<>();
392         headers.add("Content-Type");
393         headers.add("X-DMAAP-DR-ON-BEHALF-OF");
394         headers.add("X-DMAAP-DR-META");
395         Enumeration<String> headerNames = Collections.enumeration(headers);
396         when(request.getHeaderNames()).thenReturn(headerNames);
397         Enumeration<String> contentTypeHeader = Collections.enumeration(Arrays.asList("text/plain"));
398         Enumeration<String> behalfHeader = Collections.enumeration(Arrays.asList("User1"));
399         Enumeration<String> metaDataHeader = Collections.enumeration(Arrays.asList(metaDataString));
400         when(request.getHeaders("Content-Type")).thenReturn(contentTypeHeader);
401         when(request.getHeaders("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(behalfHeader);
402         when(request.getHeaders("X-DMAAP-DR-META")).thenReturn(metaDataHeader);
403     }
404
405     private void createFilesAndDirectories() throws IOException {
406         File nodeDir = new File("spool/n/172.0.0.1");
407         File spoolDir = new File("spool/s/0/1");
408         File dataFile = new File("spool/s/0/1/dmaap-dr-node.1234567");
409         File metaDataFile = new File("spool/s/0/1/dmaap-dr-node.1234567.M");
410         nodeDir.mkdirs();
411         spoolDir.mkdirs();
412         dataFile.createNewFile();
413         metaDataFile.createNewFile();
414     }
415
416     private static void deleteCreatedDirectories() {
417         File spoolDir = new File("spool");
418         delete(spoolDir);
419     }
420
421     private static void delete(File file) {
422         if (file.isDirectory()) {
423             for (File f: file.listFiles()) {
424                 delete(f);
425             }
426         }
427         if (!file.delete()) {
428             System.out.println("Failed to delete: " + file);
429         }
430     }
431 }