♻️ Add defensive defaults and unit tests for content negotiation

Both case expressions dispatching on the negotiated format now
fall back to the transit encoding/format when an unexpected value
shows up, instead of silently returning nil.

Add direct unit tests for app.http.content-negotiation/negotiate-format
covering the Accept header, the _fmt=json query parameter precedence,
and the transit fallback when no explicit signal is present.

AI-assisted-by: glm-5.3-flash
This commit is contained in:
Andrey Antukh 2026-08-28 07:44:50 +00:00
parent 4dccd254b6
commit feda8ac9f2
3 changed files with 47 additions and 4 deletions

View File

@ -178,9 +178,11 @@
response)))
(format-response [response request]
(case (cnegot/negotiate-format request)
:transit (format-response-with-transit response request)
:json (format-response-with-json response request)))
(let [format (cnegot/negotiate-format request)]
(case format
:transit (format-response-with-transit response request)
:json (format-response-with-json response request)
(format-response-with-transit response request))))
(process-response [response request]
(cond-> response

View File

@ -43,7 +43,8 @@
[format]
(case format
:transit #(t/encode-str % {:type :json-verbose})
:json cnegot/json-encode-str))
:json cnegot/json-encode-str
#(t/encode-str % {:type :json-verbose})))
;; ---- PUBLIC API

View File

@ -0,0 +1,40 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns backend-tests.http-content-negotiation-test
(:require
[app.http.content-negotiation :as cnegot]
[clojure.test :as t]))
(t/deftest negotiate-format-defaults-to-transit
(t/is (= :transit (cnegot/negotiate-format {})))
(t/is (= :transit (cnegot/negotiate-format {:headers {"accept" "*/*"}})))
(t/is (= :transit (cnegot/negotiate-format {:headers {"accept" "text/event-stream"}}))))
(t/deftest negotiate-format-from-accept-header
(t/is (= :transit (cnegot/negotiate-format
{:headers {"accept" "application/transit+json"}})))
(t/is (= :transit (cnegot/negotiate-format
{:headers {"accept" "application/transit+json,text/event-stream,*/*"}})))
(t/is (= :json (cnegot/negotiate-format
{:headers {"accept" "application/json"}})))
(t/is (= :json (cnegot/negotiate-format
{:headers {"accept" "application/json, text/event-stream"}}))))
(t/deftest negotiate-format-from-query-param
(t/is (= :json (cnegot/negotiate-format {:query-params {:_fmt "json"}})))
(t/is (= :json (cnegot/negotiate-format
{:query-params {:_fmt "json"}
:headers {"accept" "application/transit+json"}}))))
(t/deftest negotiate-format-only-json-param-is-recognized
(t/is (= :json (cnegot/negotiate-format
{:query-params {:_fmt "transit"}
:headers {"accept" "application/json"}})))
(t/is (= :transit (cnegot/negotiate-format {:query-params {:_fmt "transit"}}))))
(t/deftest negotiate-format-without-accept-header
(t/is (= :transit (cnegot/negotiate-format {:headers {}}))))