From f7d434de0b0ea52c9a4ae9684e64b70091204a52 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Tue, 5 May 2026 05:15:40 +0000 Subject: [PATCH 1/9] Add test for duplicate definition names --- src/test/incompatiblePropertiesTest.ts | 18 +++++++++++ .../incompatible-properties/common-types.json | 18 +++++++++++ .../different-names.json | 31 +++++++++++++++++++ .../duplicate-names.json | 31 +++++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 src/test/specs/incompatible-properties/common-types.json create mode 100644 src/test/specs/incompatible-properties/different-names.json create mode 100644 src/test/specs/incompatible-properties/duplicate-names.json diff --git a/src/test/incompatiblePropertiesTest.ts b/src/test/incompatiblePropertiesTest.ts index 7cf13e32..07713802 100644 --- a/src/test/incompatiblePropertiesTest.ts +++ b/src/test/incompatiblePropertiesTest.ts @@ -3,6 +3,24 @@ import * as path from "path" import { OpenApiDiff } from ".." import { fileUrl } from "./fileUrl" +test("should-allow-different-names", async () => { + const diff = new OpenApiDiff({}) + const file = `src/test/specs/incompatible-properties/different-names.json` + const filePath = fileUrl(path.resolve(file)) + + // expected to pass + await diff.compare(file, file) +}) + +test("should-allow-duplicate-names", async () => { + const diff = new OpenApiDiff({}) + const file = `src/test/specs/incompatible-properties/duplicate-names.json` + const filePath = fileUrl(path.resolve(file)) + + // expected to pass + await diff.compare(file, file) +}) + // This test is part of regression test suite for https://github.com/Azure/azure-sdk-tools/issues/5981 // Given a property with given type and name // When another property with the same name but an incompatible type is referenced diff --git a/src/test/specs/incompatible-properties/common-types.json b/src/test/specs/incompatible-properties/common-types.json new file mode 100644 index 00000000..7bf6c064 --- /dev/null +++ b/src/test/specs/incompatible-properties/common-types.json @@ -0,0 +1,18 @@ +{ + "swagger": "2.0", + "info": { + "title": "common-types", + "version": "1.0" + }, + "paths": {}, + "definitions": { + "Bar": { + "type": "object", + "properties": { + "p1": { + "type": "string" + } + } + } + } +} diff --git a/src/test/specs/incompatible-properties/different-names.json b/src/test/specs/incompatible-properties/different-names.json new file mode 100644 index 00000000..951529ab --- /dev/null +++ b/src/test/specs/incompatible-properties/different-names.json @@ -0,0 +1,31 @@ +{ + "swagger": "2.0", + "info": { + "title": "different-names", + "version": "1.0" + }, + "paths": {}, + "definitions": { + "Foo": { + "type": "object", + "properties": { + "p1": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "./common-types.json#/definitions/Bar" + } + ] + }, + "Baz": { + "type": "object", + "properties": { + "p1": { + "type": "object" + } + } + } + } +} diff --git a/src/test/specs/incompatible-properties/duplicate-names.json b/src/test/specs/incompatible-properties/duplicate-names.json new file mode 100644 index 00000000..343147a9 --- /dev/null +++ b/src/test/specs/incompatible-properties/duplicate-names.json @@ -0,0 +1,31 @@ +{ + "swagger": "2.0", + "info": { + "title": "duplicate-names", + "version": "1.0" + }, + "paths": {}, + "definitions": { + "Foo": { + "type": "object", + "properties": { + "p1": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "./common-types.json#/definitions/Bar" + } + ] + }, + "Bar": { + "type": "object", + "properties": { + "p1": { + "type": "object" + } + } + } + } +} From 65537057f578fc80e1745924acf215ca1e94b057 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Tue, 5 May 2026 05:18:30 +0000 Subject: [PATCH 2/9] improve tests --- src/test/specs/incompatible-properties/common-types.json | 2 +- src/test/specs/incompatible-properties/different-names.json | 4 ++-- src/test/specs/incompatible-properties/duplicate-names.json | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/specs/incompatible-properties/common-types.json b/src/test/specs/incompatible-properties/common-types.json index 7bf6c064..dc7b6474 100644 --- a/src/test/specs/incompatible-properties/common-types.json +++ b/src/test/specs/incompatible-properties/common-types.json @@ -6,7 +6,7 @@ }, "paths": {}, "definitions": { - "Bar": { + "Foo": { "type": "object", "properties": { "p1": { diff --git a/src/test/specs/incompatible-properties/different-names.json b/src/test/specs/incompatible-properties/different-names.json index 951529ab..5f4e7b86 100644 --- a/src/test/specs/incompatible-properties/different-names.json +++ b/src/test/specs/incompatible-properties/different-names.json @@ -6,7 +6,7 @@ }, "paths": {}, "definitions": { - "Foo": { + "Bar": { "type": "object", "properties": { "p1": { @@ -15,7 +15,7 @@ }, "allOf": [ { - "$ref": "./common-types.json#/definitions/Bar" + "$ref": "./common-types.json#/definitions/Foo" } ] }, diff --git a/src/test/specs/incompatible-properties/duplicate-names.json b/src/test/specs/incompatible-properties/duplicate-names.json index 343147a9..1cba0803 100644 --- a/src/test/specs/incompatible-properties/duplicate-names.json +++ b/src/test/specs/incompatible-properties/duplicate-names.json @@ -6,7 +6,7 @@ }, "paths": {}, "definitions": { - "Foo": { + "Bar": { "type": "object", "properties": { "p1": { @@ -15,11 +15,11 @@ }, "allOf": [ { - "$ref": "./common-types.json#/definitions/Bar" + "$ref": "./common-types.json#/definitions/Foo" } ] }, - "Bar": { + "Foo": { "type": "object", "properties": { "p1": { From 7409f93bca31f38c815acb0fa0660be130ee9849 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Wed, 6 May 2026 04:03:15 +0000 Subject: [PATCH 3/9] more tests --- src/test/incompatiblePropertiesTest.ts | 14 +------ .../reference-equals.json | 37 ++++++++++++++++++ .../structural-equals.json | 39 +++++++++++++++++++ 3 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 src/test/specs/incompatible-properties/reference-equals.json create mode 100644 src/test/specs/incompatible-properties/structural-equals.json diff --git a/src/test/incompatiblePropertiesTest.ts b/src/test/incompatiblePropertiesTest.ts index 07713802..dc2e16aa 100644 --- a/src/test/incompatiblePropertiesTest.ts +++ b/src/test/incompatiblePropertiesTest.ts @@ -3,19 +3,9 @@ import * as path from "path" import { OpenApiDiff } from ".." import { fileUrl } from "./fileUrl" -test("should-allow-different-names", async () => { +test.each(["different-names", "duplicate-names", "reference-equals", "structural-equals"])("should pass for %s", async testName => { const diff = new OpenApiDiff({}) - const file = `src/test/specs/incompatible-properties/different-names.json` - const filePath = fileUrl(path.resolve(file)) - - // expected to pass - await diff.compare(file, file) -}) - -test("should-allow-duplicate-names", async () => { - const diff = new OpenApiDiff({}) - const file = `src/test/specs/incompatible-properties/duplicate-names.json` - const filePath = fileUrl(path.resolve(file)) + const file = `src/test/specs/incompatible-properties/${testName}.json` // expected to pass await diff.compare(file, file) diff --git a/src/test/specs/incompatible-properties/reference-equals.json b/src/test/specs/incompatible-properties/reference-equals.json new file mode 100644 index 00000000..b3b77ea9 --- /dev/null +++ b/src/test/specs/incompatible-properties/reference-equals.json @@ -0,0 +1,37 @@ +{ + "swagger": "2.0", + "info": { + "title": "structural-equality", + "version": "1.0" + }, + "paths": {}, + "definitions": { + "Foo": { + "type": "object", + "properties": { + "bar": { + "$ref": "#/definitions/MyObject" + } + }, + "allOf": [ + { + "$ref": "#/definitions/Foo2" + } + ] + }, + "Foo2": { + "type": "object", + "properties": { + "bar": { + "$ref": "#/definitions/MyObject" + } + } + }, + "MyObject": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } +} diff --git a/src/test/specs/incompatible-properties/structural-equals.json b/src/test/specs/incompatible-properties/structural-equals.json new file mode 100644 index 00000000..6e01db69 --- /dev/null +++ b/src/test/specs/incompatible-properties/structural-equals.json @@ -0,0 +1,39 @@ +{ + "swagger": "2.0", + "info": { + "title": "structural-equality", + "version": "1.0" + }, + "paths": {}, + "definitions": { + "Foo": { + "type": "object", + "properties": { + "bar": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "allOf": [ + { + "$ref": "#/definitions/Foo2" + } + ] + }, + "Foo2": { + "type": "object", + "properties": { + "bar": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } +} + + \ No newline at end of file From 880bdc385a371c99c2352b94f52124699ae03b9b Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Wed, 6 May 2026 04:03:53 +0000 Subject: [PATCH 4/9] allow inherited properties to be structurally equal --- src/lib/util/resolveSwagger.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/util/resolveSwagger.ts b/src/lib/util/resolveSwagger.ts index adac48f7..1c171f20 100644 --- a/src/lib/util/resolveSwagger.ts +++ b/src/lib/util/resolveSwagger.ts @@ -6,6 +6,7 @@ import { toArray } from "@ts-common/iterator" import { cloneDeep, Data, FilePosition, getFilePosition, getInfo, getPath, ObjectInfo } from "@ts-common/source-map" import * as sourceMap from "source-map" import * as sm from "@ts-common/string-map" +import equal from "fast-deep-equal" import { readFileSync, writeFileSync } from "fs" import * as path from "path" import { pathToJsonPointer } from "./utils" @@ -247,12 +248,13 @@ export class ResolveSwagger { if (allOfSchema.properties) { sm.keys(allOfSchema.properties).forEach(key => { if (sm.keys(schemaList).some(k => k === key)) { - if (!this.isEqual(allOfSchema.properties[key], schemaList[key])) { - const allOfProp = allOfSchema.properties[key] + const allOfProp = allOfSchema.properties[key] + const schemaListProp = schemaList[key] + + if (!this.isEqual(allOfProp, schemaListProp) && !equal(allOfProp, schemaListProp)) { const allOfPath = getPath(getInfo(allOfProp) as ObjectInfo) const allOfOriginalPosition = this.map.originalPositionFor(getFilePosition(allOfProp) as FilePosition) - const schemaListProp = schemaList[key] const schemaListPath = getPath(getInfo(schemaListProp) as ObjectInfo) const schemaListOriginalPosition = this.map.originalPositionFor(getFilePosition(schemaListProp) as FilePosition) From 678d15377a0c18c2b6accba47c27cf53a22f9762 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Wed, 6 May 2026 04:05:11 +0000 Subject: [PATCH 5/9] add fast-deep-equal --- package-lock.json | 2 +- package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index d2fcf3fc..d7d115c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@ts-common/source-map": "0.5.0", "@ts-common/string-map": "0.3.0", "autorest": "^3.8.0", + "fast-deep-equal": "^3.1.3", "js-yaml": "^4.1.0", "json-pointer": "0.6.2", "json-refs": "^3.0.15", @@ -3318,7 +3319,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { diff --git a/package.json b/package.json index 725b0297..05e3bdf4 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@ts-common/source-map": "0.5.0", "@ts-common/string-map": "0.3.0", "autorest": "^3.8.0", + "fast-deep-equal": "^3.1.3", "js-yaml": "^4.1.0", "json-pointer": "0.6.2", "json-refs": "^3.0.15", From f36cf111dde13754cf40c2d958ec412d6d0afd85 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Tue, 5 May 2026 21:06:32 -0700 Subject: [PATCH 6/9] Clean up structural-equals.json formatting Remove trailing whitespace and ensure proper formatting. --- src/test/specs/incompatible-properties/structural-equals.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/specs/incompatible-properties/structural-equals.json b/src/test/specs/incompatible-properties/structural-equals.json index 6e01db69..641f80c9 100644 --- a/src/test/specs/incompatible-properties/structural-equals.json +++ b/src/test/specs/incompatible-properties/structural-equals.json @@ -35,5 +35,3 @@ } } } - - \ No newline at end of file From 8d82e008c9829bc5e8d3c275f718fa359a2a2901 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Tue, 5 May 2026 21:08:53 -0700 Subject: [PATCH 7/9] Refactor condition for property equality check --- src/lib/util/resolveSwagger.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/util/resolveSwagger.ts b/src/lib/util/resolveSwagger.ts index 1c171f20..b2615481 100644 --- a/src/lib/util/resolveSwagger.ts +++ b/src/lib/util/resolveSwagger.ts @@ -248,10 +248,10 @@ export class ResolveSwagger { if (allOfSchema.properties) { sm.keys(allOfSchema.properties).forEach(key => { if (sm.keys(schemaList).some(k => k === key)) { - const allOfProp = allOfSchema.properties[key] - const schemaListProp = schemaList[key] + if (!this.isEqual(allOfSchema.properties[key], schemaList[key]) && !equal(allOfSchema.properties[key], schemaList[key])) { + const allOfProp = allOfSchema.properties[key] + const schemaListProp = schemaList[key] - if (!this.isEqual(allOfProp, schemaListProp) && !equal(allOfProp, schemaListProp)) { const allOfPath = getPath(getInfo(allOfProp) as ObjectInfo) const allOfOriginalPosition = this.map.originalPositionFor(getFilePosition(allOfProp) as FilePosition) From 7d93365ce12f95f23ad17e95f19053a22dbdd3e8 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Wed, 6 May 2026 04:09:50 +0000 Subject: [PATCH 8/9] format --- src/lib/util/resolveSwagger.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib/util/resolveSwagger.ts b/src/lib/util/resolveSwagger.ts index b2615481..f0b1cfd4 100644 --- a/src/lib/util/resolveSwagger.ts +++ b/src/lib/util/resolveSwagger.ts @@ -4,11 +4,11 @@ import * as jsonPointer from "json-pointer" import { toArray } from "@ts-common/iterator" import { cloneDeep, Data, FilePosition, getFilePosition, getInfo, getPath, ObjectInfo } from "@ts-common/source-map" -import * as sourceMap from "source-map" import * as sm from "@ts-common/string-map" import equal from "fast-deep-equal" import { readFileSync, writeFileSync } from "fs" import * as path from "path" +import * as sourceMap from "source-map" import { pathToJsonPointer } from "./utils" /* @@ -250,11 +250,10 @@ export class ResolveSwagger { if (sm.keys(schemaList).some(k => k === key)) { if (!this.isEqual(allOfSchema.properties[key], schemaList[key]) && !equal(allOfSchema.properties[key], schemaList[key])) { const allOfProp = allOfSchema.properties[key] - const schemaListProp = schemaList[key] - const allOfPath = getPath(getInfo(allOfProp) as ObjectInfo) const allOfOriginalPosition = this.map.originalPositionFor(getFilePosition(allOfProp) as FilePosition) + const schemaListProp = schemaList[key] const schemaListPath = getPath(getInfo(schemaListProp) as ObjectInfo) const schemaListOriginalPosition = this.map.originalPositionFor(getFilePosition(schemaListProp) as FilePosition) From 9b75bc5ee62dd26de959c16c002ff1e385671815 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Wed, 6 May 2026 04:10:13 +0000 Subject: [PATCH 9/9] format --- src/lib/util/resolveSwagger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/util/resolveSwagger.ts b/src/lib/util/resolveSwagger.ts index f0b1cfd4..45bbfa6a 100644 --- a/src/lib/util/resolveSwagger.ts +++ b/src/lib/util/resolveSwagger.ts @@ -4,11 +4,11 @@ import * as jsonPointer from "json-pointer" import { toArray } from "@ts-common/iterator" import { cloneDeep, Data, FilePosition, getFilePosition, getInfo, getPath, ObjectInfo } from "@ts-common/source-map" +import * as sourceMap from "source-map" import * as sm from "@ts-common/string-map" import equal from "fast-deep-equal" import { readFileSync, writeFileSync } from "fs" import * as path from "path" -import * as sourceMap from "source-map" import { pathToJsonPointer } from "./utils" /*