Files
SIG-CM2.0/tests/SIGCM2.Application.Tests/Pricing/ChargeableChars/SchedulePriceChangeCommandValidatorTests.cs

102 lines
3.9 KiB
C#

using FluentValidation.TestHelper;
using Microsoft.Extensions.Time.Testing;
using SIGCM2.Application.Pricing.ChargeableChars.SchedulePrice;
namespace SIGCM2.Application.Tests.Pricing.ChargeableChars;
/// <summary>
/// PRC-001 — Validator tests for SchedulePriceChangeCommand.
/// Covers: PricePerUnit > 0, ValidFrom >= today_AR.
/// Forward-only check (ValidFrom > existing.ValidFrom) is done in handler, not validator.
/// </summary>
public class SchedulePriceChangeCommandValidatorTests
{
private readonly FakeTimeProvider _time = new(new DateTimeOffset(2026, 4, 20, 12, 0, 0, TimeSpan.Zero));
private readonly SchedulePriceChangeCommandValidator _validator;
private static readonly DateOnly Today = new(2026, 4, 20);
private static readonly DateOnly Yesterday = new(2026, 4, 19);
private static readonly DateOnly Tomorrow = new(2026, 4, 21);
public SchedulePriceChangeCommandValidatorTests()
{
_validator = new SchedulePriceChangeCommandValidator(_time);
}
private static SchedulePriceChangeCommand ValidCmd() => new(
Id: 1L,
PricePerUnit: 2.0m,
ValidFrom: Tomorrow);
// ── PricePerUnit ─────────────────────────────────────────────────────────────
[Fact]
public void PricePerUnit_Zero_FailsValidation()
{
var cmd = ValidCmd() with { PricePerUnit = 0m };
_validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.PricePerUnit);
}
[Fact]
public void PricePerUnit_Negative_FailsValidation()
{
var cmd = ValidCmd() with { PricePerUnit = -1m };
_validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.PricePerUnit);
}
[Fact]
public void PricePerUnit_Positive_Passes()
{
var cmd = ValidCmd() with { PricePerUnit = 0.01m };
_validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.PricePerUnit);
}
// ── ValidFrom ────────────────────────────────────────────────────────────────
[Fact]
public void ValidFrom_InPast_FailsValidation()
{
var cmd = ValidCmd() with { ValidFrom = Yesterday };
_validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.ValidFrom);
}
[Fact]
public void ValidFrom_Today_Passes()
{
// today is valid — forward-only check vs existing row is done in handler
var cmd = ValidCmd() with { ValidFrom = Today };
_validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.ValidFrom);
}
[Fact]
public void ValidFrom_Future_Passes()
{
var cmd = ValidCmd() with { ValidFrom = Tomorrow };
_validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.ValidFrom);
}
// ── Id ───────────────────────────────────────────────────────────────────────
[Fact]
public void Id_Zero_FailsValidation()
{
var cmd = ValidCmd() with { Id = 0L };
_validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Id);
}
[Fact]
public void Id_Positive_Passes()
{
var cmd = ValidCmd() with { Id = 1L };
_validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.Id);
}
// ── Happy path ────────────────────────────────────────────────────────────────
[Fact]
public void ValidCommand_PassesAllRules()
{
_validator.TestValidate(ValidCmd()).ShouldNotHaveAnyValidationErrors();
}
}