|
1 | 1 | (ns eca.features.chat.tool-calls-test |
2 | 2 | (:require |
3 | 3 | [clojure.test :refer [deftest is testing]] |
| 4 | + [eca.features.chat.lifecycle :as lifecycle] |
4 | 5 | [eca.features.chat.tool-calls :as tc] |
5 | 6 | [eca.features.hooks :as f.hooks] |
6 | 7 | [eca.features.tools :as f.tools] |
|
276 | 277 | :hook-rejected? false |
277 | 278 | :arguments-modified? true} |
278 | 279 | plan))))))) |
| 280 | + |
| 281 | +(deftest on-tools-called!-returns-provider-auth-test |
| 282 | + (testing "returns refreshed provider auth in result after token is renewed during tool execution" |
| 283 | + ;; Regression test for: auth captured in LLM provider closure at prompt start. |
| 284 | + ;; When a token expires mid-stream, maybe-renew-auth-token updates db*, |
| 285 | + ;; and on-tools-called! must return the refreshed auth so provider-specific |
| 286 | + ;; continuation metadata (not just the api key) can be reused. |
| 287 | + (h/reset-components!) |
| 288 | + (let [chat-id "test-chat" |
| 289 | + provider "github-copilot" |
| 290 | + renewed-provider-auth {:api-key "fresh-token-xyz" |
| 291 | + :expires-at 9999999999} |
| 292 | + db* (h/db*) |
| 293 | + _ (swap! db* #(-> % |
| 294 | + (assoc-in [:auth provider :api-key] "stale-token-abc") |
| 295 | + (assoc-in [:chats chat-id :status] :running) |
| 296 | + (assoc-in [:chats chat-id :messages] []) |
| 297 | + (assoc-in [:chats chat-id :tool-calls "call-1" :status] :preparing))) |
| 298 | + ;; Pre-set tool call to :preparing state, as it would be after on-prepare-tool-call |
| 299 | + ;; fires during the streaming phase before on-tools-called! is invoked. |
| 300 | + chat-ctx {:db* db* |
| 301 | + :config (h/config) |
| 302 | + :chat-id chat-id |
| 303 | + :provider provider |
| 304 | + :agent :default |
| 305 | + :messenger (h/messenger) |
| 306 | + :metrics (h/metrics)} |
| 307 | + received-msgs* (atom "") |
| 308 | + add-to-history! (fn [msg] |
| 309 | + (swap! db* update-in [:chats chat-id :messages] (fnil conj []) msg)) |
| 310 | + tool-calls [{:id "call-1" |
| 311 | + :full-name "eca__test_tool" |
| 312 | + :arguments {} |
| 313 | + :arguments-text "{}"}] |
| 314 | + all-tools [{:name "test_tool" |
| 315 | + :full-name "eca__test_tool" |
| 316 | + :origin :eca |
| 317 | + :server {:name "eca"}}] |
| 318 | + expected-provider-auth (merge {:api-key "stale-token-abc"} renewed-provider-auth)] |
| 319 | + (with-redefs [f.tools/all-tools (constantly all-tools) |
| 320 | + f.tools/approval (constantly :allow) |
| 321 | + f.hooks/trigger-if-matches! (fn [_ _ _ _ _] nil) |
| 322 | + f.tools/call-tool! (fn [& _] {:contents [{:text "result" :type :text}]}) |
| 323 | + f.tools/tool-call-details-before-invocation (constantly nil) |
| 324 | + f.tools/tool-call-details-after-invocation (constantly nil) |
| 325 | + f.tools/tool-call-summary (constantly "Test tool") |
| 326 | + lifecycle/maybe-renew-auth-token |
| 327 | + (fn [ctx] |
| 328 | + (swap! (:db* ctx) update-in [:auth provider] merge renewed-provider-auth))] |
| 329 | + (let [result ((tc/on-tools-called! chat-ctx received-msgs* add-to-history! []) tool-calls)] |
| 330 | + (is (= (:api-key expected-provider-auth) (:fresh-api-key result)) |
| 331 | + "fresh-api-key must be returned so the provider can use it in the recursive streaming call") |
| 332 | + (is (= expected-provider-auth |
| 333 | + (:provider-auth result)) |
| 334 | + "provider-auth must be returned so providers can reuse refreshed auth metadata")))))) |
0 commit comments