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/dmsPermission/DmsPermissionGuardTest.java |  197 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 197 insertions(+), 0 deletions(-)

diff --git a/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsPermission/DmsPermissionGuardTest.java b/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsPermission/DmsPermissionGuardTest.java
new file mode 100644
index 0000000..c850c9a
--- /dev/null
+++ b/jnpf-dms/jnpf-dms-biz/src/test/java/jnpf/dmsPermission/DmsPermissionGuardTest.java
@@ -0,0 +1,197 @@
+package jnpf.dmsPermission;
+
+import jnpf.base.UserInfo;
+import jnpf.dmsEntity.permission.entity.DmsFilePermissionEntity;
+import jnpf.dmsEntity.permission.entity.DmsPermissionUsageEntity;
+import jnpf.dmsEntity.permission.enums.DmsPermissionAction;
+import jnpf.dmsEntity.permission.enums.DmsResourceType;
+import jnpf.dmsEntity.permission.form.PermissionDecisionForm;
+import jnpf.dmsEntity.permission.model.DmsResourcePath;
+import jnpf.dmsEntity.permission.model.DmsSubjectSet;
+import jnpf.dmsEntity.permission.vo.DmsPermissionDecisionVO;
+import jnpf.dmsMapper.DmsFilePermissionMapper;
+import jnpf.dmsMapper.DmsPermissionUsageMapper;
+import jnpf.dmsService.DmsPermissionDecisionService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Date;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+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 DmsPermissionGuardTest {
+    private final DmsCurrentIdentity currentIdentity = mock(DmsCurrentIdentity.class);
+    private final DmsPermissionDecisionService decisionService = mock(DmsPermissionDecisionService.class);
+    private final DmsFilePermissionMapper permissionMapper = mock(DmsFilePermissionMapper.class);
+    private final DmsPermissionUsageMapper usageMapper = mock(DmsPermissionUsageMapper.class);
+    private final DmsResourceResolver resourceResolver = mock(DmsResourceResolver.class);
+    private final DmsSubjectResolver subjectResolver = mock(DmsSubjectResolver.class);
+    private final DmsPermissionGroupMembershipResolver groupResolver = mock(DmsPermissionGroupMembershipResolver.class);
+    private final DmsPermissionMatcher matcher = mock(DmsPermissionMatcher.class);
+    private final DmsAccessExclusionResolver exclusionResolver = mock(DmsAccessExclusionResolver.class);
+    private final DmsPermissionGuard guard = new DmsPermissionGuard(currentIdentity, decisionService,
+            permissionMapper, usageMapper, resourceResolver, subjectResolver, groupResolver, matcher,
+            exclusionResolver);
+    private final UUID permissionId = UUID.randomUUID();
+    private final UUID resourceId = UUID.randomUUID();
+    private final UserInfo user = new UserInfo();
+
+    @BeforeEach
+    void setUp() {
+        user.setTenantId("tenant-1");
+        user.setUserId("user-1");
+        when(currentIdentity.requireUser()).thenReturn(user);
+        when(currentIdentity.tenantId(user)).thenReturn("tenant-1");
+        when(subjectResolver.resolve(user)).thenReturn(mock(DmsSubjectSet.class));
+        when(resourceResolver.resolve(any(), any(), any())).thenReturn(mock(DmsResourcePath.class));
+        when(exclusionResolver.isExcluded(any(), any(), any())).thenReturn(false);
+        when(matcher.matches(any(), any(), any())).thenReturn(true);
+        DmsPermissionDecisionVO decision = new DmsPermissionDecisionVO();
+        decision.setAllowed(true);
+        decision.setMatchedPermissionId(permissionId);
+        when(decisionService.evaluate(any(PermissionDecisionForm.class))).thenReturn(decision);
+    }
+
+    @Test
+    void rejectsPermissionRevokedWhileWaitingForLock() {
+        DmsFilePermissionEntity permission = activePermission();
+        permission.setRevokedAt(new Date());
+        when(permissionMapper.selectByTenantAndIdForUpdate("tenant-1", permissionId)).thenReturn(permission);
+        DmsPermissionOperation<String> operation = mockOperation();
+
+        DmsPermissionException error = assertThrows(DmsPermissionException.class,
+                () -> guard.execute(context(), operation));
+
+        assertEquals(DmsPermissionError.PERMISSION_DENIED, error.getError());
+        verify(operation, never()).execute();
+    }
+
+    @Test
+    void rejectsPermissionExhaustedWhileWaitingForLock() {
+        DmsFilePermissionEntity permission = activePermission();
+        permission.setMaxUseCount(1L);
+        permission.setUsedCount(1L);
+        when(permissionMapper.selectByTenantAndIdForUpdate("tenant-1", permissionId)).thenReturn(permission);
+        DmsPermissionOperation<String> operation = mockOperation();
+
+        DmsPermissionException error = assertThrows(DmsPermissionException.class,
+                () -> guard.execute(context(), operation));
+
+        assertEquals(DmsPermissionError.PERMISSION_EXHAUSTED, error.getError());
+        verify(operation, never()).execute();
+    }
+
+    @Test
+    void recordsLimitedUseAfterBusinessOperationSucceeds() {
+        DmsFilePermissionEntity permission = activePermission();
+        permission.setMaxUseCount(2L);
+        when(permissionMapper.selectByTenantAndIdForUpdate("tenant-1", permissionId)).thenReturn(permission);
+        DmsPermissionOperation<String> operation = mockOperation();
+        when(operation.execute()).thenReturn("created");
+        DmsPermissionExecutionContext context = context();
+
+        String result = guard.execute(context, operation);
+
+        assertEquals("created", result);
+        org.mockito.ArgumentCaptor<DmsPermissionUsageEntity> captor =
+                org.mockito.ArgumentCaptor.forClass(DmsPermissionUsageEntity.class);
+        verify(usageMapper).insert(captor.capture());
+        DmsPermissionUsageEntity usage = captor.getValue();
+        assertEquals(permissionId, usage.getPermissionId());
+        assertEquals(resourceId, usage.getActualResourceId());
+        assertEquals(context.getOperationId(), usage.getOperationId());
+        assertEquals("READ", usage.getActionCode());
+    }
+
+    @Test
+    void replaysPriorUseWithoutExecutingBusinessOperationAgain() {
+        DmsPermissionExecutionContext context = context();
+        DmsPermissionUsageEntity prior = new DmsPermissionUsageEntity();
+        prior.setPermissionId(permissionId);
+        prior.setUserId("user-1");
+        prior.setActualResourceType("DOCUMENT");
+        prior.setActualResourceId(resourceId);
+        prior.setActionCode("READ");
+        prior.setOperationId(context.getOperationId());
+        when(usageMapper.selectByOperation("tenant-1", "user-1", context.getOperationId())).thenReturn(prior);
+        when(permissionMapper.selectByTenantAndId("tenant-1", permissionId)).thenReturn(activePermission());
+        DmsPermissionOperation<String> operation = mockOperation();
+        when(operation.replay()).thenReturn("replayed");
+
+        String result = guard.execute(context, operation);
+
+        assertEquals("replayed", result);
+        verify(operation, never()).execute();
+        verify(operation).replay();
+        verify(usageMapper, never()).insert(any(DmsPermissionUsageEntity.class));
+    }
+
+    @Test
+    void rejectsReadExcludedLimitedPermissionWithoutUsingPermission() {
+        DmsFilePermissionEntity permission = activePermission();
+        permission.setMaxUseCount(2L);
+        when(permissionMapper.selectByTenantAndIdForUpdate("tenant-1", permissionId)).thenReturn(permission);
+        when(exclusionResolver.isExcluded(any(), any(), any())).thenReturn(true);
+        DmsPermissionOperation<String> operation = mockOperation();
+
+        DmsPermissionException error = assertThrows(DmsPermissionException.class,
+                () -> guard.execute(context(), operation));
+
+        assertEquals(DmsPermissionError.PERMISSION_DENIED, error.getError());
+        verify(operation, never()).execute();
+        verify(usageMapper, never()).insert(any(DmsPermissionUsageEntity.class));
+    }
+
+    @Test
+    void rejectsIdempotentReplayAfterReadExclusionIsAdded() {
+        DmsPermissionExecutionContext context = context();
+        DmsPermissionUsageEntity prior = new DmsPermissionUsageEntity();
+        prior.setPermissionId(permissionId);
+        prior.setUserId("user-1");
+        prior.setActualResourceType("DOCUMENT");
+        prior.setActualResourceId(resourceId);
+        prior.setActionCode("READ");
+        prior.setOperationId(context.getOperationId());
+        when(usageMapper.selectByOperation("tenant-1", "user-1", context.getOperationId())).thenReturn(prior);
+        when(exclusionResolver.isExcluded(any(), any(), any())).thenReturn(true);
+        DmsPermissionOperation<String> operation = mockOperation();
+
+        DmsPermissionException error = assertThrows(DmsPermissionException.class,
+                () -> guard.execute(context, operation));
+
+        assertEquals(DmsPermissionError.PERMISSION_DENIED, error.getError());
+        verify(operation, never()).replay();
+        verify(permissionMapper, never()).selectByTenantAndId(any(), any());
+    }
+
+    @SuppressWarnings("unchecked")
+    private DmsPermissionOperation<String> mockOperation() {
+        return mock(DmsPermissionOperation.class);
+    }
+
+    private DmsPermissionExecutionContext context() {
+        DmsPermissionExecutionContext context = new DmsPermissionExecutionContext();
+        context.setResourceType(DmsResourceType.DOCUMENT);
+        context.setResourceId(resourceId);
+        context.setActionCode(DmsPermissionAction.READ);
+        context.setOperationId(UUID.randomUUID());
+        return context;
+    }
+
+    private DmsFilePermissionEntity activePermission() {
+        DmsFilePermissionEntity permission = new DmsFilePermissionEntity();
+        permission.setId(permissionId);
+        permission.setTenantId("tenant-1");
+        permission.setActionCode("READ");
+        permission.setValidFrom(new Date(System.currentTimeMillis() - 60_000));
+        permission.setUsedCount(0L);
+        return permission;
+    }
+}

--
Gitblit v1.8.0