package jnpf.elnService.template;
|
|
import feign.Request;
|
import feign.Response;
|
import jnpf.base.vo.DownloadVO;
|
import jnpf.elnEntity.ElnDocumentFileVo;
|
import jnpf.exception.DataException;
|
import jnpf.file.FileApi;
|
import jnpf.file.FileUploadApi;
|
import jnpf.model.ExportModel;
|
import jnpf.model.FileListVO;
|
import jnpf.model.upload.UploadFileModel;
|
import jnpf.util.JsonUtil;
|
import org.dromara.x.file.storage.core.FileInfo;
|
import org.junit.Test;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import java.io.ByteArrayInputStream;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.io.Reader;
|
import java.nio.charset.Charset;
|
import java.nio.charset.StandardCharsets;
|
import java.util.ArrayList;
|
import java.util.Collection;
|
import java.util.Collections;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.fail;
|
|
public class ElnTemplateFileServiceTest {
|
|
@Test
|
public void copiesUniqueAuthoritativeDocxAndClosesResponses() {
|
Fixture fixture = new Fixture();
|
|
String attachments = fixture.service.copyDocx(sourceAttachments(), "target-template");
|
|
List<ElnDocumentFileVo> files = JsonUtil.getJsonToList(attachments, ElnDocumentFileVo.class);
|
assertEquals(1, files.size());
|
assertEquals("target-template.docx", files.get(0).getFileId());
|
assertEquals("docx", files.get(0).getFileExtension());
|
assertNotEquals("source.docx", files.get(0).getFileId());
|
assertArrayEquals(Fixture.DOCX, fixture.uploadApi.files.get("target-template.docx"));
|
assertEquals(Collections.singletonList("target-template.docx"), fixture.uploadApi.replacedIds);
|
assertEquals(2, fixture.uploadApi.closedBodies);
|
assertEquals("annex", fixture.uploadApi.downloads.get(0).getFolderName());
|
assertEquals("source.docx", fixture.uploadApi.downloads.get(0).getObjectName());
|
assertEquals("WebAnnexFile/", fixture.uploadApi.lastFolder);
|
assertEquals(0, fixture.uploadApi.uploadCalls);
|
}
|
|
@Test
|
public void retriesByOverwritingTheSameDeterministicTarget() {
|
Fixture fixture = new Fixture();
|
|
fixture.service.copyDocx(sourceAttachments(), "target-template");
|
fixture.service.copyDocx(sourceAttachments(), "target-template");
|
|
assertEquals(2, fixture.uploadApi.replacedIds.size());
|
assertEquals("target-template.docx", fixture.uploadApi.replacedIds.get(0));
|
assertEquals("target-template.docx", fixture.uploadApi.replacedIds.get(1));
|
assertEquals(2, fixture.uploadApi.files.size());
|
assertTrue(fixture.uploadApi.files.containsKey("target-template.docx"));
|
}
|
|
@Test
|
public void rejectsReplaceFalseWithoutReturningAttachmentJson() {
|
Fixture fixture = new Fixture();
|
fixture.uploadApi.replaceResult = false;
|
|
expectDataException(() -> fixture.service.copyDocx(sourceAttachments(), "target-template"));
|
|
assertEquals(1, fixture.uploadApi.replacedIds.size());
|
}
|
|
@Test
|
public void rejectsFailedTargetReadback() {
|
Fixture fixture = new Fixture();
|
fixture.uploadApi.failTargetReadback = true;
|
|
expectDataException(() -> fixture.service.copyDocx(sourceAttachments(), "target-template"));
|
}
|
|
@Test
|
public void rejectsTargetDigestMismatch() {
|
Fixture fixture = new Fixture();
|
fixture.uploadApi.corruptTargetReadback = true;
|
|
expectDataException(() -> fixture.service.copyDocx(sourceAttachments(), "target-template"));
|
}
|
|
@Test
|
public void rejectsAmbiguousEmptyAndOversizedSourceDocuments() {
|
Fixture fixture = new Fixture();
|
expectDataException(() -> fixture.service.copyDocx(
|
"[{\"fileId\":\"a.docx\",\"fileExtension\":\"docx\"},"
|
+ "{\"fileId\":\"b.docx\",\"fileExtension\":\"docx\"}]", "target"));
|
|
fixture.uploadApi.files.put("source.docx", new byte[0]);
|
expectDataException(() -> fixture.service.copyDocx(sourceAttachments(), "target"));
|
|
fixture.uploadApi.reportedLength = 50 * 1024 * 1024 + 1;
|
expectDataException(() -> fixture.service.copyDocx(sourceAttachments(), "target"));
|
}
|
|
@Test
|
public void acceptsUnknownContentLengthAfterReadingActualBytes() {
|
Fixture fixture = new Fixture();
|
fixture.uploadApi.reportedLength = -1;
|
|
String attachments = fixture.service.copyDocx(sourceAttachments(), "target-template");
|
|
assertTrue(attachments.contains("target-template.docx"));
|
}
|
|
private static String sourceAttachments() {
|
return "[{\"fileId\":\"source.docx\",\"name\":\"source.docx\","
|
+ "\"fileExtension\":\"docx\"}]";
|
}
|
|
private static void expectDataException(Runnable action) {
|
try {
|
action.run();
|
fail("预期抛出 DataException");
|
} catch (DataException expected) {
|
assertFalse(expected.getMessage().trim().isEmpty());
|
}
|
}
|
|
private static final class Fixture {
|
private static final byte[] DOCX = "docx-content".getBytes(StandardCharsets.UTF_8);
|
private final StubFileApi fileApi = new StubFileApi();
|
private final StubFileUploadApi uploadApi = new StubFileUploadApi();
|
private final ElnTemplateFileService service = new ElnTemplateFileService(fileApi, uploadApi);
|
|
private Fixture() {
|
uploadApi.files.put("source.docx", DOCX);
|
}
|
}
|
|
private static final class StubFileApi implements FileApi {
|
@Override
|
public String getPath(String type) {
|
assertEquals("annex", type);
|
return "WebAnnexFile/";
|
}
|
|
@Override
|
public DownloadVO exportFile(ExportModel exportModel) {
|
return null;
|
}
|
|
@Override
|
public String getLocalBasePath() {
|
return null;
|
}
|
|
@Override
|
public String getAllowPreviewFileType() {
|
return null;
|
}
|
|
@Override
|
public Boolean fileExists(String type, String fileName) {
|
return null;
|
}
|
}
|
|
private static final class StubFileUploadApi implements FileUploadApi {
|
private final Map<String, byte[]> files = new LinkedHashMap<>();
|
private final List<String> replacedIds = new ArrayList<>();
|
private final List<UploadFileModel> downloads = new ArrayList<>();
|
private boolean replaceResult = true;
|
private boolean failTargetReadback;
|
private boolean corruptTargetReadback;
|
private Integer reportedLength;
|
private int uploadCalls;
|
private int closedBodies;
|
private String lastFolder;
|
|
@Override
|
public FileInfo uploadFile(MultipartFile multipartFile, String folderName, String fileName) {
|
uploadCalls++;
|
return null;
|
}
|
|
@Override
|
public boolean replaceFile(MultipartFile multipartFile, String folderName, String fileName) {
|
replacedIds.add(fileName);
|
lastFolder = folderName;
|
if (replaceResult) {
|
try {
|
files.put(fileName, multipartFile.getBytes());
|
} catch (IOException e) {
|
throw new IllegalStateException(e);
|
}
|
}
|
return replaceResult;
|
}
|
|
@Override
|
public byte[] getByte(String fileName, String folderName) {
|
return null;
|
}
|
|
@Override
|
public List<FileListVO> getFileList(String folderName) {
|
return Collections.emptyList();
|
}
|
|
@Override
|
public Response downFile(UploadFileModel model) {
|
downloads.add(model);
|
boolean target = model.getObjectName().startsWith("target-");
|
if (target && failTargetReadback) {
|
return response(404, null);
|
}
|
byte[] bytes = files.get(model.getObjectName());
|
if (target && corruptTargetReadback && bytes != null) {
|
bytes = "different-content".getBytes(StandardCharsets.UTF_8);
|
}
|
return response(bytes == null ? 404 : 200, bytes);
|
}
|
|
private Response response(int status, byte[] bytes) {
|
return Response.builder()
|
.status(status)
|
.reason(status == 200 ? "OK" : "Not Found")
|
.request(Request.create(Request.HttpMethod.GET, "http://file/download",
|
Collections.<String, Collection<String>>emptyMap(), null, Charset.defaultCharset()))
|
.body(bytes == null ? null : new TrackingBody(bytes,
|
reportedLength == null ? bytes.length : reportedLength, this))
|
.build();
|
}
|
|
@Override
|
public FileInfo getFile(String fileId) {
|
return null;
|
}
|
|
@Override
|
public String getDefaultPlatform() {
|
return null;
|
}
|
|
@Override
|
public List<FileListVO> getVisualList(String folderName) {
|
return Collections.emptyList();
|
}
|
}
|
|
private static final class TrackingBody implements Response.Body {
|
private final byte[] bytes;
|
private final int length;
|
private final StubFileUploadApi owner;
|
|
private TrackingBody(byte[] bytes, int length, StubFileUploadApi owner) {
|
this.bytes = bytes;
|
this.length = length;
|
this.owner = owner;
|
}
|
|
@Override
|
public Integer length() {
|
return length;
|
}
|
|
@Override
|
public boolean isRepeatable() {
|
return true;
|
}
|
|
@Override
|
public InputStream asInputStream() {
|
return new ByteArrayInputStream(bytes);
|
}
|
|
@Override
|
public Reader asReader(Charset charset) {
|
throw new UnsupportedOperationException();
|
}
|
|
@Override
|
public void close() {
|
owner.closedBodies++;
|
}
|
}
|
}
|