using System.Net; using System.Text.Json; using API.Models.Outgoing; using API.Services; using Moq; using Moq.Protected; namespace API.Tests.Services; public class PlunkEmailSenderTests { private const string TestSecretKey = "test-secret-key"; private const string PlunkApiUrl = "https://api.useplunk.com/v1/send"; private static HttpClient CreateMockHttpClient(HttpStatusCode statusCode, string responseContent = "{}") { var mockHandler = new Mock(); mockHandler .Protected() .Setup>( "SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(new HttpResponseMessage { StatusCode = statusCode, Content = new StringContent(responseContent) }); return new HttpClient(mockHandler.Object); } private static (HttpClient client, Mock handler) CreateMockHttpClientWithHandler( HttpStatusCode statusCode, string responseContent = "{}") { var mockHandler = new Mock(); mockHandler .Protected() .Setup>( "SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(new HttpResponseMessage { StatusCode = statusCode, Content = new StringContent(responseContent) }); return (new HttpClient(mockHandler.Object), mockHandler); } [Fact] public async Task SendEmailAsync_SuccessfulSend_DoesNotThrow() { // Arrange var httpClient = CreateMockHttpClient(HttpStatusCode.OK); var emailSender = new PlunkEmailSender(httpClient, TestSecretKey); // Act & Assert - should not throw await emailSender.SendEmailAsync("test@example.com", "Test Subject", "

Test Body

"); } [Fact] public async Task SendEmailAsync_ApiReturnsError_ThrowsException() { // Arrange var errorMessage = "Invalid API key"; var httpClient = CreateMockHttpClient(HttpStatusCode.Unauthorized, errorMessage); var emailSender = new PlunkEmailSender(httpClient, TestSecretKey); // Act & Assert var exception = await Assert.ThrowsAsync(() => emailSender.SendEmailAsync("test@example.com", "Test Subject", "

Test Body

")); Assert.Contains("Fehler beim Senden der E-Mail über Plunk", exception.Message); Assert.Contains(errorMessage, exception.Message); } [Fact] public async Task SendEmailAsync_ServerError_ThrowsException() { // Arrange var httpClient = CreateMockHttpClient(HttpStatusCode.InternalServerError, "Server error"); var emailSender = new PlunkEmailSender(httpClient, TestSecretKey); // Act & Assert await Assert.ThrowsAsync(() => emailSender.SendEmailAsync("test@example.com", "Test Subject", "

Test Body

")); } [Fact] public async Task SendEmailAsync_BadRequest_ThrowsException() { // Arrange var httpClient = CreateMockHttpClient(HttpStatusCode.BadRequest, "Invalid request"); var emailSender = new PlunkEmailSender(httpClient, TestSecretKey); // Act & Assert await Assert.ThrowsAsync(() => emailSender.SendEmailAsync("test@example.com", "Test Subject", "

Test Body

")); } [Fact] public async Task SendEmailAsync_SendsCorrectPayload() { // Arrange var mockHandler = new Mock(); HttpRequestMessage? capturedRequest = null; mockHandler .Protected() .Setup>( "SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) .Callback((request, _) => capturedRequest = request) .ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent("{}") }); var httpClient = new HttpClient(mockHandler.Object); var emailSender = new PlunkEmailSender(httpClient, TestSecretKey); // Act await emailSender.SendEmailAsync("recipient@example.com", "My Subject", "

Hello

"); // Assert Assert.NotNull(capturedRequest); Assert.Equal(HttpMethod.Post, capturedRequest.Method); Assert.Equal(PlunkApiUrl, capturedRequest.RequestUri?.ToString()); var requestContent = await capturedRequest.Content!.ReadAsStringAsync(); var payload = JsonSerializer.Deserialize(requestContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); Assert.NotNull(payload); Assert.Equal("recipient@example.com", payload.To); Assert.Equal("My Subject", payload.Subject); Assert.Equal("

Hello

", payload.Body); } [Fact] public async Task SendEmailAsync_SendsCorrectAuthorizationHeader() { // Arrange var mockHandler = new Mock(); HttpRequestMessage? capturedRequest = null; mockHandler .Protected() .Setup>( "SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) .Callback((request, _) => capturedRequest = request) .ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent("{}") }); var httpClient = new HttpClient(mockHandler.Object); var emailSender = new PlunkEmailSender(httpClient, "my-secret-api-key"); // Act await emailSender.SendEmailAsync("test@example.com", "Subject", "Body"); // Assert Assert.NotNull(capturedRequest); var authHeader = httpClient.DefaultRequestHeaders.Authorization; Assert.NotNull(authHeader); Assert.Equal("Bearer", authHeader.Scheme); Assert.Equal("my-secret-api-key", authHeader.Parameter); } [Fact] public async Task SendEmailAsync_SendsCorrectContentType() { // Arrange var mockHandler = new Mock(); HttpRequestMessage? capturedRequest = null; mockHandler .Protected() .Setup>( "SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) .Callback((request, _) => capturedRequest = request) .ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent("{}") }); var httpClient = new HttpClient(mockHandler.Object); var emailSender = new PlunkEmailSender(httpClient, TestSecretKey); // Act await emailSender.SendEmailAsync("test@example.com", "Subject", "Body"); // Assert Assert.NotNull(capturedRequest); Assert.NotNull(capturedRequest.Content); Assert.Equal("application/json", capturedRequest.Content.Headers.ContentType?.MediaType); Assert.Equal("utf-8", capturedRequest.Content.Headers.ContentType?.CharSet); } }