|
|
|
@@ -0,0 +1,99 @@
|
|
|
|
|
(ns tbd.http-test
|
|
|
|
|
(:require [clojure.test :refer [deftest is testing]]
|
|
|
|
|
[muuntaja.core :as m]
|
|
|
|
|
[tbd.auth.routes-test :as t]
|
|
|
|
|
[tbd.core :as core]
|
|
|
|
|
[tbd.http :as http]
|
|
|
|
|
[tbd.test-util :as test-util]
|
|
|
|
|
[tbd.todo.db-test :as db-test])
|
|
|
|
|
(:import (java.io ByteArrayInputStream)))
|
|
|
|
|
|
|
|
|
|
(defn call
|
|
|
|
|
"`headers` is a map; `body` a string or nil. The response body is decoded by its Content-Type."
|
|
|
|
|
[app method uri headers & [body]]
|
|
|
|
|
(test-util/decode-body
|
|
|
|
|
(app (cond-> {:request-method method :uri uri :headers headers}
|
|
|
|
|
body (assoc :body (ByteArrayInputStream. (.getBytes ^String body)))))))
|
|
|
|
|
|
|
|
|
|
(defn content-type [resp] (get-in resp [:headers "Content-Type"]))
|
|
|
|
|
|
|
|
|
|
(defn get-add-swagger []
|
|
|
|
|
(core/app {:request-method :get :uri "/swagger.json" :headers {}}))
|
|
|
|
|
|
|
|
|
|
(deftest response-format-test
|
|
|
|
|
(let [get-add (fn [accept]
|
|
|
|
|
(test-util/decode-body
|
|
|
|
|
(core/app (cond-> {:request-method :get :uri "/api/add" :query-string "x=2&y=3" :headers {}}
|
|
|
|
|
accept (assoc-in [:headers "accept"] accept)))))]
|
|
|
|
|
(testing "EDN is the default: no Accept, or one nothing here satisfies"
|
|
|
|
|
(doseq [accept [nil "*/*" "application/xml"]]
|
|
|
|
|
(let [resp (get-add accept)]
|
|
|
|
|
(is (= "application/edn; charset=utf-8" (content-type resp)) (str "accept: " accept))
|
|
|
|
|
(is (= {:result 5} (:body resp))))))
|
|
|
|
|
(testing "JSON when asked for"
|
|
|
|
|
(let [resp (core/app {:request-method :get :uri "/api/add" :query-string "x=2&y=3"
|
|
|
|
|
:headers {"accept" "application/json"}})]
|
|
|
|
|
(is (= "application/json; charset=utf-8" (content-type resp)))
|
|
|
|
|
(is (= "{\"result\":5}" (slurp (:body resp))))))
|
|
|
|
|
(testing "Transit when asked for"
|
|
|
|
|
(let [resp (get-add "application/transit+json")]
|
|
|
|
|
(is (= "application/transit+json; charset=utf-8" (content-type resp)))
|
|
|
|
|
(is (= {:result 5} (:body resp)))))
|
|
|
|
|
(testing "the first Accept type we can produce wins"
|
|
|
|
|
(is (= "application/json; charset=utf-8" (content-type (get-add "application/xml, application/json")))))
|
|
|
|
|
(testing "text/plain responses are left alone"
|
|
|
|
|
(let [resp (core/app {:request-method :get :uri "/api/ping" :headers {"accept" "application/json"}})]
|
|
|
|
|
(is (= "text/plain; charset=utf-8" (content-type resp)))
|
|
|
|
|
(is (= "pong" (:body resp)))))
|
|
|
|
|
(testing "swagger.json stays JSON, even with no Accept header"
|
|
|
|
|
(is (= "application/json; charset=utf-8" (content-type (get-add-swagger)))))))
|
|
|
|
|
|
|
|
|
|
(deftest error-format-test
|
|
|
|
|
(testing "errors follow the same negotiation"
|
|
|
|
|
(let [req (fn [accept] (core/app (cond-> {:request-method :get :uri "/api/add" :query-string "x=abc&y=3" :headers {}}
|
|
|
|
|
accept (assoc-in [:headers "accept"] accept))))]
|
|
|
|
|
(is (= "application/edn; charset=utf-8" (content-type (req nil))))
|
|
|
|
|
(is (= "application/json; charset=utf-8" (content-type (req "application/json"))))
|
|
|
|
|
(is (= 400 (:status (req "application/json"))))
|
|
|
|
|
(is (contains? (:body (test-util/decode-body (req "application/json"))) :error)))
|
|
|
|
|
(testing "including the auth middleware's 401"
|
|
|
|
|
(let [app (t/make-app (t/gitea-stub "alice"))
|
|
|
|
|
resp (call app :get "/api/todos" {"accept" "application/json"})]
|
|
|
|
|
(is (= 401 (:status resp)))
|
|
|
|
|
(is (= "application/json; charset=utf-8" (content-type resp)))
|
|
|
|
|
(is (= {:error "login required"} (:body resp)))
|
|
|
|
|
(is (= "application/edn; charset=utf-8" (content-type (call app :get "/api/todos" {}))))))))
|
|
|
|
|
|
|
|
|
|
(deftest request-format-test
|
|
|
|
|
(let [app (t/make-app (t/gitea-stub "alice" 7) :todo {:db-path (db-test/temp-db-path)})
|
|
|
|
|
cookie (t/login! app)
|
|
|
|
|
post (fn [headers body] (call app :post "/api/todos" (assoc headers "cookie" cookie) body))]
|
|
|
|
|
(testing "JSON in, EDN out by default"
|
|
|
|
|
(let [resp (post {"content-type" "application/json"} "{\"title\":\"a\"}")]
|
|
|
|
|
(is (= 201 (:status resp)))
|
|
|
|
|
(is (= "application/edn; charset=utf-8" (content-type resp)))
|
|
|
|
|
(is (= "a" (:title (:body resp))))))
|
|
|
|
|
(testing "EDN in, JSON out when asked for"
|
|
|
|
|
(let [resp (post {"content-type" "application/edn" "accept" "application/json"} "{:title \"b\"}")]
|
|
|
|
|
(is (= 201 (:status resp)))
|
|
|
|
|
(is (= "application/json; charset=utf-8" (content-type resp)))
|
|
|
|
|
(is (= "b" (:title (:body resp))))))
|
|
|
|
|
(testing "with no Content-Type the body is EDN"
|
|
|
|
|
(let [resp (post {} "{:title \"c\"}")]
|
|
|
|
|
(is (= 201 (:status resp)))
|
|
|
|
|
(is (= "c" (:title (:body resp))))))
|
|
|
|
|
(testing "Transit in"
|
|
|
|
|
(let [body (String. (.readAllBytes (m/encode http/muuntaja "application/transit+json" {:title "d"})) "UTF-8")
|
|
|
|
|
resp (post {"content-type" "application/transit+json"} body)]
|
|
|
|
|
(is (= 201 (:status resp)))
|
|
|
|
|
(is (= "d" (:title (:body resp))))))
|
|
|
|
|
(testing "a body that isn't valid for its type is a 400 in the negotiated format"
|
|
|
|
|
(let [resp (post {"content-type" "application/json" "accept" "application/json"} "{:title \"e\"}")]
|
|
|
|
|
(is (= 400 (:status resp)))
|
|
|
|
|
(is (= "application/json; charset=utf-8" (content-type resp)))
|
|
|
|
|
(is (= {:error "malformed request body"} (:body resp))))
|
|
|
|
|
(is (= 400 (:status (post {} "{\"title\":\"f\"}")))
|
|
|
|
|
"JSON isn't assumed when nothing says so"))
|
|
|
|
|
(testing "listing shows only what was created"
|
|
|
|
|
(is (= ["a" "b" "c" "d"]
|
|
|
|
|
(map :title (:todos (:body (call app :get "/api/todos" {"cookie" cookie "accept" "application/json"})))))))))
|