using Api.SeaHavenIndustries.Controllers; using Api.SeaHavenIndustries.DTOs; using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Helpers; using SeaHaven.Services.Interfaces; using System.Security.Claims; using System.Text.Json; using Xunit; namespace Api.SeaHavenIndustries.Tests; public class UserControllerTests { private static string Json(object? value) => JsonSerializer.Serialize(value, value?.GetType() ?? typeof(object)); private static UserController NewController( Mock service, string? userId = null, string? role = null) { var controller = new UserController(service.Object, Mock.Of>()); var claims = new List(); if (userId != null) claims.Add(new Claim(ClaimTypes.NameIdentifier, userId)); if (role != null) claims.Add(new Claim(ClaimTypes.Role, role)); controller.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal(new ClaimsIdentity(claims, "Test")) } }; return controller; } [Fact] public async Task GetUsers_ReturnsServiceList() { var service = new Mock(); service.Setup(s => s.GetUsersAsync(It.IsAny())) .ReturnsAsync(new List { new() { Id = "1", Name = "A", Email = "a@x.com", RoleName = "Admin", Status = "Active", Date = "Jan 01 2026" } }); var controller = NewController(service); var result = await controller.GetUsers(CancellationToken.None); var ok = result.Should().BeOfType().Subject; ok.Value.Should().BeAssignableTo>(); } [Fact] public async Task AddUser_Success_ReturnsUpdatedSuccessfully() { var service = new Mock(); service.Setup(s => s.AddUserAsync( It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new AddUserOutcomeDTO { Success = true }); var controller = NewController(service, "admin-1", "Admin"); var result = await controller.AddUser(new User_DTO { Name = "N", Email = "n@x.com", Role = "Admin" }, CancellationToken.None); var ok = result.Should().BeOfType().Subject; var response = ok.Value.Should().BeOfType().Subject; response.Status.Should().Be("200"); response.Message.Should().Be("Updated Successfully"); service.Verify(s => s.AddUserAsync( It.IsAny(), It.Is(p => p.IsInRole("Admin")), It.IsAny()), Times.Once); } [Fact] public async Task AddUser_Failure_ReturnsBadRequestWithServiceError() { var service = new Mock(); service.Setup(s => s.AddUserAsync( It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new AddUserOutcomeDTO { Success = false, Error = "boom" }); var controller = NewController(service, "admin-1", "Admin"); var result = await controller.AddUser(new User_DTO(), CancellationToken.None); var bad = result.Should().BeOfType().Subject; var response = bad.Value.Should().BeOfType().Subject; response.Status.Should().Be("Error"); response.Message.Should().Be("boom"); } [Fact] public async Task AddUser_ServiceForbidden_ReturnsForbid() { var service = new Mock(); service.Setup(s => s.AddUserAsync( It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new AddUserOutcomeDTO { Success = false, Error = UserMutationErrors.Forbidden }); var controller = NewController(service, "tech-1", "User"); var result = await controller.AddUser( new User_DTO { Name = "N", Email = "n@x.com", Role = "User", AccountId = 99 }, CancellationToken.None); result.Should().BeOfType(); } [Fact] public async Task EditUser_NotFound_ReturnsUserNotFoundMessage() { var service = new Mock(); service.Setup(s => s.EditUserAsync( It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new AddUserOutcomeDTO { Success = false, Error = UserMutationErrors.UserNotFound }); var controller = NewController(service, "admin-1", "Admin"); var result = await controller.EditUser(new EditUser_DTO { Id = "9" }, CancellationToken.None); var bad = result.Should().BeOfType().Subject; var response = bad.Value.Should().BeOfType().Subject; response.Message.Should().Be(UserMutationErrors.UserNotFound); } [Fact] public async Task EditUser_Found_ReturnsUpdatedSuccessfully() { var service = new Mock(); service.Setup(s => s.EditUserAsync( It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new AddUserOutcomeDTO { Success = true }); var controller = NewController(service, "admin-1", "Admin"); var result = await controller.EditUser(new EditUser_DTO { Id = "9" }, CancellationToken.None); var ok = result.Should().BeOfType().Subject; ok.Value.Should().BeOfType(); } [Fact] public async Task EditUser_ServiceForbidden_ReturnsForbid() { var service = new Mock(); service.Setup(s => s.EditUserAsync( It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new AddUserOutcomeDTO { Success = false, Error = UserMutationErrors.Forbidden }); var controller = NewController(service, "tech-1", "User"); var result = await controller.EditUser( new EditUser_DTO { Id = "tech-1", AccountId = 42, Email = "me@x.com", Role = "User" }, CancellationToken.None); result.Should().BeOfType(); } [Fact] public async Task DeleteUser_NotFound_ReturnsIdNotMatchedMessage() { var service = new Mock(); service.Setup(s => s.DeleteUserAsync( It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new AddUserOutcomeDTO { Success = false, Error = UserMutationErrors.UserNotFound }); var controller = NewController(service, "admin-1", "Admin"); var result = await controller.DeleteUser(new EditUser_DTO { Id = "missing" }, CancellationToken.None); var bad = result.Should().BeOfType().Subject; var response = bad.Value.Should().BeOfType().Subject; response.Message.Should().Be("ID not matched!"); } [Fact] public async Task DeleteUser_Found_DelegatesAndReturnsOk() { var service = new Mock(); service.Setup(s => s.DeleteUserAsync( "5", It.IsAny(), It.IsAny())) .ReturnsAsync(new AddUserOutcomeDTO { Success = true }); var controller = NewController(service, "admin-1", "Admin"); var result = await controller.DeleteUser(new EditUser_DTO { Id = "5" }, CancellationToken.None); result.Should().BeOfType(); service.Verify(s => s.DeleteUserAsync( "5", It.IsAny(), It.IsAny()), Times.Once); } [Fact] public async Task DeleteUser_Forbidden_ReturnsForbid() { var service = new Mock(); service.Setup(s => s.DeleteUserAsync( It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new AddUserOutcomeDTO { Success = false, Error = UserMutationErrors.Forbidden }); var controller = NewController(service, "tech-1", "User"); var result = await controller.DeleteUser(new EditUser_DTO { Id = "5" }, CancellationToken.None); result.Should().BeOfType(); } [Fact] public async Task DeleteCurrentUser_UsesClaimUserId() { var service = new Mock(); service.Setup(s => s.DeleteCurrentUserAsync("77", It.IsAny())).Returns(Task.CompletedTask); var controller = NewController(service, "77"); var result = await controller.DeleteCurrentUser(CancellationToken.None); var ok = result.Should().BeOfType().Subject; ok.Value.Should().BeOfType(); service.Verify(s => s.DeleteCurrentUserAsync("77", It.IsAny()), Times.Once); } [Fact] public async Task UserProfile_PreservesLowercaseAddedDateKey() { var service = new Mock(); service.Setup(s => s.GetUserProfileAsync("77", It.IsAny())) .ReturnsAsync(new List { new() { Id = "77", Name = "Me", AddedDate = new DateTime(2026, 1, 1), Email = "me@x.com", Contact = "321" } }); var controller = NewController(service, "77"); var result = await controller.UserProfile(CancellationToken.None); var ok = result.Should().BeOfType().Subject; var json = Json(ok.Value); json.Should().Contain("\"Id\":\"77\""); json.Should().Contain("\"Name\":\"Me\""); json.Should().Contain("\"addeddate\":\""); json.Should().Contain("\"Email\":\"me@x.com\""); json.Should().Contain("\"Contact\":\"321\""); } }