using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Implementation; using SeaHaven.Services.DTOs; using SeaHaven.Services.Implementation; using SeaHaven.Services.Validation; using Xunit; namespace Api.SeaHavenIndustries.Tests; public class LocationServiceTests { private static ApplicationDbContext NewContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } private static LocationService NewService(ApplicationDbContext ctx) => new(new LocationDataService(ctx), new CreateLocationValidation(), new UpdateLocationValidation()); private static Locations SeedLocation(ApplicationDbContext ctx, string name, string? city = null, string? status = "Active") { var loc = new Locations { Name = name, City = city, Status = status, CreatedDate = DateTime.Now }; ctx.Locations.Add(loc); ctx.SaveChanges(); return loc; } [Fact] public async Task CreateLocationFromRequestAsync_PersistsMappedFields() { using var ctx = NewContext(); var service = NewService(ctx); await service.CreateLocationFromRequestAsync(new LocationCreateRequestDTO { Name = "Warehouse", Title = "Main WH", Address = "1 Depot Rd", City = "Austin", State = "TX", ZipCode = "73301", Phone = "555-1000", ContactEmail = "wh@example.com", Status = "Active" }, CancellationToken.None); var entity = ctx.Locations.Single(); entity.Name.Should().Be("Warehouse"); entity.Title.Should().Be("Main WH"); entity.Address1.Should().Be("1 Depot Rd"); entity.City.Should().Be("Austin"); entity.State.Should().Be("TX"); entity.Zip.Should().Be("73301"); entity.PhoneNumber.Should().Be("555-1000"); entity.Email.Should().Be("wh@example.com"); entity.Status.Should().Be("Active"); } [Fact] public async Task GetLocationListPagedAsync_FiltersBySearchAndMapsShape() { using var ctx = NewContext(); SeedLocation(ctx, "Alpha Site", "Austin"); SeedLocation(ctx, "Beta Site", "Dallas"); SeedLocation(ctx, "Gamma Yard", "Austin"); var page = await NewService(ctx).GetLocationListPagedAsync(1, 10, "Austin", CancellationToken.None); page.Items.Should().HaveCount(2); page.TotalCount.Should().Be(2); page.Items.All(l => l.City == "Austin").Should().BeTrue(); page.Items.Select(l => l.Name).Should().NotBeNull(); } [Fact] public async Task GetLocationDetailAsync_ReturnsMappedDtoOrNull() { using var ctx = NewContext(); var existing = SeedLocation(ctx, "Depot", "Houston"); var service = NewService(ctx); var found = await service.GetLocationDetailAsync(existing.Id, CancellationToken.None); var missing = await service.GetLocationDetailAsync(existing.Id + 999, CancellationToken.None); missing.Should().BeNull(); found.Should().NotBeNull(); found!.Name.Should().Be("Depot"); found.City.Should().Be("Houston"); } [Fact] public async Task UpdateLocationFromRequestAsync_UpdatesFieldsAndThrowsWhenMissing() { using var ctx = NewContext(); var existing = SeedLocation(ctx, "Old", "Round Rock"); await NewService(ctx).UpdateLocationFromRequestAsync(existing.Id, new LocationUpdateRequestDTO { Name = "New", Address = "9 New St", City = "Plano", Status = "Inactive" }, CancellationToken.None); var row = ctx.Locations.Single(); row.Name.Should().Be("New"); row.Address1.Should().Be("9 New St"); row.City.Should().Be("Plano"); row.Status.Should().Be("Inactive"); var act = () => NewService(ctx).UpdateLocationFromRequestAsync(existing.Id + 999, new LocationUpdateRequestDTO { Name = "X" }, CancellationToken.None); await act.Should().ThrowAsync(); } [Fact] public async Task DeleteLocationByIdAsync_RemovesAndReturnsFalseWhenMissing() { using var ctx = NewContext(); var existing = SeedLocation(ctx, "Gone", "Cedar Park"); var removed = await NewService(ctx).DeleteLocationByIdAsync(existing.Id, CancellationToken.None); var again = await NewService(ctx).DeleteLocationByIdAsync(existing.Id, CancellationToken.None); removed.Should().BeTrue(); again.Should().BeFalse(); ctx.Locations.Should().BeEmpty(); } }