|
- using CartWise.Application.DTOs;
- using CartWise.Application.Services;
- using CartWise.Domain.Entities;
- using CartWise.Domain.Enums;
- using CartWise.Infrastructure.Data;
- using Microsoft.EntityFrameworkCore;
-
- namespace CartWise.Application.Tests;
-
- public class ProductServiceTests : IDisposable
- {
- private static readonly DateTime Now = new(2026, 8, 10, 12, 0, 0, DateTimeKind.Utc);
-
- private readonly CartWiseDbContext _db;
- private readonly ProductService _sut;
-
- public ProductServiceTests()
- {
- var options = new DbContextOptionsBuilder<CartWiseDbContext>()
- .UseInMemoryDatabase(Guid.NewGuid().ToString())
- .Options;
-
- _db = new CartWiseDbContext(options);
- _sut = new ProductService(_db, [], TimeProvider.System);
- }
-
- public void Dispose()
- {
- _db.Dispose();
- GC.SuppressFinalize(this);
- }
-
- [Fact]
- public async Task SearchAsync_ReturnsMatchingProductsWithBrandName()
- {
- var brand = new Brand("Jif");
- var product = new Product("Jif Creamy Peanut Butter", ProductSourceType.Manual, Now, brandId: brand.BrandId, sizeValue: 16m, sizeUnit: "oz");
- _db.Brands.Add(brand);
- _db.Products.Add(product);
- await _db.SaveChangesAsync();
-
- var results = await _sut.SearchAsync("peanut");
-
- var result = Assert.Single(results);
- Assert.Equal("Jif Creamy Peanut Butter", result.Name);
- Assert.Equal("Jif", result.BrandName);
- Assert.Equal("16 oz", result.PackageDisplay);
- }
-
- [Fact]
- public async Task SearchAsync_ReturnsEmptyForBlankQuery()
- {
- var results = await _sut.SearchAsync(" ");
-
- Assert.Empty(results);
- }
-
- [Fact]
- public async Task SearchAsync_ReturnsEmptyWhenNothingMatches()
- {
- _db.Products.Add(new Product("Whole Milk", ProductSourceType.Manual, Now));
- await _db.SaveChangesAsync();
-
- var results = await _sut.SearchAsync("cereal");
-
- Assert.Empty(results);
- }
-
- [Fact]
- public async Task GetDetailsAsync_ReturnsNullWhenProductDoesNotExist()
- {
- var details = await _sut.GetDetailsAsync(Guid.NewGuid());
-
- Assert.Null(details);
- }
-
- [Fact]
- public async Task GetDetailsAsync_ReturnsBrandConceptAndIdentifiers()
- {
- var brand = new Brand("Jif");
- var concept = new GroceryConcept("Peanut Butter", Now);
- var product = new Product("Jif Creamy 16 oz", ProductSourceType.Manual, Now, groceryConceptId: concept.GroceryConceptId, brandId: brand.BrandId);
- product.AddIdentifier(ProductIdentifierType.UpcA, "051500255516");
-
- _db.Brands.Add(brand);
- _db.GroceryConcepts.Add(concept);
- _db.Products.Add(product);
- await _db.SaveChangesAsync();
-
- var details = await _sut.GetDetailsAsync(product.ProductId);
-
- Assert.NotNull(details);
- Assert.Equal("Jif", details!.BrandName);
- Assert.Equal("Peanut Butter", details.ConceptName);
- Assert.Equal("051500255516", Assert.Single(details.BarcodeIdentifiers));
- }
-
- [Fact]
- public async Task ResolveBarcodeAsync_ReturnsLocalProductWithoutCallingProvider()
- {
- var product = new Product("Whole Milk", ProductSourceType.Manual, Now);
- product.AddIdentifier(ProductIdentifierType.UpcA, "111111111111");
- _db.Products.Add(product);
- await _db.SaveChangesAsync();
-
- var sut = new ProductService(_db, [], TimeProvider.System);
-
- var result = await sut.ResolveBarcodeAsync("111111111111");
-
- Assert.True(result.IsSuccess);
- Assert.Equal("Whole Milk", result.Value!.Name);
- }
-
- [Fact]
- public async Task ResolveBarcodeAsync_FallsBackToProviderAndPersistsResult()
- {
- var provider = new FakeProductDataProvider(new Dictionary<string, ProductLookupResult>
- {
- ["222222222222"] = new ProductLookupResult
- {
- Name = "Imported Cereal",
- BrandName = "Acme",
- SizeValue = 12m,
- SizeUnit = "oz",
- SourceExternalId = "off-123"
- }
- });
- var sut = new ProductService(_db, [provider], TimeProvider.System);
-
- var result = await sut.ResolveBarcodeAsync("222222222222");
-
- Assert.True(result.IsSuccess);
- Assert.Equal("Imported Cereal", result.Value!.Name);
- Assert.Equal("Acme", result.Value.BrandName);
-
- var persisted = await _db.Products.AsNoTracking().SingleAsync();
- Assert.Equal(ProductSourceType.OpenFoodFacts, persisted.SourceType);
- Assert.Equal("off-123", persisted.SourceExternalId);
- }
-
- [Fact]
- public async Task ResolveBarcodeAsync_FailsWhenNoLocalMatchAndNoProviderResult()
- {
- var provider = new FakeProductDataProvider(new Dictionary<string, ProductLookupResult>());
- var sut = new ProductService(_db, [provider], TimeProvider.System);
-
- var result = await sut.ResolveBarcodeAsync("999999999999");
-
- Assert.False(result.IsSuccess);
- }
-
- [Fact]
- public async Task ResolveBarcodeAsync_FailsWhenBarcodeIsMissing()
- {
- var result = await _sut.ResolveBarcodeAsync(" ");
-
- Assert.False(result.IsSuccess);
- }
- }
|