diff --git a/backend/src/main/java/org/raddatz/familienarchiv/audit/ActivityFeedRow.java b/backend/src/main/java/org/raddatz/familienarchiv/audit/ActivityFeedRow.java index 59cf64c9..d6b1f899 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/audit/ActivityFeedRow.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/audit/ActivityFeedRow.java @@ -12,6 +12,7 @@ public interface ActivityFeedRow { UUID getDocumentId(); Instant getHappenedAt(); boolean isYouMentioned(); + boolean isYouParticipated(); int getCount(); Instant getHappenedAtUntil(); } diff --git a/backend/src/test/java/org/raddatz/familienarchiv/dashboard/AuditLogQueryRepositoryRolledUpTest.java b/backend/src/test/java/org/raddatz/familienarchiv/dashboard/AuditLogQueryRepositoryRolledUpTest.java index 1f38a8a0..d7197b4c 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/dashboard/AuditLogQueryRepositoryRolledUpTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/dashboard/AuditLogQueryRepositoryRolledUpTest.java @@ -17,6 +17,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.Instant; import java.time.OffsetDateTime; import java.util.List; +import java.util.Map; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @@ -28,6 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; class AuditLogQueryRepositoryRolledUpTest { static final UUID USER_ID = UUID.fromString("dddddddd-dddd-dddd-dddd-dddddddddddd"); + static final UUID OTHER_USER_ID = UUID.fromString("cccccccc-cccc-cccc-cccc-cccccccccccc"); static final UUID DOC_ID = UUID.fromString("eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee"); static final UUID OTHER_DOC_ID = UUID.fromString("ffffffff-ffff-ffff-ffff-ffffffffffff"); @@ -42,6 +44,9 @@ class AuditLogQueryRepositoryRolledUpTest { jdbcTemplate.update( "INSERT INTO users (id, enabled, email, password) VALUES (?, true, ?, 'pw')", USER_ID, "rollup-" + USER_ID + "@test.com"); + jdbcTemplate.update( + "INSERT INTO users (id, enabled, email, password) VALUES (?, true, ?, 'pw')", + OTHER_USER_ID, "rollup-" + OTHER_USER_ID + "@test.com"); jdbcTemplate.update( "INSERT INTO documents (id, title, original_filename, status) VALUES (?, 'Brief A', 'a.pdf', 'PLACEHOLDER')", DOC_ID); @@ -51,17 +56,32 @@ class AuditLogQueryRepositoryRolledUpTest { } private void insertAuditEvent(UUID actorId, UUID docId, String kind, Instant happenedAt) { + insertAuditEvent(actorId, docId, kind, happenedAt, Map.of()); + } + + private void insertAuditEvent(UUID actorId, UUID docId, String kind, Instant happenedAt, Map payload) { + String payloadJson = payload.isEmpty() ? null + : payload.entrySet().stream() + .map(e -> "\"" + e.getKey() + "\": \"" + e.getValue() + "\"") + .collect(java.util.stream.Collectors.joining(", ", "{", "}")); MapSqlParameterSource params = new MapSqlParameterSource() .addValue("kind", kind) .addValue("actor", actorId) .addValue("doc", docId) - .addValue("t", OffsetDateTime.ofInstant(happenedAt, java.time.ZoneOffset.UTC)); + .addValue("t", OffsetDateTime.ofInstant(happenedAt, java.time.ZoneOffset.UTC)) + .addValue("payload", payloadJson, java.sql.Types.OTHER); named().update( - "INSERT INTO audit_log (kind, actor_id, document_id, happened_at) " - + "VALUES (:kind, :actor, :doc, :t)", + "INSERT INTO audit_log (kind, actor_id, document_id, happened_at, payload) " + + "VALUES (:kind, :actor, :doc, :t, :payload::jsonb)", params); } + private void insertReplyNotification(UUID recipientId, UUID docId, UUID commentId) { + jdbcTemplate.update( + "INSERT INTO notifications (recipient_id, type, document_id, reference_id) VALUES (?, 'REPLY', ?, ?)", + recipientId, docId, commentId); + } + @Test void rolledUpFeed_combines_same_actor_same_doc_within_2h() { insertUserAndDocs(); @@ -173,4 +193,59 @@ class AuditLogQueryRepositoryRolledUpTest { assertThat(r.getHappenedAtUntil()).isNull(); }); } + + @Test + void youParticipated_is_true_when_user_has_reply_notification_for_comment() { + insertUserAndDocs(); + UUID commentId = UUID.randomUUID(); + insertAuditEvent(OTHER_USER_ID, DOC_ID, "COMMENT_ADDED", + Instant.parse("2026-04-20T10:00:00Z"), Map.of("commentId", commentId.toString())); + insertReplyNotification(USER_ID, DOC_ID, commentId); + + List rows = auditLogQueryRepository.findRolledUpActivityFeed(USER_ID.toString(), 40); + + assertThat(rows).anySatisfy(r -> + assertThat(r.isYouParticipated()).isTrue() + ); + } + + @Test + void youParticipated_is_false_for_comment_with_no_reply_notification() { + insertUserAndDocs(); + UUID commentId = UUID.randomUUID(); + insertAuditEvent(OTHER_USER_ID, DOC_ID, "COMMENT_ADDED", + Instant.parse("2026-04-20T10:00:00Z"), Map.of("commentId", commentId.toString())); + + List rows = auditLogQueryRepository.findRolledUpActivityFeed(USER_ID.toString(), 40); + + assertThat(rows).allSatisfy(r -> + assertThat(r.isYouParticipated()).isFalse() + ); + } + + @Test + void youMentioned_is_true_when_mention_created_payload_matches_current_user() { + insertUserAndDocs(); + insertAuditEvent(OTHER_USER_ID, DOC_ID, "MENTION_CREATED", + Instant.parse("2026-04-20T10:00:00Z"), Map.of("mentionedUserId", USER_ID.toString())); + + List rows = auditLogQueryRepository.findRolledUpActivityFeed(USER_ID.toString(), 40); + + assertThat(rows).anySatisfy(r -> + assertThat(r.isYouMentioned()).isTrue() + ); + } + + @Test + void youMentioned_is_false_when_mention_created_payload_targets_different_user() { + insertUserAndDocs(); + insertAuditEvent(USER_ID, DOC_ID, "MENTION_CREATED", + Instant.parse("2026-04-20T10:00:00Z"), Map.of("mentionedUserId", OTHER_USER_ID.toString())); + + List rows = auditLogQueryRepository.findRolledUpActivityFeed(USER_ID.toString(), 40); + + assertThat(rows).allSatisfy(r -> + assertThat(r.isYouMentioned()).isFalse() + ); + } } diff --git a/backend/src/test/java/org/raddatz/familienarchiv/dashboard/DashboardServiceTest.java b/backend/src/test/java/org/raddatz/familienarchiv/dashboard/DashboardServiceTest.java index 19692ad4..30a163d1 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/dashboard/DashboardServiceTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/dashboard/DashboardServiceTest.java @@ -104,6 +104,7 @@ class DashboardServiceTest { public UUID getDocumentId() { return docId; } public Instant getHappenedAt() { return Instant.now(); } public boolean isYouMentioned() { return false; } + public boolean isYouParticipated() { return false; } public int getCount() { return 1; } public Instant getHappenedAtUntil() { return null; } };