Part A — MedioId → ProductTypeId rename across all C# layers:
Domain, Application, Infrastructure, API, all test projects.
Solution was non-compilable after BD refactor (5c1675e); now compiles clean (0 errors).
Part B — PATCH /api/v1/admin/chargeable-chars/{id}/reactivate:
ReactivateChargeableCharConfigCommand/Handler, SP guard maps 50410/50411/50412
→ ChargeableCharConfigReactivationNotAllowedException(Reason) → HTTP 409.
Part C — DELETE /api/v1/admin/chargeable-chars/{id}:
DeleteChargeableCharConfigCommand/Handler, physical DELETE on SYSTEM_VERSIONED table.
KeyNotFoundException → 404 via ExceptionFilter.
Tests: +30 unit tests (TDD RED→GREEN). All 1266 unit tests pass.
146 lines
5.3 KiB
C#
146 lines
5.3 KiB
C#
using FluentValidation.TestHelper;
|
|
using Microsoft.Extensions.Time.Testing;
|
|
using SIGCM2.Application.Pricing.ChargeableChars.Create;
|
|
using SIGCM2.Domain.Pricing.ChargeableChars;
|
|
|
|
namespace SIGCM2.Application.Tests.Pricing.ChargeableChars;
|
|
|
|
/// <summary>
|
|
/// PRC-001 — Validator tests for CreateChargeableCharConfigCommand.
|
|
/// Covers: Symbol length, Category enum, PricePerUnit > 0, ValidFrom >= today_AR.
|
|
/// </summary>
|
|
public class CreateChargeableCharConfigCommandValidatorTests
|
|
{
|
|
private readonly FakeTimeProvider _time = new(new DateTimeOffset(2026, 4, 20, 12, 0, 0, TimeSpan.Zero));
|
|
private readonly CreateChargeableCharConfigCommandValidator _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 CreateChargeableCharConfigCommandValidatorTests()
|
|
{
|
|
_validator = new CreateChargeableCharConfigCommandValidator(_time);
|
|
}
|
|
|
|
private static CreateChargeableCharConfigCommand ValidCmd() => new(
|
|
ProductTypeId: null,
|
|
Symbol: "$",
|
|
Category: ChargeableCharCategories.Currency,
|
|
PricePerUnit: 1.0m,
|
|
ValidFrom: Today);
|
|
|
|
// ── Symbol ───────────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Symbol_Empty_FailsValidation()
|
|
{
|
|
var cmd = ValidCmd() with { Symbol = "" };
|
|
_validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Symbol);
|
|
}
|
|
|
|
[Fact]
|
|
public void Symbol_TooLong_FailsValidation()
|
|
{
|
|
// max 4 chars
|
|
var cmd = ValidCmd() with { Symbol = "ABCDE" };
|
|
_validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Symbol);
|
|
}
|
|
|
|
[Fact]
|
|
public void Symbol_SingleChar_Passes()
|
|
{
|
|
var cmd = ValidCmd() with { Symbol = "$" };
|
|
_validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.Symbol);
|
|
}
|
|
|
|
[Fact]
|
|
public void Symbol_FourChars_Passes()
|
|
{
|
|
var cmd = ValidCmd() with { Symbol = "ABCD" };
|
|
_validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.Symbol);
|
|
}
|
|
|
|
// ── Category ─────────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Category_Invalid_FailsValidation()
|
|
{
|
|
var cmd = ValidCmd() with { Category = "Unknown" };
|
|
_validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Category);
|
|
}
|
|
|
|
[Fact]
|
|
public void Category_Empty_FailsValidation()
|
|
{
|
|
var cmd = ValidCmd() with { Category = "" };
|
|
_validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Category);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("Currency")]
|
|
[InlineData("Percentage")]
|
|
[InlineData("Exclamation")]
|
|
[InlineData("Question")]
|
|
[InlineData("Other")]
|
|
public void Category_ValidValues_Pass(string category)
|
|
{
|
|
var cmd = ValidCmd() with { Category = category };
|
|
_validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.Category);
|
|
}
|
|
|
|
// ── 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()
|
|
{
|
|
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);
|
|
}
|
|
|
|
// ── Happy path ────────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void ValidCommand_PassesAllRules()
|
|
{
|
|
_validator.TestValidate(ValidCmd()).ShouldNotHaveAnyValidationErrors();
|
|
}
|
|
}
|