using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Implementation; using Xunit; namespace Api.SeaHavenIndustries.Tests; public class VendorDataServiceTests { private static ApplicationDbContext NewContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } private static Vendor VendorWithJobs( string company, string city, string state, string trades, int jobCount, bool isActive = true) { return new Vendor { CompanyName = company, ContactName = $"{company} Technician", City = city, State = state, TradeSpecialties = trades, IsActive = isActive, IsDeleted = false, Dispatches = Enumerable.Range(1, jobCount) .Select(workOrderId => new Dispatch { WorkOrderId = workOrderId }) .ToList() }; } private static async Task SeedBoundaryVendors(ApplicationDbContext context) { context.Vendors.AddRange( VendorWithJobs("Under Co", "Chicago", "IL", "Plumbing, HVAC", 49), VendorWithJobs("Fifty Co", "Chicago", "IL", "Electrical", 50), VendorWithJobs("Hundred Co", "Dallas", "TX", "Plumbing", 100), VendorWithJobs("One Fifty Co", "Dallas", "TX", "Roofing", 150)); await context.SaveChangesAsync(); } [Theory] [InlineData("under-50", "Under Co")] [InlineData("50-99", "Fifty Co")] [InlineData("100-149", "Hundred Co")] [InlineData("150-plus", "One Fifty Co")] public async Task GetPagedAsync_AppliesExactJobBucketBoundaries( string bucket, string expectedCompany) { await using var context = NewContext(); await SeedBoundaryVendors(context); var service = new VendorDataService(context); var (items, totalCount) = await service.GetPagedAsync( 1, 12, isActive: true, jobBuckets: new[] { bucket }); totalCount.Should().Be(1); items.Single().CompanyName.Should().Be(expectedCompany); } [Fact] public async Task GetPagedAsync_UsesOrWithinFacetAndAndAcrossFacets() { await using var context = NewContext(); await SeedBoundaryVendors(context); var service = new VendorDataService(context); var (items, totalCount) = await service.GetPagedAsync( 1, 12, isActive: true, companies: new[] { "Under Co", "Hundred Co" }, trades: new[] { "HVAC", "Plumbing" }, locations: new[] { "Chicago, IL" }, jobBuckets: new[] { "under-50", "100-149" }); totalCount.Should().Be(1); items.Single().CompanyName.Should().Be("Under Co"); } [Fact] public async Task GetPagedAsync_StatusAndCountAreIndependentOfInactiveHistory() { await using var context = NewContext(); context.Vendors.AddRange( VendorWithJobs("Active Co", "Chicago", "IL", "Plumbing", 1), VendorWithJobs("Inactive Co", "Chicago", "IL", "Plumbing", 1, false)); await context.SaveChangesAsync(); var service = new VendorDataService(context); var active = await service.GetPagedAsync(1, 1, isActive: true); var inactive = await service.GetPagedAsync(1, 1, isActive: false); active.TotalCount.Should().Be(1); inactive.TotalCount.Should().Be(1); active.Items.Single().CompanyName.Should().Be("Active Co"); inactive.Items.Single().CompanyName.Should().Be("Inactive Co"); } [Fact] public async Task GetPagedAsync_LocationFacetMatchesCityStateAndFullLabels() { await using var context = NewContext(); context.Vendors.AddRange( new Vendor { CompanyName = "CityOnly", City = "Austin", State = null, TradeSpecialties = "Plumbing", IsActive = true, IsDeleted = false }, new Vendor { CompanyName = "StateOnly", City = null, State = "TX", TradeSpecialties = "Plumbing", IsActive = true, IsDeleted = false }, new Vendor { CompanyName = "FullLoc", City = "Austin", State = "TX", TradeSpecialties = "Plumbing", IsActive = true, IsDeleted = false }); await context.SaveChangesAsync(); var service = new VendorDataService(context); var cityOnly = await service.GetPagedAsync(1, 12, isActive: true, locations: new[] { "Austin" }); cityOnly.TotalCount.Should().Be(1); cityOnly.Items.Single().CompanyName.Should().Be("CityOnly"); var stateOnly = await service.GetPagedAsync(1, 12, isActive: true, locations: new[] { "TX" }); stateOnly.TotalCount.Should().Be(1); stateOnly.Items.Single().CompanyName.Should().Be("StateOnly"); var full = await service.GetPagedAsync(1, 12, isActive: true, locations: new[] { "Austin, TX" }); full.TotalCount.Should().Be(1); full.Items.Single().CompanyName.Should().Be("FullLoc"); } [Fact] public async Task GetPagedAsync_TradeFacetMatchesExactTokensNotSubstrings() { await using var context = NewContext(); context.Vendors.AddRange( new Vendor { CompanyName = "HvacOnly", City = "Chicago", State = "IL", TradeSpecialties = "HVAC", IsActive = true, IsDeleted = false }, new Vendor { CompanyName = "HvacControls", City = "Chicago", State = "IL", TradeSpecialties = "HVAC Controls", IsActive = true, IsDeleted = false }, new Vendor { CompanyName = "PlumbingCo", City = "Chicago", State = "IL", TradeSpecialties = "Plumbing", IsActive = true, IsDeleted = false }); await context.SaveChangesAsync(); var service = new VendorDataService(context); var hvac = await service.GetPagedAsync(1, 12, isActive: true, trades: new[] { "HVAC" }); hvac.TotalCount.Should().Be(1); hvac.Items.Single().CompanyName.Should().Be("HvacOnly"); var hvacControls = await service.GetPagedAsync(1, 12, isActive: true, trades: new[] { "HVAC Controls" }); hvacControls.TotalCount.Should().Be(1); hvacControls.Items.Single().CompanyName.Should().Be("HvacControls"); var plumb = await service.GetPagedAsync(1, 12, isActive: true, trades: new[] { "Plumb" }); plumb.TotalCount.Should().Be(0); plumb.Items.Should().BeEmpty(); var plumbing = await service.GetPagedAsync(1, 12, isActive: true, trades: new[] { "Plumbing" }); plumbing.TotalCount.Should().Be(1); plumbing.Items.Single().CompanyName.Should().Be("PlumbingCo"); } [Fact] public async Task GetPagedAsync_TradeFacetMatchesTokenInCommaDelimitedList() { await using var context = NewContext(); context.Vendors.AddRange( new Vendor { CompanyName = "Multi", City = "Chicago", State = "IL", TradeSpecialties = "Plumbing, HVAC", IsActive = true, IsDeleted = false }, new Vendor { CompanyName = "Middle", City = "Chicago", State = "IL", TradeSpecialties = "Electrical, Plumbing, Roofing", IsActive = true, IsDeleted = false }); await context.SaveChangesAsync(); var service = new VendorDataService(context); var plumbing = await service.GetPagedAsync(1, 12, isActive: true, trades: new[] { "Plumbing" }); plumbing.TotalCount.Should().Be(2); plumbing.Items.Select(v => v.CompanyName).Should().BeEquivalentTo("Multi", "Middle"); var roofing = await service.GetPagedAsync(1, 12, isActive: true, trades: new[] { "Roofing" }); roofing.TotalCount.Should().Be(1); roofing.Items.Single().CompanyName.Should().Be("Middle"); } }