package jnpf.dmsService; import jnpf.dmsEntity.storage.approval.DmsDisableOccupancy; import jnpf.dmsEntity.storage.form.DmsDisableSubmitValidationForm; import jnpf.dmsMapper.DmsDisableApplicationMapper; import jnpf.exception.DataException; import jnpf.flowable.WorkFlowApi; import jnpf.flowable.entity.TaskEntity; import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.test.util.ReflectionTestUtils; import java.util.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; class DmsDisableOccupancyServiceTest { DmsDisableApplicationMapper mapper=mock(DmsDisableApplicationMapper.class); WorkFlowApi workflow=mock(WorkFlowApi.class); DmsDisableOccupancyService service=new DmsDisableOccupancyService(mapper,workflow); TaskEntity task; DmsDisableOccupancy application; @BeforeEach void init() { ReflectionTestUtils.setField(service,"enabled",true); task=new TaskEntity();task.setId("task");task.setFlowId("flow");task.setTemplateId("template"); application=new DmsDisableOccupancy();application.setApplicationId("app");application.setFlowId("template");application.setCreatorUserId("applicant"); when(workflow.getInfoSubmit("task")).thenReturn(task); when(mapper.application("tenant","task","yichexiao")).thenReturn(application); } void state(int status, boolean ended) {task.setStatus(status);task.setEndTime(ended?new Date():null);application.setFlowState(status);} DmsDisableOccupancy file(String id) {DmsDisableOccupancy r=new DmsDisableOccupancy();r.setFileId(id);r.setFolderId("folder-"+id);return r;} boolean event(String type) {return service.onEvent("tenant","flow","task","yichexiao",type);} @Test void submittedRowsProduceTenantTaskAndDirectorySnapshot() { state(1,false);DmsDisableOccupancy row=file("main"); when(mapper.applicationFiles("tenant","app","yichexiao")).thenReturn(List.of(row)); when(mapper.selectedFiles("tenant",List.of("main"))).thenReturn(List.of(row)); assertFalse(event("start")); verify(mapper).conflictingFiles("tenant",List.of("main"),"task"); verify(mapper).releaseTask("tenant","task","yichexiao");verify(mapper).occupy(row); assertEquals("tenant",row.getTenantId());assertEquals("task",row.getTaskId());assertEquals("app",row.getApplicationId()); assertEquals("folder-main",row.getFolderId()); } @Test void resubmitReplacesOnlyOwnSnapshotAfterCheckingLatestSelection() { state(1,false);DmsDisableOccupancy current=file("replacement"); when(mapper.applicationFiles("tenant","app","yichexiao")).thenReturn(List.of(current)); when(mapper.selectedFiles("tenant",List.of("replacement"))).thenReturn(List.of(current)); event("start"); org.mockito.InOrder order=inOrder(mapper); order.verify(mapper).application("tenant","task","yichexiao"); order.verify(mapper).applicationFiles("tenant","app","yichexiao"); order.verify(mapper).selectedFiles("tenant",List.of("replacement")); order.verify(mapper).conflictingFiles("tenant",List.of("replacement"),"task"); order.verify(mapper).releaseTask("tenant","task","yichexiao"); order.verify(mapper).occupy(current); } @ParameterizedTest @ValueSource(ints={0,1,3,4,5,6,7,8,9,10}) void noReleaseWithoutTrueEndEvenForReturnAndReject(int status) { state(status,false);assertFalse(event(status==8?"back":"reject")); verify(mapper,never()).releaseTask(any(),any(),any());verify(mapper,never()).occupy(any()); } @ParameterizedTest @ValueSource(ints={3,4,7,9}) void trueUnsuccessfulEndReleasesOwnTaskOnly(int status) { state(status,true);assertFalse(event("end"));verify(mapper).releaseTask("tenant","task","yichexiao"); verify(mapper,never()).applicationFiles(any(),any(),any()); } @Test void approvedEndDispatchesDisableAndKeepsOccupancyUntilProjection() { state(2,true);assertTrue(event("end"));verify(mapper,never()).releaseTask(any(),any(),any()); } @Test void lateStartCannotReoccupyFinishedApprovedFile() { state(2,true);assertFalse(event("start"));verify(mapper,never()).occupy(any()); } @Test void eventCannotChangeAnotherNativeApplication() { state(1,false);application.setFlowId("another");assertThrows(DataException.class,()->event("start")); verify(mapper,never()).releaseTask(any(),any(),any()); } DmsDisableSubmitValidationForm form(String taskId) { DmsDisableSubmitValidationForm f=new DmsDisableSubmitValidationForm();f.setFlowId("flow");f.setTaskId(taskId);f.setFileIds(List.of("main")); when(mapper.selectedFiles("tenant",List.of("main"))).thenReturn(List.of(file("main")));return f; } @Test void crossOperationConflictBlocksBeforeNativeSubmit() { DmsDisableSubmitValidationForm f=form(null);when(mapper.conflictingFiles("tenant",List.of("main"),null)).thenReturn(1); assertThrows(DataException.class,()->service.validateSubmission("tenant","applicant","yishixiao",f)); verify(mapper,never()).occupy(any()); } @ParameterizedTest @ValueSource(ints={0,8,9}) void ownDraftReturnedOrRecalledApplicationUsesNativeCreatorWhenWorkflowOmitsIt(int status) { state(status,false);service.validateSubmission("tenant","applicant","yichexiao",form("task")); verify(mapper).conflictingFiles("tenant",List.of("main"),"task");verify(mapper,never()).releaseTask(any(),any(),any()); } @Test void cannotExcludeAnotherApplicantsOccupancy() { state(8,false);assertThrows(DataException.class,()->service.validateSubmission("tenant","other","yichexiao",form("task"))); verify(mapper,never()).conflictingFiles(any(),any(),any()); } @Test void activeChangedOrFinishedTaskCannotBeUsedAsNewSubmissionExclusion() { state(1,false);assertThrows(DataException.class,()->service.validateSubmission("tenant","applicant","yichexiao",form("task"))); state(3,true);assertThrows(DataException.class,()->service.validateSubmission("tenant","applicant","yichexiao",form("task"))); } @Test void nativeApprovalPreflightAllowsUnchangedOwnApplicationForReviewer() { state(1,false);when(mapper.applicationFiles("tenant","app","yichexiao")).thenReturn(List.of(file("main"))); service.validateSubmission("tenant","reviewer","yichexiao",form("task")); verify(mapper).conflictingFiles("tenant",List.of("main"),"task"); verify(mapper,never()).occupy(any()); } @Test void startConflictDoesNotClearExistingSnapshot() { state(1,false);DmsDisableOccupancy row=file("main");when(mapper.applicationFiles("tenant","app","yichexiao")).thenReturn(List.of(row)); when(mapper.selectedFiles("tenant",List.of("main"))).thenReturn(List.of(row)); when(mapper.conflictingFiles("tenant",List.of("main"),"task")).thenReturn(1); assertThrows(DataException.class,()->event("start"));verify(mapper,never()).releaseTask(any(),any(),any()); } @Test void disabledFeatureKeepsExistingEndCallbackUsableWithoutTechnicalTable() { ReflectionTestUtils.setField(service,"enabled",false);state(3,true);assertFalse(event("end")); state(2,true);assertTrue(event("end"));verify(mapper,never()).releaseTask(any(),any(),any()); assertThrows(DataException.class,()->event("start")); } }