micronaut-core icon indicating copy to clipboard operation
micronaut-core copied to clipboard

@Digits validation fails to raise constraint error for illegal BigDecimal arguments

Open wetted opened this issue 2 years ago • 1 comments

Expected Behavior

Given the following classes, each with a BigDecimal property. One has validation constraint on the constructor arg, the other on the field.

@Introspected
class Offer {

    @Digits(integer = 6, fraction = 2)
    BigDecimal price
}

@Introspected
class CounterOffer {

    BigDecimal price

    CounterOffer(@Digits(integer = 6, fraction = 2) BigDecimal price) {
        this.price = price
    }
}

The following test asserts the validation constraint checks fail, as expected. The test should pass:

    void "test BigDecimal Digits property validation"() {
        when:
        Offer offer = new Offer(price: 1234567.890)
        def violations = validator.validate(offer)

        then:
        violations.size() > 0

        when:
        CounterOffer counterOffer = new CounterOffer(9876543.210)
        violations = validator.validate(counterOffer)

        then:
        violations.size() == 1
    }

Actual Behaviour

The first validation check (for Offer) passes, where the constraint is annotated on the class field.

The second (for CounterOffer) – where the constructor argument rather than class field is annotated with the constraint - fails to raise the same violation as it should, so the illegal constructor argument sneaks past the Validator.

Condition not satisfied:

violations.size() == 1
|          |      |
[]         0      false

This is apparently an issue not just with constructor arguments, but method arguments in general. The following also fails to raise validation errors for illegal BigDecimal arguments.

    @SingleResult
    Publisher<Offer> save(
            @NotBlank String slug,
            @Digits(integer = 6, fraction = 2) BigDecimal price,
            @NotNull Duration duration,
            @NotBlank String description);

I have only tested this with BigDecimal, so it might also be a problem with other @Digits compatible types (BigInteger, CharSequence, etc).

Steps To Reproduce

No response

Environment Information

No response

Example Application

No response

Version

3.5.0 (also occurs with 3.4.x)

wetted avatar Jun 02 '22 17:06 wetted

I think this is normal? .validate checks the constraints of the property, but it can't know that the constructor was called incorrectly. Similar for method calls.

You get the same behavior for other validation annotations, like @NotBlank with a String value:

    @Inject
    Validator validator;

    void "test BigDecimal Digits property validation"() {
        when:
        Offer offer = new Offer(price: "  ")
        def violations = validator.validate(offer)

        then:
        violations.size() > 0

        when:
        CounterOffer counterOffer = new CounterOffer("  ")
        violations = validator.validate(counterOffer)

        then:
        violations.size() == 1
    }

    @Introspected
    static class Offer {

        @NotBlank
        String price
    }

    @Introspected
    static class CounterOffer {

        String price

        CounterOffer(@NotBlank String price) {
            this.price = price
        }
    }

yawkat avatar Jun 21 '22 12:06 yawkat