|
1 | 1 | (ns eca.features.login-test |
2 | 2 | (:require |
| 3 | + [babashka.fs :as fs] |
| 4 | + [clojure.java.io :as io] |
3 | 5 | [clojure.test :refer [deftest is testing]] |
| 6 | + [eca.cache :as cache] |
| 7 | + [eca.db :as db] |
4 | 8 | [eca.features.login :as login] |
5 | 9 | [hato.client :as http] |
6 | 10 | [matcher-combinators.test :refer [match?]])) |
|
67 | 71 | 1 {}}} |
68 | 72 |
|
69 | 73 | @db*))))))) |
| 74 | + |
| 75 | +(def ^:private renew-auth!* @#'login/renew-auth!) |
| 76 | + |
| 77 | +(deftest renew-auth!-skips-refresh-when-peer-already-refreshed-test |
| 78 | + (testing "if disk holds fresher tokens than memory, renew-auth! adopts them and does NOT call login-step" |
| 79 | + (let [tmpdir (str (fs/create-temp-dir))] |
| 80 | + (with-redefs [cache/global-dir (constantly (io/file tmpdir))] |
| 81 | + (try |
| 82 | + (let [fresh {:type :auth/oauth :mode :max :step :login/done |
| 83 | + :refresh-token "peer-fresh" :api-key "peer-access" |
| 84 | + :expires-at 9999999999} |
| 85 | + stale {:type :auth/oauth :mode :max :step :login/done |
| 86 | + :refresh-token "mine-stale" :api-key "mine-access" |
| 87 | + :expires-at 1000} |
| 88 | + _ (db/update-global-cache! {:auth {"anthropic" fresh}} nil) |
| 89 | + db* (atom {:auth {"anthropic" stale}}) |
| 90 | + step-calls (atom 0) |
| 91 | + on-error-msgs (atom [])] |
| 92 | + (with-redefs [login/login-step (fn [_ctx] (swap! step-calls inc))] |
| 93 | + (renew-auth!* "anthropic" |
| 94 | + {:db* db* :messenger nil :config nil :metrics nil} |
| 95 | + {:on-error #(swap! on-error-msgs conj %)})) |
| 96 | + (is (zero? @step-calls) |
| 97 | + "login-step should not be invoked when disk has fresh tokens") |
| 98 | + (is (empty? @on-error-msgs)) |
| 99 | + (is (= "peer-fresh" (get-in @db* [:auth "anthropic" :refresh-token]))) |
| 100 | + (is (= "peer-access" (get-in @db* [:auth "anthropic" :api-key]))) |
| 101 | + (is (= 9999999999 (get-in @db* [:auth "anthropic" :expires-at])))) |
| 102 | + (finally (fs/delete-tree tmpdir))))))) |
| 103 | + |
| 104 | +(deftest renew-auth!-refreshes-when-disk-also-stale-test |
| 105 | + (testing "when memory and disk are both expired, renew-auth! invokes login-step exactly once and persists" |
| 106 | + (let [tmpdir (str (fs/create-temp-dir))] |
| 107 | + (with-redefs [cache/global-dir (constantly (io/file tmpdir))] |
| 108 | + (try |
| 109 | + (let [stale {:type :auth/oauth :mode :max :step :login/done |
| 110 | + :refresh-token "stale" :api-key "stale-access" |
| 111 | + :expires-at 1000} |
| 112 | + _ (db/update-global-cache! {:auth {"anthropic" stale}} nil) |
| 113 | + db* (atom {:auth {"anthropic" stale}}) |
| 114 | + step-calls (atom 0) |
| 115 | + on-error-msgs (atom [])] |
| 116 | + (with-redefs [login/login-step |
| 117 | + (fn [{:keys [db* provider]}] |
| 118 | + (swap! step-calls inc) |
| 119 | + (swap! db* update-in [:auth provider] merge |
| 120 | + {:refresh-token "rotated" |
| 121 | + :api-key "rotated-access" |
| 122 | + :expires-at 9999999999}))] |
| 123 | + (renew-auth!* "anthropic" |
| 124 | + {:db* db* :messenger nil :config nil :metrics nil} |
| 125 | + {:on-error #(swap! on-error-msgs conj %)})) |
| 126 | + (is (= 1 @step-calls)) |
| 127 | + (is (empty? @on-error-msgs)) |
| 128 | + (is (= "rotated" (get-in @db* [:auth "anthropic" :refresh-token]))) |
| 129 | + ;; And the rotated tokens should have been written to disk so the next |
| 130 | + ;; process in the race sees them. |
| 131 | + (let [reloaded (atom {:auth {"anthropic" {}}})] |
| 132 | + (db/sync-auth-from-cache! reloaded "anthropic" nil) |
| 133 | + (is (= "rotated" (get-in @reloaded [:auth "anthropic" :refresh-token]))))) |
| 134 | + (finally (fs/delete-tree tmpdir))))))) |
| 135 | + |
| 136 | +(deftest renew-auth!-calls-on-error-when-login-step-throws-test |
| 137 | + (testing "exceptions from the provider refresh propagate to on-error and do not persist" |
| 138 | + (let [tmpdir (str (fs/create-temp-dir))] |
| 139 | + (with-redefs [cache/global-dir (constantly (io/file tmpdir))] |
| 140 | + (try |
| 141 | + (let [stale {:type :auth/oauth :refresh-token "stale" :expires-at 1000} |
| 142 | + _ (db/update-global-cache! {:auth {"anthropic" stale}} nil) |
| 143 | + db* (atom {:auth {"anthropic" stale}}) |
| 144 | + on-error-msgs (atom [])] |
| 145 | + (with-redefs [login/login-step |
| 146 | + (fn [_ctx] (throw (ex-info "Anthropic refresh token failed" {})))] |
| 147 | + (renew-auth!* "anthropic" |
| 148 | + {:db* db* :messenger nil :config nil :metrics nil} |
| 149 | + {:on-error #(swap! on-error-msgs conj %)})) |
| 150 | + (is (= 1 (count @on-error-msgs))) |
| 151 | + (is (= "Anthropic refresh token failed" (first @on-error-msgs)))) |
| 152 | + (finally (fs/delete-tree tmpdir))))))) |
0 commit comments