/***
* Copyright (c) 2009 Caelum - www.caelum.com.br/opensource All rights reserved.
*
* Licensed 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 br.com.caelum.vraptor.observer.upload;
import static com.google.common.io.ByteStreams.toByteArray;
import static org.hamcrest.MatcherAssert.assertThat;
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.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.fileupload.FileItem;
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.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import br.com.caelum.vraptor.controller.ControllerMethod;
import br.com.caelum.vraptor.controller.DefaultControllerMethod;
import br.com.caelum.vraptor.core.InterceptorStack;
import br.com.caelum.vraptor.events.ControllerFound;
import br.com.caelum.vraptor.http.MutableRequest;
import br.com.caelum.vraptor.validator.I18nMessage;
import br.com.caelum.vraptor.validator.Validator;
/**
* Test class for uploading features using commons-fileupload.
*
* @author Otávio Scherer Garcia
*/
public class CommonsUploadMultipartObserverTest {
@Rule
public ExpectedException exception = ExpectedException.none();
@Rule
public TemporaryFolder tmpdir = new TemporaryFolder();
@Mock private InterceptorStack stack;
@Mock private ControllerFound event;
@Mock private MutableRequest request;
@Mock private Validator validator;
@Mock private ServletFileUpload servletFileUpload;
private MultipartConfig config;
private CommonsUploadMultipartObserver observer;
private ControllerMethod uploadMethodController;
private ControllerMethod uploadMethodControllerWithAnnotation;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
config = new DefaultMultipartConfig();
when(request.getContentType()).thenReturn("multipart/form-data");
when(request.getMethod()).thenReturn("POST");
observer = spy(new CommonsUploadMultipartObserver());
Method uploadMethod = getClass().getDeclaredMethod("uploadMethod", UploadedFile.class);
Method uploadMethodWthAnnotation = getClass().getDeclaredMethod("uploadMethodWthAnnotation", UploadedFile.class);
uploadMethodController = DefaultControllerMethod.instanceFor(getClass(), uploadMethod);
uploadMethodControllerWithAnnotation = DefaultControllerMethod.instanceFor(getClass(), uploadMethodWthAnnotation);
}
@Test
public void shouldNotAcceptFormURLEncoded() {
MultipartConfig config = spy(new DefaultMultipartConfig());
when(request.getContentType()).thenReturn("application/x-www-form-urlencoded");
when(request.getMethod()).thenReturn("POST");
observer.upload(event, request, config, validator);
verifyZeroInteractions(config);
}
@Test
public void withFieldsOnly() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodController);
final List<FileItem> elements = new ArrayList<>();
elements.add(new MockFileItem("foo", "blah"));
elements.add(new MockFileItem("bar", "blah blah"));
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenReturn(elements);
when(request.getCharacterEncoding()).thenReturn("UTF-8");
observer.upload(event, request, config, validator);
verify(request).setParameter("foo", "blah");
verify(request).setParameter("bar", "blah blah");
}
@Test
public void withFieldsOnlyWithInvalidCharset() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodController);
final List<FileItem> elements = new ArrayList<>();
elements.add(new MockFileItem("foo", "blah"));
elements.add(new MockFileItem("bar", "blah blah"));
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenReturn(elements);
when(request.getCharacterEncoding()).thenReturn("BLAH");
observer.upload(event, request, config, validator);
verify(request).setParameter("foo", "blah");
verify(request).setParameter("bar", "blah blah");
}
@Test
public void withFilesAndFields() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodController);
final List<FileItem> elements = new ArrayList<>();
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()));
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenReturn(elements);
observer.upload(event, request, config, validator);
verify(request).setParameter("foo", "blah");
verify(request).setParameter("bar", "blah blah");
verify(request).setParameter("thefile0", "thefile0");
verify(request).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 {
when(event.getMethod()).thenReturn(uploadMethodController);
final List<FileItem> elements = new ArrayList<>();
elements.add(new MockFileItem("thefile0", "", new byte[0]));
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenReturn(elements);
observer.upload(event, request, config, validator);
}
@Test
public void fieldsWithSameName() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodController);
final List<FileItem> elements = new ArrayList<>();
elements.add(new MockFileItem("myfile0", "foo.txt", "foo".getBytes()));
elements.add(new MockFileItem("myfile1", "foo.txt", "bar".getBytes()));
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenReturn(elements);
observer.upload(event, request, config, validator);
verify(request).setParameter("myfile0", "myfile0");
verify(request).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 {
when(event.getMethod()).thenReturn(uploadMethodController);
final List<FileItem> elements = new ArrayList<>();
elements.add(new MockFileItem("myfile0[]", "foo.txt", "foo".getBytes()));
elements.add(new MockFileItem("myfile0[]", "foo.txt", "bar".getBytes()));
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenReturn(elements);
observer.upload(event, request, config, validator);
verify(request).setParameter("myfile0[0]", "myfile0[0]");
verify(request).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 {
when(event.getMethod()).thenReturn(uploadMethodController);
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenThrow(new FileUploadException());
observer.upload(event, request, config, validator);
}
@Test
public void shouldValidateWhenSizeLimitExceededExceptionOccurs() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodController);
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenThrow(new FileUploadBase.SizeLimitExceededException("", 3L, 2L));
observer.upload(event, request, config, validator);
verify(validator).add(any(I18nMessage.class));
}
@Test
public void handleValidatorMessageWhenFileUploadExceptionOccurs() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodController);
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenThrow(new FileUploadException());
observer.upload(event, request, config, validator);
verify(validator).add(any(I18nMessage.class));
}
@Test
public void shouldValidateWhenSizeLimitExceededExceptionOccursFromAnnotation() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodControllerWithAnnotation);
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenThrow(new FileUploadBase.SizeLimitExceededException("", 3L, 2L));
observer.upload(event, request, config, validator);
verify(servletFileUpload).setFileSizeMax(10);
verify(servletFileUpload).setSizeMax(20);
}
@Test
public void shouldCreateDirInsideAppIfTempDirAreNotAvailable() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodController);
DefaultMultipartConfig configSpy = (DefaultMultipartConfig) spy(config);
doReturn(null).when(configSpy).getTemporaryDirectory();
final List<FileItem> elements = new ArrayList<>();
elements.add(new MockFileItem("myfile", "foo.txt", "bar".getBytes()));
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenReturn(elements);
observer.upload(event, request, configSpy, validator);
verify(configSpy).createDirInsideApplication();
}
@Test
public void checkIfFileHasBeenUploaded() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodController);
final List<FileItem> elements = new ArrayList<>();
byte[] content = "foo".getBytes();
elements.add(new MockFileItem("thefile0", "text/plain", "file.txt", content));
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenReturn(elements);
observer.upload(event, request, config, validator);
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 mustConvertUnixPathToFileName() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodController);
List<FileItem> elements = new ArrayList<>();
elements.add(new MockFileItem("thefile0", "text/plain", "/unix/path/file0.txt", new byte[0]));
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenReturn(elements);
observer.upload(event, request, config, validator);
ArgumentCaptor<UploadedFile> argument = ArgumentCaptor.forClass(UploadedFile.class);
verify(request).setAttribute(anyString(), argument.capture());
assertThat(argument.getValue().getFileName(), is("file0.txt"));
}
@Test
public void mustConvertWindowsPathToFileName() throws Exception {
when(event.getMethod()).thenReturn(uploadMethodController);
List<FileItem> elements = new ArrayList<>();
elements.add(new MockFileItem("thefile0", "text/plain", "c:/windows/path/file0.txt", new byte[0]));
when(observer.createServletFileUpload(config)).thenReturn(servletFileUpload);
when(servletFileUpload.parseRequest(request)).thenReturn(elements);
observer.upload(event, request, config, validator);
ArgumentCaptor<UploadedFile> argument = ArgumentCaptor.forClass(UploadedFile.class);
verify(request).setAttribute(anyString(), argument.capture());
assertThat(argument.getValue().getFileName(), is("file0.txt"));
}
@Test
public void ensureCopyUploadedFileToOutputStream() throws IOException {
File outputFile = tmpdir.newFile();
OutputStream outputStream = new FileOutputStream(outputFile);
byte[] byteOnlyFileContent = { 0x0, 0x1, 0x2 };
FileItem mockedFileItem = mock(FileItem.class);
when(mockedFileItem.getInputStream()).thenReturn(new ByteArrayInputStream(byteOnlyFileContent));
UploadedFile file = new CommonsUploadedFile(mockedFileItem);
file.writeTo(outputStream);
assertThat(outputFile.length(), is(new Long(byteOnlyFileContent.length)));
assertThat(Files.readAllBytes(outputFile.toPath()), is(byteOnlyFileContent));
}
public void uploadMethod(UploadedFile file) {
}
@UploadSizeLimit(fileSizeLimit = 10, sizeLimit = 20)
public void uploadMethodWthAnnotation(UploadedFile file) {
}
}