using FluentAssertions; using Microsoft.Extensions.Time.Testing; using NSubstitute; using SIGCM2.Application.Abstractions.Persistence; using SIGCM2.Application.Rubros.GetById; using SIGCM2.Domain.Entities; using SIGCM2.Domain.Exceptions; namespace SIGCM2.Application.Tests.Rubros.GetById; public class GetRubroByIdQueryHandlerTests { private static readonly FakeTimeProvider FakeTime = new(new DateTimeOffset(2026, 4, 18, 12, 0, 0, TimeSpan.Zero)); private readonly IRubroRepository _repo = Substitute.For(); private static DateTime UtcNow => FakeTime.GetUtcNow().UtcDateTime; private static Rubro MakeRubro( int id, int? parentId = null, bool activo = true, int? tarifarioBaseId = null, DateTime? fechaModificacion = null) => new(id, parentId, $"Rubro{id}", orden: 1, activo, tarifarioBaseId, fechaCreacion: UtcNow, fechaModificacion); // ── Handle_RubroExiste_ActivoPorDefecto_RetornaDto ─────────────────────── [Fact] public async Task Handle_RubroExiste_ActivoPorDefecto_RetornaDto() { var rubro = MakeRubro(id: 5, parentId: 2, tarifarioBaseId: 10); _repo.GetByIdAsync(5, Arg.Any()).Returns(rubro); var handler = new GetRubroByIdQueryHandler(_repo); var result = await handler.Handle(new GetRubroByIdQuery(Id: 5)); result.Should().NotBeNull(); result.Id.Should().Be(5); result.Nombre.Should().Be("Rubro5"); result.ParentId.Should().Be(2); result.Orden.Should().Be(1); result.Activo.Should().BeTrue(); result.TarifarioBaseId.Should().Be(10); result.FechaCreacion.Should().Be(UtcNow); result.FechaModificacion.Should().BeNull(); } // ── Handle_RubroNoExiste_LanzaRubroNotFoundException ──────────────────── [Fact] public async Task Handle_RubroNoExiste_LanzaRubroNotFoundException() { _repo.GetByIdAsync(99, Arg.Any()).Returns((Rubro?)null); var handler = new GetRubroByIdQueryHandler(_repo); var act = () => handler.Handle(new GetRubroByIdQuery(Id: 99)); await act.Should().ThrowAsync() .WithMessage("*99*"); } // ── Handle_RubroNotFoundException_ContienIdCorrecto ───────────────────── [Fact] public async Task Handle_RubroNotFoundException_ContieneIdCorrecto() { _repo.GetByIdAsync(42, Arg.Any()).Returns((Rubro?)null); var handler = new GetRubroByIdQueryHandler(_repo); var ex = await Assert.ThrowsAsync( () => handler.Handle(new GetRubroByIdQuery(Id: 42))); ex.Id.Should().Be(42); } // ── Handle_CancellationToken_SePropagaAlRepo ───────────────────────────── [Fact] public async Task Handle_CancellationToken_SePropagaAlRepo() { // The handler signature accepts CancellationToken and forwards it to the repo. // We verify the repo is called exactly once with the matching id and any token. _repo.GetByIdAsync(7, Arg.Any()).Returns(MakeRubro(7)); var handler = new GetRubroByIdQueryHandler(_repo); await handler.Handle(new GetRubroByIdQuery(Id: 7)); await _repo.Received(1).GetByIdAsync(7, Arg.Any()); } // ── Handle_FechaCreacion_SeSerializaComoInstantUTC ─────────────────────── [Fact] public async Task Handle_FechaCreacion_SeSerializaComoInstantUTC() { var expectedUtc = new DateTime(2026, 1, 15, 8, 30, 0, DateTimeKind.Utc); var rubro = new Rubro(3, null, "RubroUTC", 0, true, null, expectedUtc, null); _repo.GetByIdAsync(3, Arg.Any()).Returns(rubro); var handler = new GetRubroByIdQueryHandler(_repo); var result = await handler.Handle(new GetRubroByIdQuery(Id: 3)); result.FechaCreacion.Should().Be(expectedUtc); result.FechaCreacion.Kind.Should().Be(DateTimeKind.Utc); } // ── Handle_FechaModificacion_Null_SeRetornaNull ────────────────────────── [Fact] public async Task Handle_FechaModificacion_Null_SeRetornaNull() { var rubro = MakeRubro(id: 8, fechaModificacion: null); _repo.GetByIdAsync(8, Arg.Any()).Returns(rubro); var handler = new GetRubroByIdQueryHandler(_repo); var result = await handler.Handle(new GetRubroByIdQuery(Id: 8)); result.FechaModificacion.Should().BeNull(); } // ── Handle_FechaModificacion_ConValor_SeRetornaCorrecto ────────────────── [Fact] public async Task Handle_FechaModificacion_ConValor_SeRetornaCorrecto() { var fechaMod = new DateTime(2026, 3, 10, 10, 0, 0, DateTimeKind.Utc); var rubro = MakeRubro(id: 9, fechaModificacion: fechaMod); _repo.GetByIdAsync(9, Arg.Any()).Returns(rubro); var handler = new GetRubroByIdQueryHandler(_repo); var result = await handler.Handle(new GetRubroByIdQuery(Id: 9)); result.FechaModificacion.Should().Be(fechaMod); } // ── Handle_RubroRaiz_SinParent_RetornaDtoConParentIdNull ───────────────── [Fact] public async Task Handle_RubroRaiz_SinParent_RetornaDtoConParentIdNull() { var rubro = MakeRubro(id: 1, parentId: null); _repo.GetByIdAsync(1, Arg.Any()).Returns(rubro); var handler = new GetRubroByIdQueryHandler(_repo); var result = await handler.Handle(new GetRubroByIdQuery(Id: 1)); result.ParentId.Should().BeNull(); } // ── Handle_RubroConTarifarioBaseId_SeRetornaCorrecto ──────────────────── [Fact] public async Task Handle_RubroConTarifarioBaseId_SeRetornaCorrecto() { var rubro = MakeRubro(id: 11, tarifarioBaseId: 42); _repo.GetByIdAsync(11, Arg.Any()).Returns(rubro); var handler = new GetRubroByIdQueryHandler(_repo); var result = await handler.Handle(new GetRubroByIdQuery(Id: 11)); result.TarifarioBaseId.Should().Be(42); } }