diff --git a/Sources/OpenAPIRuntime/Base/OpenAPIValue.swift b/Sources/OpenAPIRuntime/Base/OpenAPIValue.swift index 0ed8ff1b..5cbc7455 100644 --- a/Sources/OpenAPIRuntime/Base/OpenAPIValue.swift +++ b/Sources/OpenAPIRuntime/Base/OpenAPIValue.swift @@ -281,9 +281,9 @@ public struct OpenAPIValueContainer: Codable, Hashable, Sendable { case let value as [(any Sendable)?]: for item in value { hasher.combine(OpenAPIValueContainer(validatedValue: item)) } case let value as [String: (any Sendable)?]: - for (key, itemValue) in value { + for key in value.keys.sorted() { hasher.combine(key) - hasher.combine(OpenAPIValueContainer(validatedValue: itemValue)) + hasher.combine(OpenAPIValueContainer(validatedValue: value[key])) } default: break } @@ -421,9 +421,9 @@ public struct OpenAPIObjectContainer: Codable, Hashable, Sendable { // swift-format-ignore: AllPublicDeclarationsHaveDocumentation public func hash(into hasher: inout Hasher) { - for (key, itemValue) in value { + for key in value.keys.sorted() { hasher.combine(key) - hasher.combine(OpenAPIValueContainer(validatedValue: itemValue)) + hasher.combine(OpenAPIValueContainer(validatedValue: value[key])) } } } diff --git a/Tests/OpenAPIRuntimeTests/Base/Test_OpenAPIValue.swift b/Tests/OpenAPIRuntimeTests/Base/Test_OpenAPIValue.swift index fb4b51e7..3f578d58 100644 --- a/Tests/OpenAPIRuntimeTests/Base/Test_OpenAPIValue.swift +++ b/Tests/OpenAPIRuntimeTests/Base/Test_OpenAPIValue.swift @@ -444,6 +444,20 @@ final class Test_OpenAPIValue: Test_Runtime { encodedData ) } + + func testHashing_objectOrderIndependence_success() throws { + let container1 = try OpenAPIObjectContainer(unvalidatedValue: ["foo": 0, "bar": 1]) + let container2 = try OpenAPIObjectContainer(unvalidatedValue: ["bar": 1, "foo": 0]) + XCTAssertEqual(container1, container2) + XCTAssertEqual(container1.hashValue, container2.hashValue) + } + + func testHashing_containerOrderIndependence_success() throws { + let container1 = try OpenAPIValueContainer(unvalidatedValue: ["foo": 0, "bar": 1]) + let container2 = try OpenAPIValueContainer(unvalidatedValue: ["bar": 1, "foo": 0]) + XCTAssertEqual(container1, container2) + XCTAssertEqual(container1.hashValue, container2.hashValue) + } } struct MyAnyOf2: Codable, Hashable,