From 402c1f30d0cff4d1f6dd09660d0f96506a8722d5 Mon Sep 17 00:00:00 2001 From: Ralf Handl Date: Thu, 30 Oct 2025 21:09:12 +0100 Subject: [PATCH] Tests for unevaluatedProperties --- ...criminator-object-unexpected-property.yaml | 10 ++ ...externalDocs-unexpected-property copy.yaml | 11 ++ tests/schema/minimal-objects.yaml | 161 ++++++++++++++++++ tests/schema/schema.test.mjs | 15 ++ 4 files changed, 197 insertions(+) create mode 100644 tests/schema/fail/discriminator-object-unexpected-property.yaml create mode 100644 tests/schema/fail/schema-object-externalDocs-unexpected-property copy.yaml create mode 100644 tests/schema/minimal-objects.yaml diff --git a/tests/schema/fail/discriminator-object-unexpected-property.yaml b/tests/schema/fail/discriminator-object-unexpected-property.yaml new file mode 100644 index 0000000000..05d84f4c96 --- /dev/null +++ b/tests/schema/fail/discriminator-object-unexpected-property.yaml @@ -0,0 +1,10 @@ +openapi: 3.3.0 +info: + title: API + version: 1.0.0 +components: + schemas: + discriminator-with-unevaluated-properties: + type: object + discriminator: + not-allowed: here diff --git a/tests/schema/fail/schema-object-externalDocs-unexpected-property copy.yaml b/tests/schema/fail/schema-object-externalDocs-unexpected-property copy.yaml new file mode 100644 index 0000000000..0c56aeb744 --- /dev/null +++ b/tests/schema/fail/schema-object-externalDocs-unexpected-property copy.yaml @@ -0,0 +1,11 @@ +openapi: 3.3.0 +info: + title: API + version: 1.0.0 +components: + schemas: + externalDocs-with-unevaluated-properties: + type: object + externalDocs: + url: https://example.com/docs + not-allowed: here diff --git a/tests/schema/minimal-objects.yaml b/tests/schema/minimal-objects.yaml new file mode 100644 index 0000000000..5d5806d0dc --- /dev/null +++ b/tests/schema/minimal-objects.yaml @@ -0,0 +1,161 @@ +# Minimal instances of Objects defined in the OpenAPI specification +# +# Each instance includes only the required properties for that Object. +# These instances are used to verify that the Object does not allow additional properties. + +# OpenAPI Object has separate test cases + +- objectName: Info Object + subSchemaPath: /$defs/info + minimalInstance: + title: Sample API + version: 1.0.0 + +- objectName: Contact Object + subSchemaPath: /$defs/contact + minimalInstance: {} + +- objectName: License Object + subSchemaPath: /$defs/license + minimalInstance: + name: Apache 2.0 + +- objectName: Server Object + subSchemaPath: /$defs/server + minimalInstance: + url: https://example.com/docs + +- objectName: Server Variable Object + subSchemaPath: /$defs/server-variable + minimalInstance: + default: defaultValue + +- objectName: Components Object + subSchemaPath: /$defs/components + minimalInstance: {} + +- objectName: Paths Object + subSchemaPath: /$defs/paths + minimalInstance: {} + +- objectName: Path Item Object + subSchemaPath: /$defs/path-item + minimalInstance: {} + +- objectName: Operation Object + subSchemaPath: /$defs/operation + minimalInstance: {} + +- objectName: External Documentation Object + subSchemaPath: /$defs/external-documentation + minimalInstance: + url: https://example.com/docs + +- objectName: Parameter Object + subSchemaPath: /$defs/parameter + minimalInstance: + name: sampleParam + in: query + schema: # content could also be used, one of them is required + type: string + +- objectName: Request Body Object + subSchemaPath: /$defs/request-body + minimalInstance: + content: + application/json: + schema: + type: object + +- objectName: Media Type Object + subSchemaPath: /$defs/media-type + minimalInstance: {} + +- objectName: Encoding Object + subSchemaPath: /$defs/encoding + minimalInstance: {} + +- objectName: Responses Object + subSchemaPath: /$defs/responses + minimalInstance: + default: {} # or "200": {}, any response would do, Responses Object requires at least one response + +- objectName: Response Object + subSchemaPath: /$defs/response + minimalInstance: {} + +- objectName: Callback Object + subSchemaPath: /$defs/callbacks + minimalInstance: {} + +- objectName: Example Object + subSchemaPath: /$defs/example + minimalInstance: {} + +- objectName: Link Object + subSchemaPath: /$defs/link + minimalInstance: + operationId: sampleOperation # operationRef could also be used, one of them is required + +- objectName: Header Object + subSchemaPath: /$defs/header + minimalInstance: + schema: # content could also be used, one of them is required + type: string + +- objectName: Tag Object + subSchemaPath: /$defs/tag + minimalInstance: + name: Sample Tag + +# Reference Object allows additional properties (which SHALL be ignored) + +# Schema Object allows additional properties + +# Discriminator Object is defined in meta.yaml, test case fail/discriminator-unexpected-property.yaml + +# XML Object is defined in meta.yaml + +- objectName: Security Scheme Object (HTTP Basic) + subSchemaPath: /$defs/security-scheme + minimalInstance: + type: http + scheme: basic + +- objectName: OAuth Flows Object + subSchemaPath: /$defs/oauth-flows + minimalInstance: {} + +- objectName: OAuth Flow Object (implicit) + subSchemaPath: /$defs/oauth-flows/$defs/implicit + minimalInstance: + authorizationUrl: https://example.com/auth + scopes: {} + +- objectName: OAuth Flow Object (password) + subSchemaPath: /$defs/oauth-flows/$defs/password + minimalInstance: + tokenUrl: https://example.com/token + scopes: {} + +- objectName: OAuth Flow Object (clientCredentials) + subSchemaPath: /$defs/oauth-flows/$defs/client-credentials + minimalInstance: + tokenUrl: https://example.com/token + scopes: {} + +- objectName: OAuth Flow Object (authorizationCode) + subSchemaPath: /$defs/oauth-flows/$defs/authorization-code + minimalInstance: + authorizationUrl: https://example.com/auth + tokenUrl: https://example.com/token + scopes: {} + +- objectName: OAuth Flow Object (deviceAuthorization) + subSchemaPath: /$defs/oauth-flows/$defs/device-authorization + minimalInstance: + deviceAuthorizationUrl: https://example.com/device + tokenUrl: https://example.com/auth + scopes: {} + +# Security Requirement Object allows additional properties diff --git a/tests/schema/schema.test.mjs b/tests/schema/schema.test.mjs index a9ee76f392..861ed8cade 100644 --- a/tests/schema/schema.test.mjs +++ b/tests/schema/schema.test.mjs @@ -53,4 +53,19 @@ describe("v3.3", () => { }); }); }); + + describe("Unevaluated properties in schema.yaml subschemas", () => { + const schema = "https://spec.openapis.org/oas/3.3/schema/WORK-IN-PROGRESS"; + const minimalObjects = parseYamlFromFile("./tests/schema/minimal-objects.yaml"); + + for (const { objectName, subSchemaPath, minimalInstance } of minimalObjects) { + test(objectName, async () => { + // verify that minimal instance passes + await expect(minimalInstance).to.matchJsonSchema(`${schema}#${subSchemaPath}`); + // verify that adding a very unlikely property fails + const extendedInstance = { ...minimalInstance, not_allowed: true }; + await expect(extendedInstance).to.not.matchJsonSchema(`${schema}#${subSchemaPath}`); + }); + } + }); });