feat(domain): capture immutable context for business event producers

Signed-off-by: 千乘妍 (Xiaoyaner) <258399167+xiaoyaner0201@users.noreply.github.com>
This commit is contained in:
千乘妍 (Xiaoyaner) 2026-10-29 01:11:03 +08:00
parent 9ebb99226d
commit 76ea586719
24 changed files with 182 additions and 31 deletions

View file

@ -0,0 +1,24 @@
package com.iflytek.skillhub.domain.event;
import com.iflytek.skillhub.domain.skill.Skill;
import com.iflytek.skillhub.domain.skill.SkillVisibility;
import java.time.Instant;
/**
* Immutable producer context, never an entity or a request object. No telemetry dependency.
* The timestamp is event occurrence (immediately before publication), not transaction commit time.
*/
public record BusinessEventSnapshot(Long namespaceId, SkillVisibility visibility, String actorId,
String actorType, String source, Instant occurredAt) {
public static BusinessEventSnapshot capture(Skill skill, String actorId, String source) {
return capture(skill, actorId, "user", source);
}
public static BusinessEventSnapshot capture(Skill skill, String actorId, String actorType, String source) {
// BuiltinSkillInitializer reserves this identity and verifies isSystemAccount before publishing.
// It uses the ordinary privileged publish path; never mislabel that service account as human.
if ("builtin-skill-publisher".equals(actorId)) { actorType = "system"; }
return new BusinessEventSnapshot(skill.getNamespaceId(), skill.getVisibility(), actorId,
actorType, source, Instant.now());
}
}

View file

@ -1,2 +1,7 @@
package com.iflytek.skillhub.domain.event;
public record PromotionApprovedEvent(Long promotionId, Long skillId, String reviewerId, String submitterId) {}
public record PromotionApprovedEvent(Long promotionId, Long skillId, String reviewerId,
String submitterId, BusinessEventSnapshot snapshot) implements SkillBusinessEvent {
public PromotionApprovedEvent(Long promotionId, Long skillId, String reviewerId, String submitterId) {
this(promotionId, skillId, reviewerId, submitterId, null);
}
}

View file

@ -1,2 +1,7 @@
package com.iflytek.skillhub.domain.event;
public record PromotionRejectedEvent(Long promotionId, Long skillId, String reviewerId, String submitterId, String reason) {}
public record PromotionRejectedEvent(Long promotionId, Long skillId, String reviewerId,
String submitterId, String reason, BusinessEventSnapshot snapshot) implements SkillBusinessEvent {
public PromotionRejectedEvent(Long promotionId, Long skillId, String reviewerId, String submitterId, String reason) {
this(promotionId, skillId, reviewerId, submitterId, reason, null);
}
}

View file

@ -1,2 +1,7 @@
package com.iflytek.skillhub.domain.event;
public record ReviewApprovedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId, String submitterId) {}
public record ReviewApprovedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId,
String submitterId, BusinessEventSnapshot snapshot) implements SkillBusinessEvent {
public ReviewApprovedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId, String submitterId) {
this(reviewId, skillId, versionId, reviewerId, submitterId, null);
}
}

View file

@ -1,2 +1,7 @@
package com.iflytek.skillhub.domain.event;
public record ReviewRejectedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId, String submitterId, String reason) {}
public record ReviewRejectedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId,
String submitterId, String reason, BusinessEventSnapshot snapshot) implements SkillBusinessEvent {
public ReviewRejectedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId, String submitterId, String reason) {
this(reviewId, skillId, versionId, reviewerId, submitterId, reason, null);
}
}

View file

@ -0,0 +1,7 @@
package com.iflytek.skillhub.domain.event;
/** Marker for existing skill facts with a producer-captured scalar analytics snapshot. */
public interface SkillBusinessEvent {
Long skillId();
BusinessEventSnapshot snapshot();
}

View file

@ -1,3 +1,8 @@
package com.iflytek.skillhub.domain.event;
public record SkillDownloadedEvent(Long skillId, Long versionId) {}
public record SkillDownloadedEvent(Long skillId, Long versionId,
BusinessEventSnapshot snapshot) implements SkillBusinessEvent {
public SkillDownloadedEvent(Long skillId, Long versionId) {
this(skillId, versionId, null);
}
}

View file

@ -1,3 +1,8 @@
package com.iflytek.skillhub.domain.event;
public record SkillPublishedEvent(Long skillId, Long versionId, String publisherId) {}
public record SkillPublishedEvent(Long skillId, Long versionId, String publisherId,
BusinessEventSnapshot snapshot) implements SkillBusinessEvent {
public SkillPublishedEvent(Long skillId, Long versionId, String publisherId) {
this(skillId, versionId, publisherId, null);
}
}

View file

@ -2,4 +2,9 @@ package com.iflytek.skillhub.domain.event;
import com.iflytek.skillhub.domain.skill.SkillStatus;
public record SkillStatusChangedEvent(Long skillId, SkillStatus oldStatus, SkillStatus newStatus) {}
public record SkillStatusChangedEvent(Long skillId, SkillStatus oldStatus, SkillStatus newStatus,
BusinessEventSnapshot snapshot) implements SkillBusinessEvent {
public SkillStatusChangedEvent(Long skillId, SkillStatus oldStatus, SkillStatus newStatus) {
this(skillId, oldStatus, newStatus, null);
}
}

View file

@ -1,3 +1,8 @@
package com.iflytek.skillhub.domain.event;
public record SkillVersionYankedEvent(Long skillId, Long versionId, String actorUserId, boolean wasPublished) {}
public record SkillVersionYankedEvent(Long skillId, Long versionId, String actorUserId, boolean wasPublished,
BusinessEventSnapshot snapshot) implements SkillBusinessEvent {
public SkillVersionYankedEvent(Long skillId, Long versionId, String actorUserId, boolean wasPublished) {
this(skillId, versionId, actorUserId, wasPublished, null);
}
}

View file

@ -4,4 +4,8 @@ import com.iflytek.skillhub.domain.skill.SkillVisibility;
/** Access changed without publishing a new package version. */
public record SkillVisibilityChangedEvent(Long skillId, SkillVisibility previousVisibility,
SkillVisibility visibility) {}
SkillVisibility visibility, BusinessEventSnapshot snapshot) implements SkillBusinessEvent {
public SkillVisibilityChangedEvent(Long skillId, SkillVisibility previousVisibility, SkillVisibility visibility) {
this(skillId, previousVisibility, visibility, null);
}
}

View file

@ -1,5 +1,7 @@
package com.iflytek.skillhub.domain.review;
import com.iflytek.skillhub.domain.event.BusinessEventSnapshot;
import com.iflytek.skillhub.domain.skill.SkillOwnerAccessPolicy;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iflytek.skillhub.domain.audit.AuditDetail;
@ -294,10 +296,11 @@ public class PromotionService {
PromotionRequest savedRequest = promotionRequestRepository.save(approvedRequest);
eventPublisher.publishEvent(new SkillPublishedEvent(
targetSkill.getId(), newVersion.getId(), reviewerId));
targetSkill.getId(), newVersion.getId(), reviewerId,
BusinessEventSnapshot.capture(targetSkill, reviewerId, "promotion")));
eventPublisher.publishEvent(new PromotionApprovedEvent(
approvedRequest.getId(), approvedRequest.getSourceSkillId(),
reviewerId, approvedRequest.getSubmittedBy()));
reviewerId, approvedRequest.getSubmittedBy(), BusinessEventSnapshot.capture(sourceSkill, reviewerId, "promotion")));
governanceNotificationService.notifyUser(
approvedRequest.getSubmittedBy(),
"PROMOTION",
@ -396,9 +399,12 @@ public class PromotionService {
}
syncPromotionRequestState(request, ReviewTaskStatus.REJECTED, reviewerId, comment);
entityManager.detach(request);
Skill sourceSkill = request.getSourceSkillId() == null ? null
: skillRepository.findById(request.getSourceSkillId()).orElse(null);
eventPublisher.publishEvent(new PromotionRejectedEvent(
request.getId(), request.getSourceSkillId(),
reviewerId, request.getSubmittedBy(), comment));
reviewerId, request.getSubmittedBy(), comment,
sourceSkill == null ? null : BusinessEventSnapshot.capture(sourceSkill, reviewerId, "promotion")));
governanceNotificationService.notifyUser(
request.getSubmittedBy(),
"PROMOTION",

View file

@ -1,5 +1,7 @@
package com.iflytek.skillhub.domain.review;
import com.iflytek.skillhub.domain.event.BusinessEventSnapshot;
import com.iflytek.skillhub.domain.skill.SkillOwnerAccessPolicy;
import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -289,10 +291,11 @@ public class ReviewService {
if (avatarParticipant != null) { avatarParticipant.decide(task, true, reviewerId, comment); }
eventPublisher.publishEvent(new SkillPublishedEvent(
skill.getId(), skillVersion.getId(), reviewerId));
skill.getId(), skillVersion.getId(), reviewerId,
BusinessEventSnapshot.capture(skill, reviewerId, "review")));
eventPublisher.publishEvent(new ReviewApprovedEvent(
task.getId(), skill.getId(), skillVersion.getId(),
reviewerId, task.getSubmittedBy()));
reviewerId, task.getSubmittedBy(), BusinessEventSnapshot.capture(skill, reviewerId, "review")));
governanceNotificationService.notifyUser(
task.getSubmittedBy(),
"REVIEW",
@ -358,7 +361,7 @@ public class ReviewService {
if (avatarParticipant != null) { avatarParticipant.decide(task, false, reviewerId, comment); }
eventPublisher.publishEvent(new ReviewRejectedEvent(
task.getId(), skill.getId(), skillVersion.getId(),
reviewerId, task.getSubmittedBy(), comment));
reviewerId, task.getSubmittedBy(), comment, BusinessEventSnapshot.capture(skill, reviewerId, "review")));
governanceNotificationService.notifyUser(
task.getSubmittedBy(),
"REVIEW",

View file

@ -1,6 +1,7 @@
package com.iflytek.skillhub.domain.skill.service;
import com.iflytek.skillhub.domain.event.SkillDownloadedEvent;
import com.iflytek.skillhub.domain.event.BusinessEventSnapshot;
import com.iflytek.skillhub.domain.namespace.Namespace;
import com.iflytek.skillhub.domain.namespace.NamespaceRepository;
import com.iflytek.skillhub.domain.namespace.NamespaceRole;
@ -172,15 +173,16 @@ public class SkillDownloadService {
// Only increment download count for PUBLISHED versions
if (version.getStatus() == SkillVersionStatus.PUBLISHED) {
recordPublishedDownload(skill, version);
recordPublishedDownload(skill, version, currentUserId);
}
return result;
}
private void recordPublishedDownload(Skill skill, SkillVersion version) {
private void recordPublishedDownload(Skill skill, SkillVersion version, String currentUserId) {
skillRepository.incrementDownloadCount(skill.getId());
skillVersionStatsRepository.incrementDownloadCount(version.getId(), skill.getId());
eventPublisher.publishEvent(new SkillDownloadedEvent(skill.getId(), version.getId()));
eventPublisher.publishEvent(new SkillDownloadedEvent(skill.getId(), version.getId(),
BusinessEventSnapshot.capture(skill, currentUserId, "download")));
}
private DownloadResult buildDownloadResult(Skill skill, SkillVersion version) {

View file

@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.iflytek.skillhub.domain.audit.AuditDetail;
import com.iflytek.skillhub.domain.audit.AuditLogService;
import com.iflytek.skillhub.domain.event.SkillStatusChangedEvent;
import com.iflytek.skillhub.domain.event.BusinessEventSnapshot;
import com.iflytek.skillhub.domain.namespace.NamespaceRole;
import com.iflytek.skillhub.domain.review.ReviewTaskRepository;
import com.iflytek.skillhub.domain.security.SecurityScanService;
@ -139,7 +140,8 @@ public class SkillGovernanceService {
skill.setUpdatedBy(actorUserId);
Skill saved = skillRepository.save(skill);
auditLogService.record(actorUserId, "ARCHIVE_SKILL", "SKILL", skill.getId(), null, clientIp, userAgent, jsonReason(reason));
eventPublisher.publishEvent(new SkillStatusChangedEvent(skill.getId(), previousStatus, SkillStatus.ARCHIVED));
eventPublisher.publishEvent(new SkillStatusChangedEvent(skill.getId(), previousStatus, SkillStatus.ARCHIVED,
BusinessEventSnapshot.capture(skill, actorUserId, "governance")));
return saved;
}
@ -180,7 +182,8 @@ public class SkillGovernanceService {
skill.setUpdatedBy(actorUserId);
Skill saved = skillRepository.save(skill);
auditLogService.record(actorUserId, "UNARCHIVE_SKILL", "SKILL", skillId, null, clientIp, userAgent, null);
eventPublisher.publishEvent(new SkillStatusChangedEvent(skillId, previousStatus, SkillStatus.ACTIVE));
eventPublisher.publishEvent(new SkillStatusChangedEvent(skillId, previousStatus, SkillStatus.ACTIVE,
BusinessEventSnapshot.capture(skill, actorUserId, "governance")));
return saved;
}
@ -353,7 +356,8 @@ public class SkillGovernanceService {
version.setYankReason(reason);
version.setDownloadReady(false);
SkillVersion saved = skillVersionRepository.save(version);
skillRepository.findById(version.getSkillId()).ifPresent(skill -> {
Optional<Skill> eventSkill = skillRepository.findById(version.getSkillId());
eventSkill.ifPresent(skill -> {
if (versionId.equals(skill.getLatestVersionId())) {
Optional<SkillVersion> fallback = findLatestPublishedVersion(skill.getId());
skill.setLatestVersionId(fallback.map(SkillVersion::getId).orElse(null));
@ -364,7 +368,8 @@ public class SkillGovernanceService {
});
auditLogService.record(actorUserId, "YANK_SKILL_VERSION", "SKILL_VERSION", versionId, null, clientIp, userAgent, jsonReason(reason));
eventPublisher.publishEvent(new com.iflytek.skillhub.domain.event.SkillVersionYankedEvent(
version.getSkillId(), versionId, actorUserId, true));
version.getSkillId(), versionId, actorUserId, true,
eventSkill.map(skill -> BusinessEventSnapshot.capture(skill, actorUserId, "governance")).orElse(null)));
return saved;
}

View file

@ -1,5 +1,7 @@
package com.iflytek.skillhub.domain.skill.service;
import com.iflytek.skillhub.domain.event.BusinessEventSnapshot;
import com.iflytek.skillhub.domain.skill.SkillOwnerAccessPolicy;
import com.iflytek.skillhub.domain.review.ReviewAvatarParticipant;
import org.springframework.beans.factory.annotation.Autowired;
@ -900,7 +902,9 @@ public class SkillPublishService {
skillRepository.save(skill);
if (autoPublish) {
eventPublisher.publishEvent(new SkillPublishedEvent(skill.getId(), version.getId(), publisherId));
eventPublisher.publishEvent(new SkillPublishedEvent(skill.getId(), version.getId(), publisherId,
BusinessEventSnapshot.capture(skill, publisherId,
forceAutoPublish ? "system" : "user", "auto_publish")));
}
// 13. Return identifiers for the created version

View file

@ -1,5 +1,7 @@
package com.iflytek.skillhub.domain.skill.service;
import com.iflytek.skillhub.domain.event.BusinessEventSnapshot;
import com.iflytek.skillhub.domain.skill.SkillOwnerAccessPolicy;
import com.iflytek.skillhub.domain.event.SkillPublishedEvent;
import com.iflytek.skillhub.domain.namespace.NamespaceMemberRepository;
@ -191,7 +193,8 @@ public class SkillReviewSubmitService {
skillRepository.save(skill);
eventPublisher.publishEvent(new SkillPublishedEvent(
skill.getId(), version.getId(), actorUserId));
skill.getId(), version.getId(), actorUserId,
BusinessEventSnapshot.capture(skill, actorUserId, "private_confirm")));
}
private void assertCanManageLifecycle(Skill skill, String actorUserId,

View file

@ -2,6 +2,7 @@ package com.iflytek.skillhub.domain.skill.service;
import com.iflytek.skillhub.domain.skill.SkillOwnerAccessPolicy;
import com.iflytek.skillhub.domain.event.SkillVisibilityChangedEvent;
import com.iflytek.skillhub.domain.event.BusinessEventSnapshot;
import com.iflytek.skillhub.domain.security.*;
import com.iflytek.skillhub.domain.namespace.*;
import com.iflytek.skillhub.domain.review.*;
@ -142,6 +143,7 @@ public class SkillVisibilityService {
skill.setVisibility(target);
skill.setUpdatedBy(actor);
skills.save(skill);
events.publishEvent(new SkillVisibilityChangedEvent(skill.getId(), previous, target));
events.publishEvent(new SkillVisibilityChangedEvent(skill.getId(), previous, target,
BusinessEventSnapshot.capture(skill, actor, "visibility")));
}
}

View file

@ -593,6 +593,14 @@ class PromotionServiceTest {
assertEquals(NEW_SKILL_ID, event.skillId());
assertEquals(NEW_VERSION_ID, event.versionId());
assertEquals(REVIEWER_ID, event.publisherId());
assertEquals(TARGET_NAMESPACE_ID, event.snapshot().namespaceId());
assertEquals(REVIEWER_ID, event.snapshot().actorId());
assertEquals("promotion", event.snapshot().source());
ArgumentCaptor<com.iflytek.skillhub.domain.event.PromotionApprovedEvent> approved =
ArgumentCaptor.forClass(com.iflytek.skillhub.domain.event.PromotionApprovedEvent.class);
verify(eventPublisher).publishEvent(approved.capture());
assertEquals(5L, approved.getValue().snapshot().namespaceId());
assertEquals(REVIEWER_ID, approved.getValue().snapshot().actorId());
// Verify targetSkillId updated on promotion request
verify(promotionRequestRepository).save(approvedRequest);
@ -921,6 +929,7 @@ class PromotionServiceTest {
void shouldRejectPromotionSuccessfully() {
PromotionRequest pr = createPendingPromotion();
when(promotionRequestRepository.findById(PROMOTION_ID)).thenReturn(Optional.of(pr));
when(skillRepository.findById(SOURCE_SKILL_ID)).thenReturn(Optional.of(createSourceSkill()));
when(permissionChecker.canReviewPromotion(pr, REVIEWER_ID, Set.of("SKILL_ADMIN"))).thenReturn(true);
when(promotionRequestRepository.updateStatusWithVersion(
PROMOTION_ID, ReviewTaskStatus.REJECTED, REVIEWER_ID, "Not ready", null, pr.getVersion()))
@ -935,7 +944,11 @@ class PromotionServiceTest {
assertEquals(Instant.now(CLOCK), result.getReviewedAt());
verify(promotionRequestRepository).updateStatusWithVersion(
PROMOTION_ID, ReviewTaskStatus.REJECTED, REVIEWER_ID, "Not ready", null, pr.getVersion());
verify(eventPublisher, never()).publishEvent(any());
var rejected = ArgumentCaptor.forClass(com.iflytek.skillhub.domain.event.PromotionRejectedEvent.class);
verify(eventPublisher).publishEvent(rejected.capture());
assertEquals(SOURCE_SKILL_ID, rejected.getValue().skillId());
assertEquals(5L, rejected.getValue().snapshot().namespaceId());
assertEquals(REVIEWER_ID, rejected.getValue().snapshot().actorId());
}
@Test

View file

@ -392,6 +392,14 @@ class ReviewServiceTest {
assertEquals(SKILL_ID, event.skillId());
assertEquals(SKILL_VERSION_ID, event.versionId());
assertEquals(REVIEWER_ID, event.publisherId());
assertEquals(NAMESPACE_ID, event.snapshot().namespaceId());
assertEquals(REVIEWER_ID, event.snapshot().actorId());
assertEquals("review", event.snapshot().source());
ArgumentCaptor<com.iflytek.skillhub.domain.event.ReviewApprovedEvent> approved =
ArgumentCaptor.forClass(com.iflytek.skillhub.domain.event.ReviewApprovedEvent.class);
verify(eventPublisher).publishEvent(approved.capture());
assertEquals(REVIEWER_ID, approved.getValue().snapshot().actorId());
assertNotNull(approved.getValue().snapshot().occurredAt());
}
@Test

View file

@ -149,7 +149,10 @@ class SkillGovernanceServiceTest {
assertThat(result.getStatus()).isEqualTo(SkillStatus.ARCHIVED);
verify(auditLogService).record("owner", "ARCHIVE_SKILL", "SKILL", 10L, null, "127.0.0.1", "JUnit", "{\"reason\":\"cleanup\"}");
verify(eventPublisher).publishEvent(any(SkillStatusChangedEvent.class));
var archived = org.mockito.ArgumentCaptor.forClass(SkillStatusChangedEvent.class);
verify(eventPublisher).publishEvent(archived.capture());
assertThat(archived.getValue().snapshot().actorId()).isEqualTo("owner");
assertThat(archived.getValue().snapshot().namespaceId()).isEqualTo(1L);
}
@Test
@ -164,7 +167,10 @@ class SkillGovernanceServiceTest {
assertThat(result.getStatus()).isEqualTo(SkillStatus.ACTIVE);
verify(auditLogService).record("owner", "UNARCHIVE_SKILL", "SKILL", 10L, null, "127.0.0.1", "JUnit", null);
verify(eventPublisher).publishEvent(any(SkillStatusChangedEvent.class));
var restored = org.mockito.ArgumentCaptor.forClass(SkillStatusChangedEvent.class);
verify(eventPublisher).publishEvent(restored.capture());
assertThat(restored.getValue().snapshot().actorId()).isEqualTo("owner");
assertThat(restored.getValue().newStatus()).isEqualTo(SkillStatus.ACTIVE);
}
@Test
@ -211,7 +217,10 @@ class SkillGovernanceServiceTest {
assertThat(result.getYankedBy()).isEqualTo("owner");
assertThat(result.isDownloadReady()).isFalse();
verify(auditLogService).record("owner", "YANK_SKILL_VERSION", "SKILL_VERSION", 22L, null, "127.0.0.1", "JUnit", "{\"reason\":\"broken\"}");
verify(eventPublisher).publishEvent(any(com.iflytek.skillhub.domain.event.SkillVersionYankedEvent.class));
var yanked = org.mockito.ArgumentCaptor.forClass(com.iflytek.skillhub.domain.event.SkillVersionYankedEvent.class);
verify(eventPublisher).publishEvent(yanked.capture());
assertThat(yanked.getValue().snapshot().actorId()).isEqualTo("owner");
assertThat(yanked.getValue().snapshot().namespaceId()).isEqualTo(1L);
}
@Test

View file

@ -804,7 +804,13 @@ class SkillPublishServiceTest {
verify(reviewTaskRepository, never()).save(any(ReviewTask.class));
verify(skillRepository).save(argThat(savedSkill ->
savedSkill.getLatestVersionId() != null && savedSkill.getLatestVersionId().equals(10L)));
verify(eventPublisher).publishEvent(any(SkillPublishedEvent.class));
var published = ArgumentCaptor.forClass(SkillPublishedEvent.class);
verify(eventPublisher).publishEvent(published.capture());
assertEquals(1L, published.getValue().snapshot().namespaceId());
assertEquals(publisherId, published.getValue().snapshot().actorId());
assertEquals("user", published.getValue().snapshot().actorType());
assertEquals("auto_publish", published.getValue().snapshot().source());
assertNotNull(published.getValue().snapshot().occurredAt());
}
@Test

View file

@ -190,6 +190,12 @@ class SkillReviewSubmitServiceTest {
assertEquals(skillId, event.skillId());
assertEquals(versionId, event.versionId());
assertEquals(userId, event.publisherId());
assertEquals(namespaceId, event.snapshot().namespaceId());
assertEquals(SkillVisibility.PRIVATE, event.snapshot().visibility());
assertEquals(userId, event.snapshot().actorId());
assertEquals("user", event.snapshot().actorType());
assertEquals("private_confirm", event.snapshot().source());
assertNotNull(event.snapshot().occurredAt());
}
@Test

View file

@ -51,7 +51,12 @@ class SkillVisibilityServiceTest {
assertThat(skill.getLatestVersionId()).isEqualTo(3L);
verify(versions, never()).save(any());
verify(reviews, never()).save(any());
verify(events).publishEvent(any(SkillVisibilityChangedEvent.class));
var event = org.mockito.ArgumentCaptor.forClass(SkillVisibilityChangedEvent.class);
verify(events).publishEvent(event.capture());
assertThat(event.getValue().snapshot().namespaceId()).isEqualTo(2L);
assertThat(event.getValue().snapshot().visibility()).isEqualTo(SkillVisibility.PRIVATE);
assertThat(event.getValue().snapshot().actorId()).isEqualTo("owner");
assertThat(event.getValue().previousVisibility()).isEqualTo(SkillVisibility.PUBLIC);
}
@Test
@ -62,9 +67,13 @@ class SkillVisibilityServiceTest {
assertThat(task.getRequestedVisibility()).isEqualTo(SkillVisibility.PUBLIC);
assertThat(task.getVisibilityVersionId()).isEqualTo(3L);
assertThat(task.getSkillVersionId()).isNull();
verifyNoInteractions(events);
service.approve(task, "admin");
assertThat(skill.getVisibility()).isEqualTo(SkillVisibility.PUBLIC);
verify(versions, never()).save(any());
var event = org.mockito.ArgumentCaptor.forClass(SkillVisibilityChangedEvent.class);
verify(events).publishEvent(event.capture());
assertThat(event.getValue().snapshot().actorId()).isEqualTo("admin");
}
@Test