using System.Security.Claims; using JobTrackerApi.Controllers; using JobTrackerApi.Models; using JobTrackerApi.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Moq; using Xunit; namespace JobTrackerApi.Tests; public sealed class ImapControllerTests { [Fact] public async Task Status_returns_connection_fields_for_connected_account() { var imap = new Mock(); imap.Setup(service => service.GetConnectionAsync("user-1", It.IsAny())) .ReturnsAsync(new ImapConnection { OwnerUserId = "user-1", Host = "imap.example.test", Port = 993, UseSsl = true, Username = "user@example.test", ConnectedAt = DateTimeOffset.UtcNow.AddDays(-1), LastSyncStatus = "ok" }); var controller = CreateController(imap.Object, "user-1"); var result = await controller.Status(CancellationToken.None); var ok = Assert.IsType(result.Result); var payload = Assert.IsType(ok.Value); Assert.True(payload.Connected); Assert.Equal("imap.example.test", payload.Host); Assert.Equal(993, payload.Port); Assert.Equal("user@example.test", payload.Username); Assert.Equal("ok", payload.LastSyncStatus); } [Fact] public async Task Status_reports_not_connected_when_no_connection_exists() { var imap = new Mock(); imap.Setup(service => service.GetConnectionAsync("user-1", It.IsAny())) .ReturnsAsync((ImapConnection?)null); var controller = CreateController(imap.Object, "user-1"); var result = await controller.Status(CancellationToken.None); var ok = Assert.IsType(result.Result); var payload = Assert.IsType(ok.Value); Assert.False(payload.Connected); } [Theory] [InlineData("", 993, "user", "pass", "Host is required.")] [InlineData("imap.example.test", 0, "user", "pass", "Valid port is required.")] [InlineData("imap.example.test", 993, "", "pass", "Username is required.")] [InlineData("imap.example.test", 993, "user", "", "Password is required.")] public async Task Connect_rejects_missing_fields(string host, int port, string username, string password, string expectedError) { var imap = new Mock(MockBehavior.Strict); var controller = CreateController(imap.Object, "user-1"); var result = await controller.Connect(new ImapController.ImapConnectRequest(host, port, true, username, password), CancellationToken.None); var badRequest = Assert.IsType(result); Assert.Equal(expectedError, badRequest.Value); } [Fact] public async Task Connect_returns_bad_request_when_service_rejects_credentials() { var imap = new Mock(); imap.Setup(service => service.ConnectAsync("user-1", "imap.example.test", 993, true, "user", "wrong", It.IsAny())) .ThrowsAsync(new InvalidOperationException("IMAP authentication failed: bad credentials")); var controller = CreateController(imap.Object, "user-1"); var result = await controller.Connect(new ImapController.ImapConnectRequest("imap.example.test", 993, true, "user", "wrong"), CancellationToken.None); var badRequest = Assert.IsType(result); Assert.Contains("authentication failed", (string)badRequest.Value!); } [Fact] public async Task Connect_succeeds_and_returns_username() { var imap = new Mock(); imap.Setup(service => service.ConnectAsync("user-1", "imap.example.test", 993, true, "user@example.test", "correct", It.IsAny())) .ReturnsAsync(new ImapConnectResult("user@example.test")); var controller = CreateController(imap.Object, "user-1"); var result = await controller.Connect(new ImapController.ImapConnectRequest("imap.example.test", 993, true, "user@example.test", "correct"), CancellationToken.None); var ok = Assert.IsType(result); var username = ok.Value!.GetType().GetProperty("username")!.GetValue(ok.Value) as string; Assert.Equal("user@example.test", username); } [Fact] public async Task Disconnect_calls_service_for_authenticated_user() { var imap = new Mock(); imap.Setup(service => service.DisconnectAsync("user-1", It.IsAny())).Returns(Task.CompletedTask); var controller = CreateController(imap.Object, "user-1"); var result = await controller.Disconnect(CancellationToken.None); Assert.IsType(result); imap.Verify(service => service.DisconnectAsync("user-1", It.IsAny()), Times.Once); } private static ImapController CreateController(IImapService imap, string userId) { return new ImapController(imap) { ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, userId) }, "test")) } } }; } }