package br.com.caelum.vraptor.interceptor.multipart; import static com.google.common.io.ByteStreams.toByteArray; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import br.com.caelum.vraptor.Validator; import br.com.caelum.vraptor.core.InterceptorStack; import br.com.caelum.vraptor.http.InvalidParameterException; import br.com.caelum.vraptor.http.MutableRequest; import br.com.caelum.vraptor.resource.ResourceMethod; import br.com.caelum.vraptor.validator.I18nMessage; /** * Test class for uploading features using commons-fileupload. * * @author Otávio Scherer Garcia */ public class CommonsUploadMultipartInterceptorTest { private Object instance; @Mock private InterceptorStack stack; @Mock private ResourceMethod method; @Mock private HttpServletRequest request; @Mock private MutableRequest parameters; @Mock private Validator validator; private MultipartConfig config; private CommonsUploadMultipartInterceptor interceptor; private ServletFileUpload mockUpload; private ServletFileUploadCreator mockCreator; @Before public void setup() { config = new DefaultMultipartConfig(); MockitoAnnotations.initMocks(this); mockCreator = mock(ServletFileUploadCreator.class); mockUpload = mock(ServletFileUpload.class); when(mockCreator.create(any(FileItemFactory.class))).thenReturn(mockUpload); } @Test public void shouldNotAcceptFormURLEncoded() { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, null); when(request.getContentType()).thenReturn("application/x-www-form-urlencoded"); when(request.getMethod()).thenReturn("POST"); assertThat(interceptor.accepts(method), equalTo(false)); } @Test public void shouldAcceptMultipart() { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, null); when(request.getContentType()).thenReturn("multipart/form-data"); when(request.getMethod()).thenReturn("POST"); assertThat(interceptor.accepts(method), equalTo(true)); } @Test public void withFieldsOnly() throws Exception { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); final List<FileItem> elements = new ArrayList<FileItem>(); elements.add(new MockFileItem("foo", "blah")); elements.add(new MockFileItem("bar", "blah blah")); when(request.getCharacterEncoding()).thenReturn("utf-8"); when(request.getContentType()).thenReturn("multipart/form-data"); when(request.getMethod()).thenReturn("POST"); when(mockUpload.parseRequest(request)).thenReturn(elements); interceptor.intercept(stack, method, instance); verify(parameters).setParameter("foo", "blah"); verify(parameters).setParameter("bar", "blah blah"); } @Test public void withFieldsOnlyWithInvalidCharset() throws Exception { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); final List<FileItem> elements = new ArrayList<FileItem>(); elements.add(new MockFileItem("foo", "blah")); elements.add(new MockFileItem("bar", "blah blah")); when(request.getCharacterEncoding()).thenReturn("www"); when(request.getContentType()).thenReturn("multipart/form-data"); when(request.getMethod()).thenReturn("POST"); when(mockUpload.parseRequest(request)).thenReturn(elements); interceptor.intercept(stack, method, instance); verify(parameters).setParameter("foo", "blah"); verify(parameters).setParameter("bar", "blah blah"); } @Test public void withFilesAndFields() throws Exception { final List<FileItem> elements = new ArrayList<FileItem>(); elements.add(new MockFileItem("foo", "blah")); elements.add(new MockFileItem("bar", "blah blah")); elements.add(new MockFileItem("thefile0", "foo.txt", "foo".getBytes())); elements.add(new MockFileItem("thefile1", "bar.txt", "bar".getBytes())); interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); when(request.getContentType()).thenReturn("multipart/form-data"); when(request.getMethod()).thenReturn("POST"); when(mockUpload.parseRequest(request)).thenReturn(elements); interceptor.intercept(stack, method, instance); verify(parameters).setParameter("foo", "blah"); verify(parameters).setParameter("bar", "blah blah"); verify(parameters).setParameter("thefile0", "thefile0"); verify(parameters).setParameter("thefile1", "thefile1"); verify(request).setAttribute(eq("thefile0"), any(UploadedFile.class)); verify(request).setAttribute(eq("thefile1"), any(UploadedFile.class)); } @Test public void emptyFiles() throws Exception { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); final List<FileItem> elements = new ArrayList<FileItem>(); elements.add(new MockFileItem("thefile0", "", new byte[0])); when(request.getContentType()).thenReturn("multipart/form-data"); when(request.getMethod()).thenReturn("POST"); when(mockUpload.parseRequest(request)).thenReturn(elements); interceptor.intercept(stack, method, instance); } @Test(expected = InvalidParameterException.class) public void throwsInvalidParameterExceptionIfIOExceptionOccurs() throws Exception { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); MockFileItem item = new MockFileItem("thefile0", "file.txt", new byte[0]); item = spy(item); doThrow(new IOException()).when(item).getInputStream(); when(request.getContentType()).thenReturn("multipart/form-data"); when(request.getMethod()).thenReturn("POST"); List<FileItem> items = new ArrayList<FileItem>(); items.add(item); when(mockUpload.parseRequest(request)).thenReturn(items); interceptor.intercept(stack, method, instance); } @Test public void fieldsWithSameName() throws Exception { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); final List<FileItem> elements = new ArrayList<FileItem>(); elements.add(new MockFileItem("myfile0", "foo.txt", "foo".getBytes())); elements.add(new MockFileItem("myfile1", "foo.txt", "bar".getBytes())); when(request.getContentType()).thenReturn("multipart/form-data"); when(request.getMethod()).thenReturn("POST"); when(mockUpload.parseRequest(request)).thenReturn(elements); interceptor.intercept(stack, method, instance); verify(parameters).setParameter("myfile0", "myfile0"); verify(parameters).setParameter("myfile1", "myfile1"); verify(request).setAttribute(eq("myfile0"), any(UploadedFile.class)); verify(request).setAttribute(eq("myfile1"), any(UploadedFile.class)); } @Test public void multipleUpload() throws Exception { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); final List<FileItem> elements = new ArrayList<FileItem>(); elements.add(new MockFileItem("myfile0[]", "foo.txt", "foo".getBytes())); elements.add(new MockFileItem("myfile0[]", "foo.txt", "bar".getBytes())); when(request.getContentType()).thenReturn("multipart/form-data"); when(request.getMethod()).thenReturn("POST"); when(mockUpload.parseRequest(request)).thenReturn(elements); interceptor.intercept(stack, method, instance); verify(parameters).setParameter("myfile0[0]", "myfile0[0]"); verify(parameters).setParameter("myfile0[1]", "myfile0[1]"); verify(request).setAttribute(eq("myfile0[0]"), any(UploadedFile.class)); verify(request).setAttribute(eq("myfile0[1]"), any(UploadedFile.class)); } @Test public void doNothingWhenFileUploadExceptionOccurs() throws Exception { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); when(mockUpload.parseRequest(request)).thenThrow(new FileUploadException()); interceptor.intercept(stack, method, instance); } @Test public void shouldValidateWhenSizeLimitExceededExceptionOccurs() throws Exception { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); when(mockUpload.parseRequest(request)).thenThrow(new FileUploadBase.SizeLimitExceededException("", 0L, 0L)); interceptor.intercept(stack, method, instance); verify(validator).add(any(I18nMessage.class)); } @Test public void shouldCreateDirInsideAppIfTempDirAreNotAvailable() throws Exception { DefaultMultipartConfig configSpy = (DefaultMultipartConfig) spy(config); doThrow(new IOException()).when(configSpy).createTempFile(); interceptor = new CommonsUploadMultipartInterceptor(request, parameters, configSpy, validator, mockCreator); final List<FileItem> elements = new ArrayList<FileItem>(); elements.add(new MockFileItem("myfile", "foo.txt", "bar".getBytes())); when(request.getContentType()).thenReturn("multipart/form-data"); when(request.getMethod()).thenReturn("POST"); when(mockUpload.parseRequest(request)).thenReturn(elements); interceptor.intercept(stack, method, instance); verify(configSpy).createDirInsideApplication(); } @Test public void checkIfFileHasBeenUploaded() throws Exception { final List<FileItem> elements = new ArrayList<FileItem>(); byte[] content = "foo".getBytes(); elements.add(new MockFileItem("thefile0", "text/plain", "file.txt", content)); interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); when(request.getContentType()).thenReturn("multipart/form-data"); when(request.getMethod()).thenReturn("POST"); when(mockUpload.parseRequest(request)).thenReturn(elements); interceptor.intercept(stack, method, instance); ArgumentCaptor<UploadedFile> argument = ArgumentCaptor.forClass(UploadedFile.class); verify(request).setAttribute(anyString(), argument.capture()); UploadedFile file = argument.getValue(); assertThat(file.getFileName(), is("file.txt")); assertThat(file.getContentType(), is("text/plain")); assertThat(toByteArray(file.getFile()), is(content)); } @Test public void handleValidatorMessageWhenFileUploadExceptionOccurs() throws Exception { interceptor = new CommonsUploadMultipartInterceptor(request, parameters, config, validator, mockCreator); when(mockUpload.parseRequest(request)).thenThrow(new FileUploadException()); interceptor.intercept(stack, method, instance); verify(validator).add(any(I18nMessage.class)); } }