package jnpf.dmsService; import jnpf.dmsEntity.permission.entity.DmsFilePermissionEntity; import jnpf.dmsEntity.storage.approval.*; import jnpf.dmsIntegration.audit.DmsAuditAdapter; import jnpf.dmsMapper.DmsApprovedReadAccessMapper; import jnpf.dmsPermission.DmsPermissionProperties; import jnpf.exception.DataException; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.*; import org.mockito.junit.jupiter.MockitoExtension; import java.util.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class DmsApprovedReadAccessServiceTest { @Mock DmsApprovedReadAccessMapper mapper; @Mock DmsAuditAdapter audit; DmsApprovedReadAccessService service; DmsApprovedBatch batch; @BeforeEach void setup() { service = new DmsApprovedReadAccessService(mapper, audit, new DmsPermissionProperties()); batch = new DmsApprovedBatch(); batch.setTenantId("tenant"); batch.setId(UUID.randomUUID()); batch.setActivatedAt(new Date()); batch.setAuditId("audit"); } DmsApprovedItem item(String key, String type, String scope) { var item = new DmsApprovedItem(); item.setSourceKey(key); item.setChangeType(type); item.setFileVersionId(UUID.randomUUID()); item.setReadAccessScope(scope); return item; } @Test void parsesDepartmentScopesAndRejectsMixedOrDynamicValues() { for (String empty : Arrays.asList(null, "", " ", "null", " null ", "[]")) assertEquals("[]", DmsApprovedDepartmentScope.normalize(empty)); assertEquals("[\"a--progenyOrg\",\"b--subOrg\",\"c\"]", DmsApprovedDepartmentScope.normalize( "[\"b--subOrg\",\"a--org\",\"a--progenyOrg\",\"c\",\"b\"]")); for (String input : List.of("{}", "[null]", "[1]", "[\"u--user\"]", "[\"p--position\"]", "[\"@department\"]", "[\"--org\"]")) assertThrows(DataException.class, () -> DmsApprovedDepartmentScope.parse(input)); } @Test void mainAndRecordReceiveIndependentReadOnlyVersionGrants() { var main = item("main", "CREATE", "[\"a\"]"); var record = item("record", "CREATE", "[\"b--subOrg\"]"); when(mapper.departments("tenant", List.of("a", "b"))).thenReturn(List.of( new DmsApprovedReadAccessMapper.Department("a"), new DmsApprovedReadAccessMapper.Department("b"))); when(mapper.insert(any())).thenAnswer(i -> i.getArgument(0)); service.apply(batch, List.of(main, record), List.of(main, record)); var captor = ArgumentCaptor.forClass(DmsFilePermissionEntity.class); verify(mapper, times(2)).insert(captor.capture()); var grants = captor.getAllValues(); assertEquals(List.of(main.getFileVersionId(), record.getFileVersionId()), grants.stream().map(DmsFilePermissionEntity::getResourceId).toList()); assertEquals(List.of("a", "b"), grants.stream().map(DmsFilePermissionEntity::getPrincipalId).toList()); assertEquals(List.of("SELF", "CHILDREN"), grants.stream().map(DmsFilePermissionEntity::getApprovedOrgScope).toList()); for (var p : grants) { assertEquals("READ",p.getActionCode()); assertEquals("ORG",p.getPrincipalType()); assertEquals("FILE_VERSION",p.getResourceType()); assertEquals(batch.getActivatedAt(),p.getValidFrom()); assertFalse(p.getIncludeChildOrgs()); assertNull(p.getMaxUseCount()); } verify(mapper, never()).revoke(any(), any()); } @Test void emptyScopeOnNewTargetNeverTouchesSourceVersionPermissions() { for (String type : List.of("CREATE", "REVISE")) { var target = item("main", type, "[]"); target.setBaseFileVersionId(UUID.randomUUID()); service.apply(batch, List.of(target), List.of(target)); verify(mapper).owned("tenant", target.getFileVersionId()); verifyNoMoreInteractions(mapper); verifyNoInteractions(audit); reset(mapper); } } @Test void emptyScopeClearsOnlyApprovalGrantsOfCurrentReviewTarget() { for (String scope : Arrays.asList("[]", null, "null", "", " ")) { var target = item("main", "REUSE", scope); target.setBaseFileVersionId(target.getFileVersionId()); var old = new DmsFilePermissionEntity(); old.setId(UUID.randomUUID()); old.setResourceId(target.getFileVersionId()); old.setSourceId("approved-read:previous-review"); when(mapper.owned("tenant", target.getFileVersionId())).thenReturn(List.of(old)); when(mapper.revoke("tenant", old.getId())).thenReturn(1); service.apply(batch, List.of(target), List.of(target)); verify(mapper).revoke("tenant", old.getId()); verify(mapper, never()).insert(any()); verify(audit).permissionChange(eq("PERMISSION_GRANT"), anyString(), anyList(), anyList(), anyList()); reset(mapper, audit); } } @Test void mainAndRecordApplyIndependentScopesToTheirOwnTargetVersions() { var main = item("main", "REUSE", "[\"a\"]"); var record = item("record", "REUSE", "[]"); var old = new DmsFilePermissionEntity(); old.setId(UUID.randomUUID()); old.setSourceId("approved-read:older"); when(mapper.departments("tenant", List.of("a"))).thenReturn(List.of( new DmsApprovedReadAccessMapper.Department("a"))); when(mapper.owned("tenant", main.getFileVersionId())).thenReturn(List.of(old)); var oldRecord = new DmsFilePermissionEntity(); oldRecord.setId(UUID.randomUUID()); oldRecord.setResourceId(record.getFileVersionId()); oldRecord.setSourceId("approved-read:previous-record-review"); when(mapper.owned("tenant", record.getFileVersionId())).thenReturn(List.of(oldRecord)); when(mapper.revoke("tenant", oldRecord.getId())).thenReturn(1); when(mapper.revoke("tenant", old.getId())).thenReturn(1); when(mapper.insert(any())).thenAnswer(i -> i.getArgument(0)); service.apply(batch, List.of(main, record), List.of(main, record)); verify(mapper).revoke("tenant", old.getId()); var captor = ArgumentCaptor.forClass(DmsFilePermissionEntity.class); verify(mapper).insert(captor.capture()); assertEquals(main.getFileVersionId(), captor.getValue().getResourceId()); assertEquals("a", captor.getValue().getPrincipalId()); verify(mapper).revoke("tenant", oldRecord.getId()); } @Test void invalidDepartmentFailsBeforeAnyPermissionMutation() { var item=item("main","CREATE","[\"foreign\"]"); assertThrows(DataException.class,()->service.apply(batch,List.of(item),List.of(item))); verify(mapper,never()).owned(any(),any()); verify(mapper,never()).insert(any()); verifyNoInteractions(audit); } @Test void snapshotMismatchAndEarlyActivationAreRejected() { var item=item("main","CREATE","[]"); var changed=item("main","CREATE","[\"a\"]"); assertThrows(DataException.class,()->service.apply(batch,List.of(item),List.of(changed))); batch.setActivatedAt(null); assertThrows(DataException.class,()->service.apply(batch,List.of(item),List.of(item))); verifyNoInteractions(mapper,audit); } @Test void duplicateActivationNeverResurrectsRevokedGrant() { var item=item("main","REUSE","[\"a\"]"); when(mapper.departments("tenant", List.of("a"))).thenReturn(List.of( new DmsApprovedReadAccessMapper.Department("a"))); var revoked=new DmsFilePermissionEntity();revoked.setRevokedAt(new Date()); revoked.setSourceId("approved-read:"+batch.getId()+":"+item.getFileVersionId()); when(mapper.owned("tenant",item.getFileVersionId())).thenReturn(List.of(revoked)); assertThrows(DataException.class,()->service.apply(batch,List.of(item),List.of(item))); verify(mapper,never()).insert(any());verifyNoInteractions(audit); } }