package jnpf.bizcommon.onlyoffice.service.impl; import jnpf.base.ActionResult; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeConfigParam; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeProperties; import jnpf.exception.DataException; import jnpf.file.WorkflowFileTicketApi; import jnpf.model.fileaccess.WorkflowFileGrant; import org.junit.Before; import org.junit.Test; import java.util.Collections; import static org.junit.Assert.*; import static org.mockito.Mockito.*; public class WorkflowFilePreviewConfigBuilderTest { private WorkflowFileTicketApi tickets; private WorkflowFilePreviewConfigBuilder builder; private OnlyOfficePluginResolver pluginResolver; private OnlyOfficeSceneConfigRegistry sceneRegistry; private OnlyOfficeConfigParam param; private WorkflowFileGrant grant; @Before public void setup() { jnpf.util.context.SpringContext.setApplicationContext(mock(org.springframework.context.ApplicationContext.class)); tickets = mock(WorkflowFileTicketApi.class); OnlyOfficeProperties properties = new OnlyOfficeProperties(); properties.setFileFetchBaseUrl("http://host.docker.internal:30000"); builder = new WorkflowFilePreviewConfigBuilder(tickets); pluginResolver = new OnlyOfficePluginResolver(properties); sceneRegistry = new OnlyOfficeSceneConfigRegistry(Collections.singletonList(builder), new DefaultSceneConfigBuilder(new OnlyOfficeEditPermissionEvaluator(properties))); param = new OnlyOfficeConfigParam(); param.setFileType("annex"); param.setMode("view"); param.setBizScene(WorkflowFilePreviewConfigBuilder.SCENE); param.setBizModule(WorkflowFilePreviewConfigBuilder.MODULE); param.setFileId("ticket"); param.setBizDataId("ticket"); param.setFileName("test.docx"); grant = new WorkflowFileGrant(); grant.setFile(java.util.Map.of("name", "test.docx", "fileId", "physical-file")); when(tickets.previewAccess("ticket")).thenReturn(ActionResult.success(grant)); } private OnlyOfficeSceneBuildPlan build() { // 与配置入口相同顺序:先真实解析插件场景,再解析业务场景并校验票据。 assertTrue(pluginResolver.resolve(param).isEmpty()); return sceneRegistry.resolve(param).build(new OnlyOfficeSceneBuildContext(param, null, null, "0")); } private void denied() { try { build(); fail("expected denial"); } catch (DataException expected) { } } @Test public void previewUsesAuthorizedPhysicalFileAndNeverEnablesEditing() { OnlyOfficeSceneBuildPlan plan = build(); assertFalse(plan.canEdit()); assertFalse(plan.uploadFileAllowDownload()); assertEquals("physical-file", plan.sourceFileId()); } @Test public void commentPreviewStillUsesTicketAndCannotWrite() { param.setMode("viewComments"); OnlyOfficeSceneBuildPlan plan = build(); assertFalse(plan.canEdit()); assertEquals("physical-file", plan.sourceFileId()); verify(tickets).previewAccess("ticket"); } @Test public void missingPhysicalFileRejected() { grant.setFile(Collections.singletonMap("name", "test.docx")); denied(); } @Test public void downloadDecisionComesFromCurrentGrant() { grant.setAllowDownload(true); assertTrue(build().uploadFileAllowDownload()); } @Test public void editModeRejectedBeforeRemoteAccess() { param.setMode("edit"); denied(); verifyNoInteractions(tickets); } @Test public void ticketContextMismatchRejected() { param.setBizDataId("other"); denied(); verifyNoInteractions(tickets); } @Test public void canonicalFilenameMismatchRejected() { param.setFileName("other.docx"); denied(); } @Test public void revocationAndUnavailableServiceNeverFallBack() { build(); when(tickets.previewAccess("ticket")).thenReturn(ActionResult.fail("revoked")); denied(); when(tickets.previewAccess("ticket")).thenReturn(null); denied(); } }