package jnpf.dmsService; import feign.Request; import feign.Response; import jnpf.base.ActionResult; import jnpf.dmsEntity.permission.enums.DmsResourceType; import jnpf.dmsEntity.preview.form.DmsFilePreviewTicketRequest; import jnpf.dmsEntity.preview.vo.DmsFilePageCountVO; import jnpf.dmsEntity.preview.vo.DmsFilePreviewTicketVO; import jnpf.dmsService.file.DmsFileContentApi; import jnpf.onlyoffice.DmsPageCountApi; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; class DmsFilePageCountServiceTest { private final DmsFilePreviewService previewService = mock(DmsFilePreviewService.class); private final DmsFileContentApi fileContentApi = mock(DmsFileContentApi.class); private final DmsDocumentPageCountService documentPageCountService = mock(DmsDocumentPageCountService.class); private final DmsPageCountApi onlyOfficePageCountApi = mock(DmsPageCountApi.class); private final DmsFilePageCountService service = new DmsFilePageCountService( previewService, fileContentApi, documentPageCountService, onlyOfficePageCountApi); private final UUID fileVersionId = UUID.fromString("018f0000-0000-7000-8000-000000000702"); @BeforeEach void setUp() { DmsFilePreviewTicketVO ticket = new DmsFilePreviewTicketVO(); ticket.setFileVersionId(fileVersionId); ticket.setFileName("quality-manual.pdf"); ticket.setAccessTicket("page-count-ticket"); when(previewService.createTicket(any())).thenReturn(ticket); } @Test void readsProtectedFileAndDeletesTemporaryCopy() throws Exception { byte[] fileBytes = new byte[]{1, 2, 3, 4}; when(fileContentApi.stream(fileVersionId, "page-count-ticket")) .thenReturn(response(200, fileBytes)); AtomicReference temporaryFile = new AtomicReference<>(); when(documentPageCountService.getPageCount(any(Path.class), eq("quality-manual.pdf"))) .thenAnswer(invocation -> { Path path = invocation.getArgument(0); temporaryFile.set(path); assertArrayEquals(fileBytes, Files.readAllBytes(path)); return 12; }); DmsFilePageCountVO result = service.getPageCount(fileVersionId); assertEquals(fileVersionId, result.getFileVersionId()); assertEquals("quality-manual.pdf", result.getFileName()); assertEquals(12, result.getPageCount()); assertNotNull(temporaryFile.get()); assertFalse(Files.exists(temporaryFile.get())); verifyNoInteractions(onlyOfficePageCountApi); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(DmsFilePreviewTicketRequest.class); verify(previewService).createTicket(requestCaptor.capture()); DmsFilePreviewTicketRequest request = requestCaptor.getValue(); assertEquals(DmsResourceType.FILE_VERSION, request.getResourceType()); assertEquals(fileVersionId, request.getResourceId()); assertNotNull(request.getOperationId()); } @Test void countsBothWordFormatsUsingOnlyOfficePreview() throws Exception { for (String fileName : new String[]{"玻思韬DMS调研报告.docx", "legacy.DOC"}) { wordTicket(fileName); when(onlyOfficePageCountApi.getPageCount(fileVersionId, "page-count-ticket")) .thenReturn(pageCountResponse(200, 19)); DmsFilePageCountVO result = service.getPageCount(fileVersionId); assertEquals(19, result.getPageCount()); assertEquals(fileVersionId, result.getFileVersionId()); assertEquals(fileName, result.getFileName()); } verifyNoInteractions(fileContentApi, documentPageCountService); } @Test void neverFallsBackToWordMetadataWhenConversionFails() { wordTicket("manual.docx"); when(onlyOfficePageCountApi.getPageCount(fileVersionId, "page-count-ticket")) .thenReturn(null, pageCountResponse(500, 20), pageCountResponse(200, 0), pageCountResponse(200, -1), pageCountResponse(200, null)); for (int attempt = 0; attempt < 5; attempt++) { assertThrows(IOException.class, () -> service.getPageCount(fileVersionId)); } when(onlyOfficePageCountApi.getPageCount(fileVersionId, "page-count-ticket")) .thenThrow(new IllegalStateException("conversion timed out")); assertThrows(IllegalStateException.class, () -> service.getPageCount(fileVersionId)); verifyNoInteractions(fileContentApi, documentPageCountService); } private void wordTicket(String fileName) { DmsFilePreviewTicketVO ticket = new DmsFilePreviewTicketVO(); ticket.setFileVersionId(fileVersionId); ticket.setFileName(fileName); ticket.setAccessTicket("page-count-ticket"); when(previewService.createTicket(any())).thenReturn(ticket); } private ActionResult pageCountResponse(int code, Integer pages) { ActionResult response = new ActionResult<>(); response.setCode(code); response.setData(pages); return response; } @Test void rejectsFileServiceErrorBeforeParsing() throws Exception { when(fileContentApi.stream(fileVersionId, "page-count-ticket")) .thenReturn(response(503, null)); IOException exception = assertThrows(IOException.class, () -> service.getPageCount(fileVersionId)); assertEquals("文件服务读取失败", exception.getMessage()); verify(documentPageCountService, never()).getPageCount(any(), any()); } private Response response(int status, byte[] body) { Request request = Request.create(Request.HttpMethod.GET, "http://file-service/content", Collections.emptyMap(), null, StandardCharsets.UTF_8); Response.Builder builder = Response.builder() .request(request) .status(status) .reason(status == 200 ? "OK" : "Service Unavailable") .headers(Collections.emptyMap()); return body == null ? builder.build() : builder.body(body).build(); } }