实现Excel转PDF手动转换接口
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
package cn.nianxx.thhotel.integrations.document.libreoffice.adapter;
|
||||
|
||||
import cn.nianxx.thhotel.platform.documentconversion.common.dto.ExcelToPdfConversionInput;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.common.dto.ExcelToPdfConvertedDocument;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.service.DocumentConversionException;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.service.impl.DocumentConversionProperties;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
class LibreOfficeExcelToPdfAdapterTest {
|
||||
|
||||
@TempDir
|
||||
private Path tempDir;
|
||||
|
||||
@Test
|
||||
void shouldInvokeLibreOfficeHeadlessAndCleanTemporaryFiles() throws Exception {
|
||||
DocumentConversionProperties properties = properties();
|
||||
AtomicReference<LibreOfficeProcessRequest> processRequestRef = new AtomicReference<>();
|
||||
LibreOfficeProcessRunner processRunner = request -> {
|
||||
processRequestRef.set(request);
|
||||
org.assertj.core.api.Assertions.assertThat(request.command())
|
||||
.contains("soffice-test")
|
||||
.contains("--headless")
|
||||
.contains("--convert-to")
|
||||
.contains("pdf");
|
||||
org.assertj.core.api.Assertions.assertThat(Files.exists(request.inputFile())).isTrue();
|
||||
writePdf(request.outputDirectory().resolve("booking-request.pdf"));
|
||||
return new LibreOfficeProcessResult(0, false, "convert ok", "");
|
||||
};
|
||||
LibreOfficeExcelToPdfAdapter adapter = new LibreOfficeExcelToPdfAdapter(properties, processRunner);
|
||||
|
||||
ExcelToPdfConvertedDocument result = adapter.convert(new ExcelToPdfConversionInput(
|
||||
"booking-request.xlsx",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
10L,
|
||||
"xlsx-bytes".getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
org.assertj.core.api.Assertions.assertThat(result.fileName()).isEqualTo("booking-request.pdf");
|
||||
org.assertj.core.api.Assertions.assertThat(result.contentType()).isEqualTo("application/pdf");
|
||||
org.assertj.core.api.Assertions.assertThat(result.content()).startsWith("%PDF".getBytes(StandardCharsets.UTF_8));
|
||||
org.assertj.core.api.Assertions.assertThat(processRequestRef.get().profileDirectory().toString())
|
||||
.contains("lo-profile");
|
||||
org.assertj.core.api.Assertions.assertThat(processRequestRef.get().workingDirectory())
|
||||
.isEqualTo(processRequestRef.get().inputFile().getParent());
|
||||
try (var children = Files.list(tempDir)) {
|
||||
org.assertj.core.api.Assertions.assertThat(children).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTimeoutErrorAndCleanTemporaryFiles() {
|
||||
DocumentConversionProperties properties = properties();
|
||||
LibreOfficeProcessRunner processRunner = request -> new LibreOfficeProcessResult(
|
||||
-1,
|
||||
true,
|
||||
"",
|
||||
"timeout");
|
||||
LibreOfficeExcelToPdfAdapter adapter = new LibreOfficeExcelToPdfAdapter(properties, processRunner);
|
||||
|
||||
org.assertj.core.api.Assertions.assertThatThrownBy(() -> adapter.convert(new ExcelToPdfConversionInput(
|
||||
"booking-request.xlsx",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
10L,
|
||||
"xlsx-bytes".getBytes(StandardCharsets.UTF_8))))
|
||||
.isInstanceOf(DocumentConversionException.class)
|
||||
.satisfies(exception -> {
|
||||
DocumentConversionException conversionException = (DocumentConversionException) exception;
|
||||
org.assertj.core.api.Assertions.assertThat(conversionException.getStatus())
|
||||
.isEqualTo(HttpStatus.GATEWAY_TIMEOUT);
|
||||
org.assertj.core.api.Assertions.assertThat(conversionException.getErrorCode())
|
||||
.isEqualTo("DOCUMENT_CONVERSION_TIMEOUT");
|
||||
});
|
||||
assertTempDirEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFailedErrorWhenLibreOfficeDoesNotProducePdf() {
|
||||
DocumentConversionProperties properties = properties();
|
||||
LibreOfficeProcessRunner processRunner = request -> new LibreOfficeProcessResult(0, false, "ok", "");
|
||||
LibreOfficeExcelToPdfAdapter adapter = new LibreOfficeExcelToPdfAdapter(properties, processRunner);
|
||||
|
||||
org.assertj.core.api.Assertions.assertThatThrownBy(() -> adapter.convert(new ExcelToPdfConversionInput(
|
||||
"booking-request.xlsx",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
10L,
|
||||
"xlsx-bytes".getBytes(StandardCharsets.UTF_8))))
|
||||
.isInstanceOf(DocumentConversionException.class)
|
||||
.satisfies(exception -> {
|
||||
DocumentConversionException conversionException = (DocumentConversionException) exception;
|
||||
org.assertj.core.api.Assertions.assertThat(conversionException.getStatus())
|
||||
.isEqualTo(HttpStatus.BAD_GATEWAY);
|
||||
org.assertj.core.api.Assertions.assertThat(conversionException.getErrorCode())
|
||||
.isEqualTo("DOCUMENT_CONVERSION_FAILED");
|
||||
});
|
||||
assertTempDirEmpty();
|
||||
}
|
||||
|
||||
private DocumentConversionProperties properties() {
|
||||
DocumentConversionProperties properties = new DocumentConversionProperties();
|
||||
properties.setEnabled(true);
|
||||
properties.setSofficePath("soffice-test");
|
||||
properties.setTempDir(tempDir.toString());
|
||||
properties.setTimeoutSeconds(3);
|
||||
properties.setOutputOssPrefix("document-conversions/excel-to-pdf/");
|
||||
return properties;
|
||||
}
|
||||
|
||||
private void writePdf(Path pdfFile) {
|
||||
try {
|
||||
Files.write(pdfFile, "%PDF-1.7\nconverted".getBytes(StandardCharsets.UTF_8));
|
||||
} catch (Exception exception) {
|
||||
throw new AssertionError("写入模拟 PDF 失败。", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTempDirEmpty() {
|
||||
try (var children = Files.list(tempDir)) {
|
||||
org.assertj.core.api.Assertions.assertThat(children).isEmpty();
|
||||
} catch (Exception exception) {
|
||||
throw new AssertionError("临时目录检查失败。", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package cn.nianxx.thhotel.platform.documentconversion.control;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import cn.nianxx.thhotel.ThHotelApplication;
|
||||
import cn.nianxx.thhotel.integrations.storage.aliyunoss.common.request.ObjectStoragePutRequest;
|
||||
import cn.nianxx.thhotel.integrations.storage.aliyunoss.common.result.ObjectStoragePutResult;
|
||||
import cn.nianxx.thhotel.integrations.storage.aliyunoss.service.ObjectStorageService;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.common.dto.ExcelToPdfConversionInput;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.common.dto.ExcelToPdfConvertedDocument;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.service.ExcelToPdfConverter;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.service.DocumentConversionException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
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.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@SpringBootTest(
|
||||
classes = ThHotelApplication.class,
|
||||
properties = {
|
||||
"document-conversion.enabled=true",
|
||||
"document-conversion.access-key=test-document-key",
|
||||
"document-conversion.max-file-bytes=1024",
|
||||
"document-conversion.output-oss-prefix=document-conversions/excel-to-pdf/"
|
||||
})
|
||||
@AutoConfigureMockMvc
|
||||
@ActiveProfiles("test")
|
||||
class DocumentConversionControllerTest {
|
||||
|
||||
private static final String ENDPOINT = "/api/system/document-conversions/excel-to-pdf";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
private ExcelToPdfConverter excelToPdfConverter;
|
||||
|
||||
@MockBean
|
||||
private ObjectStorageService objectStorageService;
|
||||
|
||||
@Test
|
||||
void shouldConvertUploadedExcelToPdfAndUploadPdfToOss() throws Exception {
|
||||
byte[] excelBytes = xlsxBytes("xlsx-bytes");
|
||||
byte[] pdfBytes = "%PDF-1.7\nconverted".getBytes(StandardCharsets.UTF_8);
|
||||
when(excelToPdfConverter.convert(any())).thenReturn(new ExcelToPdfConvertedDocument(
|
||||
"booking-request.pdf",
|
||||
MediaType.APPLICATION_PDF_VALUE,
|
||||
(long) pdfBytes.length,
|
||||
pdfBytes,
|
||||
123L));
|
||||
when(objectStorageService.putObject(any())).thenAnswer(invocation -> {
|
||||
ObjectStoragePutRequest request = invocation.getArgument(0);
|
||||
return new ObjectStoragePutResult(
|
||||
request.objectKey(),
|
||||
"https://oss.example.test/" + request.objectKey(),
|
||||
request.contentType(),
|
||||
request.sizeBytes());
|
||||
});
|
||||
|
||||
mockMvc.perform(multipart(ENDPOINT)
|
||||
.file(excelFile("booking-request.xlsx", excelBytes))
|
||||
.param("hotel_id", "HOTEL-TEST")
|
||||
.header("X-TH-Hotel-Document-Conversion-Key", "test-document-key"))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.conversion_status").value("SUCCEEDED"))
|
||||
.andExpect(jsonPath("$.source_file_name").value("booking-request.xlsx"))
|
||||
.andExpect(jsonPath("$.pdf_file_name").value("booking-request.pdf"))
|
||||
.andExpect(jsonPath("$.pdf_url", containsString("https://oss.example.test/")))
|
||||
.andExpect(jsonPath("$.object_key", containsString("document-conversions/excel-to-pdf/")))
|
||||
.andExpect(jsonPath("$.content_type").value(MediaType.APPLICATION_PDF_VALUE))
|
||||
.andExpect(jsonPath("$.pdf_size_bytes").value(pdfBytes.length))
|
||||
.andExpect(jsonPath("$.safe_error_summary").doesNotExist())
|
||||
.andExpect(content().string(not(containsString("test-document-key"))));
|
||||
|
||||
ArgumentCaptor<ExcelToPdfConversionInput> converterInputCaptor =
|
||||
ArgumentCaptor.forClass(ExcelToPdfConversionInput.class);
|
||||
verify(excelToPdfConverter).convert(converterInputCaptor.capture());
|
||||
org.assertj.core.api.Assertions.assertThat(converterInputCaptor.getValue().fileName())
|
||||
.isEqualTo("booking-request.xlsx");
|
||||
org.assertj.core.api.Assertions.assertThat(converterInputCaptor.getValue().content())
|
||||
.containsExactly(excelBytes);
|
||||
|
||||
ArgumentCaptor<ObjectStoragePutRequest> storageRequestCaptor =
|
||||
ArgumentCaptor.forClass(ObjectStoragePutRequest.class);
|
||||
verify(objectStorageService).putObject(storageRequestCaptor.capture());
|
||||
org.assertj.core.api.Assertions.assertThat(storageRequestCaptor.getValue().objectKey())
|
||||
.startsWith("document-conversions/excel-to-pdf/HOTEL-TEST/")
|
||||
.endsWith("/booking-request.pdf");
|
||||
org.assertj.core.api.Assertions.assertThat(storageRequestCaptor.getValue().contentType())
|
||||
.isEqualTo(MediaType.APPLICATION_PDF_VALUE);
|
||||
org.assertj.core.api.Assertions.assertThat(storageRequestCaptor.getValue().content())
|
||||
.containsExactly(pdfBytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectUploadWhenDocumentConversionKeyMissing() throws Exception {
|
||||
mockMvc.perform(multipart(ENDPOINT)
|
||||
.file(excelFile("booking-request.xlsx", xlsxBytes("xlsx-bytes"))))
|
||||
.andExpect(status().isUnauthorized())
|
||||
.andExpect(jsonPath("$.error_code").value("DOCUMENT_CONVERSION_KEY_INVALID"))
|
||||
.andExpect(content().string(not(containsString("test-document-key"))));
|
||||
|
||||
verifyNoInteractions(excelToPdfConverter, objectStorageService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectUnsupportedExcelFileType() throws Exception {
|
||||
mockMvc.perform(multipart(ENDPOINT)
|
||||
.file(excelFile("booking-request.txt", "not-excel".getBytes(StandardCharsets.UTF_8)))
|
||||
.header("X-TH-Hotel-Document-Conversion-Key", "test-document-key"))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.error_code").value("DOCUMENT_CONVERSION_FILE_TYPE_UNSUPPORTED"));
|
||||
|
||||
verifyNoInteractions(excelToPdfConverter, objectStorageService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectExcelExtensionWhenContentHeaderInvalid() throws Exception {
|
||||
mockMvc.perform(multipart(ENDPOINT)
|
||||
.file(excelFile("booking-request.xlsx", "not-excel".getBytes(StandardCharsets.UTF_8)))
|
||||
.header("X-TH-Hotel-Document-Conversion-Key", "test-document-key"))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.error_code").value("DOCUMENT_CONVERSION_FILE_CONTENT_INVALID"));
|
||||
|
||||
verifyNoInteractions(excelToPdfConverter, objectStorageService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectFileLargerThanConfiguredLimit() throws Exception {
|
||||
mockMvc.perform(multipart(ENDPOINT)
|
||||
.file(excelFile("booking-request.xlsx", "x".repeat(2048).getBytes(StandardCharsets.UTF_8)))
|
||||
.header("X-TH-Hotel-Document-Conversion-Key", "test-document-key"))
|
||||
.andExpect(status().isPayloadTooLarge())
|
||||
.andExpect(jsonPath("$.error_code").value("DOCUMENT_CONVERSION_FILE_TOO_LARGE"));
|
||||
|
||||
verifyNoInteractions(excelToPdfConverter, objectStorageService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnSafeErrorWhenLibreOfficeConversionFails() throws Exception {
|
||||
when(excelToPdfConverter.convert(any())).thenThrow(new DocumentConversionException(
|
||||
HttpStatus.GATEWAY_TIMEOUT,
|
||||
"DOCUMENT_CONVERSION_TIMEOUT",
|
||||
"Excel 转 PDF 超时。"));
|
||||
|
||||
mockMvc.perform(multipart(ENDPOINT)
|
||||
.file(excelFile("booking-request.xlsx", xlsxBytes("xlsx-bytes")))
|
||||
.header("X-TH-Hotel-Document-Conversion-Key", "test-document-key"))
|
||||
.andExpect(status().isGatewayTimeout())
|
||||
.andExpect(jsonPath("$.error_code").value("DOCUMENT_CONVERSION_TIMEOUT"))
|
||||
.andExpect(jsonPath("$.message").value("Excel 转 PDF 超时。"))
|
||||
.andExpect(content().string(not(containsString("xlsx-bytes"))));
|
||||
|
||||
verifyNoInteractions(objectStorageService);
|
||||
}
|
||||
|
||||
private MockMultipartFile excelFile(String fileName, byte[] content) {
|
||||
return new MockMultipartFile(
|
||||
"file",
|
||||
fileName,
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
content);
|
||||
}
|
||||
|
||||
private byte[] xlsxBytes(String content) {
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
outputStream.write(new byte[]{0x50, 0x4B, 0x03, 0x04});
|
||||
outputStream.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
return outputStream.toByteArray();
|
||||
} catch (IOException exception) {
|
||||
throw new AssertionError("构造测试 xlsx 字节失败。", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.nianxx.thhotel.platform.documentconversion.control;
|
||||
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import cn.nianxx.thhotel.ThHotelApplication;
|
||||
import cn.nianxx.thhotel.integrations.storage.aliyunoss.service.ObjectStorageService;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.service.ExcelToPdfConverter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.junit.jupiter.api.Test;
|
||||
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.MockMultipartFile;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@SpringBootTest(
|
||||
classes = ThHotelApplication.class,
|
||||
properties = {
|
||||
"document-conversion.enabled=false",
|
||||
"document-conversion.access-key=test-document-key"
|
||||
})
|
||||
@AutoConfigureMockMvc
|
||||
@ActiveProfiles("test")
|
||||
class DocumentConversionDisabledControllerTest {
|
||||
|
||||
private static final String ENDPOINT = "/api/system/document-conversions/excel-to-pdf";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
private ExcelToPdfConverter excelToPdfConverter;
|
||||
|
||||
@MockBean
|
||||
private ObjectStorageService objectStorageService;
|
||||
|
||||
@Test
|
||||
void shouldRejectUploadWhenDocumentConversionDisabled() throws Exception {
|
||||
mockMvc.perform(multipart(ENDPOINT)
|
||||
.file(new MockMultipartFile(
|
||||
"file",
|
||||
"booking-request.xlsx",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"xlsx-bytes".getBytes(StandardCharsets.UTF_8)))
|
||||
.header("X-TH-Hotel-Document-Conversion-Key", "test-document-key"))
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(jsonPath("$.error_code").value("DOCUMENT_CONVERSION_DISABLED"));
|
||||
|
||||
verifyNoInteractions(excelToPdfConverter, objectStorageService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package cn.nianxx.thhotel.platform.documentconversion.service.impl;
|
||||
|
||||
import cn.nianxx.thhotel.integrations.storage.aliyunoss.common.request.ObjectStoragePutRequest;
|
||||
import cn.nianxx.thhotel.integrations.storage.aliyunoss.common.result.ObjectStoragePutResult;
|
||||
import cn.nianxx.thhotel.integrations.storage.aliyunoss.service.ObjectStorageService;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.common.dto.ExcelToPdfConvertedDocument;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.service.DocumentConversionException;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.service.DocumentConversionService;
|
||||
import cn.nianxx.thhotel.platform.documentconversion.service.ExcelToPdfConverter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
|
||||
class DocumentConversionServiceImplTest {
|
||||
|
||||
@Test
|
||||
void shouldRejectSecondConversionWhenConcurrencyLimitReachedBeforeReadingBytes() throws Exception {
|
||||
byte[] pdfBytes = "%PDF-1.7".getBytes(StandardCharsets.UTF_8);
|
||||
CountDownLatch converterEntered = new CountDownLatch(1);
|
||||
CountDownLatch releaseConverter = new CountDownLatch(1);
|
||||
ExcelToPdfConverter converter = input -> {
|
||||
converterEntered.countDown();
|
||||
await(releaseConverter);
|
||||
return new ExcelToPdfConvertedDocument(
|
||||
"booking-request.pdf",
|
||||
MediaType.APPLICATION_PDF_VALUE,
|
||||
(long) pdfBytes.length,
|
||||
pdfBytes,
|
||||
10L);
|
||||
};
|
||||
ObjectStorageService storageService = request -> putResult(request);
|
||||
DocumentConversionService service = new DocumentConversionServiceImpl(properties(), converter, storageService);
|
||||
|
||||
Thread firstThread = new Thread(() -> service.convertExcelToPdf(
|
||||
"test-document-key",
|
||||
excelFile("booking-request.xlsx"),
|
||||
"HOTEL-TEST"));
|
||||
firstThread.start();
|
||||
org.assertj.core.api.Assertions.assertThat(converterEntered.await(2, TimeUnit.SECONDS)).isTrue();
|
||||
|
||||
org.assertj.core.api.Assertions.assertThatThrownBy(() -> service.convertExcelToPdf(
|
||||
"test-document-key",
|
||||
excelFile("second-request.xlsx"),
|
||||
"HOTEL-TEST"))
|
||||
.isInstanceOf(DocumentConversionException.class)
|
||||
.satisfies(exception -> {
|
||||
DocumentConversionException conversionException = (DocumentConversionException) exception;
|
||||
org.assertj.core.api.Assertions.assertThat(conversionException.getErrorCode())
|
||||
.isEqualTo("DOCUMENT_CONVERSION_BUSY");
|
||||
});
|
||||
|
||||
releaseConverter.countDown();
|
||||
firstThread.join(2000);
|
||||
}
|
||||
|
||||
private DocumentConversionProperties properties() {
|
||||
DocumentConversionProperties properties = new DocumentConversionProperties();
|
||||
properties.setEnabled(true);
|
||||
properties.setAccessKey("test-document-key");
|
||||
properties.setMaxConcurrent(1);
|
||||
properties.setMaxFileBytes(1024);
|
||||
properties.setOutputOssPrefix("document-conversions/excel-to-pdf/");
|
||||
return properties;
|
||||
}
|
||||
|
||||
private MockMultipartFile excelFile(String fileName) {
|
||||
return new MockMultipartFile(
|
||||
"file",
|
||||
fileName,
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
xlsxBytes(fileName));
|
||||
}
|
||||
|
||||
private byte[] xlsxBytes(String content) {
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
outputStream.write(new byte[]{0x50, 0x4B, 0x03, 0x04});
|
||||
outputStream.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
return outputStream.toByteArray();
|
||||
} catch (Exception exception) {
|
||||
throw new AssertionError("构造测试 xlsx 字节失败。", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectStoragePutResult putResult(ObjectStoragePutRequest request) {
|
||||
return new ObjectStoragePutResult(
|
||||
request.objectKey(),
|
||||
"https://oss.example.test/" + request.objectKey(),
|
||||
request.contentType(),
|
||||
request.sizeBytes());
|
||||
}
|
||||
|
||||
private void await(CountDownLatch latch) {
|
||||
try {
|
||||
org.assertj.core.api.Assertions.assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue();
|
||||
} catch (InterruptedException exception) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new AssertionError("等待转换器释放失败。", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user