From eef9b4a76eb36e635f8848d661421df06bbc9667 Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Fri, 21 Aug 2026 10:28:15 +0800 Subject: [PATCH] test(security): cover scan task after-commit publishing Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- .../security/SecurityScanServiceTest.java | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/security/SecurityScanServiceTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/security/SecurityScanServiceTest.java index 0d71b596..4150c57d 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/security/SecurityScanServiceTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/security/SecurityScanServiceTest.java @@ -5,21 +5,26 @@ import com.iflytek.skillhub.domain.skill.SkillVersion; import com.iflytek.skillhub.domain.skill.SkillVersionRepository; import com.iflytek.skillhub.domain.skill.SkillVersionStatus; import com.iflytek.skillhub.domain.skill.validation.PackageEntry; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; import java.lang.reflect.Field; import java.nio.file.Path; import java.util.List; import java.util.Optional; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) @@ -48,6 +53,13 @@ class SecurityScanServiceTest { ); } + @AfterEach + void clearTransactionSynchronization() { + if (TransactionSynchronizationManager.isSynchronizationActive()) { + TransactionSynchronizationManager.clearSynchronization(); + } + } + @Test void securityAudit_startsWithSuspiciousUnsafeDefaults() { SecurityAudit audit = new SecurityAudit(42L, ScannerType.SKILL_SCANNER); @@ -124,6 +136,54 @@ class SecurityScanServiceTest { assertThat(task.bundleKey()).isEqualTo("packages/8/42/bundle.zip"); } + @Test + void triggerScan_defersTaskPublishingUntilTransactionCommit() throws Exception { + SkillVersion version = new SkillVersion(8L, "1.0.0", "publisher-1"); + setId(version, 42L); + PackageEntry entry = new PackageEntry( + "README.md", + "# demo".getBytes(), + 6L, + "text/markdown" + ); + + given(skillVersionRepository.findById(42L)).willReturn(Optional.of(version)); + TransactionSynchronizationManager.initSynchronization(); + + service.triggerScan(42L, List.of(entry), "publisher-1"); + + verify(auditRepository).save(any(SecurityAudit.class)); + verify(skillVersionRepository).save(version); + verify(scanTaskProducer, never()).publishScanTask(any(ScanTask.class)); + assertThat(version.getStatus()).isEqualTo(SkillVersionStatus.SCANNING); + + commitRegisteredSynchronizations(); + + ArgumentCaptor taskCaptor = ArgumentCaptor.forClass(ScanTask.class); + verify(scanTaskProducer).publishScanTask(taskCaptor.capture()); + assertThat(taskCaptor.getValue().versionId()).isEqualTo(42L); + } + + @Test + void triggerScan_doesNotPublishTaskWhenTransactionNeverCommits() throws Exception { + SkillVersion version = new SkillVersion(8L, "1.0.0", "publisher-1"); + setId(version, 42L); + PackageEntry entry = new PackageEntry( + "README.md", + "# demo".getBytes(), + 6L, + "text/markdown" + ); + + given(skillVersionRepository.findById(42L)).willReturn(Optional.of(version)); + TransactionSynchronizationManager.initSynchronization(); + + service.triggerScan(42L, List.of(entry), "publisher-1"); + + verify(auditRepository).save(any(SecurityAudit.class)); + verify(scanTaskProducer, never()).publishScanTask(any(ScanTask.class)); + } + @Test void triggerScan_rejectsDirectoryTraversalEntries() throws Exception { SkillVersion version = new SkillVersion(8L, "1.0.0", "publisher-1"); @@ -264,4 +324,10 @@ class SecurityScanServiceTest { field.setAccessible(true); field.set(target, id); } + + private void commitRegisteredSynchronizations() { + for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { + synchronization.afterCommit(); + } + } }