package jnpf.base.util; import jnpf.base.model.flow.DataModel; import jnpf.base.service.UploadEditDraftService; import jnpf.base.service.UploadFilePermissionService; import jnpf.exception.DataException; import jnpf.exception.WorkFlowException; import org.junit.Test; import java.util.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; /** 验证首次原生保存与后续受控保存的边界,不替代真实数据库保存验收。 */ public class FlowFormFilePermissionBoundaryTest { @Test public void initialCreateKeepsNativeAttachmentsWithoutAdoptingDraftState() throws Exception { UploadEditDraftService drafts = mock(UploadEditDraftService.class); UploadFilePermissionService permissions = mock(UploadFilePermissionService.class); FlowFormDataUtil forms = spy(new FlowFormDataUtil(null, null, null, null, null, null, drafts, permissions)); Map mainFile = Collections.singletonMap("fileId", "main-source"); Map childFile = Collections.singletonMap("fileId", "child-source"); Map data = new HashMap<>(); data.put("files", Collections.singletonList(mainFile)); data.put("rows", Collections.singletonList(Collections.singletonMap("files", Collections.singletonList(childFile)))); Map original = new HashMap<>(data); data.put(UploadEditDraftService.SUBMIT_STATE, Collections.singletonMap("draftId", "untrusted")); DataModel model = new DataModel(); model.setDataNewMap(data); doAnswer(call -> { assertEquals(original, model.getDataNewMap()); return Collections.singletonMap("id", "saved-record"); }).when(forms).createDataList(model); assertEquals("saved-record", forms.create(model).get("id")); verify(forms).createDataList(model); verifyNoInteractions(permissions); verify(drafts).prepareRevisionSave(model); verifyNoMoreInteractions(drafts); } @Test public void invalidAuditCopyNeverReachesNativeInsert() throws Exception { UploadEditDraftService drafts = mock(UploadEditDraftService.class); FlowFormDataUtil forms = spy(new FlowFormDataUtil(null, null, null, null, null, null, drafts, mock(UploadFilePermissionService.class))); DataModel model = new DataModel(); model.setDataNewMap(new HashMap<>()); doThrow(new DataException("附件来源已失效")).when(drafts).prepareRevisionSave(model); try { forms.create(model); fail("invalid receipt must block native create"); } catch (WorkFlowException expected) { assertTrue(expected.getMessage().contains("来源")); } verify(forms, never()).createDataList(any()); } @Test public void subsequentSaveDenialNeverFallsBackToCreateOrWritesData() throws Exception { UploadEditDraftService drafts = mock(UploadEditDraftService.class); UploadFilePermissionService permissions = mock(UploadFilePermissionService.class); FlowFormDataUtil forms = spy(new FlowFormDataUtil(null, null, null, null, null, null, drafts, permissions)); DataModel model = new DataModel(); model.setDataNewMap(new HashMap<>()); when(permissions.prepare(model)).thenThrow(new DataException("当前流程不允许附件操作:ADD")); try { forms.update(model); fail("saved records must remain subject to instance permissions"); } catch (WorkFlowException expected) { assertTrue(expected.getMessage().contains("ADD")); } verify(forms, never()).createDataList(any()); verify(forms, never()).updateDataList(any()); verify(drafts).lockDmsRecord(model.getVisualId(), model.getMainId()); verifyNoMoreInteractions(drafts); } @Test public void revisionCreateChecksCanonicalAttachmentBeforeInsert() throws Exception { UploadEditDraftService drafts = mock(UploadEditDraftService.class); FlowFormDataUtil forms = spy(new FlowFormDataUtil(null, null, null, null, null, null, drafts, mock(UploadFilePermissionService.class))); var check = attachCheck(forms); DataModel model = revisionModel(); doAnswer(call -> { model.getDataNewMap().put("wenjian", "canonical"); return null; }).when(drafts).prepareRevisionSave(model); doAnswer(call -> { assertEquals("canonical", model.getDataNewMap().get("wenjian")); assertFalse(model.getDataNewMap().containsKey(jnpf.flowable.model.filepermission.DmsRevisionAttachmentCheck.REQUIRED)); throw new DataException("内容不同"); }).when(check).validate("revision", model.getDataNewMap()); try { forms.create(model); fail("changed content"); } catch (WorkFlowException expected) { } var order = inOrder(drafts, check); order.verify(drafts).prepareRevisionSave(model); order.verify(check).validate("revision", model.getDataNewMap()); verify(forms, never()).createDataList(any()); } @Test public void revisionUpdateChecksAdoptedAuthorizedContentBeforePersistence() throws Exception { UploadEditDraftService drafts = mock(UploadEditDraftService.class); UploadFilePermissionService permissions = mock(UploadFilePermissionService.class); FlowFormDataUtil forms = spy(new FlowFormDataUtil(null, null, null, null, null, null, drafts, permissions)); var check = attachCheck(forms); DataModel model = revisionModel(); doAnswer(call -> { model.getDataNewMap().put("wenjian", "adopted-B"); return null; }) .when(permissions).validate(any(), eq(model), anyList(), any()); doAnswer(call -> { assertEquals("adopted-B", model.getDataNewMap().get("wenjian")); throw new DataException("内容不同"); }).when(check).validate("revision", model.getDataNewMap()); try { forms.update(model); fail("changed content"); } catch (WorkFlowException expected) { } var order = inOrder(drafts, permissions, check); order.verify(drafts).lockDmsRecord("revision", "record"); order.verify(permissions).prepare(model); order.verify(drafts).prepareSave(eq(model), any(), any()); order.verify(permissions).validate(any(), eq(model), anyList(), any()); order.verify(check).validate("revision", model.getDataNewMap()); verify(forms, never()).updateDataList(any()); verify(drafts, never()).completeSave(any()); } private DataModel revisionModel() { DataModel model = new DataModel(); model.setVisualId("revision"); model.setMainId("record"); model.setDataNewMap(new HashMap<>()); model.getDataNewMap().put(jnpf.flowable.model.filepermission.DmsRevisionAttachmentCheck.REQUIRED, true); return model; } private jnpf.flowable.model.filepermission.DmsRevisionAttachmentCheck attachCheck(FlowFormDataUtil forms) throws Exception { var check = mock(jnpf.flowable.model.filepermission.DmsRevisionAttachmentCheck.class); org.springframework.beans.factory.ObjectProvider provider = mock(org.springframework.beans.factory.ObjectProvider.class); when(provider.getIfAvailable()).thenReturn(check); var field = FlowFormDataUtil.class.getDeclaredField("dmsAttachmentChecks"); field.setAccessible(true); field.set(forms, provider); return check; } }