fix(review): handle metadata inbox without version context

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
This commit is contained in:
XiaoSeS 2026-10-14 20:46:51 +08:00
parent 57bb09fde7
commit 3bfd8a322a
3 changed files with 58 additions and 2 deletions

View file

@ -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;

View file

@ -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"));

View file

@ -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");