package de.is24.infrastructure.gridfs.http.web.controller;
import de.is24.infrastructure.gridfs.http.exception.GridFSFileNotFoundException;
import de.is24.infrastructure.gridfs.http.storage.FileDescriptor;
import de.is24.infrastructure.gridfs.http.storage.FileStorageItem;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import static org.apache.commons.lang.RandomStringUtils.random;
import static org.apache.commons.lang.StringUtils.repeat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.http.MediaType.valueOf;
import static org.springframework.test.util.AssertionErrors.assertEquals;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class FileControllerTest extends AbstractControllerTest {
public static final MediaType APPLICATION_X_RPM = valueOf("application/x-rpm");
public static final String REPO = "files";
public static final String ARCH = "test";
public static final String FILENAME = "file";
public static final String RPM_URL = "/repo/" + REPO + "/" + ARCH + "/" + FILENAME + ".rpm";
private static final MockHttpServletRequestBuilder DELETE_REQUEST = MockMvcRequestBuilders.delete(RPM_URL);
public static final String CONTENT_WITH_200_CHARS = random(200, "a");
@SuppressWarnings("unchecked")
@Test
public void get404ResponseWhenFileIsNotFound() throws Exception {
when(fileStorageService.getResource(any(FileDescriptor.class))).thenThrow(GridFSFileNotFoundException.class);
performRpmGet().andExpect(status().isNotFound());
}
@Test
public void deliverFileWhenFileIsFoundInDB() throws Exception {
givenGridFSDBFile();
performRpmGet().andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_X_RPM))
.andExpect(content().string(CONTENT_WITH_200_CHARS))
.andExpect(contentLengthIs(200));
}
@Test
public void deliverFirstBytesWhenByteRangeRequestWithEnd() throws Exception {
givenGridFSDBFile();
performRpmGetWithRange("bytes=0-100").andExpect(status().isPartialContent())
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("Content-Range", "bytes 0-100/200"))
.andExpect(contentLengthIs(101))
.andExpect(content().contentType(APPLICATION_X_RPM))
.andExpect(content().string(repeat("a", 101)));
}
@Test
public void deliver416WhenByteRangeInvalidChars() throws Exception {
givenGridFSDBFile();
performRpmGetWithRange("bytes=0-abc").andExpect(badRangeStatus());
}
@Test
public void deliver416WhenByteRangeInvalidNumFormat() throws Exception {
givenGridFSDBFile();
performRpmGetWithRange("bytes=0-001").andExpect(badRangeStatus());
}
@Test
public void deliver416WhenByteRangeEndCouldNotBeParsedAsLong() throws Exception {
givenGridFSDBFile();
performRpmGetWithRange("bytes=0-12345678912345679812").andExpect(badRangeStatus());
}
@Test
public void deliver416WhenByteRangeStartCouldNotBeParsedAsLong() throws Exception {
givenGridFSDBFile();
performRpmGetWithRange("bytes=12345678912345679812-").andExpect(badRangeStatus());
}
@Test
public void deliver416WhenByteRangeStartIsBeforeRangeEnd() throws Exception {
givenGridFSDBFile();
performRpmGetWithRange("bytes=123-100").andExpect(badRangeStatus());
}
@Test
public void deliver204IfRpmDoesNotExists() throws Exception {
doThrow(GridFSFileNotFoundException.class).when(storageService).delete(any(FileDescriptor.class));
mockMvc.perform(DELETE_REQUEST).andExpect(status().isNoContent());
}
@Test
public void deliver204IfRpmIsRemoved() throws Exception {
givenGridFSDBFile();
mockMvc.perform(DELETE_REQUEST).andExpect(status().isNoContent());
}
private void givenGridFSDBFile() throws IOException {
FileStorageItem fileStorageItem = storageItem(CONTENT_WITH_200_CHARS);
when(fileStorageService.getFileBy(any(FileDescriptor.class))).thenReturn(fileStorageItem);
when(fileStorageService.getResource(any(FileDescriptor.class))).thenCallRealMethod();
when(fileStorageService.getResource(any(FileDescriptor.class), anyLong())).thenCallRealMethod();
when(fileStorageService.getResource(any(FileDescriptor.class), anyLong(), anyLong())).thenCallRealMethod();
}
private ResultActions performRpmGet() throws Exception {
return performSimpleGet(RPM_URL);
}
private ResultMatcher contentLengthIs(final int length) {
return result -> assertEquals("Response Content-Length", length, result.getResponse().getContentLength());
}
private ResultActions performRpmGetWithRange(final String rangeHeader) throws Exception {
return mockMvc.perform(MockMvcRequestBuilders.get(RPM_URL).header("Range", rangeHeader));
}
private String toString(ResponseEntity<InputStreamResource> response) throws IOException {
return IOUtils.toString(response.getBody().getInputStream());
}
private FileStorageItem storageItem(String content) {
FileStorageItem storageItem = mock(FileStorageItem.class);
when(storageItem.getInputStream()).thenReturn(new ByteArrayInputStream(content.getBytes()));
when(storageItem.getSize()).thenReturn((long) content.length());
when(storageItem.getContentType()).thenReturn("application/x-rpm");
when(storageItem.getFilename()).thenReturn("files/test/file.rpm");
return storageItem;
}
}