/** * Copyright 2016 StreamSets Inc. * <p> * Licensed under the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.streamsets.pipeline.lib.http; import com.google.common.collect.ImmutableList; import com.streamsets.pipeline.api.OnRecordError; import com.streamsets.pipeline.api.Stage; import com.streamsets.pipeline.sdk.ContextInfoCreator; import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class TestHttpReceiverServlet { @Test public void testValidateAppId() throws Exception { Stage.Context context = ContextInfoCreator.createSourceContext("n", false, OnRecordError.TO_ERROR, ImmutableList.of("a")); HttpReceiver receiver = Mockito.mock(HttpReceiverWithFragmenterWriter.class); Mockito.when(receiver.getAppId()).thenReturn("id"); HttpReceiverServlet servlet = new HttpReceiverServlet(context, receiver, null); HttpServletRequest req = Mockito.mock(HttpServletRequest.class); HttpServletResponse res = Mockito.mock(HttpServletResponse.class); // no ID; Assert.assertFalse(servlet.validateAppId(req, res)); Mockito.verify(res).sendError(Mockito.eq(HttpServletResponse.SC_FORBIDDEN), Mockito.anyString()); // invalid ID Mockito.when(req.getHeader(Mockito.eq(HttpConstants.X_SDC_APPLICATION_ID_HEADER))).thenReturn("invalid"); Mockito.reset(res); Assert.assertFalse(servlet.validateAppId(req, res)); Mockito.verify(res).sendError(Mockito.eq(HttpServletResponse.SC_FORBIDDEN), Mockito.anyString()); // valid ID Mockito.when(req.getHeader(Mockito.eq(HttpConstants.X_SDC_APPLICATION_ID_HEADER))).thenReturn("id"); Mockito.reset(res); Assert.assertTrue(servlet.validateAppId(req, res)); Mockito.verifyZeroInteractions(res); } @Test public void testDoGet() throws Exception { Stage.Context context = ContextInfoCreator.createSourceContext("n", false, OnRecordError.TO_ERROR, ImmutableList.of("a")); HttpReceiver receiver = Mockito.mock(HttpReceiverWithFragmenterWriter.class); HttpReceiverServlet servlet = new HttpReceiverServlet(context, receiver, null); servlet = Mockito.spy(servlet); HttpServletRequest req = Mockito.mock(HttpServletRequest.class); HttpServletResponse res = Mockito.mock(HttpServletResponse.class); // invalid ID; Mockito.doReturn(false).when(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); servlet.doGet(req, res); Mockito.verifyZeroInteractions(res); // valid ID Mockito.doReturn(true).when(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Mockito.reset(res); servlet.doGet(req, res); Mockito.verify(res).setStatus(Mockito.eq(HttpServletResponse.SC_OK)); Mockito .verify(res) .setHeader(Mockito.eq(HttpConstants.X_SDC_PING_HEADER), Mockito.eq(HttpConstants.X_SDC_PING_VALUE)); } @Test public void testValidatePostRequest() throws Exception { Stage.Context context = ContextInfoCreator.createSourceContext("n", false, OnRecordError.TO_ERROR, ImmutableList.of("a")); HttpReceiver receiver = Mockito.mock(HttpReceiverWithFragmenterWriter.class); Mockito.when(receiver.getAppId()).thenReturn("id"); HttpReceiverServlet servlet = new HttpReceiverServlet(context, receiver, null); servlet = Mockito.spy(servlet); HttpServletRequest req = Mockito.mock(HttpServletRequest.class); HttpServletResponse res = Mockito.mock(HttpServletResponse.class); // invalid AppId Mockito.doReturn(false).when(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Assert.assertFalse(servlet.validatePostRequest(req, res)); Mockito.verify(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Mockito.verifyZeroInteractions(req); Mockito.verifyZeroInteractions(res); Mockito.verify(servlet, Mockito.times(0)).getReceiver(); // valid AppID no compression valid receiver Mockito.reset(req); Mockito.reset(res); Mockito.reset(servlet); Mockito.doReturn(true).when(receiver).validate(Mockito.eq(req), Mockito.eq(res)); Mockito.doReturn(true).when(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Assert.assertTrue(servlet.validatePostRequest(req, res)); Mockito.verify(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Mockito.verify(req, Mockito.times(1)).getHeader(Mockito.eq(HttpConstants.X_SDC_COMPRESSION_HEADER)); Mockito.verifyZeroInteractions(res); Mockito.verify(servlet, Mockito.times(1)).getReceiver(); Mockito.verify(receiver, Mockito.times(1)).validate(Mockito.eq(req), Mockito.eq(res)); // valid AppID no compression invalid receiver Mockito.reset(req); Mockito.reset(res); Mockito.reset(servlet); Mockito.reset(receiver); Mockito.doReturn(false).when(receiver).validate(Mockito.eq(req), Mockito.eq(res)); Mockito.doReturn(true).when(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Assert.assertFalse(servlet.validatePostRequest(req, res)); Mockito.verify(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Mockito.verify(req, Mockito.times(1)).getHeader(Mockito.eq(HttpConstants.X_SDC_COMPRESSION_HEADER)); Mockito.verifyZeroInteractions(res); Mockito.verify(servlet, Mockito.times(1)).getReceiver(); Mockito.verify(receiver, Mockito.times(1)).validate(Mockito.eq(req), Mockito.eq(res)); // valid AppID with compression valid receiver Mockito.reset(req); Mockito.reset(res); Mockito.reset(servlet); Mockito.reset(receiver); Mockito.doReturn(true).when(receiver).validate(Mockito.eq(req), Mockito.eq(res)); Mockito.doReturn(true).when(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Mockito.doReturn(HttpConstants.SNAPPY_COMPRESSION).when(req).getHeader(HttpConstants.X_SDC_COMPRESSION_HEADER); Assert.assertTrue(servlet.validatePostRequest(req, res)); Mockito.verify(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Mockito.verify(req, Mockito.times(1)).getHeader(Mockito.eq(HttpConstants.X_SDC_COMPRESSION_HEADER)); Mockito.verify(servlet, Mockito.times(1)).getReceiver(); Mockito.verify(receiver, Mockito.times(1)).validate(Mockito.eq(req), Mockito.eq(res)); // valid AppID with compression valid receiver Mockito.reset(req); Mockito.reset(res); Mockito.reset(servlet); Mockito.reset(receiver); Mockito.doReturn(true).when(receiver).validate(Mockito.eq(req), Mockito.eq(res)); Mockito.doReturn(true).when(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Mockito.doReturn("invalid-compression").when(req).getHeader(HttpConstants.X_SDC_COMPRESSION_HEADER); Assert.assertFalse(servlet.validatePostRequest(req, res)); Mockito.verify(servlet).validateAppId(Mockito.eq(req), Mockito.eq(res)); Mockito.verify(req, Mockito.times(1)).getHeader(Mockito.eq(HttpConstants.X_SDC_COMPRESSION_HEADER)); Mockito.verify(servlet, Mockito.times(0)).getReceiver(); Mockito.verify(receiver, Mockito.times(0)).validate(Mockito.eq(req), Mockito.eq(res)); Mockito .verify(res, Mockito.times(1)) .sendError(Mockito.eq(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE), Mockito.anyString()); } @Test public void testDoPost() throws Exception { Stage.Context context = ContextInfoCreator.createSourceContext("n", false, OnRecordError.TO_ERROR, ImmutableList.of("a")); HttpReceiver receiver = Mockito.mock(HttpReceiverWithFragmenterWriter.class); Mockito.when(receiver.getAppId()).thenReturn("id"); BlockingQueue<Exception> errorQueue = new ArrayBlockingQueue<Exception>(1); HttpReceiverServlet servlet = new HttpReceiverServlet(context, receiver, errorQueue); servlet = Mockito.spy(servlet); HttpServletRequest req = Mockito.mock(HttpServletRequest.class); HttpServletResponse res = Mockito.mock(HttpServletResponse.class); // shutting down Mockito.doReturn(true).when(servlet).isShuttingDown(); servlet.doPost(req, res); Mockito.verify(res, Mockito.times(1)).setStatus(Mockito.eq(HttpServletResponse.SC_GONE)); Mockito.verifyNoMoreInteractions(res); // invalid post request Mockito.reset(res); Mockito.doReturn(false).when(servlet).isShuttingDown(); Mockito.doReturn(false).when(servlet).validatePostRequest(Mockito.eq(req), Mockito.eq(res)); servlet.doPost(req, res); Mockito.verifyNoMoreInteractions(res); // valid post request no compression ServletInputStream is = Mockito.mock(ServletInputStream.class); Mockito.reset(req); Mockito.doReturn(is).when(req).getInputStream(); Mockito.reset(res); Mockito.doReturn(false).when(servlet).isShuttingDown(); Mockito.doReturn(true).when(servlet).validatePostRequest(Mockito.eq(req), Mockito.eq(res)); servlet.doPost(req, res); Mockito.verify(req, Mockito.times(1)).getInputStream(); Mockito.verify(receiver, Mockito.times(1)).process(Mockito.eq(req), Mockito.eq(is)); Mockito.verify(res, Mockito.times(1)).setStatus(Mockito.eq(HttpServletResponse.SC_OK)); // valid post request with compression is = new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return -1; } }; Mockito.reset(req); Mockito.doReturn(is).when(req).getInputStream(); Mockito .doReturn(HttpConstants.SNAPPY_COMPRESSION) .when(req) .getHeader(Mockito.eq(HttpConstants.X_SDC_COMPRESSION_HEADER)); Mockito.reset(res); Mockito.doReturn(false).when(servlet).isShuttingDown(); Mockito.doReturn(true).when(servlet).validatePostRequest(Mockito.eq(req), Mockito.eq(res)); servlet.doPost(req, res); Mockito.verify(req, Mockito.times(1)).getInputStream(); ArgumentCaptor<InputStream> isCaptor = ArgumentCaptor.forClass(InputStream.class); Mockito.verify(receiver, Mockito.times(1)).process(Mockito.eq(req), isCaptor.capture()); // we are failing here becuase the stream does not have a valid snappy payload, // we use this to test also the exception path Mockito .verify(res, Mockito.times(1)) .sendError(Mockito.eq(HttpServletResponse.SC_INTERNAL_SERVER_ERROR), Mockito.anyString()); Assert.assertEquals(1, errorQueue.size()); } }