package jnpf.fileStorage.dms; import jnpf.base.ActionResult; import jnpf.dmsEntity.download.model.DmsFileDownloadTicket; import jnpf.dmsEntity.preview.model.DmsFilePreviewTicket; import jnpf.dmsEntity.preview.model.DmsWatermarkSnapshot; import jnpf.exception.DataException; import jnpf.onlyoffice.PageWatermarkApi; import jnpf.onlyoffice.PageWatermarkRequest; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import static org.mockito.ArgumentMatchers.*; public class DmsFileWatermarkServiceTest { @Test public void previewPrintAndDownloadDelegateCleanSourceAndKeepAllPagesWatermarksIndependent() throws Exception { for (String scope : List.of(DmsFilePreviewTicket.SCOPE, DmsFilePreviewTicket.PRINT_SCOPE, DmsFileDownloadTicket.SCOPE)) { DmsDocxWatermarkService docx = mock(DmsDocxWatermarkService.class); PageWatermarkApi renderer = mock(PageWatermarkApi.class); AtomicReference delegated = new AtomicReference<>(); DmsFileWatermarkService service = new DmsFileWatermarkService(docx, renderer, ticket -> { delegated.set(ticket); return "server-only-source-ticket"; }); byte[] source = {1, 2, 3}; DmsWatermarkSnapshot custom = new DmsWatermarkSnapshot(); custom.setContent("REC-001"); custom.setRenderPages(List.of(1, 3, 5)); DmsWatermarkSnapshot everyPage = new DmsWatermarkSnapshot(); everyPage.setContent("Original watermark"); List overlays = List.of(new PageWatermarkRequest.Overlay(List.of(1, 3, 5), List.of())); when(docx.pageOverlays(source, List.of(custom))).thenReturn(overlays); when(renderer.render(any())).thenAnswer(invocation -> { PageWatermarkRequest request = invocation.getArgument(0); assertEquals(overlays, request.getOverlays()); assertEquals("server-only-source-ticket", request.getSourceTicket()); ActionResult result = new ActionResult<>(); result.setCode(200); result.setData(drawings(request.getMarkerPrefix())); return result; }); when(docx.apply(any(byte[].class), eq(List.of(everyPage)))).thenReturn(new byte[]{4, 5, 6}); DmsFilePreviewTicket ticket = new DmsFilePreviewTicket(); ticket.setScope(scope); ticket.setFileVersionId(UUID.randomUUID()); ticket.setTenantId("tenant-1"); ticket.setUserId("user-1"); ticket.setFileName("original.docx"); ticket.setObjectName("original.docx"); ticket.setWatermarks(List.of(custom, everyPage)); byte[] result; if (DmsFileDownloadTicket.SCOPE.equals(scope)) { DmsFileDownloadTicket download = new DmsFileDownloadTicket(); org.springframework.beans.BeanUtils.copyProperties(ticket, download); result = service.apply(source, download); } else result = service.apply(source, ticket); assertArrayEquals(new byte[]{4, 5, 6}, result); assertEquals(DmsFilePreviewTicket.SCOPE, delegated.get().getScope()); assertEquals(ticket.getFileVersionId(), delegated.get().getFileVersionId()); assertEquals("tenant-1", delegated.get().getTenantId()); assertEquals("original.docx", delegated.get().getObjectName()); assertTrue(delegated.get().getWatermarks().isEmpty()); assertTrue(delegated.get().getExpiresAt() > System.currentTimeMillis()); assertEquals(List.of(custom, everyPage), ticket.getWatermarks()); assertEquals(scope, ticket.getScope()); verify(docx).apply(any(byte[].class), eq(List.of(everyPage))); } } @Test public void aFailedPageJobNeverFallsBackToEveryPageOrUnwatermarkedSource() throws Exception { DmsDocxWatermarkService docx = mock(DmsDocxWatermarkService.class); PageWatermarkApi renderer = mock(PageWatermarkApi.class); DmsFileWatermarkService service = new DmsFileWatermarkService(docx, renderer, ignored -> "source-ticket"); DmsWatermarkSnapshot mark = new DmsWatermarkSnapshot(); mark.setRenderPages(List.of(2)); DmsFilePreviewTicket ticket = new DmsFilePreviewTicket(); ticket.setFileName("original.docx"); ticket.setWatermarks(List.of(mark)); when(docx.pageOverlays(any(), any())).thenReturn(List.of(new PageWatermarkRequest.Overlay(List.of(2), List.of()))); when(renderer.render(any())).thenThrow(new DataException("水印页码 2 超过文档总页数 1")); assertThrows(DataException.class, () -> service.apply(new byte[]{1}, ticket)); verify(docx, never()).apply(any(), any()); } @Test public void previewAndDownloadImagesUseRasterWatermarkWithoutOnlyOffice() { DmsDocxWatermarkService docx = mock(DmsDocxWatermarkService.class); DmsImageWatermarkService image = mock(DmsImageWatermarkService.class); PageWatermarkApi renderer = mock(PageWatermarkApi.class); DmsFileWatermarkService service = new DmsFileWatermarkService(docx, image, renderer, ignored -> "unused-ticket"); byte[] source = {1, 2, 3}; byte[] watermarked = {4, 5, 6}; DmsWatermarkSnapshot mark = new DmsWatermarkSnapshot(); List marks = List.of(mark); when(image.apply(source, "photo.PNG", null, marks)).thenReturn(watermarked); DmsFilePreviewTicket preview = new DmsFilePreviewTicket(); preview.setFileName("photo.PNG"); preview.setWatermarks(marks); DmsFileDownloadTicket download = new DmsFileDownloadTicket(); download.setFileName("photo.PNG"); download.setWatermarks(marks); assertArrayEquals(watermarked, service.apply(source, preview)); assertArrayEquals(watermarked, service.apply(source, download)); verify(image, times(2)).apply(source, "photo.PNG", null, marks); verifyNoInteractions(docx, renderer); } @Test public void previewPrintAndDownloadPdfUsePdfWatermark() { DmsDocxWatermarkService docx = mock(DmsDocxWatermarkService.class); DmsPdfWatermarkService pdf = mock(DmsPdfWatermarkService.class); PageWatermarkApi renderer = mock(PageWatermarkApi.class); DmsFileWatermarkService service = new DmsFileWatermarkService(docx, null, pdf, renderer, ignored -> "unused-ticket"); byte[] source = {1, 2, 3}; byte[] watermarked = {4, 5, 6}; DmsWatermarkSnapshot mark = new DmsWatermarkSnapshot(); List marks = List.of(mark); when(pdf.apply(source, marks)).thenReturn(watermarked); for (String scope : List.of(DmsFilePreviewTicket.SCOPE, DmsFilePreviewTicket.PRINT_SCOPE)) { DmsFilePreviewTicket ticket = new DmsFilePreviewTicket(); ticket.setScope(scope); ticket.setFileName("report.pdf"); ticket.setWatermarks(marks); assertArrayEquals(watermarked, service.apply(source, ticket)); } DmsFileDownloadTicket download = new DmsFileDownloadTicket(); download.setFileName("report.pdf"); download.setWatermarks(marks); assertArrayEquals(watermarked, service.apply(source, download)); verify(pdf, times(3)).apply(source, marks); verifyNoInteractions(docx, renderer); } private byte[] drawings(String prefix) throws Exception { StringBuilder xml = new StringBuilder(""); for (int page : List.of(1, 3, 5)) { xml.append("") .append("0") .append("0") .append(""); } xml.append(""); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); try (ZipOutputStream zip = new ZipOutputStream(bytes)) { zip.putNextEntry(new ZipEntry("word/document.xml")); zip.write(xml.toString().getBytes(StandardCharsets.UTF_8)); zip.closeEntry(); } return bytes.toByteArray(); } }