8fc7b363d5
feat(api): ChargeableCharConfigController + DI + ExceptionFilter integration (PRC-001)
2026-04-20 12:46:07 -03:00
3b1edfd696
feat(infrastructure): ChargeableCharConfigRepository Dapper + SP invocation (PRC-001)
...
- ChargeableCharConfigRepository implements IChargeableCharConfigRepository via Dapper
- InsertWithCloseAsync calls usp_ChargeableCharConfig_InsertWithClose with OUTPUT params;
maps SqlException 50409 → ChargeableCharConfigForwardOnlyException, 50404 → ChargeableCharConfigInvalidException
- GetActiveForMedioAsync calls usp_ChargeableCharConfig_GetActiveForMedio; returns all rows
(global + per-medio) — Application service handles priority resolution
- ListAsync / CountAsync use parameterized SQL with OFFSET/FETCH and NULL-aware MedioId filter
- GetByIdAsync / DeactivateAsync cover single-entity read and idempotent deactivation
- DateOnly mapping: DateTime → DateOnly.FromDateTime() pattern, same as ProductPriceRepository
- Registered IChargeableCharConfigRepository → ChargeableCharConfigRepository in DI
- 14 integration tests against SIGCM2_Test_App (all GREEN); 1571/1571 total tests pass
2026-04-20 12:32:17 -03:00
f1b38cd9ce
feat(application): commands/queries + IChargeableCharConfigService (PRC-001)
2026-04-20 12:24:06 -03:00
ded76fcdc7
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
9144c2e89e
feat(bd): V021 crea dbo.ChargeableCharConfig + SPs + índices (PRC-001)
2026-04-20 12:01:49 -03:00
e997409e95
test(integration): pagination edge cases (prd-003-prices-pagination)
2026-04-19 20:01:09 -03:00
0dce3ee4ac
feat(api): pagination on GET product prices ( closes #47 )
...
- GET /api/v1/products/{id}/prices now returns PagedResult<ProductPriceDto>
with OFFSET/FETCH + COUNT via Dapper (two queries on same connection)
- Query params: ?page (default 1) and ?pageSize (default 20, max 100)
- Clamping: Math.Max(1, page) + Math.Clamp(pageSize, 1, 100) in handler
- Auth upgraded from [Authorize] to [RequirePermission("catalogo:productos:gestionar")]
- IProductPriceRepository.GetByProductIdAsync signature updated to paginated form
- AddProductPriceCommandHandler adapted to read back via page=1, pageSize=2
- TDD cycle: RED (tests updated to PagedResult shape) -> GREEN (implementation) -> REFACTOR
- Tests: 1418 total (1106 Application + 312 Api), 0 failures
closes #47
2026-04-19 19:47:18 -03:00
7cabb677f3
test(integration): concurrency + SYSTEM_VERSIONING + e2e extra (PRD-003)
2026-04-19 18:43:11 -03:00
f6f24bc4be
feat(api): ProductPricesController + DI + ExceptionFilter integration (PRD-003)
...
- GET /api/v1/products/{id}/prices [Authorize] → 200 IReadOnlyList<ProductPriceDto>
- POST /api/v1/admin/products/{id}/prices [RequirePermission catalogo:productos:gestionar] → 201 AddProductPriceResponse + Location header
- ExceptionFilter: 3 new cases (ProductPriceForwardOnlyException→409, ProductPriceInvalidException→400, ProductSinPrecioActivoException→404)
- Fix AddProductPriceCommandHandler: move GetByProductIdAsync outside TransactionScope using block to avoid InvalidOperationException (scope already complete)
- 16 e2e tests in ProductPricesControllerTests: 401/403, 200 history ordered DESC, 404 not found, 201 first/second price, 400 validation, 409 forward-only, audit event, DateOnly yyyy-MM-dd roundtrip
- 305 Api.Tests + 1088 Application.Tests = 1393 total, 0 red
2026-04-19 18:26:24 -03:00
2d2e90fa3c
feat(infrastructure): ProductPriceRepository Dapper + SP invocation (PRD-003)
2026-04-19 18:15:30 -03:00
4b0567d252
feat(application): commands/queries + IProductPricingService (PRD-003)
...
- IProductPriceRepository (AddAsync/GetByProductIdAsync/GetActiveAsync)
- ProductPriceDto, AddProductPriceCommand/Response, GetProductPricesQuery
- AddProductPriceCommandValidator (FluentValidation + TimeProvider, fecha >= hoy_AR)
- AddProductPriceCommandHandler (TransactionScope AsyncFlow, audit fail-closed)
- GetProductPricesQueryHandler (verifica producto existe, lista vacía válida)
- IProductPricingService + ProductPricingService (GetPriceAtAsync → decimal?)
- DI wiring en DependencyInjection.cs
- 29 tests NSubstitute + FakeTimeProvider, 1081 Application.Tests GREEN
2026-04-19 18:08:16 -03:00
54b0265994
feat(domain): ProductPrice entity + exceptions (PRD-003)
2026-04-19 17:59:43 -03:00
59f30cddfb
feat(bd): V019 crea dbo.ProductPrices + SP + índices (PRD-003)
2026-04-19 17:53:58 -03:00
50a5118a78
feat(api): ExceptionFilter + e2e 409 para RubroConProductosActivos ( closes #41 )
...
Mapea RubroConProductosActivosException → HTTP 409 con error code
rubro_con_productos_activos. Test e2e usa DI override (patrón issue #36 )
para stub IProductQueryRepository sin sembrar Products reales en DB.
2026-04-19 17:08:42 -03:00
c974e824e0
feat(infrastructure): ProductQueryRepository.CountActiveByRubroAsync + integration test
...
Implementa SELECT COUNT(1) FROM dbo.Product WHERE RubroId = @RubroId AND IsActive = 1.
Tests de integración verifican: 0 sin productos, count correcto con mix
activos/inactivos/otro rubro, y solo inactivos retorna 0.
2026-04-19 17:08:35 -03:00
900fd5e975
feat(application): DeactivateRubroCommandHandler guard contra Products activos
...
Extiende IProductQueryRepository con CountActiveByRubroAsync, inyecta
el repositorio en el handler e intercala el chequeo después del guard
de hijos activos. Tests de unidad cubren: throw, success con 0 productos,
y estabilidad del orden de guardas (hijos primero).
2026-04-19 17:08:30 -03:00
e9d1e3237d
feat(domain): RubroConProductosActivosException + test ( closes #41 )
...
Co-authored-by: fix/issue-41-rubro-deactivation-guard
2026-04-19 17:08:23 -03:00
0e363d1cfc
refactor(tests): TestWebAppFactory.CreateClientWithOverrides para DI override por test ( closes #36 )
...
Agrega helper CreateClientWithOverrides en TestWebAppFactory que envuelve
WithWebHostBuilder+ConfigureTestServices para inyectar stubs por test sin
tocar la fábrica compartida. Usa el patrón para agregar 2 tests e2e:
Deactivate_WhenProductQueryReturnsInUse_Returns409WithErrorCode (PRD-001/PRD-002)
y CreateRubro_WhenParentHasAvisos_Returns409WithErrorCode (CAT-002).
Remueve el comentario TODO PRD-002. 287 Api tests verdes.
2026-04-19 16:59:53 -03:00
d7fb3105fa
feat(bd): V018 crea dbo.Product + SqlTestFixture consolida V018 + permisos catalogo (PRD-002 W6)
2026-04-19 13:46:11 -03:00
b4f17d6961
refactor: eliminar NullProductQueryRepository dead code + EXISTS en ProductQueryRepository (PRD-002 S1 S2)
2026-04-19 13:37:10 -03:00
d262454b28
fix(api): ExceptionFilter 409 para ProductTypeInactivo y RubroInactivo (PRD-002 W1)
2026-04-19 13:31:38 -03:00
a41a4ea341
test(api): guard proof — ProductType deactivation returns 409 when active Products exist (PRD-002)
2026-04-19 13:18:21 -03:00
165abc8245
feat(api): ProductsController + ExceptionFilter Product cases, fix permiso count to 27 (PRD-002)
2026-04-19 13:17:31 -03:00
733ca0e2e2
test(infrastructure): ProductRepository integration tests — roundtrip, update, deactivate history, UQ (PRD-002)
2026-04-19 13:11:21 -03:00
8c9a50504d
feat(infrastructure): ProductRepository + ProductQueryRepository, DI swap activates guard (PRD-002)
2026-04-19 13:10:21 -03:00
bb455be745
feat(application): Product handlers + DI registration, fix permiso count to 27 (PRD-002)
2026-04-19 13:07:59 -03:00
8b555e1f8b
feat(application): Product commands, DTOs, IProductRepository, validators (PRD-002)
2026-04-19 13:02:42 -03:00
16197cf242
feat(domain): Product entity + 5 domain exceptions (PRD-002)
2026-04-19 12:59:58 -03:00
d6ec618ff2
docs(tests): TODO W1 PRD-002 en ProductTypesControllerTests + audit doc ya completo (PRD-001)
...
Agrega comentario TODO antes del bloque DELETE explicando el gap e2e
para 409 IsInUse (bloqueado por RSA singleton, issue #36 , PRD-002).
Auditoría.md ya tenía las entradas producto_tipo.* desde apply anterior.
2026-04-19 12:10:35 -03:00
170789886b
feat(api): ProductTypesController + ExceptionFilter 4 casos PRD-001
...
CRUD endpoints con validación FluentValidation inline; 4 nuevas excepciones mapeadas
en ExceptionFilter; conteos de permisos 25→26 actualizados; 12 e2e tests nuevos.
2026-04-19 09:57:11 -03:00
936d1dc353
feat(infrastructure): ProductTypeRepository Dapper + DI wiring (PRD-001)
...
CRUD + paginado con filtros sobre dbo.ProductType; history temporal verificada en tests.
11 integration tests nuevos, suite total 935 GREEN.
2026-04-19 09:49:08 -03:00
5c8f19bf39
feat(application): CRUD handlers + validators + DI de ProductType (PRD-001)
...
Create/Update/Deactivate handlers con TransactionScope + audit; validators FluentValidation;
DI wiring NullProductQueryRepository + 5 handlers; SqlTestFixture V017 + permiso count 25→26.
2026-04-19 09:46:31 -03:00
3c9e852379
feat(application): IProductTypeRepository + IProductQueryRepository stub + queries (PRD-001)
2026-04-19 09:38:51 -03:00
132d17c99f
feat(domain): ProductType entity + domain exceptions (PRD-001)
2026-04-19 09:36:29 -03:00
bb5dde6e24
feat(api): ExceptionFilter 409 para regla de oro + DTO delta (CAT-002)
2026-04-19 08:31:39 -03:00
f861dfa826
feat(application): RubroTreeBuilder + GetRubroTree con tieneAvisos (CAT-002)
2026-04-19 08:25:13 -03:00
c03aad8c5a
feat(application): guard avisos en MoveRubroCommandHandler (CAT-002)
2026-04-19 08:24:07 -03:00
216983623a
feat(application): guard avisos en CreateRubroCommandHandler (CAT-002)
2026-04-19 08:22:55 -03:00
9e50a929ae
feat(application): RubroTreeBuilder + GetRubroTree con tieneAvisos (CAT-002)
2026-04-19 08:20:36 -03:00
673194e249
feat(application): IAvisoQueryRepository + NullAvisoQueryRepository (CAT-002)
2026-04-19 08:18:56 -03:00
ddd28ea4d5
feat(domain): excepciones regla de oro rama/hoja (CAT-002)
2026-04-19 08:17:45 -03:00
389dda6e5e
fix(tests): consolidar V016 en SqlTestFixture post issue #29
...
Rebase de CAT-001 sobre main (post #29 ) requiere:
- EnsureV016SchemaAsync en SqlTestFixture
- Rubro_History en TablesToIgnore central (el commit original b1be4a5 se skipeo por ser obsoleto post consolidacion)
- catalogo:rubros:gestionar en seed canonical de Permiso + RolPermiso admin
- RubroRepositoryTests refactorizado al patron [Collection] + SqlTestFixture
- RubrosControllerTests apunta a TestConnectionStrings.ApiTestDb
- Counts de permisos admin actualizados 24 -> 25 en 5 tests
Verify: App 819/819 + Api 251/251 + vitest 349/349 verde post-rebase.
2026-04-19 07:49:18 -03:00
022a36a90c
test(application): GetRubroByIdQueryHandlerTests dedicado (CAT-001)
2026-04-19 07:42:55 -03:00
5e2323e0bc
feat(api): RubrosController + integration tests e2e + audit verification (CAT-001)
2026-04-19 07:42:54 -03:00
f8e9d18379
feat(infrastructure): RubroRepository Dapper + DI + integration tests (CAT-001)
2026-04-19 07:42:53 -03:00
d9fc9a2867
feat(application): Rubros commands/queries + RubroTreeBuilder + audit (CAT-001)
2026-04-19 07:42:26 -03:00
dcb2e5ada6
feat(domain): Rubro entity + domain exceptions (CAT-001)
2026-04-19 07:42:26 -03:00
9886524645
Merge pull request 'fix: issue #29 — integration tests flakiness (DB split + SqlTestFixture consolidado)' ( #34 ) from fix/issue-29-flakiness into main
2026-04-19 10:41:27 +00:00
8daadc8a77
fix(tests): timestamp determinístico en QueryAsync_Limit_EmitsCursor
...
DATETIME2(3) + cursor roundtrip via O format perdía sub-ms de
DateTime.UtcNow causando ~37% flake rate. Timestamp fijo con sub-ms=0
elimina la ambigüedad.
Fixes residual flake del issue #29 .
2026-04-19 07:40:32 -03:00
e5b6c06f64
refactor(tests): Api.Tests apunta a SIGCM2_Test_Api via TestConnectionStrings
...
Todos los archivos de Api.Tests reemplazan la connection string hardcodeada
por TestConnectionStrings.ApiTestDb. Cada proyecto de tests ahora tiene su
propia base de datos aislada, eliminando la contención entre Application.Tests
y Api.Tests que causaba flakiness.
2026-04-18 21:44:40 -03:00