package jnpf.bizcommon.onlyoffice.service.impl; import jnpf.base.ActionResult; import jnpf.base.UserInfo; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeConfigParam; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeProperties; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeSessionEntity; import jnpf.eln.RecordFillApi; import jnpf.exception.DataException; import org.junit.Test; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class OnlyOfficeSceneConfigRegistryTest { @Test public void dispatchesKnownScenesOnceAndFallsBackToTheDefaultBuilder() { OnlyOfficeSceneConfigBuilder annotate = mock(OnlyOfficeSceneConfigBuilder.class); when(annotate.scene()).thenReturn("eln.template.annotate"); when(annotate.bizModule()).thenReturn("eln.template"); OnlyOfficeSceneConfigBuilder recordFill = mock(OnlyOfficeSceneConfigBuilder.class); when(recordFill.scene()).thenReturn("eln.record.fill"); when(recordFill.bizModule()).thenReturn("eln.record"); DefaultSceneConfigBuilder defaultBuilder = mock(DefaultSceneConfigBuilder.class); OnlyOfficeSceneConfigRegistry registry = new OnlyOfficeSceneConfigRegistry( Arrays.asList(annotate, recordFill), defaultBuilder); assertSame(recordFill, registry.resolve(param("eln.record.fill", "eln.record"))); assertSame(annotate, registry.resolve(param("eln.template.annotate", "eln.template"))); assertSame(defaultBuilder, registry.resolve(param("ordinary.view", "lims.document"))); try { registry.resolve(param("eln.record.fill", "eln.template")); fail("scene and module mismatch must be rejected"); } catch (DataException expected) { assertEquals("OnlyOffice 插件场景与业务模块不匹配", expected.getMessage()); } } @Test public void recordFillBuilderOwnsAdmissionLockAndPluginContext() { RecordFillApi recordFillApi = mock(RecordFillApi.class); ActionResult allowed = mock(ActionResult.class); when(allowed.getCode()).thenReturn(200); when(allowed.getData()).thenReturn(true); when(recordFillApi.canFill(any(), any(), any(), any())).thenReturn(allowed); ElnRecordFillTicketIssuer ticketIssuer = mock(ElnRecordFillTicketIssuer.class); when(ticketIssuer.issue(any(), any(), any())).thenReturn("record-ticket"); ElnRecordFillConfigBuilder builder = new ElnRecordFillConfigBuilder(recordFillApi, ticketIssuer); OnlyOfficeConfigParam param = param("eln.record.fill", "eln.record"); param.setFileId("file-1"); param.setFileType("annex"); param.setBizDataId("record-1"); UserInfo user = new UserInfo(); user.setUserId("user-1"); OnlyOfficeSessionEntity session = new OnlyOfficeSessionEntity(); session.setTenantId("tenant-1"); session.setFileId("file-1"); session.setFileType("annex"); session.setBizModule("eln.record"); session.setBizDataId("record-1"); OnlyOfficeSceneBuildPlan plan = builder.build( new OnlyOfficeSceneBuildContext(param, session, user, "tenant-1")); Map pluginContext = new LinkedHashMap<>(); plan.customizePluginContext("eln-record-filler", "guid-1", pluginContext, true); assertTrue(plan.canEdit()); assertTrue(plan.allowSameUserLockReopen()); assertTrue(plan.hideCommentsWhenWritable()); assertEquals(true, pluginContext.get("canFill")); assertEquals("onlyoffice:eln-record-fill", pluginContext.get("scope")); assertEquals("record-ticket", pluginContext.get("ticket")); Map unrelated = new LinkedHashMap<>(); plan.customizePluginContext("document-helper", "guid-2", unrelated, true); assertFalse(unrelated.containsKey("ticket")); } @Test public void qianpiyeModeCanEditWithoutAPluginScene() { UserInfo administrator = mock(UserInfo.class); when(administrator.getIsAdministrator()).thenReturn(true); OnlyOfficeConfigParam param = new OnlyOfficeConfigParam(); param.setMode("qianpiye"); OnlyOfficeSceneBuildPlan plan = new DefaultSceneConfigBuilder( new OnlyOfficeEditPermissionEvaluator(new OnlyOfficeProperties())).build( new OnlyOfficeSceneBuildContext(param, new OnlyOfficeSessionEntity(), administrator, "tenant-1")); assertTrue(plan.canEdit()); } @Test public void qianpiyeBuilderIssuesTicketOnlyForItsOwnWritablePlugin() { UserInfo administrator = mock(UserInfo.class); when(administrator.getIsAdministrator()).thenReturn(true); DmsQianpiyeTicketIssuer ticketIssuer = mock(DmsQianpiyeTicketIssuer.class); when(ticketIssuer.issue(any(), any(), any())).thenReturn("qianpiye-ticket"); OnlyOfficeConfigParam param = param("dms.qianpiye", "dms"); param.setFileId("file-1"); DmsQianpiyeConfigBuilder builder = new DmsQianpiyeConfigBuilder( new OnlyOfficeEditPermissionEvaluator(new OnlyOfficeProperties()), ticketIssuer); OnlyOfficeSceneBuildPlan plan = builder.build(new OnlyOfficeSceneBuildContext( param, new OnlyOfficeSessionEntity(), administrator, "tenant-1")); Map context = new LinkedHashMap<>(); plan.customizePluginContext("dms-qianpiye-annotator", "asc.{C764F7D6-055D-4C7D-97FD-83D2C4440585}", context, true); assertTrue(plan.canEdit()); assertTrue(plan.allowSameUserLockReopen()); assertTrue(plan.hideCommentsWhenWritable()); assertEquals(true, context.get("canAnnotate")); assertEquals("onlyoffice:dms-qianpiye", context.get("scope")); assertEquals("qianpiye-ticket", context.get("ticket")); assertFalse(plan.keepPluginInReadOnly("dms-qianpiye-annotator", false)); } @Test public void qianpiyeBuilderOnlyAdoptsAnIdleUnboundSessionForTheSameFile() { DmsQianpiyeConfigBuilder builder = new DmsQianpiyeConfigBuilder( new OnlyOfficeEditPermissionEvaluator(new OnlyOfficeProperties()), mock(DmsQianpiyeTicketIssuer.class)); OnlyOfficeConfigParam param = param("dms.qianpiye", "dms"); param.setFileId("file-1"); param.setFileType("annex"); param.setBizDataId("qianpiye-1"); OnlyOfficeSessionEntity session = new OnlyOfficeSessionEntity(); session.setTenantId("tenant-1"); session.setFileId("file-1"); session.setFileType("annex"); session.setSessionStatus(OnlyOfficeSessionEntity.SessionStatus.IDLE); assertTrue(builder.canAdoptUnboundSession(session, param, "tenant-1")); // 旧版普通编辑会话可能记录了页面模块,但尚未绑定具体业务记录,允许签批页迁移。 session.setBizModule("签批页管理"); assertTrue(builder.canAdoptUnboundSession(session, param, "tenant-1")); session.setBizDataId("another-business"); assertFalse(builder.canAdoptUnboundSession(session, param, "tenant-1")); session.setBizDataId(null); session.setSessionStatus(OnlyOfficeSessionEntity.SessionStatus.EDITING); assertFalse(builder.canAdoptUnboundSession(session, param, "tenant-1")); } @Test public void qianpiyeSessionCanBeReusedWithoutBizDataId() { DmsQianpiyeConfigBuilder builder = new DmsQianpiyeConfigBuilder( new OnlyOfficeEditPermissionEvaluator(new OnlyOfficeProperties()), mock(DmsQianpiyeTicketIssuer.class)); OnlyOfficeSessionEntity session = new OnlyOfficeSessionEntity(); session.setTenantId("tenant-1"); session.setFileId("file-1"); session.setFileType("annex"); session.setBizModule("dms"); session.setBizDataId("qianpiye-1"); OnlyOfficeConfigParam genericPreview = new OnlyOfficeConfigParam(); genericPreview.setFileId("file-1"); genericPreview.setFileType("annex"); genericPreview.setMode("view"); assertTrue(builder.matchesSession(session, genericPreview, "tenant-1")); OnlyOfficeConfigParam annotation = param("dms.qianpiye", "dms"); annotation.setFileId("file-1"); annotation.setFileType("annex"); annotation.setMode("qianpiye"); assertTrue(builder.matchesSession(session, annotation, "tenant-1")); OnlyOfficeConfigParam otherBusiness = param("eln.record.fill", "eln.record"); otherBusiness.setFileId("file-1"); otherBusiness.setFileType("annex"); otherBusiness.setMode("view"); assertFalse(builder.matchesSession(session, otherBusiness, "tenant-1")); annotation.setBizDataId("another-business"); assertFalse(builder.matchesSession(session, annotation, "tenant-1")); } @Test(expected = IllegalStateException.class) public void rejectsDuplicateSceneBuilders() { OnlyOfficeSceneConfigBuilder first = builder("eln.record.fill", "eln.record"); OnlyOfficeSceneConfigBuilder duplicate = builder("eln.record.fill", "eln.other-record"); new OnlyOfficeSceneConfigRegistry(Arrays.asList(first, duplicate), mock(DefaultSceneConfigBuilder.class)); } @Test(expected = IllegalStateException.class) public void rejectsDuplicateBusinessModuleGuards() { OnlyOfficeSceneConfigBuilder first = builder("eln.record.fill", "eln.record"); OnlyOfficeSceneConfigBuilder duplicate = builder("eln.record.review", "eln.record"); new OnlyOfficeSceneConfigRegistry(Arrays.asList(first, duplicate), mock(DefaultSceneConfigBuilder.class)); } private OnlyOfficeSceneConfigBuilder builder(String scene, String module) { OnlyOfficeSceneConfigBuilder builder = mock(OnlyOfficeSceneConfigBuilder.class); when(builder.scene()).thenReturn(scene); when(builder.bizModule()).thenReturn(module); when(builder.guardsBusinessContext()).thenReturn(true); return builder; } private OnlyOfficeConfigParam param(String scene, String module) { OnlyOfficeConfigParam param = new OnlyOfficeConfigParam(); param.setBizScene(scene); param.setBizModule(module); return param; } }