feat(application): guard avisos en MoveRubroCommandHandler (CAT-002)

This commit is contained in:
2026-04-19 08:24:07 -03:00
parent 216983623a
commit c03aad8c5a
2 changed files with 77 additions and 2 deletions

View File

@@ -13,17 +13,20 @@ public sealed class MoveRubroCommandHandler : ICommandHandler<MoveRubroCommand,
private readonly IAuditLogger _audit;
private readonly TimeProvider _timeProvider;
private readonly RubrosOptions _options;
private readonly IAvisoQueryRepository _avisoQuery;
public MoveRubroCommandHandler(
IRubroRepository repo,
IAuditLogger audit,
TimeProvider timeProvider,
IOptions<RubrosOptions> options)
IOptions<RubrosOptions> options,
IAvisoQueryRepository avisoQuery)
{
_repo = repo;
_audit = audit;
_timeProvider = timeProvider;
_options = options.Value;
_avisoQuery = avisoQuery;
}
public async Task<RubroMovedDto> Handle(MoveRubroCommand command)
@@ -47,6 +50,12 @@ public sealed class MoveRubroCommandHandler : ICommandHandler<MoveRubroCommand,
if (!newParent.Activo)
throw new RubroPadreInactivoException(command.NuevoParentId.Value);
// CAT-002: Regla de Oro — nuevo padre no puede ser hoja con avisos
// CAT-002/PRD-002 TOCTOU — evaluar upgrade a RepeatableRead o constraint DB cuando exista dbo.Aviso
var avisosCount = await _avisoQuery.CountAvisosEnRubroAsync(command.NuevoParentId.Value);
if (avisosCount > 0)
throw new RubroPadreEsHojaConAvisosException(command.NuevoParentId.Value, avisosCount);
// Depth check
var parentDepth = await _repo.GetDepthAsync(command.NuevoParentId);
var newDepth = parentDepth + 1;