diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ValidationAnnotationsUtils.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ValidationAnnotationsUtils.java index d490043ea0..39a7d52982 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ValidationAnnotationsUtils.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ValidationAnnotationsUtils.java @@ -1,5 +1,6 @@ package io.swagger.v3.core.util; +import io.swagger.v3.oas.models.SpecVersion; import io.swagger.v3.oas.models.media.Schema; import javax.validation.constraints.*; @@ -132,12 +133,25 @@ public static boolean applySizeConstraint(Schema schema, Size annotation) { * @return whether the schema has been modified or not */ public static boolean applyDecimalMinConstraint(Schema schema, DecimalMin annotation) { - if (isNumberSchema(schema)) { - schema.setMinimum(new BigDecimal(annotation.value())); + if (!isNumberSchema(schema)) { + return false; + } + BigDecimal value = new BigDecimal(annotation.value()); + if (schema.getSpecVersion().equals(SpecVersion.V31)) { + if (!annotation.inclusive()) { + schema.setExclusiveMinimumValue(value); + BigDecimal minimum = schema.getMinimum(); + if (minimum != null && minimum.compareTo(value) <= 0) { + schema.setMinimum(null); + } + } else { + schema.setMinimum(value); + } + } else { + schema.setMinimum(value); schema.setExclusiveMinimum(!annotation.inclusive()); - return true; } - return false; + return true; } /** @@ -146,12 +160,25 @@ public static boolean applyDecimalMinConstraint(Schema schema, DecimalMin annota * @return whether the schema has been modified or not */ public static boolean applyDecimalMaxConstraint(Schema schema, DecimalMax annotation) { - if (isNumberSchema(schema)) { - schema.setMaximum(new BigDecimal(annotation.value())); + if (!isNumberSchema(schema)) { + return false; + } + BigDecimal value = new BigDecimal(annotation.value()); + if (schema.getSpecVersion().equals(SpecVersion.V31)) { + if (!annotation.inclusive()) { + schema.setExclusiveMaximumValue(value); + BigDecimal maximum = schema.getMaximum(); + if (maximum != null && maximum.compareTo(value) >= 0) { + schema.setMaximum(null); + } + } else { + schema.setMaximum(value); + } + } else { + schema.setMaximum(value); schema.setExclusiveMaximum(!annotation.inclusive()); - return true; } - return false; + return true; } /** @@ -189,17 +216,48 @@ public static boolean applyEmailConstraint(Schema schema, Email annotation) { } public static boolean applyPositiveConstraint(Schema schema) { - if (isNumberSchema(schema)) { - BigDecimal current = schema.getMinimum(); - if (current == null || current.compareTo(BigDecimal.ZERO) < 0) { - schema.setMinimum(BigDecimal.ZERO); - schema.setExclusiveMinimum(true); - } else if (current.compareTo(BigDecimal.ZERO) == 0 && !Boolean.TRUE.equals(schema.getExclusiveMinimum())) { - schema.setExclusiveMinimum(true); + if (!isNumberSchema(schema)) { + return false; + } + if (schema.getSpecVersion().equals(SpecVersion.V30)) { + return applyPositiveConstraintV30(schema); + } + return applyPositiveConstraintV31(schema); + } + + private static boolean applyPositiveConstraintV30(Schema schema) { + BigDecimal minimum = schema.getMinimum(); + if (currentMinimumOutsidePositiveRange(minimum)) { + schema.setMinimum(BigDecimal.ZERO); + schema.setExclusiveMinimum(true); + } else if (minimum.compareTo(BigDecimal.ZERO) == 0 && !Boolean.TRUE.equals(schema.getExclusiveMinimum())) { + schema.setExclusiveMinimum(true); + } + return true; + } + + private static boolean applyPositiveConstraintV31(Schema schema) { + BigDecimal exclusiveMinimum = schema.getExclusiveMinimumValue(); + if (exclusiveMinimum != null) { + if (exclusiveMinimum.compareTo(BigDecimal.ZERO) < 0) { + BigDecimal minimum = schema.getMinimum(); + if (minimum != null && minimum.compareTo(BigDecimal.ZERO) > 0) { + schema.setExclusiveMinimumValue(null); + } else { + schema.setMinimum(null); + schema.setExclusiveMinimumValue(BigDecimal.ZERO); + } + return true; } - return true; + return false; } - return false; + BigDecimal minimum = schema.getMinimum(); + if (minimum != null && minimum.compareTo(BigDecimal.ZERO) > 0) { + return false; + } + schema.setMinimum(null); + schema.setExclusiveMinimumValue(BigDecimal.ZERO); + return true; } public static boolean applyPositiveOrZeroConstraint(Schema schema) { @@ -214,17 +272,48 @@ public static boolean applyPositiveOrZeroConstraint(Schema schema) { } public static boolean applyNegativeConstraint(Schema schema) { - if (isNumberSchema(schema)) { - BigDecimal current = schema.getMaximum(); - if (current == null || current.compareTo(BigDecimal.ZERO) > 0) { - schema.setMaximum(BigDecimal.ZERO); - schema.setExclusiveMaximum(true); - } else if (current.compareTo(BigDecimal.ZERO) == 0 && !Boolean.TRUE.equals(schema.getExclusiveMaximum())) { - schema.setExclusiveMaximum(true); + if (!isNumberSchema(schema)) { + return false; + } + if (schema.getSpecVersion().equals(SpecVersion.V30)) { + return applyNegativeConstraintV30(schema); + } + return applyNegativeConstraintV31(schema); + } + + private static boolean applyNegativeConstraintV30(Schema schema) { + BigDecimal maximum = schema.getMaximum(); + if (currentMaximumOutsideNegativeRange(maximum)) { + schema.setMaximum(BigDecimal.ZERO); + schema.setExclusiveMaximum(true); + } else if (maximum.compareTo(BigDecimal.ZERO) == 0 && !Boolean.TRUE.equals(schema.getExclusiveMaximum())) { + schema.setExclusiveMaximum(true); + } + return true; + } + + private static boolean applyNegativeConstraintV31(Schema schema) { + BigDecimal exclusiveMaximum = schema.getExclusiveMaximumValue(); + if (exclusiveMaximum != null) { + if (exclusiveMaximum.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal maximum = schema.getMaximum(); + if (maximum != null && maximum.compareTo(BigDecimal.ZERO) < 0) { + schema.setExclusiveMaximumValue(null); + } else { + schema.setMaximum(null); + schema.setExclusiveMaximumValue(BigDecimal.ZERO); + } + return true; } - return true; + return false; } - return false; + BigDecimal maximum = schema.getMaximum(); + if (maximum != null && maximum.compareTo(BigDecimal.ZERO) < 0) { + return false; + } + schema.setMaximum(null); + schema.setExclusiveMaximumValue(BigDecimal.ZERO); + return true; } /** @@ -278,4 +367,12 @@ public static boolean applyNegativeOrZeroConstraint(Schema schema) { return false; } + private static boolean currentMinimumOutsidePositiveRange(BigDecimal currentMinimum) { + return currentMinimum == null || currentMinimum.compareTo(BigDecimal.ZERO) < 0; + } + + private static boolean currentMaximumOutsideNegativeRange(BigDecimal currentMaximum) { + return currentMaximum == null || currentMaximum.compareTo(BigDecimal.ZERO) > 0; + } + } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/BeanValidatorTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/BeanValidatorTest.java index f582b0d35e..a1afea688b 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/BeanValidatorTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/BeanValidatorTest.java @@ -2,12 +2,7 @@ import io.swagger.v3.core.converter.ModelConverters; import io.swagger.v3.core.oas.models.BeanValidationsModel; -import io.swagger.v3.oas.models.media.ArraySchema; -import io.swagger.v3.oas.models.media.EmailSchema; -import io.swagger.v3.oas.models.media.IntegerSchema; -import io.swagger.v3.oas.models.media.NumberSchema; -import io.swagger.v3.oas.models.media.Schema; -import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.media.*; import org.testng.annotations.Test; import java.math.BigDecimal; @@ -20,7 +15,7 @@ public class BeanValidatorTest { @Test(description = "read bean validations") - public void readBeanValidatorTest() { + public void readBeanValidatorOAS30Test() { final Map schemas = ModelConverters.getInstance().readAll(BeanValidationsModel.class); final Schema model = schemas.get("BeanValidationsModel"); final Map properties = model.getProperties(); @@ -94,4 +89,91 @@ public void readBeanValidatorTest() { assertEquals(negativeOrZeroWithMax.getMaximum(), new BigDecimal("-2")); assertNull(negativeOrZeroWithMax.getExclusiveMaximum()); } + + @Test(description = "read bean validations") + public void readBeanValidatorOAS31Test() { + final Map schemas = ModelConverters.getInstance(true).readAll(BeanValidationsModel.class); + final Schema model = schemas.get("BeanValidationsModel"); + final Map properties = model.getProperties(); + + assertTrue(model.getRequired().contains("id")); + assertTrue(model.getRequired().contains("username")); + + final JsonSchema username = (JsonSchema) properties.get("username"); + assertEquals(username.getPattern(), "(?![-._])[-._a-zA-Z0-9]{3,32}"); + + final JsonSchema age = (JsonSchema) properties.get("age"); + assertEquals(age.getMinimum(), new BigDecimal(13.0)); + assertEquals(age.getMaximum(), new BigDecimal(99.0)); + + final JsonSchema password = (JsonSchema) properties.get("password"); + assertEquals((int) password.getMinLength(), 6); + assertEquals((int) password.getMaxLength(), 20); + + final JsonSchema email = (JsonSchema) properties.get("email"); + assertEquals(email.getFormat(), "email"); + + final JsonSchema minBalance = (JsonSchema) properties.get("minBalance"); + assertEquals(minBalance.getExclusiveMinimumValue(), new BigDecimal("0.1")); + assertNull(minBalance.getMinimum()); + assertNull(minBalance.getExclusiveMinimum()); + + final JsonSchema maxBalance = (JsonSchema) properties.get("maxBalance"); + assertEquals(maxBalance.getExclusiveMaximumValue(), new BigDecimal("1000000")); + assertNull(maxBalance.getMaximum()); + assertNull(maxBalance.getExclusiveMaximum()); + + final JsonSchema items = (JsonSchema) properties.get("items"); + assertEquals((int) items.getMinItems(), 2); + assertEquals((int) items.getMaxItems(), 10); + assertEquals((int) items.getItems().getMinLength(), 3); + assertEquals((int) items.getItems().getMaxLength(), 4); + + final JsonSchema optionalValue = (JsonSchema) properties.get("optionalValue"); + assertEquals((int) optionalValue.getMinLength(), 1); + assertEquals((int) optionalValue.getMaxLength(), 10); + + final JsonSchema positiveAmount = (JsonSchema) properties.get("positiveAmount"); + assertEquals(positiveAmount.getExclusiveMinimumValue(), BigDecimal.ZERO); + assertNull(positiveAmount.getMinimum()); + assertNull(positiveAmount.getExclusiveMinimum()); + + final JsonSchema positiveOrZeroAmount = (JsonSchema) properties.get("positiveOrZeroAmount"); + assertEquals(positiveOrZeroAmount.getMinimum(), BigDecimal.ZERO); + assertNull(positiveOrZeroAmount.getExclusiveMinimum()); + + final JsonSchema negativeAmount = (JsonSchema) properties.get("negativeAmount"); + assertEquals(negativeAmount.getExclusiveMaximumValue(), BigDecimal.ZERO); + assertNull(negativeAmount.getMaximum()); + assertNull(negativeAmount.getExclusiveMaximum()); + + final JsonSchema negativeOrZeroAmount = (JsonSchema) properties.get("negativeOrZeroAmount"); + assertEquals(negativeOrZeroAmount.getMaximum(), BigDecimal.ZERO); + assertNull(negativeOrZeroAmount.getExclusiveMaximum()); + + final JsonSchema positiveWithMin = (JsonSchema) properties.get("positiveWithMin"); + assertEquals(positiveWithMin.getMinimum(), new BigDecimal("5")); + assertNull(positiveWithMin.getExclusiveMinimum()); + assertNull(positiveWithMin.getExclusiveMinimumValue()); + + final JsonSchema positiveWithDecimalMin = (JsonSchema) properties.get("positiveWithDecimalMin"); + assertEquals(positiveWithDecimalMin.getExclusiveMinimumValue(), new BigDecimal("5.5")); + assertNull(positiveWithDecimalMin.getMinimum()); + assertNull(positiveWithDecimalMin.getExclusiveMinimum()); + + final JsonSchema positiveOrZeroWithMin = (JsonSchema) properties.get("positiveOrZeroWithMin"); + assertEquals(positiveOrZeroWithMin.getMinimum(), new BigDecimal("3")); + assertNull(positiveOrZeroWithMin.getExclusiveMinimum()); + assertNull(positiveOrZeroWithMin.getExclusiveMinimumValue()); + + final JsonSchema negativeWithMax = (JsonSchema) properties.get("negativeWithMax"); + assertEquals(negativeWithMax.getMaximum(), new BigDecimal("-3")); + assertNull(negativeWithMax.getExclusiveMaximum()); + assertNull(negativeWithMax.getExclusiveMaximumValue()); + + final JsonSchema negativeOrZeroWithMax = (JsonSchema) properties.get("negativeOrZeroWithMax"); + assertEquals(negativeOrZeroWithMax.getMaximum(), new BigDecimal("-2")); + assertNull(negativeOrZeroWithMax.getExclusiveMaximum()); + assertNull(negativeOrZeroWithMax.getExclusiveMaximumValue()); + } } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/util/ValidationAnnotationsUtilsTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/util/ValidationAnnotationsUtilsTest.java index 82d65ac9db..3d1c7e8d5f 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/util/ValidationAnnotationsUtilsTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/util/ValidationAnnotationsUtilsTest.java @@ -483,13 +483,128 @@ public void testApplyDecimalMaxConstraintOnNumberSchemaExclusive() { public void testApplyDecimalMaxConstraintOnStringSchema() { Schema schema = new StringSchema(); DecimalMax maxAnnotation = createDecimalMaxAnnotation("100.5", true); - + boolean modified = ValidationAnnotationsUtils.applyDecimalMaxConstraint(schema, maxAnnotation); - + assertFalse(modified); assertNull(schema.getMaximum()); } + // --- OpenAPI 3.1 tests for @DecimalMin --- + + @Test + public void testApplyDecimalMinConstraintV31Exclusive() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + DecimalMin minAnnotation = createDecimalMinAnnotation("10.5", false); + + boolean modified = ValidationAnnotationsUtils.applyDecimalMinConstraint(schema, minAnnotation); + + assertTrue(modified); + assertEquals(schema.getExclusiveMinimumValue(), new BigDecimal("10.5")); + assertNull(schema.getMinimum()); + assertNull(schema.getExclusiveMinimum()); + } + + @Test + public void testApplyDecimalMinConstraintV31Inclusive() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + DecimalMin minAnnotation = createDecimalMinAnnotation("10.5", true); + + boolean modified = ValidationAnnotationsUtils.applyDecimalMinConstraint(schema, minAnnotation); + + assertTrue(modified); + assertEquals(schema.getMinimum(), new BigDecimal("10.5")); + assertNull(schema.getExclusiveMinimumValue()); + assertNull(schema.getExclusiveMinimum()); + } + + @Test + public void testApplyDecimalMinConstraintV31ExclusiveClearsWeakerMinimum() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setMinimum(new BigDecimal("2")); + DecimalMin minAnnotation = createDecimalMinAnnotation("5.5", false); + + boolean modified = ValidationAnnotationsUtils.applyDecimalMinConstraint(schema, minAnnotation); + + assertTrue(modified); + assertEquals(schema.getExclusiveMinimumValue(), new BigDecimal("5.5")); + assertNull(schema.getMinimum()); + } + + @Test + public void testApplyDecimalMinConstraintV31ExclusiveKeepsStricterMinimum() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setMinimum(new BigDecimal("10")); + DecimalMin minAnnotation = createDecimalMinAnnotation("5.5", false); + + boolean modified = ValidationAnnotationsUtils.applyDecimalMinConstraint(schema, minAnnotation); + + assertTrue(modified); + assertEquals(schema.getExclusiveMinimumValue(), new BigDecimal("5.5")); + assertEquals(schema.getMinimum(), new BigDecimal("10")); + } + + // --- OpenAPI 3.1 tests for @DecimalMax --- + + @Test + public void testApplyDecimalMaxConstraintV31Exclusive() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + DecimalMax maxAnnotation = createDecimalMaxAnnotation("100.5", false); + + boolean modified = ValidationAnnotationsUtils.applyDecimalMaxConstraint(schema, maxAnnotation); + + assertTrue(modified); + assertEquals(schema.getExclusiveMaximumValue(), new BigDecimal("100.5")); + assertNull(schema.getMaximum()); + assertNull(schema.getExclusiveMaximum()); + } + + @Test + public void testApplyDecimalMaxConstraintV31Inclusive() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + DecimalMax maxAnnotation = createDecimalMaxAnnotation("100.5", true); + + boolean modified = ValidationAnnotationsUtils.applyDecimalMaxConstraint(schema, maxAnnotation); + + assertTrue(modified); + assertEquals(schema.getMaximum(), new BigDecimal("100.5")); + assertNull(schema.getExclusiveMaximumValue()); + assertNull(schema.getExclusiveMaximum()); + } + + @Test + public void testApplyDecimalMaxConstraintV31ExclusiveClearsWeakerMaximum() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setMaximum(new BigDecimal("200")); + DecimalMax maxAnnotation = createDecimalMaxAnnotation("100.5", false); + + boolean modified = ValidationAnnotationsUtils.applyDecimalMaxConstraint(schema, maxAnnotation); + + assertTrue(modified); + assertEquals(schema.getExclusiveMaximumValue(), new BigDecimal("100.5")); + assertNull(schema.getMaximum()); + } + + @Test + public void testApplyDecimalMaxConstraintV31ExclusiveKeepsStricterMaximum() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setMaximum(new BigDecimal("50")); + DecimalMax maxAnnotation = createDecimalMaxAnnotation("100.5", false); + + boolean modified = ValidationAnnotationsUtils.applyDecimalMaxConstraint(schema, maxAnnotation); + + assertTrue(modified); + assertEquals(schema.getExclusiveMaximumValue(), new BigDecimal("100.5")); + assertEquals(schema.getMaximum(), new BigDecimal("50")); + } @Test public void testApplyPatternConstraintOnStringSchema() { @@ -841,4 +956,230 @@ public void testApplyNegativeOrZeroConstraintOverridesPositiveMax() { assertEquals(schema.getMaximum(), BigDecimal.ZERO); assertNull(schema.getExclusiveMaximum()); } + + // --- OpenAPI 3.1 tests for @Positive --- + + @Test + public void testApplyPositiveConstraintOnNumberSchemaV31() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + boolean modified = ValidationAnnotationsUtils.applyPositiveConstraint(schema); + + assertTrue(modified); + assertNull(schema.getMinimum()); + assertNull(schema.getExclusiveMinimum()); + assertEquals(schema.getExclusiveMinimumValue(), BigDecimal.ZERO); + } + + @Test + public void testApplyPositiveConstraintV31KeepsStricterValue() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setExclusiveMinimumValue(new BigDecimal("10")); + boolean modified = ValidationAnnotationsUtils.applyPositiveConstraint(schema); + + assertFalse(modified); + assertEquals(schema.getExclusiveMinimumValue(), new BigDecimal("10")); + } + + @Test + public void testApplyPositiveConstraintV31OverridesWeakerExclusiveValue() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setExclusiveMinimumValue(new BigDecimal("-5")); + boolean modified = ValidationAnnotationsUtils.applyPositiveConstraint(schema); + + assertTrue(modified); + assertEquals(schema.getExclusiveMinimumValue(), BigDecimal.ZERO); + } + + @Test + public void testApplyPositiveConstraintV31NegativeMinimumOnly() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setMinimum(new BigDecimal("-5")); + boolean modified = ValidationAnnotationsUtils.applyPositiveConstraint(schema); + + assertTrue(modified); + assertNull(schema.getMinimum()); + assertEquals(schema.getExclusiveMinimumValue(), BigDecimal.ZERO); + } + + @Test + public void testApplyPositiveConstraintV31NegativeExclusiveAndZeroMinimum() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setExclusiveMinimumValue(new BigDecimal("-3")); + schema.setMinimum(BigDecimal.ZERO); + boolean modified = ValidationAnnotationsUtils.applyPositiveConstraint(schema); + + assertTrue(modified); + assertNull(schema.getMinimum()); + assertEquals(schema.getExclusiveMinimumValue(), BigDecimal.ZERO); + } + + @Test + public void testApplyPositiveConstraintV31BothNegative() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setExclusiveMinimumValue(new BigDecimal("-5")); + schema.setMinimum(new BigDecimal("-2")); + boolean modified = ValidationAnnotationsUtils.applyPositiveConstraint(schema); + + assertTrue(modified); + assertNull(schema.getMinimum()); + assertEquals(schema.getExclusiveMinimumValue(), BigDecimal.ZERO); + } + + @Test + public void testApplyPositiveConstraintV31DropsNegativeExclusiveWhenPositiveMinimumExists() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setExclusiveMinimumValue(new BigDecimal("-5")); + schema.setMinimum(new BigDecimal("2")); + boolean modified = ValidationAnnotationsUtils.applyPositiveConstraint(schema); + + assertTrue(modified); + assertNull(schema.getExclusiveMinimumValue()); + assertEquals(schema.getMinimum(), new BigDecimal("2")); + } + + @Test + public void testApplyPositiveConstraintV31TightensZeroInclusiveMinimum() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setMinimum(BigDecimal.ZERO); + boolean modified = ValidationAnnotationsUtils.applyPositiveConstraint(schema); + + assertTrue(modified); + assertNull(schema.getMinimum()); + assertEquals(schema.getExclusiveMinimumValue(), BigDecimal.ZERO); + } + + // --- OpenAPI 3.1 tests for @Negative --- + + @Test + public void testApplyNegativeConstraintOnNumberSchemaV31() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + boolean modified = ValidationAnnotationsUtils.applyNegativeConstraint(schema); + + assertTrue(modified); + assertNull(schema.getMaximum()); + assertNull(schema.getExclusiveMaximum()); + assertEquals(schema.getExclusiveMaximumValue(), BigDecimal.ZERO); + } + + @Test + public void testApplyNegativeConstraintV31KeepsStricterValue() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setExclusiveMaximumValue(new BigDecimal("-5")); + boolean modified = ValidationAnnotationsUtils.applyNegativeConstraint(schema); + + assertFalse(modified); + assertEquals(schema.getExclusiveMaximumValue(), BigDecimal.valueOf(-5)); + } + + @Test + public void testApplyNegativeConstraintV31OverridesWeakerExclusiveValue() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setExclusiveMaximumValue(new BigDecimal("10")); + boolean modified = ValidationAnnotationsUtils.applyNegativeConstraint(schema); + + assertTrue(modified); + assertEquals(schema.getExclusiveMaximumValue(), BigDecimal.ZERO); + } + + @Test + public void testApplyNegativeConstraintV31PositiveMaximumOnly() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setMaximum(new BigDecimal("5")); + boolean modified = ValidationAnnotationsUtils.applyNegativeConstraint(schema); + + assertTrue(modified); + assertNull(schema.getMaximum()); + assertEquals(schema.getExclusiveMaximumValue(), BigDecimal.ZERO); + } + + @Test + public void testApplyNegativeConstraintV31PositiveExclusiveAndZeroMaximum() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setExclusiveMaximumValue(new BigDecimal("3")); + schema.setMaximum(BigDecimal.ZERO); + boolean modified = ValidationAnnotationsUtils.applyNegativeConstraint(schema); + + assertTrue(modified); + assertNull(schema.getMaximum()); + assertEquals(schema.getExclusiveMaximumValue(), BigDecimal.ZERO); + } + + @Test + public void testApplyNegativeConstraintV31BothPositive() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setExclusiveMaximumValue(new BigDecimal("5")); + schema.setMaximum(new BigDecimal("2")); + boolean modified = ValidationAnnotationsUtils.applyNegativeConstraint(schema); + + assertTrue(modified); + assertNull(schema.getMaximum()); + assertEquals(schema.getExclusiveMaximumValue(), BigDecimal.ZERO); + } + + @Test + public void testApplyNegativeConstraintV31DropsPositiveExclusiveWhenNegativeMaximumExists() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setExclusiveMaximumValue(new BigDecimal("10")); + schema.setMaximum(new BigDecimal("-3")); + boolean modified = ValidationAnnotationsUtils.applyNegativeConstraint(schema); + + assertTrue(modified); + assertNull(schema.getExclusiveMaximumValue()); + assertEquals(schema.getMaximum(), new BigDecimal("-3")); + } + + @Test + public void testApplyNegativeConstraintV31TightensZeroInclusiveMaximum() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + schema.setMaximum(BigDecimal.ZERO); + boolean modified = ValidationAnnotationsUtils.applyNegativeConstraint(schema); + + assertTrue(modified); + assertNull(schema.getMaximum()); + assertEquals(schema.getExclusiveMaximumValue(), BigDecimal.ZERO); + } + + // --- OpenAPI 3.1 tests for @PositiveOrZero --- + + @Test + public void testApplyPositiveOrZeroConstraintV31() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + boolean modified = ValidationAnnotationsUtils.applyPositiveOrZeroConstraint(schema); + + assertTrue(modified); + assertEquals(schema.getMinimum(), BigDecimal.ZERO); + assertNull(schema.getExclusiveMinimumValue()); + assertNull(schema.getExclusiveMinimum()); + } + + // --- OpenAPI 3.1 tests for @NegativeOrZero --- + + @Test + public void testApplyNegativeOrZeroConstraintV31() { + Schema schema = new NumberSchema(); + schema.setSpecVersion(io.swagger.v3.oas.models.SpecVersion.V31); + boolean modified = ValidationAnnotationsUtils.applyNegativeOrZeroConstraint(schema); + + assertTrue(modified); + assertEquals(schema.getMaximum(), BigDecimal.ZERO); + assertNull(schema.getExclusiveMaximumValue()); + assertNull(schema.getExclusiveMaximum()); + } }