Newer
Older
/*
* This file is part of vospace-file-service
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import it.inaf.ia2.transfer.persistence.model.FileInfo;
import it.inaf.ia2.transfer.persistence.FileDAO;
import it.inaf.ia2.transfer.persistence.JobDAO;
import it.inaf.oats.vospace.exception.QuotaExceededException;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Optional;
import java.util.UUID;
import javax.servlet.ServletInputStream;
import net.ivoa.xml.uws.v1.ExecutionPhase;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import org.mockito.Mockito;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.util.FileSystemUtils;
@SpringBootTest
@AutoConfigureMockMvc
public class PutFileControllerTest {
@MockBean
private FileDAO fileDao;
@MockBean
private JobDAO jobDAO;
private static File temporaryDirectory;
@BeforeAll
public static void setUp() {
temporaryDirectory = Files.newTemporaryFolder();
}
@AfterAll
public static void tearDown() {
FileSystemUtils.deleteRecursively(temporaryDirectory);
}
@Test
public void putGenericFile() throws Exception {
when(fileDao.getRemainingQuota(any())).thenReturn(null);
String randomFileName = UUID.randomUUID().toString();
createBaseFileInfo(randomFileName);
MockMultipartFile fakeFile = new MockMultipartFile("file", "test.txt", "text/plain", "content".getBytes());
mockMvc.perform(putMultipart("/path/to/test.txt")
.file(fakeFile))
.andDo(print())
.andExpect(status().isOk());
File file = Path.of(getTestFilePath(randomFileName)).toFile();
assertTrue(file.exists());
assertEquals("content", Files.contentOf(file, StandardCharsets.UTF_8));
public void putGenericFileWithNameConflictExtension() throws Exception {
putGenericFileWithNameConflict("test.txt", "test-1.txt", "test-2.txt");
}
@Test
public void putGenericFileWithNameConflictNoExtension() throws Exception {
putGenericFileWithNameConflict("test", "test-1", "test-2");
}
private void putGenericFileWithNameConflict(String name1, String name2, String name3) throws Exception {
when(fileDao.getRemainingQuota(any())).thenReturn(null);
MockMultipartFile fakeFile = new MockMultipartFile("file", "test.txt", "text/plain", "content".getBytes());
mockMvc.perform(putMultipart("/path/to/test.txt")
.file(fakeFile))
.andDo(print())
.andExpect(status().isOk());
File file = Path.of(getTestFilePath(name1)).toFile();
assertTrue(file.exists());
assertEquals("content", Files.contentOf(file, StandardCharsets.UTF_8));
MockMultipartFile fakeFile2 = new MockMultipartFile("file", "test.txt", "text/plain", "content2".getBytes());
mockMvc.perform(putMultipart("/path/to/test.txt")
.file(fakeFile2))
.andDo(print())
.andExpect(status().isOk());
File file2 = Path.of(getTestFilePath(name2)).toFile();
assertTrue(file2.exists());
assertEquals("content2", Files.contentOf(file2, StandardCharsets.UTF_8));
MockMultipartFile fakeFile3 = new MockMultipartFile("file", "test.txt", "text/plain", "content3".getBytes());
mockMvc.perform(putMultipart("/path/to/test.txt")
.file(fakeFile3))
.andDo(print())
.andExpect(status().isOk());
File file3 = Path.of(getTestFilePath(name3)).toFile();
assertTrue(file3.exists());
assertEquals("content3", Files.contentOf(file3, StandardCharsets.UTF_8));
assertTrue(file.delete());
assertTrue(file2.delete());
}
@Test
public void putGenericFileWithJobId() throws Exception {
when(fileDao.getRemainingQuota(any())).thenReturn(null);
when(jobDAO.isJobExisting("pippo10")).thenReturn(false);
when(jobDAO.isJobExisting("pippo5")).thenReturn(true);
String randomFileName = UUID.randomUUID().toString();
createBaseFileInfo(randomFileName);
MockMultipartFile fakeFile = new MockMultipartFile("file", "test.txt", "text/plain", "content".getBytes());
// Try with invalid jobid
mockMvc.perform(putMultipart("/path/to/test.txt")
.file(fakeFile).param("jobId", "pippo10"))
.andDo(print())
.andExpect(status().is4xxClientError());
verify(jobDAO, times(1)).isJobExisting(eq("pippo10"));
// Retry with valid jobid
mockMvc.perform(putMultipart("/path/to/test.txt")
.file(fakeFile).param("jobId", "pippo5"))
.andDo(print())
.andExpect(status().is2xxSuccessful());
verify(jobDAO, times(1)).isJobExisting(eq("pippo5"));
verify(jobDAO, times(1)).updateJobPhase(eq(ExecutionPhase.COMPLETED), any());
File file = Path.of(getTestFilePath(randomFileName)).toFile();
assertTrue(file.exists());
assertEquals("content", Files.contentOf(file, StandardCharsets.UTF_8));
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
@Test
public void testPutFileWithoutNodeInDatabase() throws Exception {
MockMultipartFile fakeFile = new MockMultipartFile("file", "foo.txt", "text/plain", "content".getBytes());
mockMvc.perform(putMultipart("/path/to/foo.txt")
.file(fakeFile))
.andDo(print())
.andExpect(status().isNotFound());
}
@Test
public void testPutWithInputStream() throws Exception {
createBaseFileInfo();
mockMvc.perform(put("/path/to/stream.txt"))
.andDo(print())
.andExpect(status().isOk());
}
@Test
public void testJobError() throws Exception {
createBaseFileInfo();
when(jobDAO.isJobExisting(eq("abcdef"))).thenReturn(true);
MockHttpServletRequestBuilder errorBuilder = put("/path/to/error");
errorBuilder.with(new RequestPostProcessor() {
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
MockHttpServletRequest spyRequest = spy(request);
when(spyRequest.getInputStream()).thenThrow(new RuntimeException());
return spyRequest;
}
});
try {
mockMvc.perform(errorBuilder
.param("jobId", "abcdef"));
} catch (Exception ex) {
}
verify(jobDAO, times(1)).setJobError(eq("abcdef"), any());
}
@Test
public void testQuotaExceededMultipart() throws Exception {
when(fileDao.getRemainingQuota(eq("/path/to"))).thenReturn(0l);
createBaseFileInfo();
MockMultipartFile fakeFile = new MockMultipartFile("file", "test.txt", null, "content".getBytes());
Exception ex = mockMvc.perform(putMultipart("/path/to/test.txt")
.file(fakeFile))
.andDo(print())
.andExpect(status().is5xxServerError())
.andReturn().getResolvedException();
verify(fileDao, times(1)).getRemainingQuota(eq("/path/to"));
assertTrue(ex instanceof QuotaExceededException);
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
}
@Test
public void testQuotaExceededStream() throws Exception {
when(fileDao.getRemainingQuota(eq("/path/to"))).thenReturn(0l);
createBaseFileInfo();
MockHttpServletRequestBuilder streamBuilder = put("/path/to/test.txt");
streamBuilder.with(new RequestPostProcessor() {
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
MockHttpServletRequest spyRequest = spy(request);
ByteArrayInputStream bais = new ByteArrayInputStream("some data".getBytes());
ServletInputStream sis = mock(ServletInputStream.class);
try {
when(sis.transferTo(any())).thenAnswer(i -> bais.transferTo(i.getArgument(0)));
} catch (IOException ex) {
}
Mockito.doReturn(sis).when(spyRequest).getInputStream();
return spyRequest;
}
});
Exception ex = mockMvc.perform(streamBuilder)
.andDo(print())
.andExpect(status().is5xxServerError())
.andReturn().getResolvedException();
verify(fileDao, times(1)).getRemainingQuota(eq("/path/to"));
assertTrue(ex instanceof QuotaExceededException);
private FileInfo createBaseFileInfo() {
String randomFileName = UUID.randomUUID().toString();
return createBaseFileInfo(randomFileName);
}
private FileInfo createBaseFileInfo(String fileName) {
fileInfo.setOsPath(getTestFilePath(fileName));
fileInfo.setVirtualPath("/path/to/" + fileName);
fileInfo.setPublic(false);
when(fileDao.getFileInfo(any())).thenReturn(Optional.of(fileInfo));
when(fileDao.setBusy(any(), any())).thenReturn(1);
private String getTestFilePath(String fileName) {
return temporaryDirectory.toPath().resolve("subdir").resolve(fileName).toFile().getAbsolutePath();
private MockMultipartHttpServletRequestBuilder putMultipart(String uri) {
MockMultipartHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(uri);
builder.with(new RequestPostProcessor() {
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
request.setMethod("PUT");
return request;
}
});
return builder;
}
}