< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Command.Input.Validation.UpdateScopeCommandValidator
Assembly: ArturRios.Heimdall.Command
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Input/Validation/UpdateScopeCommandValidator.cs
Line coverage
100%
Covered lines: 25
Uncovered lines: 0
Coverable lines: 25
Total lines: 55
Line coverage: 100%
Branch coverage
90%
Covered branches: 9
Total branches: 10
Branch coverage: 90%
Method coverage
100%
Covered methods: 1
Fully covered methods: 1
Total methods: 1
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()90%1010100%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Input/Validation/UpdateScopeCommandValidator.cs

#LineLine coverage
 1using ArturRios.Heimdall.Domain.Enums;
 2using ArturRios.Heimdall.Shared.Messages;
 3using FluentValidation;
 4
 5namespace ArturRios.Heimdall.Command.Input.Validation;
 6
 7/// <summary>
 8///     Input validation for <see cref="UpdateScopeCommand" /> (UC-03). Only checks the shape of the
 9///     request — including the field lengths the columns impose, so an overlong value is a named 400
 10///     here rather than the persistence layer's unclassified data-access failure; business rules
 11///     that require data access (existence, name uniqueness) are enforced by the handler.
 12/// </summary>
 13public class UpdateScopeCommandValidator : AbstractValidator<UpdateScopeCommand>
 14{
 3315    public UpdateScopeCommandValidator()
 16    {
 3317        RuleFor(command => command.Name)
 3318            .NotEmpty()
 3319            .WithMessage(ScopeMessages.NameRequired)
 3320            .MaximumLength(200)
 3321            .WithMessage(ScopeMessages.NameTooLong);
 22
 23        // FluentValidation's MaximumLength rule skips a null argument, so only a non-null overlong
 24        // description is rejected — matching how the Application and ScopePermission validators
 25        // treat their optional descriptions.
 3326        RuleFor(command => command.Description)
 3327            .MaximumLength(500)
 3328            .WithMessage(ScopeMessages.DescriptionTooLong);
 29
 30        // NFR-23. Consent is refused rather than accepted-and-ignored: a tenant that asked for it
 31        // and was silently given contract performance would believe a basis applied that did not.
 3332        RuleFor(command => command.DefaultLegalBasis)
 2233            .Must(basis => basis != (int)LegalBases.Consent)
 3334            .WithMessage(ScopeMessages.ConsentBasisNotSupported);
 35
 3336        RuleFor(command => command.DefaultLegalBasis)
 2237            .Must(basis => basis is null || (Enum.IsDefined((LegalBases)basis.Value)
 2238                                             && basis.Value != (int)LegalBases.Unrecorded))
 3339            .WithMessage(ScopeMessages.LegalBasisUnknown);
 40
 41        // The scheme is checked, not just absoluteness. On Unix, Uri.TryCreate parses "/some/path"
 42        // as an absolute file:// URI, so an absoluteness check alone accepts a local filesystem path
 43        // as a tenant's published privacy notice — worse than the relative link it was meant to
 44        // refuse, because it looks valid and points at the host's disk.
 3345        RuleFor(command => command.PrivacyNoticeUri)
 2246            .Must(uri => string.IsNullOrWhiteSpace(uri) ||
 2247                         (Uri.TryCreate(uri, UriKind.Absolute, out var parsed)
 2248                          && (parsed.Scheme == Uri.UriSchemeHttps || parsed.Scheme == Uri.UriSchemeHttp)))
 3349            .WithMessage(ScopeMessages.PrivacyNoticeUriInvalid);
 50
 3351        RuleFor(command => command.PrivacyNoticeUri)
 3352            .MaximumLength(2048)
 3353            .WithMessage(ScopeMessages.PrivacyNoticeUriInvalid);
 3354    }
 55}

Methods/Properties

.ctor()