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);
|
}
|
}
|