using Api.SeaHavenIndustries.Controllers; using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Moq; using SeaHaven.DataServices.Implementation; using SeaHaven.Services.DTOs; using SeaHaven.Services.Implementation; using SeaHaven.Services.Validation; using Xunit; namespace Api.SeaHavenIndustries.Tests; public class LocationControllerSitesTests { private static ApplicationDbContext NewContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } private static LocationController NewController(ApplicationDbContext ctx) { var dataService = new LocationDataService(ctx); var service = new LocationService( dataService, new AccountDataService(ctx), Mock.Of(), Mock.Of()); var controller = new LocationController(service, Mock.Of>()) { ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() } }; return controller; } private static async Task SeedSitesAsync(ApplicationDbContext ctx) { ctx.Locations.AddRange( new Locations { Id = 1, Name = "STL8", City = "St. Louis", State = "MO", AccountId = 100 }, new Locations { Id = 2, Name = "BK5", City = "Dallas", State = "TX", AccountId = 100 }, new Locations { Id = 3, Name = "IND9", City = "Indianapolis", State = "IN", AccountId = 100 }, new Locations { Id = 4, Name = "DFW8", City = "Dallas", State = "TX", AccountId = 100 }); await ctx.SaveChangesAsync(); } [Fact] public async Task GetSites_ReturnsIdCodeCityState_OrderedByCode() { await using var ctx = NewContext(); await SeedSitesAsync(ctx); var controller = NewController(ctx); var result = await controller.GetSites(); var ok = result.Should().BeOfType().Subject; var data = ok.Value.Should().BeAssignableTo>().Subject; data.Should().HaveCount(4); data.Select(s => s.Code).Should().ContainInOrder("BK5", "DFW8", "IND9", "STL8"); data[0].Should().BeEquivalentTo(new SiteOptionDTO { Id = 2, Code = "BK5", City = "Dallas", State = "TX" }); } [Fact] public async Task GetSites_FiltersByCode() { await using var ctx = NewContext(); await SeedSitesAsync(ctx); var controller = NewController(ctx); var result = await controller.GetSites("bk5"); var ok = result.Should().BeOfType().Subject; var data = ok.Value.Should().BeAssignableTo>().Subject; data.Should().ContainSingle(); data[0].Code.Should().Be("BK5"); } [Fact] public async Task GetSites_FiltersByCity() { await using var ctx = NewContext(); await SeedSitesAsync(ctx); var controller = NewController(ctx); var result = await controller.GetSites("Dallas"); var ok = result.Should().BeOfType().Subject; var data = ok.Value.Should().BeAssignableTo>().Subject; data.Should().HaveCount(2); data.Select(s => s.Code).Should().BeEquivalentTo("BK5", "DFW8"); } [Fact] public async Task GetSites_OmitsLocationsWithoutAccountMapping() { await using var ctx = NewContext(); ctx.Locations.AddRange( new Locations { Id = 1, Name = "STL8", City = "St. Louis", State = "MO", AccountId = 100 }, new Locations { Id = 2, Name = "UNMAPPED1", City = "Nowhere", State = "ZZ" }); await ctx.SaveChangesAsync(); var controller = NewController(ctx); var result = await controller.GetSites(); var ok = result.Should().BeOfType().Subject; var data = ok.Value.Should().BeAssignableTo>().Subject; data.Should().ContainSingle(); data[0].Code.Should().Be("STL8"); var searchResult = await controller.GetSites("unmapped1"); var searchOk = searchResult.Should().BeOfType().Subject; var searchData = searchOk.Value.Should().BeAssignableTo>().Subject; searchData.Should().BeEmpty(); } [Fact] public async Task GetSites_ForwardsCancellationToken() { await using var ctx = NewContext(); await SeedSitesAsync(ctx); var controller = NewController(ctx); using var cancellation = new CancellationTokenSource(); await cancellation.CancelAsync(); var act = () => controller.GetSites(cancellationToken: cancellation.Token); await act.Should().ThrowAsync(); } }