using FluentAssertions; using Moq; using TakeoutSaaS.Application.App.Tenants.Handlers; using TakeoutSaaS.Application.App.Tenants.Queries; using TakeoutSaaS.Application.Tests.TestUtilities; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Repositories; using TakeoutSaaS.Shared.Abstractions.Security; using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Application.Tests.App.Tenants.Handlers; public sealed class GetAnnouncementByIdQueryHandlerTests { [Fact] public async Task GivenAnnouncementMissing_WhenHandle_ThenReturnsNull() { // Arrange var tenantProvider = new Mock(); tenantProvider.Setup(x => x.GetCurrentTenantId()).Returns(99); var announcementRepository = new Mock(); announcementRepository .Setup(x => x.FindByIdInScopeAsync(99, 500, It.IsAny())) .ReturnsAsync((TenantAnnouncement?)null); var readRepository = new Mock(); var handler = new GetAnnouncementByIdQueryHandler( announcementRepository.Object, readRepository.Object, tenantProvider.Object); // Act var result = await handler.Handle(new GetAnnouncementByIdQuery { AnnouncementId = 500 }, CancellationToken.None); // Assert result.Should().BeNull(); } [Fact] public async Task GivenTargetNotMatched_WhenHandle_ThenReturnsNullAndSkipsReadLookup() { // Arrange var tenantProvider = new Mock(); tenantProvider.Setup(x => x.GetCurrentTenantId()).Returns(100); var currentUserAccessor = new Mock(); currentUserAccessor.SetupGet(x => x.UserId).Returns(123); currentUserAccessor.SetupGet(x => x.IsAuthenticated).Returns(true); var announcement = AnnouncementTestData.CreateAnnouncement(1, 100, 1, DateTime.UtcNow); announcement.TargetType = "SPECIFIC_USERS"; announcement.TargetParameters = "{\"userIds\":[999]}"; var announcementRepository = new Mock(); announcementRepository .Setup(x => x.FindByIdInScopeAsync(100, 1, It.IsAny())) .ReturnsAsync(announcement); var readRepository = new Mock(); var handler = new GetAnnouncementByIdQueryHandler( announcementRepository.Object, readRepository.Object, tenantProvider.Object, currentUserAccessor.Object); // Act var result = await handler.Handle(new GetAnnouncementByIdQuery { AnnouncementId = 1 }, CancellationToken.None); // Assert result.Should().BeNull(); readRepository.Verify(x => x.GetByAnnouncementAsync( It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task GivenUserReadRecord_WhenHandle_ThenReturnsDtoWithReadState() { // Arrange var tenantProvider = new Mock(); tenantProvider.Setup(x => x.GetCurrentTenantId()).Returns(200); var currentUserAccessor = new Mock(); currentUserAccessor.SetupGet(x => x.UserId).Returns(321); currentUserAccessor.SetupGet(x => x.IsAuthenticated).Returns(true); var announcement = AnnouncementTestData.CreateAnnouncement(10, 200, 1, DateTime.UtcNow); var readAt = DateTime.UtcNow.AddMinutes(-3); var announcementRepository = new Mock(); announcementRepository .Setup(x => x.FindByIdInScopeAsync(200, 10, It.IsAny())) .ReturnsAsync(announcement); var readRepository = new Mock(); readRepository .Setup(x => x.GetByAnnouncementAsync(200, It.IsAny>(), 321, It.IsAny())) .ReturnsAsync(new List { new() { AnnouncementId = 10, TenantId = 200, UserId = 321, ReadAt = readAt } }); var handler = new GetAnnouncementByIdQueryHandler( announcementRepository.Object, readRepository.Object, tenantProvider.Object, currentUserAccessor.Object); // Act var result = await handler.Handle(new GetAnnouncementByIdQuery { AnnouncementId = 10 }, CancellationToken.None); // Assert result.Should().NotBeNull(); result!.IsRead.Should().BeTrue(); result.ReadAt.Should().BeCloseTo(readAt, TimeSpan.FromSeconds(1)); } }