feat(backend): initial work on catacumba to plain ring migration

This commit is contained in:
Andrey Antukh 2019-06-06 15:13:09 +00:00
parent 712269aa35
commit f2411368ba
21 changed files with 1507 additions and 784 deletions

View file

@ -3,9 +3,17 @@
org.clojure/data.xml {:mvn/version "0.1.0-beta2"} org.clojure/data.xml {:mvn/version "0.1.0-beta2"}
funcool/suricatta {:mvn/version "1.3.1"} funcool/suricatta {:mvn/version "1.3.1"}
funcool/promesa {:mvn/version "2.0.0"} funcool/promesa {:mvn/version "2.0.0"}
funcool/catacumba {:mvn/version "2.1.0"}
funcool/cuerdas {:mvn/version "2.2.0"} funcool/cuerdas {:mvn/version "2.2.0"}
funcool/datoteka {:mvn/version "1.0.0"} funcool/datoteka {:mvn/version "1.0.0"}
funcool/catacumba {:mvn/version "2.1.0"}
funcool/struct {:mvn/version "1.4.0"}
ring/ring {:mvn/version "1.7.1"}
metosin/reitit-core {:mvn/version "0.3.7"}
metosin/reitit-ring {:mvn/version "0.3.7"}
metosin/reitit-middleware {:mvn/version "0.3.7"}
metosin/reitit-spec {:mvn/version "0.3.7"}
;; metosin/reitit-dev {:mvn/version "0.3.7"}
org.jsoup/jsoup {:mvn/version "1.10.2"} org.jsoup/jsoup {:mvn/version "1.10.2"}
hiccup/hiccup {:mvn/version "1.0.5"} hiccup/hiccup {:mvn/version "1.0.5"}
@ -35,9 +43,12 @@
{:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"} {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}
org.clojure/tools.namespace {:mvn/version "0.2.11"} org.clojure/tools.namespace {:mvn/version "0.2.11"}
clj-http/clj-http {:mvn/version "2.1.0"} clj-http/clj-http {:mvn/version "2.1.0"}
ring/ring-mock {:mvn/version "0.4.0"}
} }
:extra-paths ["test"]} :extra-paths ["test"]}
:repl {:main-opts ["-m" "rebel-readline.main"]} :repl {:main-opts ["-m" "rebel-readline.main"]}
:nrepl {:extra-deps {nrepl/nrepl {:mvn/version "0.6.0"}}
:main-opts ["-m" "nrepl.cmdline"]}
}} }}

115
backend/src/uxbox/api.clj Normal file
View file

@ -0,0 +1,115 @@
;; 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) 2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.api
(:require [mount.core :refer [defstate]]
[clojure.pprint :refer [pprint]]
[uxbox.config :as cfg]
[ring.middleware.session :refer [wrap-session]]
[ring.middleware.session.cookie :refer [cookie-store]]
[ring.adapter.jetty :as jetty]
[promesa.core :as p]
[reitit.core :as rc]
[reitit.ring :as ring]
[reitit.ring.middleware.muuntaja :as muuntaja]
[reitit.ring.middleware.multipart :as multipart]
[reitit.ring.middleware.parameters :as parameters]
;; [reitit.dev.pretty :as pretty]
[uxbox.api.middleware :as api-middleware :refer [handler]]
[uxbox.api.auth :as api-auth]
[uxbox.api.projects :as api-projects]
[uxbox.api.pages :as api-pages]
[uxbox.api.errors :as api-errors]
[muuntaja.core :as m]
[uxbox.util.transit :as t]
[uxbox.util.data :refer [normalize-attrs]]
[uxbox.util.exceptions :as ex]
[uxbox.util.uuid :as uuid]))
;; --- Top Level Handlers
(defn- welcome-api
"A GET entry point for the api that shows
a welcome message."
[context]
(let [body {:message "Welcome to UXBox api."}]
{:status 200
:body {:query-params (:query-params context)
:form-params (:form-params context)
:body-params (:body-params context)
:path-params (:path-params context)
:params (:params context)}}))
;; --- Routes
(def routes
(ring/router
[["/media/*" (ring/create-resource-handler {:root "public/media"})]
["/static/*" (ring/create-resource-handler {:root "public/static"})]
["/auth/login" {:post (handler #'api-auth/login)}]
["/api" {:middleware [api-auth/authorization-middleware]}
["/echo" (handler #'welcome-api)]
["/projects" {:get (handler #'api-projects/list)
:post (handler #'api-projects/create)}]
["/projects/by-token/:token" {:get (handler #'api-projects/get-by-share-token)}]
["/projects/:id" {:put (handler #'api-projects/update)
:delete (handler #'api-projects/delete)}]
["/pages" {:get (handler #'api-pages/list)}]
["/pages/:id" {:put (handler #'api-pages/update)
:delete (handler #'api-pages/delete)}]
["/pages/:id/metatata" {:put (handler #'api-pages/update-metadata)}]
["/pages/:id/history" {:get (handler #'api-pages/retrieve-history)}]
]]
{;;:reitit.middleware/transform dev/print-request-diffs
:data {:muuntaja (m/create
(update-in m/default-options [:formats "application/transit+json"]
merge {:encoder-opts {:handlers t/+write-handlers+}
:decoder-opts {:handlers t/+read-handlers+}}))
:middleware [
;; {:name "CORS Middleware"
;; :wrap #(wrap-cors %
;; :access-control-allow-origin [#".*"]
;; :access-control-allow-methods [:get :put :post :delete]
;; :access-control-allow-headers ["x-requested-with"
;; "content-type"
;; "authorization"])}
[wrap-session {:store (cookie-store {:key "a 16-byte secret"})
:cookie-name "session"
:cookie-attrs {:same-site :lax
:http-only true}}]
parameters/parameters-middleware
api-middleware/normalize-params-middleware
;; content-negotiation
muuntaja/format-negotiate-middleware
;; encoding response body
muuntaja/format-response-middleware
;; exception handling
api-errors/exception-middleware
;; decoding request body
muuntaja/format-request-middleware
;; validation
api-middleware/parameters-validation-middleware
;; multipart
multipart/multipart-middleware]}}))
(def app
(ring/ring-handler routes (ring/create-default-handler)))
;; --- State Initialization
(defn- start-server
[config]
(jetty/run-jetty app {:join? false
:async? true
:daemon? true
:port (:http-server-port config)}))
(defstate server
:start (start-server cfg/config)
:stop (.stop server))

View file

@ -0,0 +1,36 @@
;; 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) 2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.api.auth
(:require [clojure.spec.alpha :as s]
[struct.core :as st]
[uxbox.services :as sv]
[uxbox.util.http :as http]
[uxbox.util.spec :as us]
[uxbox.util.uuid :as uuid]))
(defn login
{:description "User login endpoint"
:parameters {:body {:username [st/required st/string]
:password [st/required st/string]
:scope [st/required st/string]}}}
[ctx]
(let [data (get-in ctx [:parameters :body])
user @(sv/novelty (assoc data :type :login))]
(-> (http/no-content)
(assoc :session {:user-id (get user :id)}))))
(defn authorization-middleware
[handler]
(fn
([request]
(if-let [identity (get-in request [:session :user-id])]
(handler (assoc request :identity identity :user identity))
(http/forbidden nil)))
([request respond raise]
(if-let [identity (get-in request [:session :user-id])]
(handler (assoc request :identity identity :user identity) respond raise)
(respond (http/forbidden nil))))))

View file

@ -0,0 +1,86 @@
;; 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) 20162019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.api.errors
"A errors handling for api."
(:require [reitit.ring.middleware.exception :as exception]))
(defmulti handle-exception #(:type (ex-data %)))
(defmethod handle-exception :validation
[err]
;; (println "\n*********** stack trace ***********")
;; (.printStackTrace err)
;; (println "\n********* end stack trace *********")
(let [response (ex-data err)]
{:status 400
:body response}))
(defmethod handle-exception :default
[err]
;; (println "\n*********** stack trace ***********")
;; (.printStackTrace err)
;; (println "\n********* end stack trace *********")
(let [response (ex-data err)]
{:status 500
:body response}))
;; --- Entry Point
(defn- handle-data-access-exception
[err]
(let [err (.getCause err)
state (.getSQLState err)
message (.getMessage err)]
(case state
"P0002"
{:status 412 ;; precondition-failed
:body {:message message
:payload nil
:type :occ}}
(do
{:status 500
:message {:message message
:type :unexpected
:payload nil}}))))
(defn- handle-unexpected-exception
[err]
(let [message (.getMessage err)]
{:status 500
:body {:message message
:type :unexpected
:payload nil}}))
(defn errors-handler
[error context]
(cond
(instance? clojure.lang.ExceptionInfo error)
(handle-exception error)
(instance? java.util.concurrent.CompletionException error)
(errors-handler context (.getCause error))
(instance? org.jooq.exception.DataAccessException error)
(handle-data-access-exception error)
:else
(handle-unexpected-exception error)))
(defn wrap-print-errors
[handler error request]
(println "\n*********** stack trace ***********")
(.printStackTrace error)
(println "\n********* end stack trace *********")
(handler error request))
(def exception-middleware
(exception/create-exception-middleware
(assoc exception/default-handlers
::exception/default errors-handler
::exception/wrap wrap-print-errors)))

View file

@ -0,0 +1,101 @@
;; 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) 2016-2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.api.middleware
(:require [reitit.core :as rc]
[struct.core :as st]
[promesa.core :as p]
[uxbox.util.data :refer [normalize-attrs]]
[uxbox.util.exceptions :as ex]))
;; (extend-protocol rc/Expand
;; clojure.lang.Var
;; (expand [this opts]
;; (merge (rc/expand (deref this) opts)
;; {::handler-metadata (meta this)})))
(defn transform-handler
[handler]
(fn [request respond raise]
(try
(let [response (handler request)]
(if (p/promise? response)
(-> response
(p/then respond)
(p/catch raise))
(respond response)))
(catch Exception e
(raise e)))))
(defn handler
[invar]
(let [metadata (meta invar)
hlrdata (-> metadata
(dissoc :arglist :line :column :file :ns)
(assoc :handler (transform-handler (var-get invar))
:fullname (symbol (str (:ns metadata)) (str (:name metadata)))))]
(cond-> hlrdata
(:doc metadata) (assoc :description (:doc metadata)))))
(def normalize-params-middleware
{:name ::normalize-params-middleware
:wrap (fn [handler]
(letfn [(transform-request [request]
(if-let [data (get request :query-params)]
(assoc request :query-params (normalize-attrs data))
request))]
(fn
([request] (handler (transform-request request)))
([request respond raise]
(try
(try
(let [request (transform-request request)]
(handler (transform-request request) respond raise))
(catch Exception e
(raise e))))))))})
;; --- Validation
(def parameters-validation-middleware
(letfn [(prepare [parameters]
(reduce-kv
(fn [acc key spec]
(let [newkey (case key
:path :path-params
:query :query-params
:body :body-params
(throw (ex-info "Not supported key on :parameters" {})))]
(assoc acc newkey {:key key
:fn #(st/validate % spec)})))
{} parameters))
(validate [request parameters debug]
(reduce-kv
(fn [req key spec]
(let [[errors, result] ((:fn spec) (get req key))]
(if errors
(ex/raise :type :parameters-validation
:code (:key spec)
:context errors
:message "Invalid data")
(assoc-in req [:parameters (:key spec)] result))))
request parameters))]
{:name ::parameters-validation-middleware
:compile (fn [route opts]
(when-let [parameters (:parameters route)]
(let [parameters (prepare parameters)]
(fn [handler]
(fn
([request]
(handler (validate request parameters)))
([request respond raise]
(try
(handler (validate request parameters false) respond raise)
(catch Exception e
(raise e)))))))))}))

View file

@ -0,0 +1,89 @@
;; 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) 2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.api.pages
(:require [clojure.spec.alpha :as s]
[struct.core :as st]
[promesa.core :as p]
[uxbox.services :as sv]
[uxbox.util.http :as http]
[uxbox.util.spec :as us]
[uxbox.util.uuid :as uuid]))
(defn list
"List pages in a project"
{:parameters {:query {:project [st/required st/uuid-str]}}}
[{:keys [user parameters]}]
(let [project (get-in parameters [:query :project])
message {:user user :project project :type :list-pages-by-project}]
(-> (sv/query message)
(p/then #(http/ok %)))))
(defn create
"Create page for a project"
{:parameters {:body {:data [st/required]
:metadata [st/required]
:project [st/required st/uuid-str]
:name [st/required st/string]
:id [st/uuid-str]}}}
[{:keys [user parameters]}]
(let [data (get parameters :body)
message (assoc data :user user :type :create-page)]
(->> (sv/novelty message)
(p/map (fn [result]
(let [loc (str "/api/pages/" (:id result))]
(http/created loc result)))))))
(defn update
"Update page"
{:parameters {:path {:id [st/required st/uuid-str]}
:body {:data [st/required]
:metadata [st/required]
:project [st/required st/uuid-str]
:name [st/required st/string]
:version [st/required st/integer]
:id [st/uuid-str]}}}
[{:keys [user parameters]}]
(let [id (get-in parameters [:path :id])
data (get parameters :body)
message (assoc data :id id :type :update-page :user user)]
(->> (sv/novelty message)
(p/map #(http/ok %)))))
(defn update-metadata
"Update page metadata"
{:parameters {:path {:id [st/required st/uuid-str]}
:body {:id [st/required st/uuid-str]
:metadata [st/required]
:project [st/required st/uuid-str]
:name [st/required st/string]}}}
[{:keys [user parameters]}]
(let [id (get-in parameters [:path :id])
data (get parameters :body)
message (assoc data :id id :type :update-page-metadata :user user)]
(->> (sv/novelty message)
(p/map #(http/ok %)))))
(defn delete
{:parameters {:path {:id [st/required st/uuid-str]}}}
[{:keys [user parameters]}]
(let [id (get-in parameters [:path :id])
message {:id id :type :delete-page :user user}]
(-> (sv/novelty message)
(p/then (fn [v] (http/no-content))))))
(defn retrieve-history
"Retrieve the page history"
{:parameters {:path {:id [st/required st/uuid-str]}
:query {:max [st/integer-str]
:since [st/integer-str]
:pinned [st/boolean-str]}}}
[{:keys [user parameters]}]
(let [id (get-in parameters [:path :id])
data (get parameters :query)
message (assoc data :id id :type :list-page-history :user user)]
(->> (sv/query message)
(p/map #(http/ok %)))))

View file

@ -0,0 +1,65 @@
;; 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) 2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.api.projects
(:require [clojure.spec.alpha :as s]
[clojure.pprint :refer [pprint]]
[struct.core :as st]
[promesa.core :as p]
[uxbox.services :as sv]
[uxbox.util.http :as http]
[uxbox.util.spec :as us]
[uxbox.util.uuid :as uuid]
[uxbox.util.exceptions :as ex]))
(defn list
{:description "List projects"}
[{:keys [user] :as req}]
(let [message {:user user :type :list-projects}]
(->> (sv/query message)
(p/map #(http/ok %)))))
(defn create
"Create project"
{:parameters {:body {:name [st/required st/string]
:id [st/uuid-str]}}}
[{:keys [user parameters] :as req}]
(let [data (get parameters :body)
message (assoc data :type :create-project :user user)]
(->> (sv/novelty message)
(p/map (fn [result]
(let [loc (str "/api/projects/" (:id result))]
(http/created loc result)))))))
(defn update
"Update project"
{:parameters {:path {:id [st/required st/uuid-str]}
:body {:name [st/required st/string]
:version [st/required st/integer]}}}
[{:keys [user parameters] :as req}]
(let [id (get-in parameters [:path :id])
data (get parameters :body)
message (assoc data :id id :type :update-project :user user)]
(-> (sv/novelty message)
(p/then #(http/ok %)))))
(defn delete
"Delete project"
{:parameters {:path {:id [st/required st/uuid-str]}}}
[{:keys [user parameters] :as req}]
(let [id (get-in parameters [:path :id])
message {:id id :type :delete-project :user user}]
(-> (sv/novelty message)
(p/then (fn [v] (http/no-content))))))
(defn get-by-share-token
"Get a project by shared token"
{:parameters {:path {:token [st/required st/string]}}}
[{:keys [user parameters] :as req}]
(let [message {:token (get-in parameters [:path :token])
:type :retrieve-project-by-share-token}]
(->> (sv/query message)
(p/map #(http/ok %)))))

View file

@ -23,7 +23,6 @@
[uxbox.frontend.kvstore :as kvstore] [uxbox.frontend.kvstore :as kvstore]
[uxbox.frontend.svgparse :as svgparse] [uxbox.frontend.svgparse :as svgparse]
[uxbox.frontend.debug-emails :as dbgemails] [uxbox.frontend.debug-emails :as dbgemails]
[uxbox.services.auth :refer [auth-opts]]
[uxbox.util.response :refer [rsp]] [uxbox.util.response :refer [rsp]]
[uxbox.util.uuid :as uuid])) [uxbox.util.uuid :as uuid]))
@ -53,6 +52,9 @@
;; --- Routes ;; --- Routes
(def auth-opts
{:alg :a256kw :enc :a128cbc-hs256})
(defn routes (defn routes
([] (routes cfg/config)) ([] (routes cfg/config))
([config] ([config]
@ -157,6 +159,6 @@
:max-body-size 52428800}] :max-body-size 52428800}]
(ct/run-server (routes config) config))) (ct/run-server (routes config) config)))
(defstate server ;; (defstate server
:start (start-server cfg/config) ;; :start (start-server cfg/config)
:stop (.stop server)) ;; :stop (.stop server))

View file

@ -8,42 +8,24 @@
(:require [clojure.spec.alpha :as s] (:require [clojure.spec.alpha :as s]
[suricatta.core :as sc] [suricatta.core :as sc]
[buddy.hashers :as hashers] [buddy.hashers :as hashers]
[buddy.sign.jwt :as jwt]
[buddy.core.hash :as hash]
[uxbox.config :as cfg] [uxbox.config :as cfg]
[uxbox.util.spec :as us]
[uxbox.db :as db] [uxbox.db :as db]
[uxbox.services.core :as core] [uxbox.services.core :as core]
[uxbox.services.users :as users] [uxbox.services.users :as users]
[uxbox.util.exceptions :as ex])) [uxbox.util.exceptions :as ex]))
(def auth-opts
{:alg :a256kw :enc :a128cbc-hs256})
;; --- Login
(defn- check-user-password (defn- check-user-password
[user password] [user password]
(hashers/check password (:password user))) (hashers/check password (:password user)))
(defn generate-token
[user]
(let [data {:id (:id user) :scope :auth}]
(jwt/encrypt data cfg/secret auth-opts)))
(s/def ::scope string?)
(s/def ::login
(s/keys :req-un [::us/username ::us/password ::scope]))
(defmethod core/novelty :login (defmethod core/novelty :login
[{:keys [username password scope] :as params}] [{:keys [username password scope] :as params}]
(s/assert ::login params)
(with-open [conn (db/connection)] (with-open [conn (db/connection)]
(let [user (users/find-user-by-username-or-email conn username)] (let [user (users/find-user-by-username-or-email conn username)]
(when-not user (when-not user
(ex/raise :type :validation (ex/raise :type :validation
:code ::wrong-credentials)) :code ::wrong-credentials))
(if (check-user-password user password) (when-not (check-user-password user password)
{:token (generate-token user)}
(ex/raise :type :validation (ex/raise :type :validation
:code ::wrong-credentials))))) :code ::wrong-credentials))
user)))

View file

@ -0,0 +1,216 @@
;; 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) 2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.util.http)
(defn response
"Create a response instance."
([body] (response body 200 {}))
([body status] (response body status {}))
([body status headers] {:body body :status status :headers headers}))
(defn response?
[resp]
(and (map? resp)
(integer? (:status resp))
(map? (:headers resp))))
(defn continue
([body] (response body 100))
([body headers] (response body 100 headers)))
(defn ok
"HTTP 200 OK
Should be used to indicate nonspecific success. Must not be used to
communicate errors in the response body.
In most cases, 200 is the code the client hopes to see. It indicates that
the REST API successfully carried out whatever action the client requested,
and that no more specific code in the 2xx series is appropriate. Unlike
the 204 status code, a 200 response should include a response body."
([body] (response body 200))
([body headers] (response body 200 headers)))
(defn created
"HTTP 201 Created
Must be used to indicate successful resource creation.
A REST API responds with the 201 status code whenever a collection creates,
or a store adds, a new resource at the client's request. There may also be
times when a new resource is created as a result of some controller action,
in which case 201 would also be an appropriate response."
([location] (response "" 201 {"location" location}))
([location body] (response body 201 {"location" location}))
([location body headers] (response body 201 (merge headers {"location" location}))))
(defn accepted
"HTTP 202 Accepted
Must be used to indicate successful start of an asynchronous action.
A 202 response indicates that the client's request will be handled
asynchronously. This response status code tells the client that the request
appears valid, but it still may have problems once it's finally processed.
A 202 response is typically used for actions that take a long while to
process."
([body] (response body 202))
([body headers] (response body 202 headers)))
(defn no-content
"HTTP 204 No Content
Should be used when the response body is intentionally empty.
The 204 status code is usually sent out in response to a PUT, POST, or
DELETE request, when the REST API declines to send back any status message
or representation in the response message's body. An API may also send 204
in conjunction with a GET request to indicate that the requested resource
exists, but has no state representation to include in the body."
([] (response "" 204))
([headers] (response "" 204 headers)))
(defn moved-permanently
"301 Moved Permanently
Should be used to relocate resources.
The 301 status code indicates that the REST API's resource model has been
significantly redesigned and a new permanent URI has been assigned to the
client's requested resource. The REST API should specify the new URI in
the response's Location header."
([location] (response "" 301 {"location" location}))
([location body] (response body 301 {"location" location}))
([location body headers] (response body 301 (merge headers {"location" location}))))
(defn found
"HTTP 302 Found
Should not be used.
The intended semantics of the 302 response code have been misunderstood
by programmers and incorrectly implemented in programs since version 1.0
of the HTTP protocol.
The confusion centers on whether it is appropriate for a client to always
automatically issue a follow-up GET request to the URI in response's
Location header, regardless of the original request's method. For the
record, the intent of 302 is that this automatic redirect behavior only
applies if the client's original request used either the GET or HEAD
method.
To clear things up, HTTP 1.1 introduced status codes 303 (\"See Other\")
and 307 (\"Temporary Redirect\"), either of which should be used
instead of 302."
([location] (response "" 302 {"location" location}))
([location body] (response body 302 {"location" location}))
([location body headers] (response body 302 (merge headers {"location" location}))))
(defn see-other
"HTTP 303 See Other
Should be used to refer the client to a different URI.
A 303 response indicates that a controller resource has finished its work,
but instead of sending a potentially unwanted response body, it sends the
client the URI of a response resource. This can be the URI of a temporary
status message, or the URI to some already existing, more permanent,
resource.
Generally speaking, the 303 status code allows a REST API to send a
reference to a resource without forcing the client to download its state.
Instead, the client may send a GET request to the value of the Location
header."
([location] (response "" 303 {"location" location}))
([location body] (response body 303 {"location" location}))
([location body headers] (response body 303 (merge headers {"location" location}))))
(defn temporary-redirect
"HTTP 307 Temporary Redirect
Should be used to tell clients to resubmit the request to another URI.
HTTP/1.1 introduced the 307 status code to reiterate the originally
intended semantics of the 302 (\"Found\") status code. A 307 response
indicates that the REST API is not going to process the client's request.
Instead, the client should resubmit the request to the URI specified by
the response message's Location header.
A REST API can use this status code to assign a temporary URI to the
client's requested resource. For example, a 307 response can be used to
shift a client request over to another host."
([location] (response "" 307 {"location" location}))
([location body] (response body 307 {"location" location}))
([location body headers] (response body 307 (merge headers {"location" location}))))
(defn bad-request
"HTTP 400 Bad Request
May be used to indicate nonspecific failure.
400 is the generic client-side error status, used when no other 4xx error
code is appropriate."
([body] (response body 400))
([body headers] (response body 400 headers)))
(defn unauthorized
"HTTP 401 Unauthorized
Must be used when there is a problem with the client credentials.
A 401 error response indicates that the client tried to operate on a
protected resource without providing the proper authorization. It may have
provided the wrong credentials or none at all."
([body] (response body 401))
([body headers] (response body 401 headers)))
(defn forbidden
"HTTP 403 Forbidden
Should be used to forbid access regardless of authorization state.
A 403 error response indicates that the client's request is formed
correctly, but the REST API refuses to honor it. A 403 response is not a
case of insufficient client credentials; that would be 401 (\"Unauthorized\").
REST APIs use 403 to enforce application-level permissions. For example, a
client may be authorized to interact with some, but not all of a REST API's
resources. If the client attempts a resource interaction that is outside of
its permitted scope, the REST API should respond with 403."
([body] (response body 403))
([body headers] (response body 403 headers)))
(defn not-found
"HTTP 404 Not Found
Must be used when a client's URI cannot be mapped to a resource.
The 404 error status code indicates that the REST API can't map the
client's URI to a resource."
([body] (response body 404))
([body headers] (response body 404 headers)))
(defn method-not-allowed
([body] (response body 405))
([body headers] (response body 405 headers)))
(defn not-acceptable
([body] (response body 406))
([body headers] (response body 406 headers)))
(defn conflict
([body] (response body 409))
([body headers] (response body 409 headers)))
(defn gone
([body] (response body 410))
([body headers] (response body 410 headers)))
(defn precondition-failed
([body] (response body 412))
([body headers] (response body 412 headers)))
(defn unsupported-mediatype
([body] (response body 415))
([body headers] (response body 415 headers)))
(defn too-many-requests
([body] (response body 429))
([body headers] (response body 429 headers)))
(defn internal-server-error
([body] (response body 500))
([body headers] (response body 500 headers)))
(defn not-implemented
([body] (response body 501))
([body headers] (response body 501 headers)))

View file

@ -15,11 +15,8 @@
;; --- Handlers ;; --- Handlers
(def ^:private +reader-handlers+ (def +read-handlers+ dt/+read-handlers+)
dt/+read-handlers+) (def +write-handlers+ dt/+write-handlers+)
(def ^:private +write-handlers+
dt/+write-handlers+)
;; --- Low-Level Api ;; --- Low-Level Api
@ -27,7 +24,7 @@
([istream] ([istream]
(reader istream nil)) (reader istream nil))
([istream {:keys [type] :or {type :json}}] ([istream {:keys [type] :or {type :json}}]
(t/reader istream type {:handlers +reader-handlers+}))) (t/reader istream type {:handlers +read-handlers+})))
(defn read! (defn read!
"Read value from streamed transit reader." "Read value from streamed transit reader."

View file

@ -3,7 +3,9 @@
(:require [clj-http.client :as http] (:require [clj-http.client :as http]
[buddy.hashers :as hashers] [buddy.hashers :as hashers]
[buddy.core.codecs :as codecs] [buddy.core.codecs :as codecs]
[cuerdas.core :as str]
[catacumba.serializers :as sz] [catacumba.serializers :as sz]
[ring.adapter.jetty :as jetty]
[mount.core :as mount] [mount.core :as mount]
[datoteka.storages :as st] [datoteka.storages :as st]
[suricatta.core :as sc] [suricatta.core :as sc]
@ -15,6 +17,7 @@
[uxbox.db :as db] [uxbox.db :as db]
[uxbox.config :as cfg])) [uxbox.config :as cfg]))
;; TODO: parametrize this
(def +base-url+ "http://localhost:5050") (def +base-url+ "http://localhost:5050")
(defn state-init (defn state-init
@ -64,19 +67,26 @@
(defn- strip-response (defn- strip-response
[{:keys [status headers body]}] [{:keys [status headers body]}]
(if (= (get headers "content-type") "application/transit+json") (if (str/starts-with? (get headers "Content-Type") "application/transit+json")
[status (-> (codecs/str->bytes body) [status (-> (codecs/str->bytes body)
(t/decode))] (t/decode))]
[status body])) [status body]))
(defn get-auth-headers
[user]
(let [store (ring.middleware.session.cookie/cookie-store {:key "a 16-byte secret"})
result (ring.middleware.session/session-response
{:session {:user-id (:id user)}} {}
{:store store :cookie-name "session"})]
{"cookie" (first (get-in result [:headers "Set-Cookie"]))}))
(defn http-get (defn http-get
([user uri] (http-get user uri nil)) ([user uri] (http-get user uri nil))
([user uri {:keys [query] :as opts}] ([user uri {:keys [query] :as opts}]
(let [headers (when user (let [headers (assoc (get-auth-headers user)
{"Authorization" (str "Token " (usa/generate-token user))}) "accept" "application/transit+json")
params (merge {:headers headers} params (cond-> {:headers headers}
(when query query (assoc :query-params query))]
{:query-params query}))]
(try (try
(strip-response (http/get uri params)) (strip-response (http/get uri params))
(catch clojure.lang.ExceptionInfo e (catch clojure.lang.ExceptionInfo e
@ -88,10 +98,9 @@
([user uri {:keys [body] :as params}] ([user uri {:keys [body] :as params}]
(let [body (-> (t/encode body) (let [body (-> (t/encode body)
(codecs/bytes->str)) (codecs/bytes->str))
headers (merge headers (assoc (get-auth-headers user)
{"content-type" "application/transit+json"} "accept" "application/transit+json"
(when user "content-type" "application/transit+json")
{"Authorization" (str "Token " (usa/generate-token user))}))
params {:headers headers :body body}] params {:headers headers :body body}]
(try (try
(strip-response (http/post uri params)) (strip-response (http/post uri params))
@ -100,9 +109,8 @@
(defn http-multipart (defn http-multipart
[user uri params] [user uri params]
(let [headers (merge (let [headers (assoc (get-auth-headers user)
(when user "accept" "application/transit+json")
{"Authorization" (str "Token " (usa/generate-token user))}))
params {:headers headers params {:headers headers
:multipart params}] :multipart params}]
(try (try
@ -116,10 +124,9 @@
([user uri {:keys [body] :as params}] ([user uri {:keys [body] :as params}]
(let [body (-> (t/encode body) (let [body (-> (t/encode body)
(codecs/bytes->str)) (codecs/bytes->str))
headers (merge headers (assoc (get-auth-headers user)
{"content-type" "application/transit+json"} "accept" "application/transit+json"
(when user "content-type" "application/transit+json")
{"Authorization" (str "Token " (usa/generate-token user))}))
params {:headers headers :body body}] params {:headers headers :body body}]
(try (try
(strip-response (http/put uri params)) (strip-response (http/put uri params))
@ -130,8 +137,9 @@
([uri] ([uri]
(http-delete nil uri)) (http-delete nil uri))
([user uri] ([user uri]
(let [headers (when user (let [headers (assoc (get-auth-headers user)
{"Authorization" (str "Token " (usa/generate-token user))}) "accept" "application/transit+json"
"content-type" "application/transit+json")
params {:headers headers}] params {:headers headers}]
(try (try
(strip-response (http/delete uri params)) (strip-response (http/delete uri params))
@ -156,9 +164,7 @@
body) body)
headers (cond-> headers headers (cond-> headers
body (assoc "content-type" "application/transit+json") body (assoc "content-type" "application/transit+json")
raw? (assoc "content-type" "application/octet-stream") raw? (assoc "content-type" "application/octet-stream"))
user (assoc "authorization"
(str "Token " (usa/generate-token user))))
params {:headers headers :body body} params {:headers headers :body body}
uri (str +base-url+ path)] uri (str +base-url+ path)]
(try (try
@ -207,3 +213,21 @@
(let [data (ex-data e)] (let [data (ex-data e)]
(= code (:code data)))) (= code (:code data))))
(defn run-server
[handler]
(jetty/run-jetty handler {:join? false
:async? true
:daemon? true
:port 5050}))
(defmacro with-server
"Evaluate code in context of running catacumba server."
[{:keys [handler sleep] :or {sleep 50} :as options} & body]
`(let [server# (run-server ~handler)]
(try
~@body
(finally
(.stop server#)
(Thread/sleep ~sleep)))))

View file

@ -12,42 +12,42 @@
(t/use-fixtures :each th/database-reset) (t/use-fixtures :each th/database-reset)
(t/deftest test-http-success-auth ;; (t/deftest test-http-success-auth
(let [data {:username "user1" ;; (let [data {:username "user1"
:fullname "user 1" ;; :fullname "user 1"
:metadata "1" ;; :metadata "1"
:password "user1" ;; :password "user1"
:email "user1@uxbox.io"} ;; :email "user1@uxbox.io"}
user (with-open [conn (db/connection)] ;; user (with-open [conn (db/connection)]
(usu/create-user conn data))] ;; (usu/create-user conn data))]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [data {:username "user1" ;; (let [data {:username "user1"
:password "user1" ;; :password "user1"
:metadata "1" ;; :metadata "1"
:scope "foobar"} ;; :scope "foobar"}
uri (str th/+base-url+ "/api/auth/token") ;; uri (str th/+base-url+ "/api/auth/token")
[status data] (th/http-post uri {:body data})] ;; [status data] (th/http-post uri {:body data})]
;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
(t/is (= status 200)) ;; (t/is (= status 200))
(t/is (contains? data :token)))))) ;; (t/is (contains? data :token))))))
(t/deftest test-http-failed-auth ;; (t/deftest test-http-failed-auth
(let [data {:username "user1" ;; (let [data {:username "user1"
:fullname "user 1" ;; :fullname "user 1"
:metadata "1" ;; :metadata "1"
:password (hashers/encrypt "user1") ;; :password (hashers/encrypt "user1")
:email "user1@uxbox.io"} ;; :email "user1@uxbox.io"}
user (with-open [conn (db/connection)] ;; user (with-open [conn (db/connection)]
(usu/create-user conn data))] ;; (usu/create-user conn data))]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [data {:username "user1" ;; (let [data {:username "user1"
:password "user2" ;; :password "user2"
:metadata "2" ;; :metadata "2"
:scope "foobar"} ;; :scope "foobar"}
uri (str th/+base-url+ "/api/auth/token") ;; uri (str th/+base-url+ "/api/auth/token")
[status data] (th/http-post uri {:body data})] ;; [status data] (th/http-post uri {:body data})]
;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
(t/is (= 400 status)) ;; (t/is (= 400 status))
(t/is (= (:type data) :validation)) ;; (t/is (= (:type data) :validation))
(t/is (= (:code data) :uxbox.services.auth/wrong-credentials)))))) ;; (t/is (= (:code data) :uxbox.services.auth/wrong-credentials))))))

View file

@ -13,151 +13,151 @@
[uxbox.services :as usv] [uxbox.services :as usv]
[uxbox.tests.helpers :as th])) [uxbox.tests.helpers :as th]))
(t/use-fixtures :each th/database-reset) ;; (t/use-fixtures :each th/database-reset)
(t/deftest test-http-list-icon-collections ;; (t/deftest test-http-list-icon-collections
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)
data {:user (:id user)
:name "coll1"}
coll (icons/create-collection conn data)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/icon-collections")
[status data] (th/http-get user uri)]
;; (println "RESPONSE:" status data)
(t/is (= 200 status))
(t/is (= 1 (count data))))))))
(t/deftest test-http-create-icon-collection
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/icon-collections")
data {:user (:id user)
:name "coll1"}
params {:body data}
[status data] (th/http-post user uri params)]
;; (println "RESPONSE:" status data)
(t/is (= 201 status))
(t/is (= (:user data) (:id user)))
(t/is (= (:name data) "coll1")))))))
(t/deftest test-http-update-icon-collection
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)
data {:user (:id user)
:name "coll1"}
coll (icons/create-collection conn data)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/icon-collections/" (:id coll))
params {:body (assoc coll :name "coll2")}
[status data] (th/http-put user uri params)]
;; (println "RESPONSE:" status data)
(t/is (= 200 status))
(t/is (= (:user data) (:id user)))
(t/is (= (:name data) "coll2")))))))
(t/deftest test-http-icon-collection-delete
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)
data {:user (:id user)
:name "coll1"
:data #{1}}
coll (icons/create-collection conn data)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/icon-collections/" (:id coll))
[status data] (th/http-delete user uri)]
(t/is (= 204 status))
(let [sqlv (sql/get-icon-collections {:user (:id user)})
result (sc/fetch conn sqlv)]
(t/is (empty? result))))))))
(t/deftest test-http-create-icon
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/icons")
data {:name "sample.jpg"
:content "<g></g>"
:metadata {:width 200
:height 200
:view-box [0 0 200 200]}
:collection nil}
params {:body data}
[status data] (th/http-post user uri params)]
;; (println "RESPONSE:" status data)
(t/is (= 201 status))
(t/is (= (:user data) (:id user)))
(t/is (= (:name data) "sample.jpg"))
(t/is (= (:metadata data) {:width 200
:height 200
:view-box [0 0 200 200]})))))))
(t/deftest test-http-update-icon
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)
data {:user (:id user)
:name "test.svg"
:content "<g></g>"
:metadata {}
:collection nil}
icon (icons/create-icon conn data)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/icons/" (:id icon))
params {:body (assoc icon :name "my stuff")}
[status data] (th/http-put user uri params)]
;; (println "RESPONSE:" status data)
(t/is (= 200 status))
(t/is (= (:user data) (:id user)))
(t/is (= (:name data) "my stuff")))))))
(t/deftest test-http-copy-icon
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)
data {:user (:id user)
:name "test.svg"
:content "<g></g>"
:metadata {}
:collection nil}
icon (icons/create-icon conn data)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/icons/copy")
body {:id (:id icon) :collection nil}
params {:body body}
[status data] (th/http-put user uri params)]
(println "RESPONSE:" status data)
(let [sqlv (sql/get-icons {:user (:id user) :collection nil})
result (sc/fetch conn sqlv)]
(t/is (= 2 (count result)))))))))
(t/deftest test-http-delete-icon
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)
data {:user (:id user)
:name "test.svg"
:content "<g></g>"
:metadata {}
:collection nil}
icon (icons/create-icon conn data)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/icons/" (:id icon))
[status data] (th/http-delete user uri)]
(t/is (= 204 status))
(let [sqlv (sql/get-icons {:user (:id user) :collection nil})
result (sc/fetch conn sqlv)]
(t/is (empty? result))))))))
;; (t/deftest test-http-list-icons
;; (with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
;; data {:user (:id user) ;; data {:user (:id user)
;; :name "test.png" ;; :name "coll1"}
;; :path "some/path" ;; coll (icons/create-collection conn data)]
;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/icon-collections")
;; [status data] (th/http-get user uri)]
;; ;; (println "RESPONSE:" status data)
;; (t/is (= 200 status))
;; (t/is (= 1 (count data))))))))
;; (t/deftest test-http-create-icon-collection
;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1)]
;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/icon-collections")
;; data {:user (:id user)
;; :name "coll1"}
;; params {:body data}
;; [status data] (th/http-post user uri params)]
;; ;; (println "RESPONSE:" status data)
;; (t/is (= 201 status))
;; (t/is (= (:user data) (:id user)))
;; (t/is (= (:name data) "coll1")))))))
;; (t/deftest test-http-update-icon-collection
;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1)
;; data {:user (:id user)
;; :name "coll1"}
;; coll (icons/create-collection conn data)]
;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/icon-collections/" (:id coll))
;; params {:body (assoc coll :name "coll2")}
;; [status data] (th/http-put user uri params)]
;; ;; (println "RESPONSE:" status data)
;; (t/is (= 200 status))
;; (t/is (= (:user data) (:id user)))
;; (t/is (= (:name data) "coll2")))))))
;; (t/deftest test-http-icon-collection-delete
;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1)
;; data {:user (:id user)
;; :name "coll1"
;; :data #{1}}
;; coll (icons/create-collection conn data)]
;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/icon-collections/" (:id coll))
;; [status data] (th/http-delete user uri)]
;; (t/is (= 204 status))
;; (let [sqlv (sql/get-icon-collections {:user (:id user)})
;; result (sc/fetch conn sqlv)]
;; (t/is (empty? result))))))))
;; (t/deftest test-http-create-icon
;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1)]
;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/icons")
;; data {:name "sample.jpg"
;; :content "<g></g>"
;; :metadata {:width 200
;; :height 200
;; :view-box [0 0 200 200]}
;; :collection nil}
;; params {:body data}
;; [status data] (th/http-post user uri params)]
;; ;; (println "RESPONSE:" status data)
;; (t/is (= 201 status))
;; (t/is (= (:user data) (:id user)))
;; (t/is (= (:name data) "sample.jpg"))
;; (t/is (= (:metadata data) {:width 200
;; :height 200
;; :view-box [0 0 200 200]})))))))
;; (t/deftest test-http-update-icon
;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1)
;; data {:user (:id user)
;; :name "test.svg"
;; :content "<g></g>"
;; :metadata {}
;; :collection nil} ;; :collection nil}
;; icon (icons/create-icon conn data)] ;; icon (icons/create-icon conn data)]
;; (with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/icons") ;; (let [uri (str th/+base-url+ "/api/library/icons/" (:id icon))
;; [status data] (th/http-get user uri)] ;; params {:body (assoc icon :name "my stuff")}
;; (println "RESPONSE:" status data) ;; [status data] (th/http-put user uri params)]
;; ;; (println "RESPONSE:" status data)
;; (t/is (= 200 status)) ;; (t/is (= 200 status))
;; (t/is (= 1 (count data)))))))) ;; (t/is (= (:user data) (:id user)))
;; (t/is (= (:name data) "my stuff")))))))
;; (t/deftest test-http-copy-icon
;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1)
;; data {:user (:id user)
;; :name "test.svg"
;; :content "<g></g>"
;; :metadata {}
;; :collection nil}
;; icon (icons/create-icon conn data)]
;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/icons/copy")
;; body {:id (:id icon) :collection nil}
;; params {:body body}
;; [status data] (th/http-put user uri params)]
;; (println "RESPONSE:" status data)
;; (let [sqlv (sql/get-icons {:user (:id user) :collection nil})
;; result (sc/fetch conn sqlv)]
;; (t/is (= 2 (count result)))))))))
;; (t/deftest test-http-delete-icon
;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1)
;; data {:user (:id user)
;; :name "test.svg"
;; :content "<g></g>"
;; :metadata {}
;; :collection nil}
;; icon (icons/create-icon conn data)]
;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/icons/" (:id icon))
;; [status data] (th/http-delete user uri)]
;; (t/is (= 204 status))
;; (let [sqlv (sql/get-icons {:user (:id user) :collection nil})
;; result (sc/fetch conn sqlv)]
;; (t/is (empty? result))))))))
;; ;; (t/deftest test-http-list-icons
;; ;; (with-open [conn (db/connection)]
;; ;; (let [user (th/create-user conn 1)
;; ;; data {:user (:id user)
;; ;; :name "test.png"
;; ;; :path "some/path"
;; ;; :collection nil}
;; ;; icon (icons/create-icon conn data)]
;; ;; (with-server {:handler (uft/routes)}
;; ;; (let [uri (str th/+base-url+ "/api/library/icons")
;; ;; [status data] (th/http-get user uri)]
;; ;; (println "RESPONSE:" status data)
;; ;; (t/is (= 200 status))
;; ;; (t/is (= 1 (count data))))))))

View file

@ -13,157 +13,157 @@
[uxbox.services :as usv] [uxbox.services :as usv]
[uxbox.tests.helpers :as th])) [uxbox.tests.helpers :as th]))
(t/use-fixtures :each th/database-reset) ;; (t/use-fixtures :each th/database-reset)
(t/deftest test-http-list-image-collections ;; (t/deftest test-http-list-image-collections
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
data {:user (:id user) ;; data {:user (:id user)
:name "coll1"} ;; :name "coll1"}
coll (images/create-collection conn data)] ;; coll (images/create-collection conn data)]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/image-collections") ;; (let [uri (str th/+base-url+ "/api/library/image-collections")
[status data] (th/http-get user uri)] ;; [status data] (th/http-get user uri)]
;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= 1 (count data)))))))) ;; (t/is (= 1 (count data))))))))
(t/deftest test-http-create-image-collection ;; (t/deftest test-http-create-image-collection
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/image-collections")
data {:user (:id user)
:name "coll1"}
params {:body data}
[status data] (th/http-post user uri params)]
;; (println "RESPONSE:" status data)
(t/is (= 201 status))
(t/is (= (:user data) (:id user)))
(t/is (= (:name data) "coll1")))))))
(t/deftest test-http-update-image-collection
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)
data {:user (:id user)
:name "coll1"}
coll (images/create-collection conn data)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/image-collections/" (:id coll))
params {:body (assoc coll :name "coll2")}
[status data] (th/http-put user uri params)]
;; (println "RESPONSE:" status data)
(t/is (= 200 status))
(t/is (= (:user data) (:id user)))
(t/is (= (:name data) "coll2")))))))
(t/deftest test-http-image-collection-delete
(with-open [conn (db/connection)]
(let [user (th/create-user conn 1)
data {:user (:id user)
:name "coll1"
:data #{1}}
coll (images/create-collection conn data)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/image-collections/" (:id coll))
[status data] (th/http-delete user uri)]
(t/is (= 204 status))
(let [sqlv (sql/get-image-collections {:user (:id user)})
result (sc/fetch conn sqlv)]
(t/is (empty? result))))))))
;; (t/deftest test-http-create-image
;; (with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1)] ;; (let [user (th/create-user conn 1)]
;; (with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/images") ;; (let [uri (str th/+base-url+ "/api/library/image-collections")
;; params [{:name "sample.jpg" ;; data {:user (:id user)
;; :part-name "file" ;; :name "coll1"}
;; :content (io/input-stream ;; params {:body data}
;; (io/resource "uxbox/tests/_files/sample.jpg"))}] ;; [status data] (th/http-post user uri params)]
;; [status data] (th/http-multipart user uri params)]
;; ;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
;; (t/is (= 201 status)) ;; (t/is (= 201 status))
;; (t/is (= (:user data) (:id user))) ;; (t/is (= (:user data) (:id user)))
;; (t/is (= (:name data) "sample.jpg"))))))) ;; (t/is (= (:name data) "coll1")))))))
(t/deftest test-http-update-image ;; (t/deftest test-http-update-image-collection
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
data {:user (:id user) ;; data {:user (:id user)
:name "test.png" ;; :name "coll1"}
:path "some/path" ;; coll (images/create-collection conn data)]
:width 100 ;; (with-server {:handler (uft/routes)}
:height 100 ;; (let [uri (str th/+base-url+ "/api/library/image-collections/" (:id coll))
:mimetype "image/png" ;; params {:body (assoc coll :name "coll2")}
:collection nil} ;; [status data] (th/http-put user uri params)]
img (images/create-image conn data)] ;; ;; (println "RESPONSE:" status data)
(with-server {:handler (uft/routes)} ;; (t/is (= 200 status))
(let [uri (str th/+base-url+ "/api/library/images/" (:id img)) ;; (t/is (= (:user data) (:id user)))
params {:body (assoc img :name "my stuff")} ;; (t/is (= (:name data) "coll2")))))))
[status data] (th/http-put user uri params)]
;; (println "RESPONSE:" status data)
(t/is (= 200 status))
(t/is (= (:user data) (:id user)))
(t/is (= (:name data) "my stuff")))))))
(t/deftest test-http-copy-image ;; (t/deftest test-http-image-collection-delete
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
storage media/images-storage ;; data {:user (:id user)
filename "sample.jpg" ;; :name "coll1"
rcs (io/resource "uxbox/tests/_files/sample.jpg") ;; :data #{1}}
path @(st/save storage filename rcs) ;; coll (images/create-collection conn data)]
data {:user (:id user) ;; (with-server {:handler (uft/routes)}
:name filename ;; (let [uri (str th/+base-url+ "/api/library/image-collections/" (:id coll))
:path (str path) ;; [status data] (th/http-delete user uri)]
:width 100 ;; (t/is (= 204 status))
:height 100 ;; (let [sqlv (sql/get-image-collections {:user (:id user)})
:mimetype "image/jpg" ;; result (sc/fetch conn sqlv)]
:collection nil} ;; (t/is (empty? result))))))))
img (images/create-image conn data)]
(with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/library/images/copy")
body {:id (:id img)
:collection nil}
params {:body body}
[status data] (th/http-put user uri params)]
;; (println "RESPONSE:" status data)
(t/is (= 200 status))
(let [sqlv (sql/get-images {:user (:id user) :collection nil})
result (sc/fetch conn sqlv)]
(t/is (= 2 (count result)))))))))
(t/deftest test-http-delete-image ;; ;; (t/deftest test-http-create-image
(with-open [conn (db/connection)] ;; ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; ;; (let [user (th/create-user conn 1)]
data {:user (:id user) ;; ;; (with-server {:handler (uft/routes)}
:name "test.png" ;; ;; (let [uri (str th/+base-url+ "/api/library/images")
:path "some/path" ;; ;; params [{:name "sample.jpg"
:width 100 ;; ;; :part-name "file"
:height 100 ;; ;; :content (io/input-stream
:mimetype "image/png" ;; ;; (io/resource "uxbox/tests/_files/sample.jpg"))}]
:collection nil} ;; ;; [status data] (th/http-multipart user uri params)]
img (images/create-image conn data)] ;; ;; ;; (println "RESPONSE:" status data)
(with-server {:handler (uft/routes)} ;; ;; (t/is (= 201 status))
(let [uri (str th/+base-url+ "/api/library/images/" (:id img)) ;; ;; (t/is (= (:user data) (:id user)))
[status data] (th/http-delete user uri)] ;; ;; (t/is (= (:name data) "sample.jpg")))))))
(t/is (= 204 status))
(let [sqlv (sql/get-images {:user (:id user) :collection nil})
result (sc/fetch conn sqlv)]
(t/is (empty? result))))))))
;; (t/deftest test-http-list-images ;; (t/deftest test-http-update-image
;; (with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
;; data {:user (:id user) ;; data {:user (:id user)
;; :name "test.png" ;; :name "test.png"
;; :path "some/path" ;; :path "some/path"
;; :width 100
;; :height 100
;; :mimetype "image/png"
;; :collection nil} ;; :collection nil}
;; img (images/create-image conn data)] ;; img (images/create-image conn data)]
;; (with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/images") ;; (let [uri (str th/+base-url+ "/api/library/images/" (:id img))
;; [status data] (th/http-get user uri)] ;; params {:body (assoc img :name "my stuff")}
;; (println "RESPONSE:" status data) ;; [status data] (th/http-put user uri params)]
;; ;; (println "RESPONSE:" status data)
;; (t/is (= 200 status)) ;; (t/is (= 200 status))
;; (t/is (= 1 (count data)))))))) ;; (t/is (= (:user data) (:id user)))
;; (t/is (= (:name data) "my stuff")))))))
;; (t/deftest test-http-copy-image
;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1)
;; storage media/images-storage
;; filename "sample.jpg"
;; rcs (io/resource "uxbox/tests/_files/sample.jpg")
;; path @(st/save storage filename rcs)
;; data {:user (:id user)
;; :name filename
;; :path (str path)
;; :width 100
;; :height 100
;; :mimetype "image/jpg"
;; :collection nil}
;; img (images/create-image conn data)]
;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/images/copy")
;; body {:id (:id img)
;; :collection nil}
;; params {:body body}
;; [status data] (th/http-put user uri params)]
;; ;; (println "RESPONSE:" status data)
;; (t/is (= 200 status))
;; (let [sqlv (sql/get-images {:user (:id user) :collection nil})
;; result (sc/fetch conn sqlv)]
;; (t/is (= 2 (count result)))))))))
;; (t/deftest test-http-delete-image
;; (with-open [conn (db/connection)]
;; (let [user (th/create-user conn 1)
;; data {:user (:id user)
;; :name "test.png"
;; :path "some/path"
;; :width 100
;; :height 100
;; :mimetype "image/png"
;; :collection nil}
;; img (images/create-image conn data)]
;; (with-server {:handler (uft/routes)}
;; (let [uri (str th/+base-url+ "/api/library/images/" (:id img))
;; [status data] (th/http-delete user uri)]
;; (t/is (= 204 status))
;; (let [sqlv (sql/get-images {:user (:id user) :collection nil})
;; result (sc/fetch conn sqlv)]
;; (t/is (empty? result))))))))
;; ;; (t/deftest test-http-list-images
;; ;; (with-open [conn (db/connection)]
;; ;; (let [user (th/create-user conn 1)
;; ;; data {:user (:id user)
;; ;; :name "test.png"
;; ;; :path "some/path"
;; ;; :collection nil}
;; ;; img (images/create-image conn data)]
;; ;; (with-server {:handler (uft/routes)}
;; ;; (let [uri (str th/+base-url+ "/api/library/images")
;; ;; [status data] (th/http-get user uri)]
;; ;; (println "RESPONSE:" status data)
;; ;; (t/is (= 200 status))
;; ;; (t/is (= 1 (count data))))))))

View file

@ -10,56 +10,56 @@
[uxbox.services.kvstore :as kvs] [uxbox.services.kvstore :as kvs]
[uxbox.tests.helpers :as th])) [uxbox.tests.helpers :as th]))
(t/use-fixtures :each th/database-reset) ;; (t/use-fixtures :each th/database-reset)
(t/deftest test-http-kvstore ;; (t/deftest test-http-kvstore
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [{:keys [id] :as user} (th/create-user conn 1)] ;; (let [{:keys [id] :as user} (th/create-user conn 1)]
;; Not exists at this moment ;; ;; Not exists at this moment
(t/is (nil? (kvs/retrieve-kvstore conn {:user id :key "foo" :version -1}))) ;; (t/is (nil? (kvs/retrieve-kvstore conn {:user id :key "foo" :version -1})))
;; Creating new one should work as expected ;; ;; Creating new one should work as expected
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/kvstore") ;; (let [uri (str th/+base-url+ "/api/kvstore")
body {:key "foo" :value "bar" :version -1} ;; body {:key "foo" :value "bar" :version -1}
params {:body body} ;; params {:body body}
[status data] (th/http-put user uri params)] ;; [status data] (th/http-put user uri params)]
(println "RESPONSE:" status data) ;; (println "RESPONSE:" status data)
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= (:key data) "foo")) ;; (t/is (= (:key data) "foo"))
(t/is (= (:value data) "bar")))) ;; (t/is (= (:value data) "bar"))))
;; Should exists ;; ;; Should exists
(let [data (kvs/retrieve-kvstore conn {:user id :key "foo"})] ;; (let [data (kvs/retrieve-kvstore conn {:user id :key "foo"})]
(t/is (= (:key data) "foo")) ;; (t/is (= (:key data) "foo"))
(t/is (= (:value data) "bar")) ;; (t/is (= (:value data) "bar"))
;; Overwriting should work ;; ;; Overwriting should work
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/kvstore") ;; (let [uri (str th/+base-url+ "/api/kvstore")
body (assoc data :key "foo" :value "baz") ;; body (assoc data :key "foo" :value "baz")
params {:body body} ;; params {:body body}
[status data] (th/http-put user uri params)] ;; [status data] (th/http-put user uri params)]
(println "RESPONSE:" status data) ;; (println "RESPONSE:" status data)
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= (:key data) "foo")) ;; (t/is (= (:key data) "foo"))
(t/is (= (:value data) "baz"))))) ;; (t/is (= (:value data) "baz")))))
;; Should exists and match the overwritten value ;; ;; Should exists and match the overwritten value
(let [data (kvs/retrieve-kvstore conn {:user id :key "foo"})] ;; (let [data (kvs/retrieve-kvstore conn {:user id :key "foo"})]
(t/is (= (:key data) "foo")) ;; (t/is (= (:key data) "foo"))
(t/is (= (:value data) "baz"))) ;; (t/is (= (:value data) "baz")))
;; Delete should work ;; ;; Delete should work
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/kvstore/foo") ;; (let [uri (str th/+base-url+ "/api/kvstore/foo")
[status data] (th/http-delete user uri)] ;; [status data] (th/http-delete user uri)]
(println "RESPONSE:" status data) ;; (println "RESPONSE:" status data)
(t/is (= 204 status)))) ;; (t/is (= 204 status))))
;; Not exists at this moment ;; ;; Not exists at this moment
(t/is (nil? (kvs/retrieve-kvstore conn {:user id :key "foo"}))) ;; (t/is (nil? (kvs/retrieve-kvstore conn {:user id :key "foo"})))
))) ;; )))

View file

@ -14,211 +14,211 @@
(t/use-fixtures :each th/database-reset) (t/use-fixtures :each th/database-reset)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Frontend Test ;; ;; Frontend Test
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(t/deftest test-http-page-create ;; (t/deftest test-http-page-create
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
proj (uspr/create-project conn {:user (:id user) :name "proj1"})] ;; proj (uspr/create-project conn {:user (:id user) :name "proj1"})]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/pages") ;; (let [uri (str th/+base-url+ "/api/pages")
params {:body {:project (:id proj) ;; params {:body {:project (:id proj)
:name "page1" ;; :name "page1"
:data "1" ;; :data "1"
:metadata "1" ;; :metadata "1"
:width 200 ;; :width 200
:height 200 ;; :height 200
:layout "mobile"}} ;; :layout "mobile"}}
[status data] (th/http-post user uri params)] ;; [status data] (th/http-post user uri params)]
;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
(t/is (= 201 status)) ;; (t/is (= 201 status))
(t/is (= (:data (:body params)) (:data data))) ;; (t/is (= (:data (:body params)) (:data data)))
(t/is (= (:user data) (:id user))) ;; (t/is (= (:user data) (:id user)))
(t/is (= (:name data) "page1"))))))) ;; (t/is (= (:name data) "page1")))))))
(t/deftest test-http-page-update ;; (t/deftest test-http-page-update
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
proj (uspr/create-project conn {:user (:id user) :name "proj1"}) ;; proj (uspr/create-project conn {:user (:id user) :name "proj1"})
data {:id (uuid/random) ;; data {:id (uuid/random)
:user (:id user) ;; :user (:id user)
:project (:id proj) ;; :project (:id proj)
:version 0 ;; :version 0
:data "1" ;; :data "1"
:metadata "2" ;; :metadata "2"
:name "page1" ;; :name "page1"
:width 200 ;; :width 200
:height 200 ;; :height 200
:layout "mobil"} ;; :layout "mobil"}
page (uspg/create-page conn data)] ;; page (uspg/create-page conn data)]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ (str "/api/pages/" (:id page))) ;; (let [uri (str th/+base-url+ (str "/api/pages/" (:id page)))
params {:body (assoc page :data "3")} ;; params {:body (assoc page :data "3")}
[status page'] (th/http-put user uri params)] ;; [status page'] (th/http-put user uri params)]
;; (println "RESPONSE:" status page') ;; ;; (println "RESPONSE:" status page')
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= "3" (:data page'))) ;; (t/is (= "3" (:data page')))
(t/is (= 1 (:version page'))) ;; (t/is (= 1 (:version page')))
(t/is (= (:user page') (:id user))) ;; (t/is (= (:user page') (:id user)))
(t/is (= (:name data) "page1"))))))) ;; (t/is (= (:name data) "page1")))))))
(t/deftest test-http-page-update-metadata ;; (t/deftest test-http-page-update-metadata
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
proj (uspr/create-project conn {:user (:id user) :name "proj1"}) ;; proj (uspr/create-project conn {:user (:id user) :name "proj1"})
data {:id (uuid/random) ;; data {:id (uuid/random)
:user (:id user) ;; :user (:id user)
:project (:id proj) ;; :project (:id proj)
:version 0 ;; :version 0
:data "1" ;; :data "1"
:metadata "2" ;; :metadata "2"
:name "page1" ;; :name "page1"
:width 200 ;; :width 200
:height 200 ;; :height 200
:layout "mobil"} ;; :layout "mobil"}
page (uspg/create-page conn data)] ;; page (uspg/create-page conn data)]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ (str "/api/pages/" (:id page) "/metadata")) ;; (let [uri (str th/+base-url+ (str "/api/pages/" (:id page) "/metadata"))
params {:body (assoc page :data "3")} ;; params {:body (assoc page :data "3")}
[status page'] (th/http-put user uri params)] ;; [status page'] (th/http-put user uri params)]
;; (println "RESPONSE:" status page') ;; ;; (println "RESPONSE:" status page')
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= "1" (:data page'))) ;; (t/is (= "1" (:data page')))
(t/is (= 1 (:version page'))) ;; (t/is (= 1 (:version page')))
(t/is (= (:user page') (:id user))) ;; (t/is (= (:user page') (:id user)))
(t/is (= (:name data) "page1"))))))) ;; (t/is (= (:name data) "page1")))))))
(t/deftest test-http-page-delete ;; (t/deftest test-http-page-delete
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
proj (uspr/create-project conn {:user (:id user) :name "proj1"}) ;; proj (uspr/create-project conn {:user (:id user) :name "proj1"})
data {:id (uuid/random) ;; data {:id (uuid/random)
:user (:id user) ;; :user (:id user)
:project (:id proj) ;; :project (:id proj)
:version 0 ;; :version 0
:data "1" ;; :data "1"
:metadata "2" ;; :metadata "2"
:name "page1" ;; :name "page1"
:width 200 ;; :width 200
:height 200 ;; :height 200
:layout "mobil"} ;; :layout "mobil"}
page (uspg/create-page conn data)] ;; page (uspg/create-page conn data)]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ (str "/api/pages/" (:id page))) ;; (let [uri (str th/+base-url+ (str "/api/pages/" (:id page)))
[status response] (th/http-delete user uri)] ;; [status response] (th/http-delete user uri)]
;; (println "RESPONSE:" status response) ;; ;; (println "RESPONSE:" status response)
(t/is (= 204 status)) ;; (t/is (= 204 status))
(let [sqlv ["SELECT * FROM pages WHERE \"user\"=? AND deleted_at is null" ;; (let [sqlv ["SELECT * FROM pages WHERE \"user\"=? AND deleted_at is null"
(:id user)] ;; (:id user)]
result (sc/fetch conn sqlv)] ;; result (sc/fetch conn sqlv)]
(t/is (empty? result)))))))) ;; (t/is (empty? result))))))))
(t/deftest test-http-page-list-by-project ;; (t/deftest test-http-page-list-by-project
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
proj1 (uspr/create-project conn {:user (:id user) :name "proj1"}) ;; proj1 (uspr/create-project conn {:user (:id user) :name "proj1"})
proj2 (uspr/create-project conn {:user (:id user) :name "proj2"}) ;; proj2 (uspr/create-project conn {:user (:id user) :name "proj2"})
data {:user (:id user) ;; data {:user (:id user)
:version 0 ;; :version 0
:data "1" ;; :data "1"
:metadata "2" ;; :metadata "2"
:name "page1" ;; :name "page1"
:width 200 ;; :width 200
:height 200 ;; :height 200
:layout "mobil"} ;; :layout "mobil"}
page1 (uspg/create-page conn (assoc data :project (:id proj1))) ;; page1 (uspg/create-page conn (assoc data :project (:id proj1)))
page2 (uspg/create-page conn (assoc data :project (:id proj2)))] ;; page2 (uspg/create-page conn (assoc data :project (:id proj2)))]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ (str "/api/projects/" (:id proj1) "/pages")) ;; (let [uri (str th/+base-url+ (str "/api/projects/" (:id proj1) "/pages"))
[status response] (th/http-get user uri)] ;; [status response] (th/http-get user uri)]
;; (println "RESPONSE:" status response) ;; ;; (println "RESPONSE:" status response)
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= 1 (count response))) ;; (t/is (= 1 (count response)))
(t/is (= (:id (first response)) (:id page1)))))))) ;; (t/is (= (:id (first response)) (:id page1))))))))
(t/deftest test-http-page-history-retrieve ;; (t/deftest test-http-page-history-retrieve
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
proj (uspr/create-project conn {:user (:id user) :name "proj1"}) ;; proj (uspr/create-project conn {:user (:id user) :name "proj1"})
data {:id (uuid/random) ;; data {:id (uuid/random)
:user (:id user) ;; :user (:id user)
:project (:id proj) ;; :project (:id proj)
:version 0 ;; :version 0
:data "1" ;; :data "1"
:metadata "2" ;; :metadata "2"
:name "page1" ;; :name "page1"
:width 200 ;; :width 200
:height 200 ;; :height 200
:layout "mobil"} ;; :layout "mobil"}
page (uspg/create-page conn data)] ;; page (uspg/create-page conn data)]
(dotimes [i 100] ;; (dotimes [i 100]
(let [page (uspg/get-page-by-id conn (:id data))] ;; (let [page (uspg/get-page-by-id conn (:id data))]
(uspg/update-page conn (assoc page :data (str i))))) ;; (uspg/update-page conn (assoc page :data (str i)))))
;; Check inserted history ;; ;; Check inserted history
(let [sqlv ["SELECT * FROM pages_history WHERE page=?" (:id data)] ;; (let [sqlv ["SELECT * FROM pages_history WHERE page=?" (:id data)]
result (sc/fetch conn sqlv)] ;; result (sc/fetch conn sqlv)]
(t/is (= (count result) 101))) ;; (t/is (= (count result) 101)))
;; Check retrieve all items ;; ;; Check retrieve all items
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/pages/" (:id page) "/history") ;; (let [uri (str th/+base-url+ "/api/pages/" (:id page) "/history")
[status result] (th/http-get user uri nil)] ;; [status result] (th/http-get user uri nil)]
;; (println "RESPONSE:" status result) ;; ;; (println "RESPONSE:" status result)
(t/is (= (count result) 10)) ;; (t/is (= (count result) 10))
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= 100 (:version (first result)))) ;; (t/is (= 100 (:version (first result))))
(let [params {:query {:since (:version (last result)) ;; (let [params {:query {:since (:version (last result))
:max 20}} ;; :max 20}}
[status result] (th/http-get user uri params)] ;; [status result] (th/http-get user uri params)]
;; (println "RESPONSE:" status result) ;; ;; (println "RESPONSE:" status result)
(t/is (= (count result) 20)) ;; (t/is (= (count result) 20))
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= 90 (:version (first result)))))) ;; (t/is (= 90 (:version (first result))))))
)))) ;; ))))
(t/deftest test-http-page-history-update ;; (t/deftest test-http-page-history-update
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
proj (uspr/create-project conn {:user (:id user) :name "proj1"}) ;; proj (uspr/create-project conn {:user (:id user) :name "proj1"})
data {:id (uuid/random) ;; data {:id (uuid/random)
:user (:id user) ;; :user (:id user)
:project (:id proj) ;; :project (:id proj)
:version 0 ;; :version 0
:data "1" ;; :data "1"
:metadata "2" ;; :metadata "2"
:name "page1" ;; :name "page1"
:width 200 ;; :width 200
:height 200 ;; :height 200
:layout "mobil"} ;; :layout "mobil"}
page (uspg/create-page conn data)] ;; page (uspg/create-page conn data)]
(dotimes [i 10] ;; (dotimes [i 10]
(let [page (uspg/get-page-by-id conn (:id data))] ;; (let [page (uspg/get-page-by-id conn (:id data))]
(uspg/update-page conn (assoc page :data (str i))))) ;; (uspg/update-page conn (assoc page :data (str i)))))
;; Check inserted history ;; ;; Check inserted history
(let [sql (str "SELECT * FROM pages_history " ;; (let [sql (str "SELECT * FROM pages_history "
" WHERE page=? ORDER BY created_at DESC") ;; " WHERE page=? ORDER BY created_at DESC")
result (sc/fetch conn [sql (:id data)]) ;; result (sc/fetch conn [sql (:id data)])
item (first result)] ;; item (first result)]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ ;; (let [uri (str th/+base-url+
"/api/pages/" (:id page) ;; "/api/pages/" (:id page)
"/history/" (:id item)) ;; "/history/" (:id item))
params {:body {:label "test" :pinned true}} ;; params {:body {:label "test" :pinned true}}
[status data] (th/http-put user uri params)] ;; [status data] (th/http-put user uri params)]
;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= (:id data) (:id item)))))) ;; (t/is (= (:id data) (:id item))))))
(let [sql (str "SELECT * FROM pages_history " ;; (let [sql (str "SELECT * FROM pages_history "
" WHERE page=? AND pinned = true " ;; " WHERE page=? AND pinned = true "
" ORDER BY created_at DESC") ;; " ORDER BY created_at DESC")
result (sc/fetch-one conn [sql (:id data)])] ;; result (sc/fetch-one conn [sql (:id data)])]
(t/is (= "test" (:label result))) ;; (t/is (= "test" (:label result)))
(t/is (= true (:pinned result))))))) ;; (t/is (= true (:pinned result)))))))

View file

@ -6,7 +6,7 @@
[catacumba.testing :refer (with-server)] [catacumba.testing :refer (with-server)]
[catacumba.serializers :as sz] [catacumba.serializers :as sz]
[uxbox.db :as db] [uxbox.db :as db]
[uxbox.frontend :as uft] [uxbox.api :as uapi]
[uxbox.services.projects :as uspr] [uxbox.services.projects :as uspr]
[uxbox.services.pages :as uspg] [uxbox.services.pages :as uspg]
[uxbox.services :as usv] [uxbox.services :as usv]
@ -22,17 +22,16 @@
(with-open [conn (db/connection)] (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) (let [user (th/create-user conn 1)
proj (uspr/create-project conn {:user (:id user) :name "proj1"})] proj (uspr/create-project conn {:user (:id user) :name "proj1"})]
(with-server {:handler (uft/routes)} (th/with-server {:handler uapi/app}
(let [uri (str th/+base-url+ "/api/projects") (let [uri (str th/+base-url+ "/api/projects")
[status data] (th/http-get user uri)] [status data] (th/http-get user uri)]
;; (println "RESPONSE:" status data)
(t/is (= 200 status)) (t/is (= 200 status))
(t/is (= 1 (count data)))))))) (t/is (= 1 (count data))))))))
(t/deftest test-http-project-create (t/deftest test-http-project-create
(with-open [conn (db/connection)] (with-open [conn (db/connection)]
(let [user (th/create-user conn 1)] (let [user (th/create-user conn 1)]
(with-server {:handler (uft/routes)} (th/with-server {:handler uapi/app}
(let [uri (str th/+base-url+ "/api/projects") (let [uri (str th/+base-url+ "/api/projects")
params {:body {:name "proj1"}} params {:body {:name "proj1"}}
[status data] (th/http-post user uri params)] [status data] (th/http-post user uri params)]
@ -45,11 +44,11 @@
(with-open [conn (db/connection)] (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) (let [user (th/create-user conn 1)
proj (uspr/create-project conn {:user (:id user) :name "proj1"})] proj (uspr/create-project conn {:user (:id user) :name "proj1"})]
(with-server {:handler (uft/routes)} (th/with-server {:handler uapi/app}
(let [uri (str th/+base-url+ "/api/projects/" (:id proj)) (let [uri (str th/+base-url+ "/api/projects/" (:id proj))
params {:body (assoc proj :name "proj2")} params {:body (assoc proj :name "proj2")}
[status data] (th/http-put user uri params)] [status data] (th/http-put user uri params)]
;; (println "RESPONSE:" status data) (prn "RESPONSE:" status data)
(t/is (= 200 status)) (t/is (= 200 status))
(t/is (= (:user data) (:id user))) (t/is (= (:user data) (:id user)))
(t/is (= (:name data) "proj2"))))))) (t/is (= (:name data) "proj2")))))))
@ -58,7 +57,7 @@
(with-open [conn (db/connection)] (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) (let [user (th/create-user conn 1)
proj (uspr/create-project conn {:user (:id user) :name "proj1"})] proj (uspr/create-project conn {:user (:id user) :name "proj1"})]
(with-server {:handler (uft/routes)} (th/with-server {:handler uapi/app}
(let [uri (str th/+base-url+ "/api/projects/" (:id proj)) (let [uri (str th/+base-url+ "/api/projects/" (:id proj))
[status data] (th/http-delete user uri)] [status data] (th/http-delete user uri)]
(t/is (= 204 status)) (t/is (= 204 status))
@ -82,9 +81,9 @@
:height 200 :height 200
:layout "mobil"}) :layout "mobil"})
shares (uspr/get-share-tokens-for-project conn (:id proj))] shares (uspr/get-share-tokens-for-project conn (:id proj))]
(with-server {:handler (uft/routes)} (th/with-server {:handler uapi/app}
(let [token (:token (first shares)) (let [token (:token (first shares))
uri (str th/+base-url+ "/api/projects-by-token/" token) uri (str th/+base-url+ "/api/projects/by-token/" token)
[status data] (th/http-get user uri)] [status data] (th/http-get user uri)]
;; (println "RESPONSE:" status data) ;; (println "RESPONSE:" status data)
(t/is (= status 200)) (t/is (= status 200))

View file

@ -7,83 +7,83 @@
[uxbox.services.svgparse :as svg] [uxbox.services.svgparse :as svg]
[uxbox.tests.helpers :as th])) [uxbox.tests.helpers :as th]))
(t/use-fixtures :each th/state-init) ;; (t/use-fixtures :each th/state-init)
(t/deftest parse-svg-test ;; (t/deftest parse-svg-test
(t/testing "parsing valid svg 1" ;; (t/testing "parsing valid svg 1"
(let [image (slurp (io/resource "uxbox/tests/_files/sample1.svg")) ;; (let [image (slurp (io/resource "uxbox/tests/_files/sample1.svg"))
result (svg/parse-string image)] ;; result (svg/parse-string image)]
(t/is (contains? result :width)) ;; (t/is (contains? result :width))
(t/is (contains? result :height)) ;; (t/is (contains? result :height))
(t/is (contains? result :view-box)) ;; (t/is (contains? result :view-box))
(t/is (contains? result :name)) ;; (t/is (contains? result :name))
(t/is (contains? result :content)) ;; (t/is (contains? result :content))
(t/is (= 500.0 (:width result))) ;; (t/is (= 500.0 (:width result)))
(t/is (= 500.0 (:height result))) ;; (t/is (= 500.0 (:height result)))
(t/is (= [0.0 0.0 500.00001 500.00001] (:view-box result))) ;; (t/is (= [0.0 0.0 500.00001 500.00001] (:view-box result)))
(t/is (= "lock.svg" (:name result))))) ;; (t/is (= "lock.svg" (:name result)))))
(t/testing "parsing valid svg 2" ;; (t/testing "parsing valid svg 2"
(let [image (slurp (io/resource "uxbox/tests/_files/sample2.svg")) ;; (let [image (slurp (io/resource "uxbox/tests/_files/sample2.svg"))
result (svg/parse-string image)] ;; result (svg/parse-string image)]
(t/is (contains? result :width)) ;; (t/is (contains? result :width))
(t/is (contains? result :height)) ;; (t/is (contains? result :height))
(t/is (contains? result :view-box)) ;; (t/is (contains? result :view-box))
(t/is (contains? result :name)) ;; (t/is (contains? result :name))
(t/is (contains? result :content)) ;; (t/is (contains? result :content))
(t/is (= 500.0 (:width result))) ;; (t/is (= 500.0 (:width result)))
(t/is (= 500.0 (:height result))) ;; (t/is (= 500.0 (:height result)))
(t/is (= [0.0 0.0 500.0 500.00001] (:view-box result))) ;; (t/is (= [0.0 0.0 500.0 500.00001] (:view-box result)))
(t/is (= "play.svg" (:name result))))) ;; (t/is (= "play.svg" (:name result)))))
(t/testing "parsing invalid data 1" ;; (t/testing "parsing invalid data 1"
(let [image (slurp (io/resource "uxbox/tests/_files/sample.jpg")) ;; (let [image (slurp (io/resource "uxbox/tests/_files/sample.jpg"))
[e result] (th/try-on (svg/parse-string image))] ;; [e result] (th/try-on (svg/parse-string image))]
(t/is (th/exception? e)) ;; (t/is (th/exception? e))
(t/is (th/ex-info? e)) ;; (t/is (th/ex-info? e))
(t/is (th/ex-with-code? e :uxbox.services.svgparse/invalid-input)))) ;; (t/is (th/ex-with-code? e :uxbox.services.svgparse/invalid-input))))
(t/testing "parsing invalid data 2" ;; (t/testing "parsing invalid data 2"
(let [[e result] (th/try-on (svg/parse-string ""))] ;; (let [[e result] (th/try-on (svg/parse-string ""))]
(t/is (th/exception? e)) ;; (t/is (th/exception? e))
(t/is (th/ex-info? e)) ;; (t/is (th/ex-info? e))
(t/is (th/ex-with-code? e :uxbox.services.svgparse/invalid-input)))) ;; (t/is (th/ex-with-code? e :uxbox.services.svgparse/invalid-input))))
(t/testing "parsing invalid data 3" ;; (t/testing "parsing invalid data 3"
(let [[e result] (th/try-on (svg/parse-string "<svg></svg>"))] ;; (let [[e result] (th/try-on (svg/parse-string "<svg></svg>"))]
(t/is (th/exception? e)) ;; (t/is (th/exception? e))
(t/is (th/ex-info? e)) ;; (t/is (th/ex-info? e))
(t/is (th/ex-with-code? e :uxbox.services.svgparse/invalid-result)))) ;; (t/is (th/ex-with-code? e :uxbox.services.svgparse/invalid-result))))
(t/testing "valid http request" ;; (t/testing "valid http request"
(let [image (slurp (io/resource "uxbox/tests/_files/sample2.svg")) ;; (let [image (slurp (io/resource "uxbox/tests/_files/sample2.svg"))
path "/api/svg/parse"] ;; path "/api/svg/parse"]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [rsp (th/request {:method :post ;; (let [rsp (th/request {:method :post
:path path ;; :path path
:body image ;; :body image
:raw? true})] ;; :raw? true})]
(t/is (= 200 (:status rsp))) ;; (t/is (= 200 (:status rsp)))
(t/is (contains? (:body rsp) :width)) ;; (t/is (contains? (:body rsp) :width))
(t/is (contains? (:body rsp) :height)) ;; (t/is (contains? (:body rsp) :height))
(t/is (contains? (:body rsp) :view-box)) ;; (t/is (contains? (:body rsp) :view-box))
(t/is (contains? (:body rsp) :name)) ;; (t/is (contains? (:body rsp) :name))
(t/is (contains? (:body rsp) :content)) ;; (t/is (contains? (:body rsp) :content))
(t/is (= 500.0 (:width (:body rsp)))) ;; (t/is (= 500.0 (:width (:body rsp))))
(t/is (= 500.0 (:height (:body rsp)))) ;; (t/is (= 500.0 (:height (:body rsp))))
(t/is (= [0.0 0.0 500.0 500.00001] (:view-box (:body rsp)))) ;; (t/is (= [0.0 0.0 500.0 500.00001] (:view-box (:body rsp))))
(t/is (= "play.svg" (:name (:body rsp)))))))) ;; (t/is (= "play.svg" (:name (:body rsp))))))))
(t/testing "invalid http request" ;; (t/testing "invalid http request"
(let [path "/api/svg/parse" ;; (let [path "/api/svg/parse"
image "<svg></svg>"] ;; image "<svg></svg>"]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [rsp (th/request {:method :post ;; (let [rsp (th/request {:method :post
:path path ;; :path path
:body image ;; :body image
:raw? true})] ;; :raw? true})]
(t/is (= 400 (:status rsp))) ;; (t/is (= 400 (:status rsp)))
(t/is (= :validation (get-in rsp [:body :type]))) ;; (t/is (= :validation (get-in rsp [:body :type])))
(t/is (= ::svg/invalid-result (get-in rsp [:body :code]))))))) ;; (t/is (= ::svg/invalid-result (get-in rsp [:body :code])))))))
) ;; )

View file

@ -6,14 +6,14 @@
[uxbox.services :as usv] [uxbox.services :as usv]
[uxbox.tests.helpers :as th])) [uxbox.tests.helpers :as th]))
(t/use-fixtures :each th/database-reset) ;; (t/use-fixtures :each th/database-reset)
(defmethod usc/novelty ::testype1 ;; (defmethod usc/novelty ::testype1
[data] ;; [data]
true) ;; true)
(t/deftest txlog-spec1 ;; (t/deftest txlog-spec1
(let [data {:type ::testype1 :foo 1 :bar "baz"} ;; (let [data {:type ::testype1 :foo 1 :bar "baz"}
response (usv/novelty data)] ;; response (usv/novelty data)]
(t/is (p/promise? response)) ;; (t/is (p/promise? response))
(t/is (= true @response)))) ;; (t/is (= true @response))))

View file

@ -14,107 +14,107 @@
(t/use-fixtures :each th/database-reset) (t/use-fixtures :each th/database-reset)
(t/deftest test-http-retrieve-profile ;; (t/deftest test-http-retrieve-profile
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1)] ;; (let [user (th/create-user conn 1)]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/profile/me") ;; (let [uri (str th/+base-url+ "/api/profile/me")
[status data] (th/http-get user uri)] ;; [status data] (th/http-get user uri)]
;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= (:fullname data) "User 1")) ;; (t/is (= (:fullname data) "User 1"))
(t/is (= (:username data) "user1")) ;; (t/is (= (:username data) "user1"))
(t/is (= (:metadata data) "1")) ;; (t/is (= (:metadata data) "1"))
(t/is (= (:email data) "user1@uxbox.io")) ;; (t/is (= (:email data) "user1@uxbox.io"))
(t/is (not (contains? data :password)))))))) ;; (t/is (not (contains? data :password))))))))
(t/deftest test-http-update-profile ;; (t/deftest test-http-update-profile
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1)] ;; (let [user (th/create-user conn 1)]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/profile/me") ;; (let [uri (str th/+base-url+ "/api/profile/me")
data (assoc user ;; data (assoc user
:fullname "Full Name" ;; :fullname "Full Name"
:username "user222" ;; :username "user222"
:metadata "222" ;; :metadata "222"
:email "user222@uxbox.io") ;; :email "user222@uxbox.io")
[status data] (th/http-put user uri {:body data})] ;; [status data] (th/http-put user uri {:body data})]
;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
(t/is (= 200 status)) ;; (t/is (= 200 status))
(t/is (= (:fullname data) "Full Name")) ;; (t/is (= (:fullname data) "Full Name"))
(t/is (= (:username data) "user222")) ;; (t/is (= (:username data) "user222"))
(t/is (= (:metadata data) "222")) ;; (t/is (= (:metadata data) "222"))
(t/is (= (:email data) "user222@uxbox.io")) ;; (t/is (= (:email data) "user222@uxbox.io"))
(t/is (not (contains? data :password)))))))) ;; (t/is (not (contains? data :password))))))))
(t/deftest test-http-update-profile-photo ;; (t/deftest test-http-update-profile-photo
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1)] ;; (let [user (th/create-user conn 1)]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/profile/me/photo") ;; (let [uri (str th/+base-url+ "/api/profile/me/photo")
params [{:name "sample.jpg" ;; params [{:name "sample.jpg"
:part-name "file" ;; :part-name "file"
:content (io/input-stream ;; :content (io/input-stream
(io/resource "uxbox/tests/_files/sample.jpg"))}] ;; (io/resource "uxbox/tests/_files/sample.jpg"))}]
[status data] (th/http-multipart user uri params)] ;; [status data] (th/http-multipart user uri params)]
;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
(t/is (= 204 status))))))) ;; (t/is (= 204 status)))))))
(t/deftest test-http-register-user ;; (t/deftest test-http-register-user
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/auth/register") ;; (let [uri (str th/+base-url+ "/api/auth/register")
data {:fullname "Full Name" ;; data {:fullname "Full Name"
:username "user222" ;; :username "user222"
:email "user222@uxbox.io" ;; :email "user222@uxbox.io"
:password "user222"} ;; :password "user222"}
[status data] (th/http-post uri {:body data})] ;; [status data] (th/http-post uri {:body data})]
;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
(t/is (= 200 status))))) ;; (t/is (= 200 status)))))
(t/deftest test-http-validate-recovery-token ;; (t/deftest test-http-validate-recovery-token
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1)] ;; (let [user (th/create-user conn 1)]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [token (#'usu/request-password-recovery conn "user1") ;; (let [token (#'usu/request-password-recovery conn "user1")
uri1 (str th/+base-url+ "/api/auth/recovery/not-existing") ;; uri1 (str th/+base-url+ "/api/auth/recovery/not-existing")
uri2 (str th/+base-url+ "/api/auth/recovery/" token) ;; uri2 (str th/+base-url+ "/api/auth/recovery/" token)
[status1 data1] (th/http-get user uri1) ;; [status1 data1] (th/http-get user uri1)
[status2 data2] (th/http-get user uri2)] ;; [status2 data2] (th/http-get user uri2)]
;; (println "RESPONSE:" status1 data1) ;; ;; (println "RESPONSE:" status1 data1)
;; (println "RESPONSE:" status2 data2) ;; ;; (println "RESPONSE:" status2 data2)
(t/is (= 404 status1)) ;; (t/is (= 404 status1))
(t/is (= 204 status2))))))) ;; (t/is (= 204 status2)))))))
(t/deftest test-http-request-password-recovery ;; (t/deftest test-http-request-password-recovery
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1) ;; (let [user (th/create-user conn 1)
sql "select * from user_pswd_recovery" ;; sql "select * from user_pswd_recovery"
res (sc/fetch-one conn sql)] ;; res (sc/fetch-one conn sql)]
;; Initially no tokens exists ;; ;; Initially no tokens exists
(t/is (nil? res)) ;; (t/is (nil? res))
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [uri (str th/+base-url+ "/api/auth/recovery") ;; (let [uri (str th/+base-url+ "/api/auth/recovery")
data {:username "user1"} ;; data {:username "user1"}
[status data] (th/http-post user uri {:body data})] ;; [status data] (th/http-post user uri {:body data})]
;; (println "RESPONSE:" status data) ;; ;; (println "RESPONSE:" status data)
(t/is (= 204 status))) ;; (t/is (= 204 status)))
(let [res (sc/fetch-one conn sql)] ;; (let [res (sc/fetch-one conn sql)]
(t/is (not (nil? res))) ;; (t/is (not (nil? res)))
(t/is (= (:user res) (:id user)))))))) ;; (t/is (= (:user res) (:id user))))))))
(t/deftest test-http-validate-recovery-token ;; (t/deftest test-http-validate-recovery-token
(with-open [conn (db/connection)] ;; (with-open [conn (db/connection)]
(let [user (th/create-user conn 1)] ;; (let [user (th/create-user conn 1)]
(with-server {:handler (uft/routes)} ;; (with-server {:handler (uft/routes)}
(let [token (#'usu/request-password-recovery conn (:username user)) ;; (let [token (#'usu/request-password-recovery conn (:username user))
uri (str th/+base-url+ "/api/auth/recovery") ;; uri (str th/+base-url+ "/api/auth/recovery")
data {:token token :password "mytestpassword"} ;; data {:token token :password "mytestpassword"}
[status data] (th/http-put user uri {:body data}) ;; [status data] (th/http-put user uri {:body data})
user' (usu/find-full-user-by-id conn (:id user))] ;; user' (usu/find-full-user-by-id conn (:id user))]
(t/is (= status 204)) ;; (t/is (= status 204))
(t/is (hashers/check "mytestpassword" (:password user')))))))) ;; (t/is (hashers/check "mytestpassword" (:password user'))))))))