using FluentAssertions; using NSubstitute; using SIGCM2.Application.Abstractions.Persistence; using SIGCM2.Application.Products.Pricing; using SIGCM2.Domain.Entities; namespace SIGCM2.Application.Tests.Products.Pricing; /// /// PRD-003 — ProductPricingService tests. /// Covers: GetPriceAtAsync happy path, fecha sin precio → null (OQ-B contrato). /// public class ProductPricingServiceTests { private readonly IProductPriceRepository _repo = Substitute.For(); private readonly ProductPricingService _service; private static readonly DateOnly QueryDate = new(2026, 4, 19); private static ProductPrice ActivePrice(decimal price = 150m) => new(Id: 1, ProductId: 1, Price: price, PriceValidFrom: new DateOnly(2026, 1, 1), PriceValidTo: null, FechaCreacion: new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc)); public ProductPricingServiceTests() { _service = new ProductPricingService(_repo); } // ── Happy path ───────────────────────────────────────────────────────────── [Fact] public async Task GetPriceAtAsync_ActivePriceCoverDate_ReturnsPrice() { _repo.GetActiveAsync(1, QueryDate, Arg.Any()) .Returns(ActivePrice(200m)); var result = await _service.GetPriceAtAsync(1, QueryDate); result.Should().Be(200m); } [Fact] public async Task GetPriceAtAsync_NoPriceForDate_ReturnsNull() { // OQ-B — si no hay historial para la fecha, retorna null. // El consumidor (PRC-001) decide si usar BasePrice o lanzar excepción. _repo.GetActiveAsync(1, QueryDate, Arg.Any()) .Returns((ProductPrice?)null); var result = await _service.GetPriceAtAsync(1, QueryDate); result.Should().BeNull(); } [Fact] public async Task GetPriceAtAsync_CallsGetActiveAsync_WithCorrectArgs() { _repo.GetActiveAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns((ProductPrice?)null); await _service.GetPriceAtAsync(productId: 5, date: QueryDate); await _repo.Received(1).GetActiveAsync(5, QueryDate, Arg.Any()); } [Fact] public async Task GetPriceAtAsync_FutureDateOutsideWindow_ReturnsNull() { // Precio cerrado cuya ventana no cubre la fecha consultada → repo devuelve null var futureDate = new DateOnly(2030, 1, 1); _repo.GetActiveAsync(1, futureDate, Arg.Any()) .Returns((ProductPrice?)null); var result = await _service.GetPriceAtAsync(1, futureDate); result.Should().BeNull(); } }