using System.Security.Claims; using Api.SeaHavenIndustries.Controllers; using Api.SeaHavenIndustries.DTOs; using Data.SeaHavenIndustries; using FluentAssertions; using FluentValidation; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Moq; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; using Xunit; namespace Api.SeaHavenIndustries.Tests; public class LocationControllerTests { private static LocationController NewController(Mock service) => new(service.Object, Mock.Of>()); private static object Prop(object source, string name) => source.GetType().GetProperty(name)!.GetValue(source)!; [Fact] public async Task GetLocationList_ReturnsPaginationEnvelopeWithServiceData() { var service = new Mock(); service.Setup(s => s.GetLocationListPagedAsync(1, 10, "a", It.IsAny())) .ReturnsAsync(new PagedResult { Items = new List { new() { Id = 1, Name = "Site" } }, TotalCount = 1, Page = 1, PageSize = 10 }); var result = await NewController(service).GetLocationList("a", 1, 10, CancellationToken.None); var ok = result.Should().BeOfType().Subject; var envelope = ok.Value.Should().BeOfType().Subject; envelope.PageNumber.Should().Be(1); envelope.PageSize.Should().Be(10); envelope.TotalCount.Should().Be(1); envelope.TotalPages.Should().Be(1); } [Fact] public async Task GetLocationById_WhenMissing_ReturnsNotFoundAndDoesNotLeakEntity() { var service = new Mock(); service.Setup(s => s.GetLocationDetailAsync(5, It.IsAny())) .ReturnsAsync((LocationDTO?)null); var result = await NewController(service).GetLocationById(5, CancellationToken.None); var notFound = result.Should().BeOfType().Subject; var response = notFound.Value.Should().BeOfType().Subject; response.Message.Should().Be("Location not found"); } [Fact] public async Task GetLocationById_WhenFound_MapsServiceDtoToAliasedShape() { var service = new Mock(); service.Setup(s => s.GetLocationDetailAsync(7, It.IsAny())) .ReturnsAsync(new LocationDTO { Id = 7, Name = "N", Address1 = "A", Zip = "Z", PhoneNumber = "P", Email = "E", Status = "Active" }); var result = await NewController(service).GetLocationById(7, CancellationToken.None); var ok = result.Should().BeOfType().Subject; var body = ok.Value!; Prop(body, "Id").Should().Be(7); Prop(body, "Name").Should().Be("N"); Prop(body, "Address").Should().Be("A"); Prop(body, "ZipCode").Should().Be("Z"); Prop(body, "Phone").Should().Be("P"); Prop(body, "ContactEmail").Should().Be("E"); Prop(body, "Status").Should().Be("Active"); } [Fact] public async Task AddLocation_WhenServiceSucceeds_ReturnsCreatedResponse() { var service = new Mock(); var result = await NewController(service).AddLocation(new Location_DTO { Name = "X" }, CancellationToken.None); var ok = result.Should().BeOfType().Subject; var response = ok.Value.Should().BeOfType().Subject; response.Status.Should().Be("200"); response.Message.Should().Be("Location Created Successfully"); service.Verify(s => s.CreateLocationFromRequestAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] public async Task AddLocation_MapsAccountIdFromBodyString() { var service = new Mock(); LocationCreateRequestDTO? captured = null; service .Setup(s => s.CreateLocationFromRequestAsync(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((dto, _, _) => captured = dto) .Returns(Task.CompletedTask); var result = await NewController(service).AddLocation( new Location_DTO { Name = "X", AccountId = "1" }, CancellationToken.None); result.Should().BeOfType(); captured.Should().NotBeNull(); captured!.AccountId.Should().Be(1); } [Fact] public async Task AddLocation_InvalidAccountIdString_ReturnsValidationError() { var service = new Mock(); var result = await NewController(service).AddLocation( new Location_DTO { Name = "X", AccountId = "abc" }, CancellationToken.None); var bad = result.Should().BeOfType().Subject; bad.Value.Should().BeOfType().Subject.Message.Should().Contain("accountId must be a valid integer."); service.Verify( s => s.CreateLocationFromRequestAsync( It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task EditLocation_InvalidAccountIdString_ReturnsValidationError() { var service = new Mock(); var result = await NewController(service).EditLocation( 1, new EditLocation_DTO { Name = "X", AccountId = "abc" }, CancellationToken.None); var bad = result.Should().BeOfType().Subject; bad.Value.Should().BeOfType().Subject.Message.Should().Contain("accountId must be a valid integer."); service.Verify( s => s.UpdateLocationFromRequestAsync( It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task EditLocation_WhenMissing_ReturnsNotFound() { var service = new Mock(); service.Setup(s => s.UpdateLocationFromRequestAsync(99, It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(new KeyNotFoundException()); var result = await NewController(service).EditLocation(99, new EditLocation_DTO { Name = "X" }, CancellationToken.None); var notFound = result.Should().BeOfType().Subject; notFound.Value.Should().BeOfType().Subject.Message.Should().Be("Location not found"); } }