From 6e58c7dd4dc8e75285aa96358284dfa65e0b348a Mon Sep 17 00:00:00 2001 From: khayashi4337 Date: Fri, 30 Oct 2026 13:01:49 +0900 Subject: [PATCH] Add regression test for OAS 3.1 scalar const: null (issue-2395) Locks in that a scalar-level "const: null" stays distinguishable from a missing const (getConst() returns a NullNode), that object-level const values keep embedded nulls, and that "const": null survives a serialize/parse round-trip. Companion fix in swagger-core adds a constSetFlag mechanism (like exampleSetFlag/defaultSetFlag) so the same distinction is preserved for schemas deserialized through swagger-core's own mappers. Refs #2395 Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 --- .../parser/test/OAI31DeserializationTest.java | 34 +++++++++++++++++++ .../src/test/resources/3.1.0/issue-2395.yaml | 21 ++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 modules/swagger-parser-v3/src/test/resources/3.1.0/issue-2395.yaml diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAI31DeserializationTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAI31DeserializationTest.java index 3860e76893..49e0347291 100644 --- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAI31DeserializationTest.java +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAI31DeserializationTest.java @@ -101,6 +101,40 @@ public void testSchemaConstOAS31() { assertEquals(openAPI.getComponents().getSchemas().get("ConstNulCharactersInStrings").getConst(), "hello\0there"); } + @Test(description = "Test OAS31 scalar const:null is distinguishable from missing const (issue-2395)") + public void testScalarConstNullOAS31() { + SwaggerParseResult result = new OpenAPIV3Parser().readLocation( "3.1.0/issue-2395.yaml", null, null); + assertNotNull(result.getOpenAPI()); + OpenAPI openAPI = result.getOpenAPI(); + + Schema mySchema = openAPI.getComponents().getSchemas().get("MySchema"); + + // const: null on a scalar property is kept (NullNode), not silently dropped + Object msgConst = ((Schema) mySchema.getProperties().get("msg")).getConst(); + assertNotNull(msgConst); + assertTrue(msgConst instanceof NullNode); + + // a missing const still reads as Java null + assertNull(((Schema) mySchema.getProperties().get("plain")).getConst()); + + // non-null const behaves as before + assertEquals(((Schema) mySchema.getProperties().get("code")).getConst(), 0); + + // object const containing a null property is preserved as before + Object outerConst = mySchema.getConst(); + assertTrue(outerConst instanceof ObjectNode); + assertEquals(((ObjectNode) outerConst).get("code").intValue(), 0); + assertTrue(((ObjectNode) outerConst).get("msg").isNull()); + + // const: null survives a serialize/parse round-trip + String serialized = io.swagger.v3.core.util.Json31.pretty(openAPI); + SwaggerParseResult reparsed = new OpenAPIV3Parser().readContents(serialized, null, null); + assertNotNull(reparsed.getOpenAPI()); + Schema mySchema2 = reparsed.getOpenAPI().getComponents().getSchemas().get("MySchema"); + assertTrue(((Schema) mySchema2.getProperties().get("msg")).getConst() instanceof NullNode); + assertTrue(((ObjectNode) mySchema2.getConst()).get("msg").isNull()); + } + @Test(description = "Test basic OAS31 deserialization/validation") public void testBasicOAS31() { SwaggerParseResult result = new OpenAPIV3Parser().readLocation( "3.1.0/test/basicOAS31.yaml", null, null); diff --git a/modules/swagger-parser-v3/src/test/resources/3.1.0/issue-2395.yaml b/modules/swagger-parser-v3/src/test/resources/3.1.0/issue-2395.yaml new file mode 100644 index 0000000000..9d4a535ded --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/3.1.0/issue-2395.yaml @@ -0,0 +1,21 @@ +openapi: "3.1.0" +info: + title: Test + version: "1.0" +paths: {} +components: + schemas: + MySchema: + type: object + properties: + msg: + type: string + const: null # scalar const: null on individual field (issue-2395) + code: + type: integer + const: 0 + plain: + type: string # no const + const: + code: 0 + msg: null # null inside object const (already worked)