|
- 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 PriceServiceTests : IDisposable
- {
- private static readonly DateTime Now = new(2026, 8, 10, 12, 0, 0, DateTimeKind.Utc);
-
- private readonly CartWiseDbContext _db;
- private readonly PriceService _sut;
-
- public PriceServiceTests()
- {
- var options = new DbContextOptionsBuilder<CartWiseDbContext>()
- .UseInMemoryDatabase(Guid.NewGuid().ToString())
- .Options;
-
- _db = new CartWiseDbContext(options);
- _sut = new PriceService(_db, TimeProvider.System);
- }
-
- public void Dispose()
- {
- _db.Dispose();
- GC.SuppressFinalize(this);
- }
-
- [Fact]
- public async Task GetHouseholdPriceInsightAsync_ReturnsNullWhenNoObservationsExist()
- {
- var insight = await _sut.GetHouseholdPriceInsightAsync(Guid.NewGuid(), Guid.NewGuid());
-
- Assert.Null(insight);
- }
-
- [Fact]
- public async Task RecordObservationAsync_ThenGetInsight_ReturnsLatestAverageMedianAndLowest()
- {
- var householdId = Guid.NewGuid();
- var productId = Guid.NewGuid();
-
- await _sut.RecordObservationAsync(productId, 3.00m, Now.AddDays(-2), PriceObservationSourceType.UserEntered, 1m, householdId: householdId);
- await _sut.RecordObservationAsync(productId, 5.00m, Now.AddDays(-1), PriceObservationSourceType.UserEntered, 1m, householdId: householdId);
- await _sut.RecordObservationAsync(productId, 4.00m, Now, PriceObservationSourceType.UserEntered, 1m, householdId: householdId);
-
- var insight = await _sut.GetHouseholdPriceInsightAsync(householdId, productId);
-
- Assert.NotNull(insight);
- Assert.Equal(3, insight!.ObservationCount);
- Assert.Equal(4.00m, insight.LatestPrice);
- Assert.Equal(Now, insight.LatestObservedUtc);
- Assert.Equal(4.00m, insight.AveragePrice);
- Assert.Equal(4.00m, insight.MedianPrice);
- Assert.Equal(3.00m, insight.LowestPrice);
- }
-
- [Fact]
- public async Task GetHouseholdPriceInsightAsync_ComputesMedianForEvenCount()
- {
- var householdId = Guid.NewGuid();
- var productId = Guid.NewGuid();
-
- await _sut.RecordObservationAsync(productId, 2.00m, Now.AddDays(-1), PriceObservationSourceType.UserEntered, 1m, householdId: householdId);
- await _sut.RecordObservationAsync(productId, 4.00m, Now, PriceObservationSourceType.UserEntered, 1m, householdId: householdId);
-
- var insight = await _sut.GetHouseholdPriceInsightAsync(householdId, productId);
-
- Assert.Equal(3.00m, insight!.MedianPrice);
- }
-
- [Fact]
- public async Task GetHouseholdPriceInsightAsync_DoesNotMixDifferentHouseholds()
- {
- var productId = Guid.NewGuid();
- await _sut.RecordObservationAsync(productId, 3.00m, Now, PriceObservationSourceType.UserEntered, 1m, householdId: Guid.NewGuid());
-
- var insight = await _sut.GetHouseholdPriceInsightAsync(Guid.NewGuid(), productId);
-
- Assert.Null(insight);
- }
-
- [Fact]
- public async Task GetHouseholdPriceHistoryAsync_ReturnsNewestFirst()
- {
- var householdId = Guid.NewGuid();
- var productId = Guid.NewGuid();
- await _sut.RecordObservationAsync(productId, 3.00m, Now.AddDays(-1), PriceObservationSourceType.UserEntered, 1m, householdId: householdId);
- await _sut.RecordObservationAsync(productId, 4.00m, Now, PriceObservationSourceType.UserEntered, 1m, householdId: householdId);
-
- var history = await _sut.GetHouseholdPriceHistoryAsync(householdId, productId);
-
- Assert.Equal(2, history.Count);
- Assert.Equal(4.00m, history[0].Price);
- Assert.Equal(3.00m, history[1].Price);
- }
-
- [Fact]
- public async Task GetHouseholdPricedProductsAsync_ReturnsEmptyWhenNoObservationsExist()
- {
- var products = await _sut.GetHouseholdPricedProductsAsync(Guid.NewGuid());
-
- Assert.Empty(products);
- }
-
- [Fact]
- public async Task GetHouseholdPricedProductsAsync_GroupsByProductAndSortsByName()
- {
- var householdId = Guid.NewGuid();
- var milk = new Product("Milk", ProductSourceType.Manual, Now);
- var bread = new Product("Bread", ProductSourceType.Manual, Now);
- _db.Products.Add(milk);
- _db.Products.Add(bread);
- await _db.SaveChangesAsync();
-
- await _sut.RecordObservationAsync(milk.ProductId, 3.00m, Now.AddDays(-1), PriceObservationSourceType.UserEntered, 1m, householdId: householdId);
- await _sut.RecordObservationAsync(milk.ProductId, 4.00m, Now, PriceObservationSourceType.UserEntered, 1m, householdId: householdId);
- await _sut.RecordObservationAsync(bread.ProductId, 2.50m, Now, PriceObservationSourceType.UserEntered, 1m, householdId: householdId);
-
- var products = await _sut.GetHouseholdPricedProductsAsync(householdId);
-
- Assert.Equal(2, products.Count);
- Assert.Equal("Bread", products[0].ProductName);
- Assert.Equal("Milk", products[1].ProductName);
- Assert.Equal(2, products[1].ObservationCount);
- Assert.Equal(4.00m, products[1].LatestPrice);
- Assert.Equal(3.50m, products[1].AveragePrice);
- Assert.Equal(3.00m, products[1].LowestPrice);
- }
-
- [Fact]
- public async Task GetHouseholdPricedProductsAsync_DoesNotMixDifferentHouseholds()
- {
- var milk = new Product("Milk", ProductSourceType.Manual, Now);
- _db.Products.Add(milk);
- await _db.SaveChangesAsync();
-
- await _sut.RecordObservationAsync(milk.ProductId, 3.00m, Now, PriceObservationSourceType.UserEntered, 1m, householdId: Guid.NewGuid());
-
- var products = await _sut.GetHouseholdPricedProductsAsync(Guid.NewGuid());
-
- Assert.Empty(products);
- }
- }
|