using Api.SeaHavenIndustries.HostedServices; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Moq; using Sentry; using SeaHaven.Services.Interfaces; using Xunit; namespace Api.SeaHavenIndustries.Tests; public class PasswordResetEmailSenderTracingTests { [Theory] [InlineData(true)] [InlineData(false)] public async Task Each_send_finishes_its_transaction_as_ok_only_when_the_provider_accepts_it(bool accepted) { var transaction = new Mock(); var hub = new Mock(); hub.Setup(h => h.PushScope()).Returns(Mock.Of()); hub.Setup(h => h.StartTransaction(It.IsAny(), It.IsAny>())) .Returns(transaction.Object); var sender = new Mock(); sender.Setup(s => s.SendEmailAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(accepted); var services = new ServiceCollection().AddSingleton(sender.Object).BuildServiceProvider(); var channel = new PasswordResetEmailChannel(NullLogger.Instance); var worker = new PasswordResetEmailSenderHostedService( channel, services.GetRequiredService(), NullLogger.Instance, hub.Object); await worker.StartAsync(CancellationToken.None); Assert.True(channel.TryEnqueue("user@example.com", "subject", "Your code is 123456")); var deadline = DateTime.UtcNow.AddSeconds(10); while (channel.Pending > 0 && DateTime.UtcNow < deadline) await Task.Delay(10); await worker.StopAsync(CancellationToken.None); Assert.Equal(0, channel.Pending); if (accepted) { transaction.Verify(t => t.Finish(SpanStatus.Ok), Times.Once); transaction.Verify(t => t.Finish(It.IsAny(), It.IsAny()), Times.Never); } else { transaction.Verify(t => t.Finish(SpanStatus.Ok), Times.Never); transaction.Verify(t => t.Finish( It.Is(ex => !ex.Message.Contains("user@example.com") && !ex.Message.Contains("123456")), SpanStatus.InternalError), Times.Once); } } }