p2p-cljs / test / ird / p2p / app_test.cljs
660 words
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
(ns ird.p2p.app-test
  "The facade: option validation, descriptor caching, resolved endpoints,
  ICE configuration from TURN credentials, mailbox construction rules,
  join refusals."
  (:require [cljs.test :refer [deftest is testing]]
            [cljs.core.async :refer [<!]]
            [ird.p2p :as p2p]
            [ird.p2p.descriptor :as desc]
            [ird.p2p.errors :as err]
            [ird.p2p.fake-relay :as fake]
            [ird.p2p.identity :as ident]
            [ird.p2p.mailbox :as mbx]
            [ird.p2p.test-util :as tu :refer [deftest-async]]
            [ird.p2p.transport.ws :as ws]))

(deftest make-app-validates
  (is (thrown? :default (p2p/make-app {})))
  (is (thrown? :default (p2p/make-app {:host "h"})))
  (is (thrown? :default (p2p/make-app {:host "h" :app-salt "s" :origin 5})))
  (let [e (try (p2p/make-app {:app-salt "s"}) (catch :default e e))]
    (is (err/config-error? e)))
  (is (map? (p2p/make-app {:host "h" :app-salt "s"}))))

(deftest-async descriptor-is-cached-and-fallback-retried
  (let [captured (atom [])
        now (atom 0)
        responses (atom [[500 {:error "x"}] [200 (tu/fixture "descriptor-full.json")]])
        http (fn [req] (swap! captured conj req)
               (let [[s b] (first @responses)] (swap! responses #(if (> (count %) 1) (subvec % 1) %))
                    (cljs.core.async/go {:status s :headers {} :body (tu/->json b)})))
        app (p2p/make-app {:host "app.example.test" :app-salt "s" :origin "https://o.example" :http-fn http :now-fn (fn [] @now)})
        d1 (<! (p2p/fetch-descriptor app))
        d2 (<! (p2p/fetch-descriptor app))]
    (is (:is-fallback d1))
    (is (:is-fallback d2) "cached within 30 s")
    (is (= 1 (count @captured)))
    (swap! now + 31000)
    (let [d3 (<! (p2p/fetch-descriptor app))]
      (is (false? (:is-fallback d3)) "a fallback is retried after 30 s")
      (is (= 2 (count @captured))))
    (swap! now + 3599000)
    (<! (p2p/fetch-descriptor app))
    (is (= 2 (count @captured)) "a real descriptor is kept for an hour")
    (swap! now + 2000)
    (<! (p2p/fetch-descriptor app))
    (is (= 3 (count @captured)))
    (<! (p2p/fetch-descriptor app {:refresh? true}))
    (is (= 4 (count @captured)))
    (is (= {:ws "wss://app.example.test/ws" :turn-credentials "https://app.example.test/turn-credentials"
            :mailbox "https://app.example.test/mailbox/v1" :mailbox-mint "https://app.example.test/mailbox-credentials"}
           (<! (p2p/resolved-endpoints app))))))

(deftest-async endpoint-overrides-win
  (let [app (p2p/make-app {:host "h" :app-salt "s" :descriptor (desc/fallback "h")
                           :endpoints {:ws "ws://127.0.0.1:8080/ws" :mailbox "http://127.0.0.1:8081/mailbox/v1"}})]
    (is (= {:ws "ws://127.0.0.1:8080/ws" :turn-credentials "https://h/turn-credentials"
            :mailbox "http://127.0.0.1:8081/mailbox/v1" :mailbox-mint nil}
           (<! (p2p/resolved-endpoints app))))))

(deftest-async ice-configuration-from-turn-credentials
  (let [now (atom 0)
        captured (atom [])
        http (tu/routing captured [["/turn-credentials" (fn [_] [200 {:username "u" :credential "c" :ttl 600 :urls ["turn:t.example:3478" "stun:t.example:3478"]}])]])
        app (p2p/make-app {:host "app.example.test" :app-salt "s" :origin "https://o.example" :http-fn http :now-fn (fn [] @now)
                           :descriptor (desc/parse (tu/fixture "descriptor-full.json"))})
        ice (<! (p2p/ice-configuration app))]
    (is (= {:servers [{:urls ["turn:t.example:3478" "stun:t.example:3478"] :username "u" :credential "c"}] :relay-only? false} ice))
    (is (= "https://o.example" (get-in (first @captured) [:headers "Origin"])))
    (<! (p2p/ice-configuration app))
    (is (= 1 (count @captured)) "cached")
    (swap! now + 301000)
    (<! (p2p/ice-configuration app))
    (is (= 2 (count @captured)) "renewed five minutes before expiry"))
  (let [app (p2p/make-app {:host "h" :app-salt "s" :http-fn (tu/responding 403 {:error "origin_not_allowed"})
                           :descriptor (desc/fallback "h") :force-relay? true})]
    (is (= {:servers [] :relay-only? true} (<! (p2p/ice-configuration app))) "degrades to no servers")))

(deftest-async mailbox-construction-rules
  (let [http (tu/responding 404 {:error "not_found"})]
    (let [app (p2p/make-app {:host "h" :app-salt "s" :http-fn http :descriptor (desc/parse (tu/fixture "descriptor-minimal.json"))})
          e (<! (p2p/open-mailbox app (p2p/random-secret)))]
      (is (err/config-error? e))
      (is (re-find #"no mailbox" (ex-message e))))
    (let [app (p2p/make-app {:host "h" :app-salt "s" :http-fn http :descriptor (desc/parse (tu/fixture "descriptor-closed-mint.json"))})
          e (<! (p2p/open-mailbox app (p2p/random-secret)))]
      (is (err/config-error? e))
      (is (re-find #"open mint is off" (ex-message e))))
    (let [app (p2p/make-app {:host "h" :app-salt "s" :http-fn http :descriptor (desc/parse (tu/fixture "descriptor-closed-mint.json"))
                             :token-fn (mbx/static-token-fn {:token "t" :expires_at 1})})
          m (<! (p2p/open-mailbox app (p2p/random-secret)))]
      (is (map? m))
      (is (= "https://closed.example.test/mailbox/v1" (get-in m [:client :endpoint]))))
    (let [app (p2p/make-app {:host "h" :app-salt "s" :http-fn http :descriptor (desc/parse (tu/fixture "descriptor-full.json"))})
          m (<! (p2p/open-mailbox app (p2p/random-secret)))]
      (is (map? m) "the open mint is the default token provider"))
    (is (err/config-error? (<! (p2p/open-mailbox (p2p/make-app {:host "h" :app-salt "s"}) "short"))))))

(deftest-async join-refusals
  (let [relay (fake/make-relay {:reject-with 403})
        app (p2p/make-app {:host "h" :app-salt "s" :descriptor (desc/fallback "h")
                           :transport (ws/transport {:socket-fn (fake/socket-fn relay) :sleep-fn tu/no-sleep})})
        e (<! (p2p/join app (p2p/random-secret)))]
    (is (err/upgrade-rejected? e))
    (is (= 403 (:status (ex-data e)))))
  (is (err/config-error? (<! (p2p/join (p2p/make-app {:host "h" :app-salt "s"}) "not-a-secret!")))))

(deftest invite-links
  (let [s (p2p/random-secret)]
    (is (= 43 (count s)))
    (is (= (str "myapp://join#" s) (p2p/invite-url "myapp://join" s)))
    (is (= (str "https://x.example/r?a=1#" s) (p2p/invite-url "https://x.example/r?a=1#old" s)))
    (is (= s (p2p/invite-secret (p2p/invite-url "myapp://join" s))))
    (is (nil? (p2p/invite-secret "myapp://join")))
    (is (nil? (p2p/invite-secret "myapp://join#")))
    (is (nil? (p2p/invite-secret "myapp://join#tooshort")))
    (is (nil? (p2p/invite-secret "myapp://join#***********************")))
    (is (= "EBESExQVFhcYGRobHB0eHw" (p2p/invite-secret "x://y#EBESExQVFhcYGRobHB0eHw")) "16 bytes is the minimum")))

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/p2p-cljs.git