package jnpf.fileStorage.dms;
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import jnpf.dmsEntity.storage.enums.DmsWorkflowFileType;
|
import jnpf.dmsEntity.storage.form.DmsWorkflowFileForm;
|
import jnpf.dmsEntity.storage.vo.DmsFileUploadVO;
|
import jnpf.dmsEntity.storage.vo.DmsWorkflowFileUploadVO;
|
import jnpf.exception.DataException;
|
import org.junit.Test;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import java.io.ByteArrayInputStream;
|
import java.io.InputStream;
|
import java.nio.charset.StandardCharsets;
|
import java.util.LinkedHashMap;
|
import java.util.Map;
|
import java.util.function.Consumer;
|
|
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertThrows;
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.when;
|
|
public class DmsCommonFileReaderTest {
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final DmsCommonFileStorage commonFileStorage = mock(DmsCommonFileStorage.class);
|
private final DmsCommonFileReader reader = new DmsCommonFileReader(
|
objectMapper, commonFileStorage);
|
|
@Test
|
public void parsesFileCommonRelativeAndAbsoluteUrls() {
|
DmsCommonFileReader.SourceFile relative = reader.parse(
|
"/api/file/Image/annex/stored%20file.docx?s=ticket");
|
DmsCommonFileReader.SourceFile absolute = reader.parse(
|
"https://files.example.com/api/file/Image/temporary/id.pdf?fullfilename=a.pdf");
|
|
assertEquals("annex", relative.getType());
|
assertEquals("stored file.docx", relative.getFileName());
|
assertEquals("temporary", absolute.getType());
|
assertEquals("id.pdf", absolute.getFileName());
|
}
|
|
@Test
|
public void rejectsUrlsOutsideFileCommonAndEncodedPathTraversal() {
|
assertThrows(DataException.class, () -> reader.parse("https://example.com/file.docx"));
|
assertThrows(DataException.class,
|
() -> reader.parse("/api/file/Image/annex/%2E%2E%2Fsecret.docx"));
|
}
|
|
@Test
|
@SuppressWarnings("unchecked")
|
public void readsStoredContentAndRestoresOriginalNameFromFileJson() {
|
byte[] content = "controlled document".getBytes(StandardCharsets.UTF_8);
|
Map<String, Object> fileJson = new LinkedHashMap<>();
|
fileJson.put("fileId", "stored-file.docx");
|
fileJson.put("name", "质量手册.docx");
|
|
when(commonFileStorage.exists("annex", "stored-file.docx")).thenReturn(true);
|
doAnswer(invocation -> {
|
Consumer<InputStream> consumer = invocation.getArgument(2);
|
consumer.accept(new ByteArrayInputStream(content));
|
return null;
|
}).when(commonFileStorage).download(eq("annex"), eq("stored-file.docx"), any());
|
|
String result = reader.withFile(
|
"/api/file/Image/annex/stored-file.docx", fileJson, "质量手册",
|
file -> verifyFile(file, content));
|
|
assertEquals("stored", result);
|
}
|
|
@Test
|
@SuppressWarnings("unchecked")
|
public void readsDownloadUrlUsingFileIdFromFileJson() {
|
byte[] content = "approval page".getBytes(StandardCharsets.UTF_8);
|
String url = "/api/file/Download?encryption=ticket&name=%E7%AD%BE%E6%89%B9%E9%A1%B52.0.docx";
|
Map<String, Object> fileJson = new LinkedHashMap<>();
|
fileJson.put("fileId", "6aad3081cdb2f4d33f7ad915.docx");
|
fileJson.put("name", "签批页2.0.docx");
|
fileJson.put("url", url);
|
|
when(commonFileStorage.exists("annex", "6aad3081cdb2f4d33f7ad915.docx"))
|
.thenReturn(true);
|
doAnswer(invocation -> {
|
Consumer<InputStream> consumer = invocation.getArgument(2);
|
consumer.accept(new ByteArrayInputStream(content));
|
return null;
|
}).when(commonFileStorage).download(
|
eq("annex"), eq("6aad3081cdb2f4d33f7ad915.docx"), any());
|
|
String result = reader.withFile(url, fileJson, "签批页",
|
file -> verifyFile(file, content, "签批页2.0.docx"));
|
|
assertEquals("stored", result);
|
}
|
|
@Test
|
public void rejectsDownloadUrlWithoutSafeFileId() {
|
String url = "/api/file/Download?encryption=ticket";
|
assertThrows(DataException.class,
|
() -> reader.withFile(url, new LinkedHashMap<>(), "签批页", file -> null));
|
|
Map<String, Object> unsafe = new LinkedHashMap<>();
|
unsafe.put("fileId", "../secret.docx");
|
unsafe.put("url", url);
|
assertThrows(DataException.class,
|
() -> reader.withFile(url, unsafe, "签批页", file -> null));
|
}
|
|
@Test
|
public void acceptsFrontendAliasesAndSerializesFlatResponseWithFileJson() throws Exception {
|
DmsWorkflowFileForm form = objectMapper.readValue(
|
"{\"filetTpe\":\"修定\",\"fileJson\":{\"name\":\"a.docx\"}}",
|
DmsWorkflowFileForm.class);
|
assertEquals(DmsWorkflowFileType.REVISE, form.getFileType());
|
assertEquals("a.docx", ((Map<?, ?>) form.getFileJson()).get("name"));
|
|
DmsFileUploadVO file = new DmsFileUploadVO();
|
file.setDocumentNo("DOC-001");
|
Map<String, Object> fileJson = new LinkedHashMap<>();
|
fileJson.put("name", "a.docx");
|
DmsWorkflowFileUploadVO response = new DmsWorkflowFileUploadVO(file, fileJson);
|
JsonNode json = objectMapper.readTree(objectMapper.writeValueAsString(response));
|
|
assertEquals("DOC-001", json.get("documentNo").asText());
|
assertEquals("a.docx", json.get("file_json").get("name").asText());
|
assertFalse(json.has("file"));
|
assertSame(fileJson, response.getFileJson());
|
}
|
|
private String verifyFile(MultipartFile file, byte[] expected) {
|
return verifyFile(file, expected, "质量手册.docx");
|
}
|
|
private String verifyFile(MultipartFile file, byte[] expected, String expectedName) {
|
try {
|
assertEquals(expectedName, file.getOriginalFilename());
|
assertArrayEquals(expected, file.getBytes());
|
return "stored";
|
} catch (Exception exception) {
|
throw new AssertionError(exception);
|
}
|
}
|
}
|