< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Command.Input.Validation.UpdateScopeCommandValidator
Assembly: ArturRios.Heimdall.Command
File(s): /home/ci/agent/workspace/heimdall-api-docs_main/app/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/ci/agent/workspace/heimdall-api-docs_main/app/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{
 3615    public UpdateScopeCommandValidator()
 16    {
 3617        RuleFor(command => command.Name)
 3618            .NotEmpty()
 3619            .WithMessage(ScopeMessages.NameRequired)
 3620            .MaximumLength(200)
 3621            .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.
 3626        RuleFor(command => command.Description)
 3627            .MaximumLength(500)
 3628            .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.
 3632        RuleFor(command => command.DefaultLegalBasis)
 2533            .Must(basis => basis != (int)LegalBases.Consent)
 3634            .WithMessage(ScopeMessages.ConsentBasisNotSupported);
 35
 3636        RuleFor(command => command.DefaultLegalBasis)
 2537            .Must(basis => basis is null || (Enum.IsDefined((LegalBases)basis.Value)
 2538                                             && basis.Value != (int)LegalBases.Unrecorded))
 3639            .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.
 3645        RuleFor(command => command.PrivacyNoticeUri)
 2546            .Must(uri => string.IsNullOrWhiteSpace(uri) ||
 2547                         (Uri.TryCreate(uri, UriKind.Absolute, out var parsed)
 2548                          && (parsed.Scheme == Uri.UriSchemeHttps || parsed.Scheme == Uri.UriSchemeHttp)))
 3649            .WithMessage(ScopeMessages.PrivacyNoticeUriInvalid);
 50
 3651        RuleFor(command => command.PrivacyNoticeUri)
 3652            .MaximumLength(2048)
 3653            .WithMessage(ScopeMessages.PrivacyNoticeUriInvalid);
 3654    }
 55}

Methods/Properties

.ctor()