@@ -0,0 +1,111 @@
|
||||
(ns tbd.auth.core-test
|
||||
(:require [clojure.string :as str]
|
||||
[clojure.test :refer [deftest is testing]]
|
||||
[tbd.auth.core :as auth]))
|
||||
|
||||
(def full-env
|
||||
{"GITEA_URL" "https://git.example.com/"
|
||||
"GITEA_CLIENT_ID" "cid"
|
||||
"GITEA_CLIENT_SECRET" "sec"
|
||||
"BASE_URL" "https://tbd.example.com/"
|
||||
"ADMIN_USERS" " Alice, bob ,,"
|
||||
"SESSION_SECRET" "s3cret"})
|
||||
|
||||
(deftest env-config-test
|
||||
(testing "a complete environment"
|
||||
(let [config (auth/env-config full-env)]
|
||||
(is (auth/configured? config))
|
||||
(is (= "https://git.example.com" (:gitea-url config)) "trailing slash is dropped")
|
||||
(is (= "https://tbd.example.com/auth/callback" (:redirect-uri config)))
|
||||
(is (= #{"alice" "bob"} (:admin-users config)) "trimmed, lower-cased, blanks dropped")
|
||||
(is (true? (:secure-cookies? config)) "https base URL => Secure cookies")
|
||||
(is (= "s3cret" (:session-secret config)))))
|
||||
(testing "plain http base URL does not mark cookies Secure"
|
||||
(is (false? (:secure-cookies? (auth/env-config (assoc full-env "BASE_URL" "http://localhost:3000"))))))
|
||||
(testing "an empty environment is unconfigured and has no admins"
|
||||
(let [config (auth/env-config {})]
|
||||
(is (not (auth/configured? config)))
|
||||
(is (= #{} (:admin-users config)))
|
||||
(is (nil? (:session-secret config)))))
|
||||
(testing "blank values count as unset"
|
||||
(is (not (auth/configured? (auth/env-config (assoc full-env "GITEA_CLIENT_SECRET" " "))))))
|
||||
(testing "each of the four settings is required"
|
||||
(doseq [k ["GITEA_URL" "GITEA_CLIENT_ID" "GITEA_CLIENT_SECRET" "BASE_URL"]]
|
||||
(is (not (auth/configured? (auth/env-config (dissoc full-env k)))) k))))
|
||||
|
||||
(deftest admin-test
|
||||
(let [config (auth/env-config full-env)]
|
||||
(is (auth/admin? config "alice"))
|
||||
(is (auth/admin? config "ALICE") "Gitea usernames are case-insensitive")
|
||||
(is (not (auth/admin? config "mallory")))
|
||||
(is (not (auth/admin? (auth/env-config {}) "alice")) "an empty allowlist admits nobody")))
|
||||
|
||||
(deftest session-key-test
|
||||
(is (= 16 (count (auth/session-key "s3cret"))))
|
||||
(is (= (seq (auth/session-key "s3cret")) (seq (auth/session-key "s3cret"))))
|
||||
(is (not= (seq (auth/session-key "a")) (seq (auth/session-key "b"))))
|
||||
(testing "without a secret each call yields a fresh random key"
|
||||
(is (= 16 (count (auth/session-key nil))))
|
||||
(is (not= (seq (auth/session-key nil)) (seq (auth/session-key nil))))))
|
||||
|
||||
(deftest state-test
|
||||
(let [a (auth/new-state)]
|
||||
(is (re-matches #"[A-Za-z0-9_-]{43}" a))
|
||||
(is (not= a (auth/new-state))))
|
||||
(is (auth/state-matches? "abc" "abc"))
|
||||
(is (not (auth/state-matches? "abc" "abd")))
|
||||
(is (not (auth/state-matches? "abc" "abcd")))
|
||||
(testing "a missing state never matches, even against another missing one"
|
||||
(is (not (auth/state-matches? nil nil)))
|
||||
(is (not (auth/state-matches? "abc" nil)))
|
||||
(is (not (auth/state-matches? nil "abc")))))
|
||||
|
||||
(deftest authorize-url-test
|
||||
(let [url (auth/authorize-url (auth/env-config full-env) "st ate")]
|
||||
(is (str/starts-with? url "https://git.example.com/login/oauth/authorize?"))
|
||||
(is (str/includes? url "client_id=cid"))
|
||||
(is (str/includes? url "response_type=code"))
|
||||
(is (str/includes? url "redirect_uri=https%3A%2F%2Ftbd.example.com%2Fauth%2Fcallback"))
|
||||
(is (str/includes? url "state=st+ate") "values are form-encoded")))
|
||||
|
||||
(defn- recording-http
|
||||
"An :http stub that records requests and answers with `responses` in order."
|
||||
[calls & responses]
|
||||
(let [responses (atom responses)]
|
||||
(fn [req]
|
||||
(swap! calls conj req)
|
||||
(let [[r & more] @responses]
|
||||
(reset! responses more)
|
||||
(if (instance? Throwable r) (throw r) r)))))
|
||||
|
||||
(deftest exchange-code-test
|
||||
(let [calls (atom [])
|
||||
config (assoc (auth/env-config full-env)
|
||||
:http (recording-http calls
|
||||
{:status 200 :body "{\"access_token\":\"tok\",\"token_type\":\"bearer\"}"}
|
||||
{:status 400 :body "{\"error\":\"invalid_grant\"}"}))]
|
||||
(is (= "tok" (auth/exchange-code config "the-code")))
|
||||
(let [{:keys [method url headers body]} (first @calls)]
|
||||
(is (= :post method))
|
||||
(is (= "https://git.example.com/login/oauth/access_token" url))
|
||||
(is (= "application/json" (get headers "Accept")))
|
||||
(is (= "application/x-www-form-urlencoded" (get headers "Content-Type")))
|
||||
(doseq [part ["grant_type=authorization_code" "client_id=cid" "client_secret=sec" "code=the-code"
|
||||
"redirect_uri=https%3A%2F%2Ftbd.example.com%2Fauth%2Fcallback"]]
|
||||
(is (str/includes? body part) part)))
|
||||
(is (nil? (auth/exchange-code config "used-code")) "a refused code yields nil")))
|
||||
|
||||
(deftest fetch-user-test
|
||||
(let [calls (atom [])
|
||||
config (assoc (auth/env-config full-env)
|
||||
:http (recording-http calls
|
||||
{:status 200 :body "{\"login\":\"alice\",\"id\":7,\"email\":\"a@x\"}"}
|
||||
{:status 401 :body "{}"}
|
||||
{:status 200 :body "{\"id\":7}"}))]
|
||||
(is (= {:login "alice" :id 7} (auth/fetch-user config "tok")))
|
||||
(let [{:keys [method url headers]} (first @calls)]
|
||||
(is (= :get method))
|
||||
(is (= "https://git.example.com/api/v1/user" url))
|
||||
(is (= "Bearer tok" (get headers "Authorization"))))
|
||||
(is (nil? (auth/fetch-user config "bad")) "a refused token yields nil")
|
||||
(is (nil? (auth/fetch-user config "odd")) "a response without a login yields nil")))
|
||||
@@ -0,0 +1,158 @@
|
||||
(ns tbd.auth.routes-test
|
||||
(:require [clojure.edn :as edn]
|
||||
[clojure.string :as str]
|
||||
[clojure.test :refer [deftest is testing]]
|
||||
[tbd.auth.middleware :as mw]
|
||||
[tbd.core :as core]))
|
||||
|
||||
(defn gitea-stub
|
||||
"Stands in for Gitea: any code is exchanged for a token that resolves to `login`."
|
||||
[login]
|
||||
(fn [{:keys [url]}]
|
||||
(cond
|
||||
(str/ends-with? url "/login/oauth/access_token") {:status 200 :body "{\"access_token\":\"tok\"}"}
|
||||
(str/ends-with? url "/api/v1/user") {:status 200 :body (format "{\"login\":\"%s\",\"id\":7}" login)})))
|
||||
|
||||
(defn make-app [http & {:as overrides}]
|
||||
(core/make-app {:auth (merge {:gitea-url "https://gitea.test"
|
||||
:client-id "cid"
|
||||
:client-secret "sec"
|
||||
:redirect-uri "http://tbd.test/auth/callback"
|
||||
:admin-users #{"alice"}
|
||||
:session-secret "test-secret"
|
||||
:secure-cookies? false
|
||||
:http http}
|
||||
overrides)
|
||||
:nrepl {:port 0 :bind "127.0.0.1"}}))
|
||||
|
||||
(defn request [app method uri & {:keys [cookie query]}]
|
||||
(let [resp (app (cond-> {:request-method method :uri uri :query-string query :headers {}}
|
||||
cookie (assoc-in [:headers "cookie"] cookie)))]
|
||||
(cond-> resp
|
||||
(and (string? (:body resp))
|
||||
(str/includes? (get-in resp [:headers "Content-Type"] "") "edn"))
|
||||
(update :body edn/read-string))))
|
||||
|
||||
(defn set-cookie
|
||||
"The `name=value` pair from a response's Set-Cookie header."
|
||||
[resp]
|
||||
(let [h (get-in resp [:headers "Set-Cookie"])]
|
||||
(first (str/split (first (if (string? h) [h] h)) #";"))))
|
||||
|
||||
(defn location-state [resp]
|
||||
(second (re-find #"[?&]state=([^&]+)" (get-in resp [:headers "Location"]))))
|
||||
|
||||
(defn login!
|
||||
"Runs the whole login dance and returns the session cookie."
|
||||
[app]
|
||||
(let [start (request app :get "/auth/login")
|
||||
done (request app :get "/auth/callback"
|
||||
:cookie (set-cookie start)
|
||||
:query (str "code=abc&state=" (location-state start)))]
|
||||
(set-cookie done)))
|
||||
|
||||
(deftest login-redirect-test
|
||||
(let [resp (request (make-app (gitea-stub "alice")) :get "/auth/login")
|
||||
location (get-in resp [:headers "Location"])]
|
||||
(is (= 302 (:status resp)))
|
||||
(is (str/starts-with? location "https://gitea.test/login/oauth/authorize?"))
|
||||
(is (str/includes? location "client_id=cid"))
|
||||
(is (some? (location-state resp)))
|
||||
(let [cookie (get-in resp [:headers "Set-Cookie"])
|
||||
cookie (first (if (string? cookie) [cookie] cookie))]
|
||||
(is (str/starts-with? cookie "tbd-session="))
|
||||
(is (re-find #"(?i)HttpOnly" cookie))
|
||||
(is (re-find #"(?i)SameSite=Lax" cookie))
|
||||
(is (not (re-find #"(?i);\s*Secure" cookie)) "http base URL, so no Secure flag"))))
|
||||
|
||||
(deftest secure-cookie-test
|
||||
(let [resp (request (make-app (gitea-stub "alice") :secure-cookies? true) :get "/auth/login")
|
||||
cookie (first (let [h (get-in resp [:headers "Set-Cookie"])] (if (string? h) [h] h)))]
|
||||
(is (re-find #"(?i);\s*Secure" cookie))))
|
||||
|
||||
(deftest unconfigured-test
|
||||
(let [app (make-app (gitea-stub "alice") :gitea-url nil)]
|
||||
(is (= 503 (:status (request app :get "/auth/login"))))
|
||||
(is (= 503 (:status (request app :get "/auth/callback" :query "code=x&state=y"))))))
|
||||
|
||||
(deftest callback-test
|
||||
(let [app (make-app (gitea-stub "Alice"))
|
||||
start (request app :get "/auth/login")
|
||||
cookie (set-cookie start)
|
||||
state (location-state start)]
|
||||
(testing "a successful login redirects home and sets a session"
|
||||
(let [resp (request app :get "/auth/callback" :cookie cookie :query (str "code=abc&state=" state))]
|
||||
(is (= 302 (:status resp)))
|
||||
(is (= "/" (get-in resp [:headers "Location"])))
|
||||
(is (some? (set-cookie resp)))))
|
||||
(testing "no session cookie (the callback wasn't preceded by a login here)"
|
||||
(is (= 400 (:status (request app :get "/auth/callback" :query (str "code=abc&state=" state))))))
|
||||
(testing "wrong or missing state"
|
||||
(is (= 400 (:status (request app :get "/auth/callback" :cookie cookie :query "code=abc&state=nope"))))
|
||||
(is (= 400 (:status (request app :get "/auth/callback" :cookie cookie :query "code=abc")))))
|
||||
(testing "the user denied access at Gitea"
|
||||
(is (= 403 (:status (request app :get "/auth/callback" :cookie cookie
|
||||
:query (str "error=access_denied&state=" state))))))
|
||||
(testing "missing code"
|
||||
(is (= 400 (:status (request app :get "/auth/callback" :cookie cookie :query (str "state=" state))))))))
|
||||
|
||||
(deftest callback-gitea-failure-test
|
||||
(let [login-with (fn [http]
|
||||
(let [app (make-app http)
|
||||
start (request app :get "/auth/login")
|
||||
resp (atom nil)]
|
||||
;; the 502 path logs; keep it out of the test output
|
||||
(with-out-str
|
||||
(reset! resp (request app :get "/auth/callback" :cookie (set-cookie start)
|
||||
:query (str "code=abc&state=" (location-state start)))))
|
||||
@resp))]
|
||||
(testing "Gitea rejects the code"
|
||||
(let [resp (login-with (fn [_] {:status 400 :body "{\"error\":\"invalid_grant\"}"}))]
|
||||
(is (= 401 (:status resp)))
|
||||
(is (nil? (get-in resp [:headers "Set-Cookie"])) "no session is created")))
|
||||
(testing "Gitea rejects the token"
|
||||
(is (= 401 (:status (login-with (fn [{:keys [url]}]
|
||||
(if (str/ends-with? url "/access_token")
|
||||
{:status 200 :body "{\"access_token\":\"tok\"}"}
|
||||
{:status 401 :body "{}"})))))))
|
||||
(testing "Gitea is unreachable or answers garbage"
|
||||
(is (= 502 (:status (login-with (fn [_] (throw (java.net.ConnectException. "refused")))))))
|
||||
(is (= 502 (:status (login-with (fn [_] {:status 200 :body "<html>not json</html>"}))))))))
|
||||
|
||||
(deftest me-and-logout-test
|
||||
(let [app (make-app (gitea-stub "Alice"))
|
||||
cookie (login! app)]
|
||||
(testing "anonymous"
|
||||
(is (= 401 (:status (request app :get "/auth/me")))))
|
||||
(testing "logged in"
|
||||
(let [resp (request app :get "/auth/me" :cookie cookie)]
|
||||
(is (= 200 (:status resp)))
|
||||
(is (= {:login "Alice" :id 7 :admin? true} (:body resp)))))
|
||||
(testing "logged in as a non-admin"
|
||||
(let [app (make-app (gitea-stub "bob"))]
|
||||
(is (= {:login "bob" :id 7 :admin? false}
|
||||
(:body (request app :get "/auth/me" :cookie (login! app)))))))
|
||||
(testing "logout replaces the session with an empty one"
|
||||
(let [resp (request app :post "/auth/logout" :cookie cookie)]
|
||||
(is (= 200 (:status resp)))
|
||||
(is (= 401 (:status (request app :get "/auth/me" :cookie (set-cookie resp)))))))))
|
||||
|
||||
(deftest forged-session-test
|
||||
(let [app (make-app (gitea-stub "alice"))
|
||||
cookie (login! app)]
|
||||
(testing "a cookie tampered with is ignored"
|
||||
(let [tampered (str (subs cookie 0 (- (count cookie) 4)) "AAAA")]
|
||||
(is (= 401 (:status (request app :get "/auth/me" :cookie tampered))))))
|
||||
(testing "a cookie minted under another SESSION_SECRET is ignored"
|
||||
(let [other (make-app (gitea-stub "alice") :session-secret "other-secret")]
|
||||
(is (= 401 (:status (request other :get "/auth/me" :cookie cookie))))))
|
||||
(testing "garbage"
|
||||
(is (= 401 (:status (request app :get "/auth/me" :cookie "tbd-session=not-a-real-cookie")))))))
|
||||
|
||||
(deftest current-user-expiry-test
|
||||
(let [req {:session {:user {:login "alice" :expires-at 1000}}}]
|
||||
(is (= {:login "alice" :expires-at 1000} (mw/current-user req 999)))
|
||||
(is (nil? (mw/current-user req 1000)) "expired exactly at expires-at")
|
||||
(is (nil? (mw/current-user req 5000)))
|
||||
(is (nil? (mw/current-user {:session {:user {:login "alice"}}} 0)) "a user with no expiry is not trusted")
|
||||
(is (nil? (mw/current-user {} 0)))))
|
||||
@@ -26,6 +26,17 @@
|
||||
(is (= "application/edn; charset=utf-8" (get-in resp [:headers "Content-Type"])))
|
||||
(is (contains? (:body resp) :error))))))
|
||||
|
||||
(deftest add-route-browser-test
|
||||
(testing "browser navigations get an inline-renderable type so Chrome doesn't download the EDN"
|
||||
(let [resp (core/app {:request-method :get :uri "/api/add" :query-string "x=2&y=3"
|
||||
:headers {"accept" "text/html,application/xhtml+xml,*/*;q=0.8"}})]
|
||||
(is (= "text/plain; charset=utf-8" (get-in resp [:headers "Content-Type"])))
|
||||
(is (= {:result 5} (edn/read-string (:body resp))))))
|
||||
(testing "programmatic clients still get application/edn"
|
||||
(let [resp (core/app {:request-method :get :uri "/api/add" :query-string "x=2&y=3"
|
||||
:headers {"accept" "*/*"}})]
|
||||
(is (= "application/edn; charset=utf-8" (get-in resp [:headers "Content-Type"]))))))
|
||||
|
||||
(deftest add-route-swagger-test
|
||||
(let [spec (-> (core/app {:request-method :get :uri "/swagger.json"
|
||||
:headers {"accept" "application/json"}})
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
(ns tbd.nrepl.core-test
|
||||
(:require [clojure.test :refer [deftest is testing use-fixtures]]
|
||||
[tbd.nrepl.core :as nrepl])
|
||||
(:import (java.net ConnectException InetSocketAddress Socket ServerSocket)))
|
||||
|
||||
(use-fixtures :each (fn [t] (try (t) (finally (nrepl/disable!)))))
|
||||
|
||||
(defn listening? [port]
|
||||
(try (with-open [s (Socket.)]
|
||||
(.connect s (InetSocketAddress. "127.0.0.1" (int port)) 500)
|
||||
true)
|
||||
(catch java.io.IOException _ false)))
|
||||
|
||||
(deftest env-config-test
|
||||
(is (= {:port 7888 :bind "127.0.0.1"} (nrepl/env-config {})))
|
||||
(is (= {:port 7888 :bind "127.0.0.1"} (nrepl/env-config {"NREPL_PORT" " " "NREPL_BIND" ""})))
|
||||
(is (= {:port 9000 :bind "0.0.0.0"} (nrepl/env-config {"NREPL_PORT" "9000" "NREPL_BIND" "0.0.0.0"}))))
|
||||
|
||||
(deftest enable-disable-test
|
||||
(is (= {:enabled false} (nrepl/status)))
|
||||
(let [{:keys [enabled bind port] :as status} (nrepl/enable! {:port 0 :bind "127.0.0.1"})]
|
||||
(is (true? enabled))
|
||||
(is (= "127.0.0.1" bind))
|
||||
(is (pos? port) "port 0 reports the port actually bound")
|
||||
(is (= status (nrepl/status)))
|
||||
(is (listening? port))
|
||||
(testing "enabling again is a no-op"
|
||||
(is (= status (nrepl/enable! {:port 0 :bind "127.0.0.1"}))))
|
||||
(testing "disabling closes the port"
|
||||
(is (= {:enabled false} (nrepl/disable!)))
|
||||
(is (= {:enabled false} (nrepl/status)))
|
||||
(is (not (listening? port))))
|
||||
(testing "disabling again is a no-op"
|
||||
(is (= {:enabled false} (nrepl/disable!))))))
|
||||
|
||||
(deftest enable-failure-test
|
||||
(with-open [taken (ServerSocket. 0 1 (java.net.InetAddress/getByName "127.0.0.1"))]
|
||||
(is (thrown? java.net.BindException
|
||||
(nrepl/enable! {:port (.getLocalPort taken) :bind "127.0.0.1"})))
|
||||
(is (= {:enabled false} (nrepl/status)) "a failed start leaves nREPL disabled")))
|
||||
@@ -0,0 +1,92 @@
|
||||
(ns tbd.nrepl.routes-test
|
||||
(:require [clojure.edn :as edn]
|
||||
[clojure.test :refer [deftest is testing use-fixtures]]
|
||||
[muuntaja.core :as m]
|
||||
[tbd.auth.routes-test :as t]
|
||||
[tbd.nrepl.core :as nrepl])
|
||||
(:import (java.io ByteArrayInputStream)
|
||||
(java.net InetAddress ServerSocket)))
|
||||
|
||||
(use-fixtures :each (fn [f] (try (f) (finally (nrepl/disable!)))))
|
||||
|
||||
(defn post-nrepl
|
||||
"POST /nrepl with `body` (a string, or nil for none), as JSON unless `content-type` says otherwise.
|
||||
The handler logs each toggle; that's kept out of the test output."
|
||||
([app cookie body] (post-nrepl app cookie body "application/json"))
|
||||
([app cookie body content-type]
|
||||
(let [resp (atom nil)]
|
||||
(with-out-str
|
||||
(reset! resp (app (cond-> {:request-method :post :uri "/nrepl"
|
||||
:headers (cond-> {"content-type" content-type}
|
||||
cookie (assoc "cookie" cookie))}
|
||||
body (assoc :body (ByteArrayInputStream. (.getBytes ^String body)))))))
|
||||
(cond-> @resp (string? (:body @resp)) (update :body edn/read-string)))))
|
||||
|
||||
(deftest anonymous-test
|
||||
(let [app (t/make-app (t/gitea-stub "alice"))]
|
||||
(doseq [body ["{\"enabled\":true}" "{\"enabled\":\"junk\"}" nil]]
|
||||
(let [resp (post-nrepl app nil body)]
|
||||
(is (= 401 (:status resp)) (str "body: " body))))
|
||||
(is (= {:enabled false} (nrepl/status)) "nothing was started")))
|
||||
|
||||
(deftest non-admin-test
|
||||
(let [app (t/make-app (t/gitea-stub "mallory"))
|
||||
cookie (t/login! app)]
|
||||
(is (= 403 (:status (post-nrepl app cookie "{\"enabled\":true}"))))
|
||||
(is (= {:enabled false} (nrepl/status)) "nothing was started")))
|
||||
|
||||
(deftest empty-allowlist-test
|
||||
(let [app (t/make-app (t/gitea-stub "alice") :admin-users #{})
|
||||
cookie (t/login! app)]
|
||||
(is (= 403 (:status (post-nrepl app cookie "{\"enabled\":true}"))))))
|
||||
|
||||
(deftest admin-test
|
||||
(let [app (t/make-app (t/gitea-stub "Alice"))
|
||||
cookie (t/login! app)]
|
||||
(testing "enable"
|
||||
(let [resp (post-nrepl app cookie "{\"enabled\":true}")]
|
||||
(is (= 200 (:status resp)))
|
||||
(is (= "application/edn; charset=utf-8" (get-in resp [:headers "Content-Type"])))
|
||||
(is (= {:enabled true :bind "127.0.0.1"} (dissoc (:body resp) :port)))
|
||||
(is (pos? (get-in resp [:body :port])))
|
||||
(is (= (:body resp) (nrepl/status)))))
|
||||
(testing "disable"
|
||||
(let [resp (post-nrepl app cookie "{\"enabled\":false}")]
|
||||
(is (= 200 (:status resp)))
|
||||
(is (= {:enabled false} (:body resp)))
|
||||
(is (= {:enabled false} (nrepl/status)))))
|
||||
(testing "EDN bodies work too"
|
||||
(let [resp (post-nrepl app cookie "{:enabled true}" "application/edn")]
|
||||
(is (= 200 (:status resp)))
|
||||
(is (true? (get-in resp [:body :enabled])))))))
|
||||
|
||||
(deftest invalid-body-test
|
||||
(let [app (t/make-app (t/gitea-stub "alice"))
|
||||
cookie (t/login! app)]
|
||||
(doseq [body [nil "{}" "{\"enabled\":\"maybe\"}" "{\"enabled\":null}" "not json"]]
|
||||
(let [resp (post-nrepl app cookie body)]
|
||||
(is (= 400 (:status resp)) (str "body: " body))
|
||||
(is (contains? (:body resp) :error) (str "body: " body))))
|
||||
(is (= {:enabled false} (nrepl/status)))))
|
||||
|
||||
(deftest bind-failure-test
|
||||
(with-open [taken (ServerSocket. 0 1 (InetAddress/getByName "127.0.0.1"))]
|
||||
(let [app (tbd.core/make-app
|
||||
{:auth {:gitea-url "https://gitea.test" :client-id "cid" :client-secret "sec"
|
||||
:redirect-uri "http://tbd.test/auth/callback" :admin-users #{"alice"}
|
||||
:session-secret "test-secret" :http (t/gitea-stub "alice")}
|
||||
:nrepl {:port (.getLocalPort taken) :bind "127.0.0.1"}})
|
||||
cookie (t/login! app)
|
||||
resp (post-nrepl app cookie "{\"enabled\":true}")]
|
||||
(is (= 500 (:status resp)))
|
||||
(is (re-find #"could not change nREPL state" (get-in resp [:body :error])))
|
||||
(is (= {:enabled false} (nrepl/status))))))
|
||||
|
||||
(deftest swagger-test
|
||||
(let [app (t/make-app (t/gitea-stub "alice"))
|
||||
spec (-> (app {:request-method :get :uri "/swagger.json" :headers {"accept" "application/json"}})
|
||||
:body
|
||||
(->> (m/decode "application/json")))
|
||||
paths (into {} (map (fn [[k v]] [(str (namespace k) "/" (name k)) v])) (:paths spec))]
|
||||
(is (contains? (get-in paths ["/nrepl"]) :post) "documented")
|
||||
(is (contains? paths "/auth/login"))))
|
||||
Reference in New Issue
Block a user