From 3bfd8a322a4efbb2d6313a02983f96408ebeed4e Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Wed, 14 Oct 2026 20:46:51 +0800 Subject: [PATCH] fix(review): handle metadata inbox without version context Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- .../JpaGovernanceQueryRepository.java | 7 ++-- ...ApprovalVisibilityFlowIntegrationTest.java | 33 +++++++++++++++++++ .../JpaGovernanceQueryRepositoryTest.java | 20 +++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/repository/JpaGovernanceQueryRepository.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/repository/JpaGovernanceQueryRepository.java index de65d107..b47e5722 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/repository/JpaGovernanceQueryRepository.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/repository/JpaGovernanceQueryRepository.java @@ -311,8 +311,11 @@ public class JpaGovernanceQueryRepository implements GovernanceQueryRepository { task.getSubmittedAt() != null ? task.getSubmittedAt().toString() : null, namespaceSlug, null, "SUITE", suiteSlug); } - SkillVersion version = bundle.versionsById().get(task.getSkillVersionId()); - Skill skill = bundle.skillsById().get(task.getSkillId() != null ? task.getSkillId() : version != null ? version.getSkillId() : null); + SkillVersion version = task.getSkillVersionId() != null + ? bundle.versionsById().get(task.getSkillVersionId()) : null; + Long skillId = task.getSkillId() != null ? task.getSkillId() + : version != null ? version.getSkillId() : null; + Skill skill = skillId != null ? bundle.skillsById().get(skillId) : null; Namespace namespace = skill != null ? bundle.namespacesById().get(skill.getNamespaceId()) : null; String namespaceSlug = namespace != null ? namespace.getSlug() : null; String skillSlug = skill != null ? skill.getSlug() : null; diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/portal/SkillApprovalVisibilityFlowIntegrationTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/portal/SkillApprovalVisibilityFlowIntegrationTest.java index ee467c27..1ca7599e 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/portal/SkillApprovalVisibilityFlowIntegrationTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/portal/SkillApprovalVisibilityFlowIntegrationTest.java @@ -12,6 +12,8 @@ import com.iflytek.skillhub.domain.namespace.NamespaceRole; import com.iflytek.skillhub.domain.namespace.NamespaceRepository; import com.iflytek.skillhub.domain.namespace.NamespaceType; import com.iflytek.skillhub.domain.review.ReviewTask; +import com.iflytek.skillhub.domain.user.UserAccount; +import com.iflytek.skillhub.domain.user.UserAccountRepository; import com.iflytek.skillhub.domain.skill.Skill; import com.iflytek.skillhub.domain.skill.SkillRepository; import com.iflytek.skillhub.domain.skill.SkillVersion; @@ -39,6 +41,11 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; @@ -55,14 +62,35 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @AutoConfigureMockMvc @ActiveProfiles("test") @Import(TestRedisConfig.class) +@Testcontainers class SkillApprovalVisibilityFlowIntegrationTest { + // Unified review takes a PostgreSQL row lock; H2 PostgreSQL mode cannot + // execute the generated FOR NO KEY UPDATE statement. + @Container + private static final PostgreSQLContainer POSTGRES = + new PostgreSQLContainer<>("postgres:16-alpine"); + + @DynamicPropertySource + static void configurePostgres(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", POSTGRES::getJdbcUrl); + registry.add("spring.datasource.username", POSTGRES::getUsername); + registry.add("spring.datasource.password", POSTGRES::getPassword); + registry.add("spring.datasource.driver-class-name", () -> "org.postgresql.Driver"); + registry.add("spring.jpa.database-platform", () -> "org.hibernate.dialect.PostgreSQLDialect"); + registry.add("spring.flyway.enabled", () -> true); + registry.add("spring.jpa.hibernate.ddl-auto", () -> "validate"); + } + @Autowired private MockMvc mockMvc; @Autowired private NamespaceRepository namespaceRepository; + @Autowired + private UserAccountRepository userAccountRepository; + @Autowired private SkillRepository skillRepository; @@ -92,6 +120,11 @@ class SkillApprovalVisibilityFlowIntegrationTest { @BeforeEach void setUp() { + for (String userId : List.of("system", "super-1", "local-user", "team-admin")) { + if (userAccountRepository.findById(userId).isEmpty()) { + userAccountRepository.save(new UserAccount(userId, userId, userId + "@example.test", null)); + } + } when(searchEmbeddingService.embed(anyString())).thenReturn(""); when(searchEmbeddingService.similarity(anyString(), anyString())).thenReturn(0.0d); when(rbacService.getUserRoleCodes("super-1")).thenReturn(Set.of("SUPER_ADMIN")); diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/repository/JpaGovernanceQueryRepositoryTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/repository/JpaGovernanceQueryRepositoryTest.java index 970114a0..1728c3d2 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/repository/JpaGovernanceQueryRepositoryTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/repository/JpaGovernanceQueryRepositoryTest.java @@ -209,6 +209,26 @@ class JpaGovernanceQueryRepositoryTest { assertThat(responses.get(0).subtitle()).isEqualTo("Spam"); } + @Test + void getReviewInboxItems_supportsMetadataWithoutVersionContext() { + ReviewTask task = ReviewTask.forSkillMetadata(201L, 11L, "submitter", 7L); + setField(task, "id", 4L); + Skill skill = new Skill(11L, "avatar-only", "submitter", SkillVisibility.PUBLIC); + setField(skill, "id", 201L); + Namespace namespace = new Namespace("team-a", "Team A", "submitter"); + setField(namespace, "id", 11L); + given(skillRepository.findByIdIn(List.of(201L))).willReturn(List.of(skill)); + given(namespaceRepository.findByIdIn(List.of(11L))).willReturn(List.of(namespace)); + + var responses = repository.getReviewInboxItems(List.of(task)); + + assertThat(responses).hasSize(1); + assertThat(responses.get(0).type()).isEqualTo("REVIEW"); + assertThat(responses.get(0).title()).isEqualTo("team-a/avatar-only"); + assertThat(responses.get(0).namespace()).isEqualTo("team-a"); + assertThat(responses.get(0).skillSlug()).isEqualTo("avatar-only"); + } + @Test void getReviewInboxItems_toleratesMissingVersionContext() { ReviewTask task = new ReviewTask(101L, 11L, "submitter");