From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master
---
jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsService/DmsAccessExclusionServiceTest.java | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 251 insertions(+), 0 deletions(-)
diff --git a/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsService/DmsAccessExclusionServiceTest.java b/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsService/DmsAccessExclusionServiceTest.java
new file mode 100644
index 0000000..1fa3eec
--- /dev/null
+++ b/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsService/DmsAccessExclusionServiceTest.java
@@ -0,0 +1,251 @@
+package jnpf.dmsService;
+
+import jnpf.dmsEntity.permission.entity.DmsAccessExclusionEntity;
+import jnpf.dmsEntity.permission.entity.DmsPermissionResourceEntity;
+import jnpf.dmsEntity.permission.enums.DmsResourceType;
+import jnpf.dmsEntity.permission.enums.DmsSourceType;
+import jnpf.dmsEntity.permission.form.AccessExclusionBatchForm;
+import jnpf.dmsEntity.permission.form.AccessExclusionItemForm;
+import jnpf.dmsEntity.permission.form.AccessExclusionRevokeForm;
+import jnpf.dmsEntity.permission.vo.DmsAccessExclusionVO;
+import jnpf.dmsEntity.permission.vo.DmsCursorPageVO;
+import jnpf.dmsIntegration.audit.DmsAuditAdapter;
+import jnpf.dmsMapper.DmsAccessExclusionMapper;
+import jnpf.dmsMapper.DmsPermissionResourceMapper;
+import jnpf.dmsPermission.DmsCurrentIdentity;
+import jnpf.dmsPermission.DmsCursorCodec;
+import jnpf.dmsPermission.DmsPermissionError;
+import jnpf.dmsPermission.DmsPermissionException;
+import jnpf.dmsPermission.DmsPermissionProperties;
+import jnpf.dmsPermission.DmsPrincipalValidator;
+import jnpf.dmsPermission.DmsUserDisplayNameResolver;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyCollection;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class DmsAccessExclusionServiceTest {
+ private static final String TENANT_ID = "tenant-1";
+ private static final String ADMIN_ID = "admin-1";
+ private static final String USER_ID = "user-1";
+ private static final String SOURCE_ID = "exclude-command-1";
+
+ @Test
+ void createsWholeExclusionSetWithOneBatchInsert() {
+ Fixture fixture = new Fixture();
+ UUID documentId = UUID.randomUUID();
+ AccessExclusionBatchForm form = form(item(DmsResourceType.DOCUMENT, documentId, "reason"));
+ DmsAccessExclusionEntity created = exclusion(DmsResourceType.DOCUMENT, documentId, "reason");
+ when(fixture.exclusionMapper.selectBySource(TENANT_ID, "MANUAL", SOURCE_ID))
+ .thenReturn(Collections.emptyList(), Collections.singletonList(created));
+ when(fixture.resourceMapper.selectByTenantAndIds(TENANT_ID, Collections.singletonList(documentId)))
+ .thenReturn(Collections.singletonList(resource(DmsResourceType.DOCUMENT, documentId)));
+ when(fixture.exclusionMapper.insertBatch(any())).thenReturn(1);
+
+ fixture.service.create(form);
+
+ ArgumentCaptor<List<DmsAccessExclusionEntity>> captor = ArgumentCaptor.forClass(List.class);
+ verify(fixture.exclusionMapper).insertBatch(captor.capture());
+ assertEquals(1, captor.getValue().size());
+ DmsAccessExclusionEntity inserted = captor.getValue().get(0);
+ assertEquals("READ", inserted.getActionCode());
+ assertEquals(USER_ID, inserted.getUserId());
+ assertEquals(ADMIN_ID, inserted.getExcludedById());
+ verify(fixture.principalValidator).validateUsers(anyCollection());
+ verify(fixture.auditAdapter).dataChange("CREATE", "鍒涘缓 DMS 鐢ㄦ埛璇诲彇鎺掗櫎",
+ "dms_access_exclusions", created.getId().toString(), "reason", null);
+ }
+
+ @Test
+ void returnsExistingSetForExactSourceRetry() {
+ Fixture fixture = new Fixture();
+ UUID documentId = UUID.randomUUID();
+ AccessExclusionBatchForm form = form(item(DmsResourceType.DOCUMENT, documentId, "reason"));
+ when(fixture.exclusionMapper.selectBySource(TENANT_ID, "MANUAL", SOURCE_ID))
+ .thenReturn(Collections.singletonList(exclusion(DmsResourceType.DOCUMENT, documentId, "reason")));
+
+ fixture.service.create(form);
+
+ verify(fixture.exclusionMapper, never()).insertBatch(any());
+ verify(fixture.resourceMapper, never()).selectByTenantAndIds(any(), any());
+ verify(fixture.principalValidator, never()).validateUsers(anyCollection());
+ }
+
+ @Test
+ void rejectsDifferentSetUsingSameSourceWithoutWriting() {
+ Fixture fixture = new Fixture();
+ UUID documentId = UUID.randomUUID();
+ AccessExclusionBatchForm form = form(item(DmsResourceType.DOCUMENT, documentId, "new reason"));
+ when(fixture.exclusionMapper.selectBySource(TENANT_ID, "MANUAL", SOURCE_ID))
+ .thenReturn(Collections.singletonList(exclusion(DmsResourceType.DOCUMENT, documentId,
+ "old reason")));
+
+ DmsPermissionException error = assertThrows(DmsPermissionException.class,
+ () -> fixture.service.create(form));
+
+ assertEquals(DmsPermissionError.SOURCE_CONFLICT, error.getError());
+ verify(fixture.exclusionMapper, never()).insertBatch(any());
+ }
+
+ @Test
+ void rejectsFolderExclusion() {
+ Fixture fixture = new Fixture();
+
+ DmsPermissionException error = assertThrows(DmsPermissionException.class,
+ () -> fixture.service.create(form(item(DmsResourceType.FOLDER, UUID.randomUUID(), "reason"))));
+
+ assertEquals(DmsPermissionError.INVALID_REQUEST, error.getError());
+ verify(fixture.exclusionMapper, never()).lockSource(any(), any(), any());
+ }
+
+ @Test
+ void revokesWholeSetInOneUpdate() {
+ Fixture fixture = new Fixture();
+ DmsAccessExclusionEntity first = exclusion(DmsResourceType.DOCUMENT, UUID.randomUUID(), "reason");
+ DmsAccessExclusionEntity second = exclusion(DmsResourceType.FILE_VERSION, UUID.randomUUID(), "reason");
+ List<UUID> ids = Arrays.asList(first.getId(), second.getId());
+ ids.sort(java.util.Comparator.comparing(UUID::toString));
+ when(fixture.exclusionMapper.selectByTenantAndIdsForUpdate(TENANT_ID, ids))
+ .thenReturn(Arrays.asList(first, second));
+ when(fixture.exclusionMapper.revokeBatch(TENANT_ID, ids, "USER", ADMIN_ID, "revoke"))
+ .thenReturn(2);
+ DmsAccessExclusionEntity revokedFirst = exclusion(
+ DmsResourceType.valueOf(first.getResourceType()), first.getResourceId(), "reason");
+ revokedFirst.setId(first.getId());
+ revokedFirst.setRevokedAt(new Date());
+ DmsAccessExclusionEntity revokedSecond = exclusion(
+ DmsResourceType.valueOf(second.getResourceType()), second.getResourceId(), "reason");
+ revokedSecond.setId(second.getId());
+ revokedSecond.setRevokedAt(new Date());
+ when(fixture.exclusionMapper.selectByTenantAndIds(TENANT_ID, ids))
+ .thenReturn(Arrays.asList(revokedFirst, revokedSecond));
+ AccessExclusionRevokeForm form = new AccessExclusionRevokeForm();
+ form.setExclusionIds(ids);
+ form.setReason("revoke");
+
+ fixture.service.revoke(form);
+
+ verify(fixture.exclusionMapper).revokeBatch(TENANT_ID, ids, "USER", ADMIN_ID, "revoke");
+ }
+
+ @Test
+ void rejectsSecondRevocationWithoutUpdating() {
+ Fixture fixture = new Fixture();
+ DmsAccessExclusionEntity existing = exclusion(DmsResourceType.DOCUMENT, UUID.randomUUID(), "reason");
+ existing.setRevokedAt(new Date());
+ List<UUID> ids = Collections.singletonList(existing.getId());
+ when(fixture.exclusionMapper.selectByTenantAndIdsForUpdate(TENANT_ID, ids))
+ .thenReturn(Collections.singletonList(existing));
+ AccessExclusionRevokeForm form = new AccessExclusionRevokeForm();
+ form.setExclusionIds(ids);
+ form.setReason("revoke again");
+
+ DmsPermissionException error = assertThrows(DmsPermissionException.class,
+ () -> fixture.service.revoke(form));
+
+ assertEquals(DmsPermissionError.EXCLUSION_ALREADY_REVOKED, error.getError());
+ verify(fixture.exclusionMapper, never()).revokeBatch(any(), any(), any(), any(), any());
+ }
+
+ @Test
+ void queriesExclusionsWithTenantScopedKeysetPage() {
+ Fixture fixture = new Fixture();
+ UUID resourceId = UUID.randomUUID();
+ DmsAccessExclusionEntity first = exclusion(DmsResourceType.DOCUMENT, resourceId, "first");
+ DmsAccessExclusionEntity second = exclusion(DmsResourceType.DOCUMENT, resourceId, "second");
+ when(fixture.exclusionMapper.selectPage(TENANT_ID, resourceId, USER_ID, null, null, 2))
+ .thenReturn(Arrays.asList(first, second));
+ when(fixture.userDisplayNameResolver.resolve(anyCollection()))
+ .thenReturn(Collections.singletonMap(USER_ID, "杩愯惀鐢ㄦ埛锛坹unying1锛�"));
+
+ DmsCursorPageVO<DmsAccessExclusionVO> result = fixture.service.page(
+ resourceId, " " + USER_ID + " ", null, 1);
+
+ assertEquals(1, result.getList().size());
+ assertEquals(first.getId(), result.getList().get(0).getId());
+ assertEquals("杩愯惀鐢ㄦ埛锛坹unying1锛�", result.getList().get(0).getUserName());
+ assertTrue(result.isHasMore());
+ assertFalse(result.getNextCursor() == null || result.getNextCursor().isEmpty());
+ verify(fixture.currentIdentity).requirePermissionAdministrator();
+ verify(fixture.userDisplayNameResolver).resolve(anyCollection());
+ }
+
+ private AccessExclusionBatchForm form(AccessExclusionItemForm item) {
+ AccessExclusionBatchForm result = new AccessExclusionBatchForm();
+ result.setSourceType(DmsSourceType.MANUAL);
+ result.setSourceId(SOURCE_ID);
+ result.setItems(Collections.singletonList(item));
+ return result;
+ }
+
+ private AccessExclusionItemForm item(DmsResourceType type, UUID id, String reason) {
+ AccessExclusionItemForm result = new AccessExclusionItemForm();
+ result.setResourceType(type);
+ result.setResourceId(id);
+ result.setUserId(USER_ID);
+ result.setExcludeReason(reason);
+ return result;
+ }
+
+ private DmsPermissionResourceEntity resource(DmsResourceType type, UUID id) {
+ DmsPermissionResourceEntity result = new DmsPermissionResourceEntity();
+ result.setId(id);
+ result.setResourceType(type.name());
+ return result;
+ }
+
+ private DmsAccessExclusionEntity exclusion(DmsResourceType type, UUID resourceId, String reason) {
+ DmsAccessExclusionEntity result = new DmsAccessExclusionEntity();
+ result.setId(UUID.randomUUID());
+ result.setTenantId(TENANT_ID);
+ result.setResourceType(type.name());
+ result.setResourceId(resourceId);
+ result.setUserId(USER_ID);
+ result.setActionCode("READ");
+ result.setSourceType("MANUAL");
+ result.setSourceId(SOURCE_ID);
+ result.setValidFrom(new Date(System.currentTimeMillis() - 60_000));
+ result.setExcludeReason(reason);
+ result.setExcludedByType("USER");
+ result.setExcludedById(ADMIN_ID);
+ result.setRevision(0L);
+ result.setCreatedAt(new Date());
+ result.setUpdatedAt(new Date());
+ return result;
+ }
+
+ private static class Fixture {
+ private final DmsCurrentIdentity currentIdentity = mock(DmsCurrentIdentity.class);
+ private final DmsPermissionProperties properties = new DmsPermissionProperties();
+ private final DmsPrincipalValidator principalValidator = mock(DmsPrincipalValidator.class);
+ private final DmsPermissionResourceMapper resourceMapper = mock(DmsPermissionResourceMapper.class);
+ private final DmsAccessExclusionMapper exclusionMapper = mock(DmsAccessExclusionMapper.class);
+ private final DmsCursorCodec cursorCodec = new DmsCursorCodec();
+ private final DmsAuditAdapter auditAdapter = mock(DmsAuditAdapter.class);
+ private final DmsUserDisplayNameResolver userDisplayNameResolver = mock(DmsUserDisplayNameResolver.class);
+ private final DmsAccessExclusionService service = new DmsAccessExclusionService(currentIdentity,
+ properties, principalValidator, resourceMapper, exclusionMapper, cursorCodec, auditAdapter,
+ userDisplayNameResolver);
+
+ private Fixture() {
+ when(currentIdentity.tenantId()).thenReturn(TENANT_ID);
+ when(currentIdentity.userId()).thenReturn(ADMIN_ID);
+ when(userDisplayNameResolver.resolve(anyCollection())).thenReturn(Collections.emptyMap());
+ }
+ }
+}
--
Gitblit v1.8.0