using Socialize.Api.Modules.ReleaseCommunications.Contracts; using Socialize.Api.Modules.ReleaseCommunications.Data; using Socialize.Api.Modules.ReleaseCommunications.Services; namespace Socialize.Tests.ReleaseCommunications; public class ReleaseUpdateRulesTests { [Fact] public void ToDto_maps_summary_to_description() { ReleaseUpdate update = new() { Id = Guid.NewGuid(), Title = "API change", Summary = "A workflow API changed.", TitleFr = "Changement API", SummaryFr = "Une API du flux de travail a change.", Status = ReleaseUpdateStatus.Published, CreatedAt = DateTimeOffset.UtcNow, UpdatedAt = DateTimeOffset.UtcNow, PublishedAt = DateTimeOffset.UtcNow, CreatedByUserId = Guid.NewGuid(), }; ReleaseUpdateDto dto = update.ToDto(isRead: true); Assert.Equal("A workflow API changed.", dto.Description); Assert.Equal("API change", dto.TitleEn); Assert.Equal("A workflow API changed.", dto.DescriptionEn); Assert.Equal("Changement API", dto.TitleFr); Assert.Equal("Une API du flux de travail a change.", dto.DescriptionFr); Assert.True(dto.IsRead); } [Fact] public void VisibleToUsers_returns_published_updates() { ReleaseUpdate update = NewPublishedUpdate(); List visibleUpdates = new[] { update } .AsQueryable() .VisibleToUsers() .ToList(); Assert.Same(update, Assert.Single(visibleUpdates)); } [Fact] public void VisibleTo_rejects_unpublished_updates() { ReleaseUpdate update = NewPublishedUpdate(); update.Status = ReleaseUpdateStatus.Draft; List visibleUpdates = new[] { update } .AsQueryable() .VisibleToUsers() .ToList(); Assert.Empty(visibleUpdates); } [Fact] public void CreateMissingReadReceipts_creates_receipts_only_for_unread_visible_updates() { Guid userId = Guid.NewGuid(); Guid unreadUpdateId = Guid.NewGuid(); Guid readUpdateId = Guid.NewGuid(); DateTimeOffset readAt = DateTimeOffset.UtcNow; IReadOnlyCollection receipts = ReleaseUpdateReadState.CreateMissingReadReceipts( userId, [unreadUpdateId, readUpdateId], new HashSet { readUpdateId }, readAt); ReleaseUpdateReadReceipt receipt = Assert.Single(receipts); Assert.Equal(unreadUpdateId, receipt.ReleaseUpdateId); Assert.Equal(userId, receipt.UserId); Assert.Equal(readAt, receipt.ReadAt); } [Fact] public void IsInactive_allows_never_authenticated_and_old_activity() { DateTimeOffset inactiveBefore = DateTimeOffset.UtcNow.AddHours(-24); Assert.True(ReleaseUpdateEmailRules.IsInactive(null, inactiveBefore)); Assert.True(ReleaseUpdateEmailRules.IsInactive(inactiveBefore.AddMinutes(-1), inactiveBefore)); } [Fact] public void IsInactive_rejects_recent_activity() { DateTimeOffset inactiveBefore = DateTimeOffset.UtcNow.AddHours(-24); Assert.False(ReleaseUpdateEmailRules.IsInactive(inactiveBefore.AddMinutes(1), inactiveBefore)); } [Fact] public void CanSendDigest_enforces_send_interval() { DateTimeOffset lastSentBefore = DateTimeOffset.UtcNow.AddHours(-24); Assert.True(ReleaseUpdateEmailRules.CanSendDigest(null, lastSentBefore)); Assert.True(ReleaseUpdateEmailRules.CanSendDigest(lastSentBefore.AddMinutes(-1), lastSentBefore)); Assert.False(ReleaseUpdateEmailRules.CanSendDigest(lastSentBefore.AddMinutes(1), lastSentBefore)); } private static ReleaseUpdate NewPublishedUpdate() { return new ReleaseUpdate { Id = Guid.NewGuid(), Title = "Update", Summary = "Something changed.", TitleFr = "Mise a jour", SummaryFr = "Quelque chose a change.", Status = ReleaseUpdateStatus.Published, CreatedAt = DateTimeOffset.UtcNow, UpdatedAt = DateTimeOffset.UtcNow, PublishedAt = DateTimeOffset.UtcNow, CreatedByUserId = Guid.NewGuid(), }; } }