feat(domain): WordCounterService + WordCountResult + ChargeableCharConfig entity + exceptions (PRC-001)
- WordCounterService: pure domain service, 7-step algorithm (null/empty fast path → length check → emoji detection via Rune.EnumerateRunes → count specials before replace → replace specials+hyphens → collapse whitespace → tokenize)
- WordCountResult: sealed record with TotalWords + IReadOnlyDictionary<string,int> SpecialCharCounts
- 4 domain exceptions extending DomainException: EmojiDetectedException, WordCountValidationException, ChargeableCharConfigInvalidException, ChargeableCharConfigForwardOnlyException
- ChargeableCharConfig: rich entity with Create factory (invariants), Rehydrate reconstructor, ScheduleNewPrice (forward-only, returns new entity), Deactivate (idempotent)
- ChargeableCharCategories: enum-as-string constants (Currency, Percentage, Exclamation, Question, Other)
- DomainTimeProviderExtensions: internal GetArgentinaToday helper (mirrors Application.Common without creating Domain→Application dependency)
- 60 new tests: 25 golden cases all GREEN, 12 entity invariant tests, 12 exception tests, 5 WordCountResult tests, 6 ChargeableCharConfig entity tests
2026-04-20 12:13:06 -03:00
|
|
|
using FluentAssertions;
|
|
|
|
|
using SIGCM2.Domain.Exceptions;
|
|
|
|
|
using SIGCM2.Domain.Pricing.Exceptions;
|
|
|
|
|
|
|
|
|
|
namespace SIGCM2.Application.Tests.Domain.Pricing.Exceptions;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// PRC-001 — T2.1 Domain unit tests for Pricing exceptions.
|
|
|
|
|
/// Verifies constructor props, message content, and DomainException inheritance.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public sealed class PricingExceptionTests
|
|
|
|
|
{
|
|
|
|
|
// ── EmojiDetectedException ────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void EmojiDetectedException_SetsDetectedCodepoint()
|
|
|
|
|
{
|
|
|
|
|
var ex = new EmojiDetectedException(0x1F697); // 🚗
|
|
|
|
|
|
|
|
|
|
ex.DetectedCodepoint.Should().Be(0x1F697);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void EmojiDetectedException_MessageContainsCodepoint()
|
|
|
|
|
{
|
|
|
|
|
var ex = new EmojiDetectedException(0x1F697);
|
|
|
|
|
|
|
|
|
|
ex.Message.Should().NotBeNullOrWhiteSpace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void EmojiDetectedException_InheritsFromDomainException()
|
|
|
|
|
{
|
|
|
|
|
var ex = new EmojiDetectedException(0x1F697);
|
|
|
|
|
|
|
|
|
|
ex.Should().BeAssignableTo<DomainException>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── WordCountValidationException ─────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void WordCountValidationException_SetsFieldAndReason()
|
|
|
|
|
{
|
|
|
|
|
var ex = new WordCountValidationException("rawText", "El texto supera el máximo de 2000 caracteres.");
|
|
|
|
|
|
|
|
|
|
ex.Field.Should().Be("rawText");
|
|
|
|
|
ex.Reason.Should().Be("El texto supera el máximo de 2000 caracteres.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void WordCountValidationException_MessageContainsFieldAndReason()
|
|
|
|
|
{
|
|
|
|
|
var ex = new WordCountValidationException("rawText", "supera el máximo");
|
|
|
|
|
|
|
|
|
|
ex.Message.Should().Contain("rawText");
|
|
|
|
|
ex.Message.Should().Contain("supera el máximo");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void WordCountValidationException_InheritsFromDomainException()
|
|
|
|
|
{
|
|
|
|
|
var ex = new WordCountValidationException("field", "reason");
|
|
|
|
|
|
|
|
|
|
ex.Should().BeAssignableTo<DomainException>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── ChargeableCharConfigInvalidException ─────────────────────────────────
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void ChargeableCharConfigInvalidException_SetsFieldAndReason()
|
|
|
|
|
{
|
|
|
|
|
var ex = new ChargeableCharConfigInvalidException("PricePerUnit", "debe ser > 0");
|
|
|
|
|
|
|
|
|
|
ex.Field.Should().Be("PricePerUnit");
|
|
|
|
|
ex.Reason.Should().Be("debe ser > 0");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void ChargeableCharConfigInvalidException_MessageContainsFieldAndReason()
|
|
|
|
|
{
|
|
|
|
|
var ex = new ChargeableCharConfigInvalidException("Symbol", "no puede estar vacío");
|
|
|
|
|
|
|
|
|
|
ex.Message.Should().Contain("Symbol");
|
|
|
|
|
ex.Message.Should().Contain("no puede estar vacío");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void ChargeableCharConfigInvalidException_InheritsFromDomainException()
|
|
|
|
|
{
|
|
|
|
|
var ex = new ChargeableCharConfigInvalidException("field", "reason");
|
|
|
|
|
|
|
|
|
|
ex.Should().BeAssignableTo<DomainException>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── ChargeableCharConfigForwardOnlyException ──────────────────────────────
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void ChargeableCharConfigForwardOnlyException_SetsAllProperties()
|
|
|
|
|
{
|
|
|
|
|
var newVf = new DateOnly(2026, 3, 1);
|
|
|
|
|
var activeVf = new DateOnly(2026, 4, 1);
|
|
|
|
|
|
|
|
|
|
var ex = new ChargeableCharConfigForwardOnlyException(
|
fix(backend+tests): reactivate endpoint 500 + test schema mismatches (PRC-001)
Three bugs surfaced while user smoke-testing Reactivate:
1. ReactivateAsync opened a SECOND connection for GetByIdAsync after the SP
call, inside the ambient TransactionScope. This promoted the tx to DTC
(distributed) which requires MSDTC — typically not enabled on dev/prod
servers. The API returned an opaque 500. Fix: run the post-SP SELECT on
the SAME connection (local tx stays lightweight / LTM).
2. Agent 1's V023 test refactor wrote 'INSERT INTO dbo.ProductType (Nombre,
Codigo, Activo)' in 2 test files — but dbo.ProductType has no 'Codigo' or
'Activo' columns (schema is Nombre + IsActive + flags + multimedia limits).
Fix: use '(Nombre, HasDuration, RequiresText, RequiresCategory, IsBundle,
AllowImages)' matching the other test files (ProductQueryRepositoryTests,
ProductRepositoryTests, ProductPriceRepositoryIntegrationTests).
3. SqlTestFixture.EnsureV021SchemaAsync unconditionally ALTERed the V021-era
SPs with '@MedioId' body. On second fixture run after V023 had already
refactored the table, ALTER PROCEDURE body referenced a MedioId column
that no longer existed — 'Invalid column name MedioId'. Fix: guard the
V021 SP ALTERs + seedV022 behind 'MedioId column exists' check. If V023
already dropped MedioId, skip V021 re-install; EnsureV023SchemaAsync
still recreates SPs with @ProductTypeId.
4. PricingExceptionTests still used 'medioId:' named-arg + '.MedioId' — Agent 2
renamed the exception property but not these 6 test references.
Tests: 1297/1297 Application.Tests green.
2026-04-21 11:32:23 -03:00
|
|
|
productTypeId: 5, symbol: "$", newValidFrom: newVf, activeValidFrom: activeVf);
|
feat(domain): WordCounterService + WordCountResult + ChargeableCharConfig entity + exceptions (PRC-001)
- WordCounterService: pure domain service, 7-step algorithm (null/empty fast path → length check → emoji detection via Rune.EnumerateRunes → count specials before replace → replace specials+hyphens → collapse whitespace → tokenize)
- WordCountResult: sealed record with TotalWords + IReadOnlyDictionary<string,int> SpecialCharCounts
- 4 domain exceptions extending DomainException: EmojiDetectedException, WordCountValidationException, ChargeableCharConfigInvalidException, ChargeableCharConfigForwardOnlyException
- ChargeableCharConfig: rich entity with Create factory (invariants), Rehydrate reconstructor, ScheduleNewPrice (forward-only, returns new entity), Deactivate (idempotent)
- ChargeableCharCategories: enum-as-string constants (Currency, Percentage, Exclamation, Question, Other)
- DomainTimeProviderExtensions: internal GetArgentinaToday helper (mirrors Application.Common without creating Domain→Application dependency)
- 60 new tests: 25 golden cases all GREEN, 12 entity invariant tests, 12 exception tests, 5 WordCountResult tests, 6 ChargeableCharConfig entity tests
2026-04-20 12:13:06 -03:00
|
|
|
|
fix(backend+tests): reactivate endpoint 500 + test schema mismatches (PRC-001)
Three bugs surfaced while user smoke-testing Reactivate:
1. ReactivateAsync opened a SECOND connection for GetByIdAsync after the SP
call, inside the ambient TransactionScope. This promoted the tx to DTC
(distributed) which requires MSDTC — typically not enabled on dev/prod
servers. The API returned an opaque 500. Fix: run the post-SP SELECT on
the SAME connection (local tx stays lightweight / LTM).
2. Agent 1's V023 test refactor wrote 'INSERT INTO dbo.ProductType (Nombre,
Codigo, Activo)' in 2 test files — but dbo.ProductType has no 'Codigo' or
'Activo' columns (schema is Nombre + IsActive + flags + multimedia limits).
Fix: use '(Nombre, HasDuration, RequiresText, RequiresCategory, IsBundle,
AllowImages)' matching the other test files (ProductQueryRepositoryTests,
ProductRepositoryTests, ProductPriceRepositoryIntegrationTests).
3. SqlTestFixture.EnsureV021SchemaAsync unconditionally ALTERed the V021-era
SPs with '@MedioId' body. On second fixture run after V023 had already
refactored the table, ALTER PROCEDURE body referenced a MedioId column
that no longer existed — 'Invalid column name MedioId'. Fix: guard the
V021 SP ALTERs + seedV022 behind 'MedioId column exists' check. If V023
already dropped MedioId, skip V021 re-install; EnsureV023SchemaAsync
still recreates SPs with @ProductTypeId.
4. PricingExceptionTests still used 'medioId:' named-arg + '.MedioId' — Agent 2
renamed the exception property but not these 6 test references.
Tests: 1297/1297 Application.Tests green.
2026-04-21 11:32:23 -03:00
|
|
|
ex.ProductTypeId.Should().Be(5);
|
feat(domain): WordCounterService + WordCountResult + ChargeableCharConfig entity + exceptions (PRC-001)
- WordCounterService: pure domain service, 7-step algorithm (null/empty fast path → length check → emoji detection via Rune.EnumerateRunes → count specials before replace → replace specials+hyphens → collapse whitespace → tokenize)
- WordCountResult: sealed record with TotalWords + IReadOnlyDictionary<string,int> SpecialCharCounts
- 4 domain exceptions extending DomainException: EmojiDetectedException, WordCountValidationException, ChargeableCharConfigInvalidException, ChargeableCharConfigForwardOnlyException
- ChargeableCharConfig: rich entity with Create factory (invariants), Rehydrate reconstructor, ScheduleNewPrice (forward-only, returns new entity), Deactivate (idempotent)
- ChargeableCharCategories: enum-as-string constants (Currency, Percentage, Exclamation, Question, Other)
- DomainTimeProviderExtensions: internal GetArgentinaToday helper (mirrors Application.Common without creating Domain→Application dependency)
- 60 new tests: 25 golden cases all GREEN, 12 entity invariant tests, 12 exception tests, 5 WordCountResult tests, 6 ChargeableCharConfig entity tests
2026-04-20 12:13:06 -03:00
|
|
|
ex.Symbol.Should().Be("$");
|
|
|
|
|
ex.NewValidFrom.Should().Be(newVf);
|
|
|
|
|
ex.ActiveValidFrom.Should().Be(activeVf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
fix(backend+tests): reactivate endpoint 500 + test schema mismatches (PRC-001)
Three bugs surfaced while user smoke-testing Reactivate:
1. ReactivateAsync opened a SECOND connection for GetByIdAsync after the SP
call, inside the ambient TransactionScope. This promoted the tx to DTC
(distributed) which requires MSDTC — typically not enabled on dev/prod
servers. The API returned an opaque 500. Fix: run the post-SP SELECT on
the SAME connection (local tx stays lightweight / LTM).
2. Agent 1's V023 test refactor wrote 'INSERT INTO dbo.ProductType (Nombre,
Codigo, Activo)' in 2 test files — but dbo.ProductType has no 'Codigo' or
'Activo' columns (schema is Nombre + IsActive + flags + multimedia limits).
Fix: use '(Nombre, HasDuration, RequiresText, RequiresCategory, IsBundle,
AllowImages)' matching the other test files (ProductQueryRepositoryTests,
ProductRepositoryTests, ProductPriceRepositoryIntegrationTests).
3. SqlTestFixture.EnsureV021SchemaAsync unconditionally ALTERed the V021-era
SPs with '@MedioId' body. On second fixture run after V023 had already
refactored the table, ALTER PROCEDURE body referenced a MedioId column
that no longer existed — 'Invalid column name MedioId'. Fix: guard the
V021 SP ALTERs + seedV022 behind 'MedioId column exists' check. If V023
already dropped MedioId, skip V021 re-install; EnsureV023SchemaAsync
still recreates SPs with @ProductTypeId.
4. PricingExceptionTests still used 'medioId:' named-arg + '.MedioId' — Agent 2
renamed the exception property but not these 6 test references.
Tests: 1297/1297 Application.Tests green.
2026-04-21 11:32:23 -03:00
|
|
|
public void ChargeableCharConfigForwardOnlyException_NullProductTypeId_IsAllowed()
|
feat(domain): WordCounterService + WordCountResult + ChargeableCharConfig entity + exceptions (PRC-001)
- WordCounterService: pure domain service, 7-step algorithm (null/empty fast path → length check → emoji detection via Rune.EnumerateRunes → count specials before replace → replace specials+hyphens → collapse whitespace → tokenize)
- WordCountResult: sealed record with TotalWords + IReadOnlyDictionary<string,int> SpecialCharCounts
- 4 domain exceptions extending DomainException: EmojiDetectedException, WordCountValidationException, ChargeableCharConfigInvalidException, ChargeableCharConfigForwardOnlyException
- ChargeableCharConfig: rich entity with Create factory (invariants), Rehydrate reconstructor, ScheduleNewPrice (forward-only, returns new entity), Deactivate (idempotent)
- ChargeableCharCategories: enum-as-string constants (Currency, Percentage, Exclamation, Question, Other)
- DomainTimeProviderExtensions: internal GetArgentinaToday helper (mirrors Application.Common without creating Domain→Application dependency)
- 60 new tests: 25 golden cases all GREEN, 12 entity invariant tests, 12 exception tests, 5 WordCountResult tests, 6 ChargeableCharConfig entity tests
2026-04-20 12:13:06 -03:00
|
|
|
{
|
|
|
|
|
var ex = new ChargeableCharConfigForwardOnlyException(
|
fix(backend+tests): reactivate endpoint 500 + test schema mismatches (PRC-001)
Three bugs surfaced while user smoke-testing Reactivate:
1. ReactivateAsync opened a SECOND connection for GetByIdAsync after the SP
call, inside the ambient TransactionScope. This promoted the tx to DTC
(distributed) which requires MSDTC — typically not enabled on dev/prod
servers. The API returned an opaque 500. Fix: run the post-SP SELECT on
the SAME connection (local tx stays lightweight / LTM).
2. Agent 1's V023 test refactor wrote 'INSERT INTO dbo.ProductType (Nombre,
Codigo, Activo)' in 2 test files — but dbo.ProductType has no 'Codigo' or
'Activo' columns (schema is Nombre + IsActive + flags + multimedia limits).
Fix: use '(Nombre, HasDuration, RequiresText, RequiresCategory, IsBundle,
AllowImages)' matching the other test files (ProductQueryRepositoryTests,
ProductRepositoryTests, ProductPriceRepositoryIntegrationTests).
3. SqlTestFixture.EnsureV021SchemaAsync unconditionally ALTERed the V021-era
SPs with '@MedioId' body. On second fixture run after V023 had already
refactored the table, ALTER PROCEDURE body referenced a MedioId column
that no longer existed — 'Invalid column name MedioId'. Fix: guard the
V021 SP ALTERs + seedV022 behind 'MedioId column exists' check. If V023
already dropped MedioId, skip V021 re-install; EnsureV023SchemaAsync
still recreates SPs with @ProductTypeId.
4. PricingExceptionTests still used 'medioId:' named-arg + '.MedioId' — Agent 2
renamed the exception property but not these 6 test references.
Tests: 1297/1297 Application.Tests green.
2026-04-21 11:32:23 -03:00
|
|
|
productTypeId: null, symbol: "$",
|
feat(domain): WordCounterService + WordCountResult + ChargeableCharConfig entity + exceptions (PRC-001)
- WordCounterService: pure domain service, 7-step algorithm (null/empty fast path → length check → emoji detection via Rune.EnumerateRunes → count specials before replace → replace specials+hyphens → collapse whitespace → tokenize)
- WordCountResult: sealed record with TotalWords + IReadOnlyDictionary<string,int> SpecialCharCounts
- 4 domain exceptions extending DomainException: EmojiDetectedException, WordCountValidationException, ChargeableCharConfigInvalidException, ChargeableCharConfigForwardOnlyException
- ChargeableCharConfig: rich entity with Create factory (invariants), Rehydrate reconstructor, ScheduleNewPrice (forward-only, returns new entity), Deactivate (idempotent)
- ChargeableCharCategories: enum-as-string constants (Currency, Percentage, Exclamation, Question, Other)
- DomainTimeProviderExtensions: internal GetArgentinaToday helper (mirrors Application.Common without creating Domain→Application dependency)
- 60 new tests: 25 golden cases all GREEN, 12 entity invariant tests, 12 exception tests, 5 WordCountResult tests, 6 ChargeableCharConfig entity tests
2026-04-20 12:13:06 -03:00
|
|
|
newValidFrom: new DateOnly(2026, 3, 1),
|
|
|
|
|
activeValidFrom: new DateOnly(2026, 4, 1));
|
|
|
|
|
|
fix(backend+tests): reactivate endpoint 500 + test schema mismatches (PRC-001)
Three bugs surfaced while user smoke-testing Reactivate:
1. ReactivateAsync opened a SECOND connection for GetByIdAsync after the SP
call, inside the ambient TransactionScope. This promoted the tx to DTC
(distributed) which requires MSDTC — typically not enabled on dev/prod
servers. The API returned an opaque 500. Fix: run the post-SP SELECT on
the SAME connection (local tx stays lightweight / LTM).
2. Agent 1's V023 test refactor wrote 'INSERT INTO dbo.ProductType (Nombre,
Codigo, Activo)' in 2 test files — but dbo.ProductType has no 'Codigo' or
'Activo' columns (schema is Nombre + IsActive + flags + multimedia limits).
Fix: use '(Nombre, HasDuration, RequiresText, RequiresCategory, IsBundle,
AllowImages)' matching the other test files (ProductQueryRepositoryTests,
ProductRepositoryTests, ProductPriceRepositoryIntegrationTests).
3. SqlTestFixture.EnsureV021SchemaAsync unconditionally ALTERed the V021-era
SPs with '@MedioId' body. On second fixture run after V023 had already
refactored the table, ALTER PROCEDURE body referenced a MedioId column
that no longer existed — 'Invalid column name MedioId'. Fix: guard the
V021 SP ALTERs + seedV022 behind 'MedioId column exists' check. If V023
already dropped MedioId, skip V021 re-install; EnsureV023SchemaAsync
still recreates SPs with @ProductTypeId.
4. PricingExceptionTests still used 'medioId:' named-arg + '.MedioId' — Agent 2
renamed the exception property but not these 6 test references.
Tests: 1297/1297 Application.Tests green.
2026-04-21 11:32:23 -03:00
|
|
|
ex.ProductTypeId.Should().BeNull();
|
feat(domain): WordCounterService + WordCountResult + ChargeableCharConfig entity + exceptions (PRC-001)
- WordCounterService: pure domain service, 7-step algorithm (null/empty fast path → length check → emoji detection via Rune.EnumerateRunes → count specials before replace → replace specials+hyphens → collapse whitespace → tokenize)
- WordCountResult: sealed record with TotalWords + IReadOnlyDictionary<string,int> SpecialCharCounts
- 4 domain exceptions extending DomainException: EmojiDetectedException, WordCountValidationException, ChargeableCharConfigInvalidException, ChargeableCharConfigForwardOnlyException
- ChargeableCharConfig: rich entity with Create factory (invariants), Rehydrate reconstructor, ScheduleNewPrice (forward-only, returns new entity), Deactivate (idempotent)
- ChargeableCharCategories: enum-as-string constants (Currency, Percentage, Exclamation, Question, Other)
- DomainTimeProviderExtensions: internal GetArgentinaToday helper (mirrors Application.Common without creating Domain→Application dependency)
- 60 new tests: 25 golden cases all GREEN, 12 entity invariant tests, 12 exception tests, 5 WordCountResult tests, 6 ChargeableCharConfig entity tests
2026-04-20 12:13:06 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void ChargeableCharConfigForwardOnlyException_MessageContainsKeyDates()
|
|
|
|
|
{
|
|
|
|
|
var newVf = new DateOnly(2026, 3, 1);
|
|
|
|
|
var activeVf = new DateOnly(2026, 4, 1);
|
|
|
|
|
|
|
|
|
|
var ex = new ChargeableCharConfigForwardOnlyException(
|
fix(backend+tests): reactivate endpoint 500 + test schema mismatches (PRC-001)
Three bugs surfaced while user smoke-testing Reactivate:
1. ReactivateAsync opened a SECOND connection for GetByIdAsync after the SP
call, inside the ambient TransactionScope. This promoted the tx to DTC
(distributed) which requires MSDTC — typically not enabled on dev/prod
servers. The API returned an opaque 500. Fix: run the post-SP SELECT on
the SAME connection (local tx stays lightweight / LTM).
2. Agent 1's V023 test refactor wrote 'INSERT INTO dbo.ProductType (Nombre,
Codigo, Activo)' in 2 test files — but dbo.ProductType has no 'Codigo' or
'Activo' columns (schema is Nombre + IsActive + flags + multimedia limits).
Fix: use '(Nombre, HasDuration, RequiresText, RequiresCategory, IsBundle,
AllowImages)' matching the other test files (ProductQueryRepositoryTests,
ProductRepositoryTests, ProductPriceRepositoryIntegrationTests).
3. SqlTestFixture.EnsureV021SchemaAsync unconditionally ALTERed the V021-era
SPs with '@MedioId' body. On second fixture run after V023 had already
refactored the table, ALTER PROCEDURE body referenced a MedioId column
that no longer existed — 'Invalid column name MedioId'. Fix: guard the
V021 SP ALTERs + seedV022 behind 'MedioId column exists' check. If V023
already dropped MedioId, skip V021 re-install; EnsureV023SchemaAsync
still recreates SPs with @ProductTypeId.
4. PricingExceptionTests still used 'medioId:' named-arg + '.MedioId' — Agent 2
renamed the exception property but not these 6 test references.
Tests: 1297/1297 Application.Tests green.
2026-04-21 11:32:23 -03:00
|
|
|
productTypeId: null, symbol: "$", newValidFrom: newVf, activeValidFrom: activeVf);
|
feat(domain): WordCounterService + WordCountResult + ChargeableCharConfig entity + exceptions (PRC-001)
- WordCounterService: pure domain service, 7-step algorithm (null/empty fast path → length check → emoji detection via Rune.EnumerateRunes → count specials before replace → replace specials+hyphens → collapse whitespace → tokenize)
- WordCountResult: sealed record with TotalWords + IReadOnlyDictionary<string,int> SpecialCharCounts
- 4 domain exceptions extending DomainException: EmojiDetectedException, WordCountValidationException, ChargeableCharConfigInvalidException, ChargeableCharConfigForwardOnlyException
- ChargeableCharConfig: rich entity with Create factory (invariants), Rehydrate reconstructor, ScheduleNewPrice (forward-only, returns new entity), Deactivate (idempotent)
- ChargeableCharCategories: enum-as-string constants (Currency, Percentage, Exclamation, Question, Other)
- DomainTimeProviderExtensions: internal GetArgentinaToday helper (mirrors Application.Common without creating Domain→Application dependency)
- 60 new tests: 25 golden cases all GREEN, 12 entity invariant tests, 12 exception tests, 5 WordCountResult tests, 6 ChargeableCharConfig entity tests
2026-04-20 12:13:06 -03:00
|
|
|
|
|
|
|
|
ex.Message.Should().Contain("2026-03-01");
|
|
|
|
|
ex.Message.Should().Contain("2026-04-01");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void ChargeableCharConfigForwardOnlyException_InheritsFromDomainException()
|
|
|
|
|
{
|
|
|
|
|
var ex = new ChargeableCharConfigForwardOnlyException(
|
fix(backend+tests): reactivate endpoint 500 + test schema mismatches (PRC-001)
Three bugs surfaced while user smoke-testing Reactivate:
1. ReactivateAsync opened a SECOND connection for GetByIdAsync after the SP
call, inside the ambient TransactionScope. This promoted the tx to DTC
(distributed) which requires MSDTC — typically not enabled on dev/prod
servers. The API returned an opaque 500. Fix: run the post-SP SELECT on
the SAME connection (local tx stays lightweight / LTM).
2. Agent 1's V023 test refactor wrote 'INSERT INTO dbo.ProductType (Nombre,
Codigo, Activo)' in 2 test files — but dbo.ProductType has no 'Codigo' or
'Activo' columns (schema is Nombre + IsActive + flags + multimedia limits).
Fix: use '(Nombre, HasDuration, RequiresText, RequiresCategory, IsBundle,
AllowImages)' matching the other test files (ProductQueryRepositoryTests,
ProductRepositoryTests, ProductPriceRepositoryIntegrationTests).
3. SqlTestFixture.EnsureV021SchemaAsync unconditionally ALTERed the V021-era
SPs with '@MedioId' body. On second fixture run after V023 had already
refactored the table, ALTER PROCEDURE body referenced a MedioId column
that no longer existed — 'Invalid column name MedioId'. Fix: guard the
V021 SP ALTERs + seedV022 behind 'MedioId column exists' check. If V023
already dropped MedioId, skip V021 re-install; EnsureV023SchemaAsync
still recreates SPs with @ProductTypeId.
4. PricingExceptionTests still used 'medioId:' named-arg + '.MedioId' — Agent 2
renamed the exception property but not these 6 test references.
Tests: 1297/1297 Application.Tests green.
2026-04-21 11:32:23 -03:00
|
|
|
productTypeId: null, symbol: "$",
|
feat(domain): WordCounterService + WordCountResult + ChargeableCharConfig entity + exceptions (PRC-001)
- WordCounterService: pure domain service, 7-step algorithm (null/empty fast path → length check → emoji detection via Rune.EnumerateRunes → count specials before replace → replace specials+hyphens → collapse whitespace → tokenize)
- WordCountResult: sealed record with TotalWords + IReadOnlyDictionary<string,int> SpecialCharCounts
- 4 domain exceptions extending DomainException: EmojiDetectedException, WordCountValidationException, ChargeableCharConfigInvalidException, ChargeableCharConfigForwardOnlyException
- ChargeableCharConfig: rich entity with Create factory (invariants), Rehydrate reconstructor, ScheduleNewPrice (forward-only, returns new entity), Deactivate (idempotent)
- ChargeableCharCategories: enum-as-string constants (Currency, Percentage, Exclamation, Question, Other)
- DomainTimeProviderExtensions: internal GetArgentinaToday helper (mirrors Application.Common without creating Domain→Application dependency)
- 60 new tests: 25 golden cases all GREEN, 12 entity invariant tests, 12 exception tests, 5 WordCountResult tests, 6 ChargeableCharConfig entity tests
2026-04-20 12:13:06 -03:00
|
|
|
newValidFrom: new DateOnly(2026, 3, 1),
|
|
|
|
|
activeValidFrom: new DateOnly(2026, 4, 1));
|
|
|
|
|
|
|
|
|
|
ex.Should().BeAssignableTo<DomainException>();
|
|
|
|
|
}
|
|
|
|
|
}
|