Files
baiyue-firstleg/common-audit/src/test/java/com/baiyue/common/audit/BusinessAuditAspectTest.java
T
2026-08-14 16:06:27 +08:00

111 lines
4.7 KiB
Java

package com.baiyue.common.audit;
import com.baiyue.common.security.AuthenticatedUser;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.util.ReflectionTestUtils;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class BusinessAuditAspectTest {
@AfterEach
void clearSecurityContext() {
SecurityContextHolder.clearContext();
}
@Test
void platformOperationUsesTargetCustomerAndKeepsOperatorScope() throws Throwable {
AuditEvent event = invoke(new AuthenticatedUser(7L, 0L, 0L, 1L,
"platform.admin", "PLATFORM", "WEB", Set.of("platform:customer:edit")), 10001L);
assertThat(event.tenantId()).isEqualTo(10001L);
assertThat(event.operatorTenantId()).isZero();
assertThat(event.userId()).isEqualTo(7L);
assertThat(event.appCode()).isEqualTo("PLATFORM");
assertThat(event.beforeData()).isEqualTo("[10001]");
assertThat(event.beforeData()).doesNotContain("platform.admin", "platform:customer:edit", "permissions");
}
@Test
void merchantOperationIgnoresTargetCustomerArgument() throws Throwable {
AuditEvent event = invoke(new AuthenticatedUser(8L, 10001L, 10001L, 2L,
"merchant.user", "MERCHANT", "WEB", Set.of()), 20002L);
assertThat(event.tenantId()).isEqualTo(10001L);
assertThat(event.operatorTenantId()).isEqualTo(10001L);
}
@Test
void failedOperationStillPublishesSanitizedBeforeSnapshot() throws Throwable {
AuditEvent event = invokeFailure(new AuthenticatedUser(7L, 0L, 0L, 1L,
"platform.admin", "PLATFORM", "WEB", Set.of()), 10001L);
assertThat(event.success()).isFalse();
assertThat(event.errorCode()).isEqualTo("IllegalStateException");
assertThat(event.beforeData()).isEqualTo("[10001]");
assertThat(event.afterData()).isNull();
}
private static AuditEvent invoke(AuthenticatedUser user, long requestedTenantId) throws Throwable {
return invoke(user, requestedTenantId, null);
}
private static AuditEvent invokeFailure(AuthenticatedUser user, long requestedTenantId) throws Throwable {
return invoke(user, requestedTenantId, new IllegalStateException("validation failed"));
}
private static AuditEvent invoke(AuthenticatedUser user, long requestedTenantId, Throwable failure) throws Throwable {
AuditSink sink = mock(AuditSink.class);
BusinessAuditAspect aspect = new BusinessAuditAspect();
ReflectionTestUtils.setField(aspect, "sink", sink);
ReflectionTestUtils.setField(aspect, "sanitizer", new SensitiveDataSanitizer(new ObjectMapper(), 1024));
ReflectionTestUtils.setField(aspect, "serviceName", "test-service");
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(user, null, List.of()));
TestOperations target = new TestOperations();
Method method = TestOperations.class.getDeclaredMethod("update", AuthenticatedUser.class, long.class);
MethodSignature signature = mock(MethodSignature.class);
when(signature.getMethod()).thenReturn(method);
when(signature.getParameterNames()).thenReturn(null);
ProceedingJoinPoint joinPoint = mock(ProceedingJoinPoint.class);
when(joinPoint.getSignature()).thenReturn(signature);
when(joinPoint.getTarget()).thenReturn(target);
when(joinPoint.getArgs()).thenReturn(new Object[]{user, requestedTenantId});
if (failure == null) when(joinPoint.proceed()).thenReturn("ok");
else when(joinPoint.proceed()).thenThrow(failure);
try {
aspect.around(joinPoint, method.getAnnotation(BusinessAudit.class));
} catch (Throwable error) {
if (failure == null) throw error;
}
ArgumentCaptor<AuditEvent> event = ArgumentCaptor.forClass(AuditEvent.class);
verify(sink).publish(event.capture());
return event.getValue();
}
private static final class TestOperations {
@BusinessAudit(event = "TEST_UPDATE", resourceType = "TEST", captureAfter = false)
public String update(AuthenticatedUser user, long tenantId) {
return "ok";
}
}
}