From feda8ac9f2300fed3568a4ced858ed1e1e3bf89c Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 28 Aug 2026 07:44:50 +0000 Subject: [PATCH] :recycle: 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 --- backend/src/app/http/middleware.clj | 8 ++-- backend/src/app/http/sse.clj | 3 +- .../http_content_negotiation_test.clj | 40 +++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 backend/test/backend_tests/http_content_negotiation_test.clj diff --git a/backend/src/app/http/middleware.clj b/backend/src/app/http/middleware.clj index 110c81349c..6da8e23ba1 100644 --- a/backend/src/app/http/middleware.clj +++ b/backend/src/app/http/middleware.clj @@ -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 diff --git a/backend/src/app/http/sse.clj b/backend/src/app/http/sse.clj index 4d70bd8bf9..6271ccb4e5 100644 --- a/backend/src/app/http/sse.clj +++ b/backend/src/app/http/sse.clj @@ -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 diff --git a/backend/test/backend_tests/http_content_negotiation_test.clj b/backend/test/backend_tests/http_content_negotiation_test.clj new file mode 100644 index 0000000000..1cff5641f1 --- /dev/null +++ b/backend/test/backend_tests/http_content_negotiation_test.clj @@ -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 {}}))))