1
0
Fork 0
mirror of https://github.com/heyarne/airsonic-ui.git synced 2026-05-06 18:33:38 +02:00

Cache API responses and make sure we remember more than just one

Closes #21.
Squashed commit of the following:

commit 964b29cf127cf51de86543d040bcb6c674b36d7e
Author: Arne Schlüter <arne@schlueter.is>
Date:   Wed Aug 22 17:56:48 2018 +0200

    Pass content for current route nicely to views

commit b469a0a4b69457ddf3a679ac1acc82fbaffdc8fd
Author: Arne Schlüter <arne@schlueter.is>
Date:   Wed Aug 22 16:01:04 2018 +0200

    Add response cache in app-db

commit da9faf89138f42ee544efc64c2e46787091b3dc7
Author: Arne Schlüter <arne@schlueter.is>
Date:   Wed Aug 22 13:40:57 2018 +0200

    Move api helpers and tests to own namespace
This commit is contained in:
Arne Schlüter 2018-08-22 17:58:03 +02:00
commit 2cdae0d683
13 changed files with 222 additions and 94 deletions

View file

@ -0,0 +1,50 @@
(ns airsonic-ui.api.events-test
(:require [cljs.test :refer-macros [deftest testing is]]
[airsonic-ui.api.events :as events]
[airsonic-ui.fixtures :as fixtures]))
(enable-console-print!)
(deftest api-failure-notifcations
(testing "Should show an error notification when airsonic responds with an error"
(let [fx (events/good-api-response {} [:api/good-response "ping" nil (:error fixtures/responses)])
ev (:dispatch fx)]
(is (= :notification/show (first ev)))
(is (= :error (second ev))))))
(deftest cached-api-requests
(letfn [(cache [fx [endpoint params]]
(get-in fx [:db :api/responses [endpoint params]]))]
(testing "Should be cached"
(testing "when the response was successful"
(let [endpoint "getScanStatus"
successful (events/good-api-response {} [:api/good-response endpoint nil (:ok fixtures/responses)])
unsuccessful (events/good-api-response {} [:api/good-response endpoint nil (:error fixtures/responses)])]
(is (map? (cache successful [endpoint])))
(is (nil? (cache unsuccessful [endpoint])))))
(testing "in an unwrapped format"
(let [endpoint "getScanStatus"
fx (events/good-api-response {} [:api/good-response endpoint nil (:ok fixtures/responses)])]
(is (= #{:count :scanning} (set (keys (cache fx [endpoint]))))))))
(testing "When being issued"
(let [endpoint "getScanStatus"
fx (events/api-request {:db {:credentials (select-keys fixtures/credentials [:server])}}
[:api/request endpoint])]
(testing "should send an http request"
(is (contains? fx :http-xhrio)))
(testing "should indicate that a request is ongoing"
(is (true? (:api/is-loading? (cache fx [endpoint]))) "for non-cached responses")
(is (true? (-> (events/good-api-response fx [:api/good-response endpoint nil (:ok fixtures/responses)])
(events/api-request [:api/request endpoint])
(cache [endpoint])
:api/is-loading?)) "for cached responses"))
(testing "should remove the indication that a request is ongoing when there is a response"
(is (not (:api/is-loading? (-> (events/good-api-response fx [:api/good-response endpoint nil (:ok fixtures/responses)])
(cache [endpoint])))) "for a good response")
(is (not (:api/is-loading? (-> (merge fx (events/good-api-response fx [:api/good-response endpoint nil (:error fixtures/responses)]))
(cache [endpoint])))) "when an error is returned")
(is (not (:api/is-loading? (-> (merge fx (events/failed-api-response fx [:api/failed-response endpoint]))
(cache [endpoint])))) "when communication with the server failed"))))
(testing "Should be able to avoid the cache"
;; FIXME: Implement this
)))

View file

@ -0,0 +1,57 @@
(ns airsonic-ui.api.helpers-test
(:require [cljs.test :refer [deftest testing is]]
[clojure.string :as str]
[airsonic-ui.fixtures :refer [responses]]
[airsonic-ui.api.helpers :as api]))
(defn- url
"Construct a url with no params"
[server endpoint]
(api/url server endpoint {}))
(def fixtures
{:default-url (url "http://localhost:8080" "ping")})
(deftest general-url-construction
(testing "Handles missing slashes"
(is (true? (str/starts-with? (fixtures :default-url) "http://localhost:8080/rest/ping")))
(is (true? (str/starts-with? (url "http://localhost:8080/" "ping") "http://localhost:8080/rest/ping"))))
(testing "Should set correct default parameters"
(is (string? (re-find #"f=json" (fixtures :default-url))))
(is (string? (re-find #"v=1\.15\.0" (fixtures :default-url))))))
(deftest song-urls
(testing "Should construct the url based on a song's id"
(let [song {:id 1234}]
(is (true? (str/includes? (api/song-url "http://localhost" {} song) (str "id=" (:id song))))))))
(deftest cover-urls
(let [album {:coverArt "cover-99999"}]
(testing "Should construct the url based on an item's cover-id"
(is (true? (str/includes? (api/cover-url "http://server.tld" {} album -1) (str "id=" (:coverArt album))))))
(testing "Should scale an image to a given size"
(is (true? (str/includes? (api/cover-url "http://server.tld" {} album 48) "size=48"))))))
(deftest response-handling
(testing "Should unwrap responses"
(let [response (:ok responses)]
(is (= (get-in response [:subsonic-response :scanStatus])
(api/unwrap-response response)))))
(testing "Should detect errors"
(is (true? (api/is-error? (:error responses))))
(is (false? (api/is-error? (:ok responses)))))
(testing "Should throw an informative error when trying to unwrap an erroneous response"
(let [error-response (:error responses)]
(is (thrown? ExceptionInfo (api/unwrap-response error-response)))
(try
(api/unwrap-response error-response)
(catch ExceptionInfo e
(= (:error error-response) (ex-data e)))))))
(deftest error-recognition
(testing "Should detect error responses"
(is (true? (api/is-error? (:error responses))))
(is (true? (api/is-error? (:auth-failure responses)))))
(testing "Should pass on good responses"
(is (false? (api/is-error? (:ok responses))))
(is (false? (api/is-error? (:auth-success responses))))))

View file

@ -0,0 +1,29 @@
(ns airsonic-ui.api.subs-test
(:require [cljs.test :refer-macros [deftest testing is]]
[airsonic-ui.api.subs :as sub]))
(enable-console-print!)
(deftest endpoint-keywordification
(testing "Should strip prefixes"
(is (= :artist-info (sub/endpoint->kw "getArtistInfo")))
(is (= :jukebox-control (sub/endpoint->kw "jukeboxControl"))))
(testing "Should strip trailing numbers"
(is (= :album-list (sub/endpoint->kw "getAlbumList2")))
(is (= :search (sub/endpoint->kw "search3")))))
(deftest responses-for-route
(testing "Should return all cached responses for a route"
(let [route-events [[:api/request "getAlbumList2" {:type "recent", :size 18}]
[:event/should-be-ignored]
[:api/request "getArtistInfo" {:id "128"}]]
db {:api/responses {["getAlbumList2" {:type "recent" :size 18}]
{:album [{:genre "foo", :artistId "12345"}
{:genre "electronic", :artistId "9999"}]}
["getArtistInfo" {:id "128"}]
{:biography "Interesting bio"
:largeImageUrl "https://lastfm-img2.akamaized.net/i/u/300x300/fb416b59cd694587aca0b2dec8f41198.png"}}}]
(is (= {:album-list (get-in db [:api/responses ["getAlbumList2" {:type "recent" :size 18}]])
:artist-info (get-in db [:api/responses ["getArtistInfo" {:id "128"}]])}
(sub/route-data db [:api/route-data route-events]))))))