Skip to content

Make OpenAPI/Swagger tests independent of parameter array order - #798

Merged
opqdonut merged 2 commits into
metosin:masterfrom
The-Alchemist:deterministic-parameter-order
Sep 18, 2026
Merged

opqdonut merged 2 commits into
metosin:masterfrom
The-Alchemist:deterministic-parameter-order

Conversation

@The-Alchemist

@The-Alchemist The-Alchemist commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • OpenAPI and Swagger :parameters are unordered arrays. Tests previously used = / match? on those vectors, which is JVM-dependent because Clojure maps do not guarantee seq order when the spec is built from :parameters maps.
  • Sort :parameters in the tests by :in and name before comparing. Production emission is unchanged.

Motivation

Clojure's map contract does not promise insertion order except for array-map. Alternate JVM implementations (like our Cloffle, based on Clojure on GraalVM) can emit path before query. Spec consumers and golden tests compare parameter vectors, which are order-sensitive.

We currently maintain a separate patch to the tests because we run reitit as part of our compatibility suite.

Compatibility

No public API change and no change to generated OpenAPI/Swagger output. Only test helpers and assertions were updated.

Comment thread test/cljc/reitit/openapi_test.clj Outdated
(testing "spec is valid"
(is (nil? (validate spec))))))))

(deftest parameter-location-order-independent-of-map-seq

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overkill, maybe?

Comment thread test/cljc/reitit/swagger_test.clj Outdated
(is (= ["query" "body" "formData" "header" "path"]
(map :in (get-in spec [:paths "/parameters" :post :parameters]))))))

(deftest parameter-location-order-independent-of-map-seq

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overkill?

strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description)
openapi (->> (strip-endpoint-keys openapi)
(merge {:openapi "3.2.0"
(merge {:openapi "3.1.0"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge/rebase artifact likely, openapi upgrade shouldn't be reverted.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch! fixed!

@opqdonut

Copy link
Copy Markdown
Member

Quick comment: I'm not sure I like this hack. I feel that the ordering should get fixed at json-serialisation-time, not in the clojure data structures.

@The-Alchemist
The-Alchemist force-pushed the deterministic-parameter-order branch 2 times, most recently from 3029278 to 3bdb298 Compare September 16, 2026 17:45
@The-Alchemist The-Alchemist changed the title Emit OpenAPI/Swagger parameters in spec location order Make OpenAPI/Swagger tests independent of parameter array order Sep 16, 2026
@The-Alchemist

The-Alchemist commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor Author

@opqdonut

Quick comment: I'm not sure I like this hack. I feel that the ordering should get fixed at json-serialisation-time, not in the clojure data structures.

I think you're totally right; I fixed the tests to be sort-order independent. let me know what you think!

@opqdonut opqdonut left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach is good. I have some improvement suggestions on small details, but I can also implement those myself if you don't feel like making yet another revision of this. Let me know which way you want to do it!

Comment thread test/cljc/reitit/openapi_test.clj Outdated
:required true
:description "description :p"
:schema {:type "string"}}]
(is (match? (sorted-parameters

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bw matcher-combinators has in-any-order, which could take care of this

Comment thread test/cljc/reitit/swagger_test.clj Outdated
:required true
:type "string"}])
(sorted-parameters
(normalize

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorted-parameters could be folded into normalize in this test namespace

Comment thread test/cljc/reitit/swagger_test.clj Outdated
spec (:body (app {:request-method :get, :uri "/swagger.json"}))
ins (map :in (get-in spec [:paths "/parameters" :post :parameters]))]
(is (= 5 (count ins)))
(is (= #{"query" "body" "formData" "header" "path"} (set ins)))))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, you could get by with one is by using (is (= [...] (sort ...)))

methods
methods)))
paths
paths)))))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm tempted to code-golf this, but I can do it after merging this PR 😁

Clojure maps do not guarantee seq order, so `=` on :parameters vectors
is JVM-dependent. Parameter order is not meaningful in the spec; sort
by :in and name in assertions instead of canonicalizing production output.

Signed-off-by: The-Alchemist <kap4020@gmail.com>
Signed-off-by: The-Alchemist <kap4020@gmail.com>
@The-Alchemist
The-Alchemist force-pushed the deterministic-parameter-order branch from 3c77bda to ab13435 Compare September 18, 2026 01:36

@opqdonut opqdonut left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the fixes!

@opqdonut
opqdonut merged commit a6a644e into metosin:master Sep 18, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

3 participants