Skip to content

Commit 0e35719

Browse files
committed
Add tests
1 parent 9faa26c commit 0e35719

File tree

3 files changed

+163
-4
lines changed

3 files changed

+163
-4
lines changed

test/eca/features/login_test.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
:send-msg! send-msg!})
2121

2222
(testing "should ask to choose a provider"
23-
(is (= "Please type the name of your chosen provider and press Enter:\n- github-copilot\n- google\n"
23+
(is (= "Choose a provider:\n- github-copilot\n- google\n"
2424
(last @msg-log)))))
2525

2626
(testing "user is confused"
@@ -32,7 +32,7 @@
3232
:send-msg! send-msg!})
3333

3434
(testing "should ask to choose a provider and provide instructions"
35-
(is (= "Sorry, \"/login github\" is not a valid provider.\nPlease type the name of your chosen provider and press Enter:\n- github-copilot\n- google\n"
35+
(is (= "Choose a provider:\n- github-copilot\n- google\n"
3636
(last @msg-log)))
3737

3838
(testing "state didn't change"

test/eca/features/rewrite_test.clj

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
(ns eca.features.rewrite-test
2+
(:require
3+
[clojure.test :refer [deftest is testing]]
4+
[eca.features.login :as f.login]
5+
[eca.features.prompt :as f.prompt]
6+
[eca.features.rewrite :as f.rewrite]
7+
[eca.llm-api :as llm-api]
8+
[eca.test-helper :as h]
9+
[matcher-combinators.test :refer [match?]]))
10+
11+
(h/reset-components-before-test)
12+
13+
(defn ^:private rewrite!
14+
"Helper to invoke rewrite/prompt with configurable mocks.
15+
Returns {:resp <function-return> :finished? <promise>}"
16+
[params {:keys [api-mock build-instructions-mock refine-file-context-mock
17+
login-renew-mock default-model-mock]
18+
:or {build-instructions-mock (constantly "INSTR")
19+
refine-file-context-mock (constantly "FULL-CONTENT")
20+
login-renew-mock (fn [& _] nil)
21+
default-model-mock (constantly "anthropic/claude-dev")}}]
22+
(let [finished? (promise)
23+
resp (with-redefs [llm-api/sync-or-async-prompt!
24+
(fn [opts]
25+
(when api-mock
26+
(api-mock (assoc opts :finished? finished?)))
27+
nil)
28+
f.prompt/build-rewrite-instructions build-instructions-mock
29+
f.login/maybe-renew-auth-token! login-renew-mock
30+
llm-api/refine-file-context refine-file-context-mock
31+
llm-api/default-model default-model-mock]
32+
;; ensure test env config is set
33+
(h/config! {:env "test"})
34+
(let [r (f.rewrite/prompt params (h/db*) (h/config) (h/messenger) (h/metrics))]
35+
;; Keep redefs in scope until async code runs
36+
(deref finished? 2000 nil)
37+
r))]
38+
{:resp resp :finished? finished?}))
39+
40+
(deftest prompt-basic-flow-test
41+
(testing "Basic rewrite flow: started -> reasoning -> text -> finish"
42+
(h/reset-components!)
43+
(let [api-opts* (atom nil)
44+
{:keys [resp finished?]}
45+
(rewrite!
46+
{:id "rw-1"
47+
:prompt "Please improve the following"
48+
:text "Original text"
49+
:range {:start {:line 1 :character 0}
50+
:end {:line 1 :character 14}}}
51+
{:api-mock
52+
(fn [{:keys [on-first-response-received on-reason on-message-received] :as opts}]
53+
(reset! api-opts* opts)
54+
;; Emit the events in the expected order
55+
(on-first-response-received {:type :text})
56+
(on-reason {:status :started})
57+
(on-reason {:status :thinking :text "..."})
58+
(on-message-received {:type :text :text "Hello"})
59+
(on-message-received {:type :text :text ", world!"})
60+
(on-message-received {:type :finish})
61+
(deliver (:finished? opts) true))
62+
:build-instructions-mock (constantly "INSTR")})]
63+
;; Wait for async future to publish the finish event
64+
(is (true? (deref finished? 2000 false)) "Timed out waiting for finish event")
65+
;; Function return value
66+
(is (= {:status "prompting" :model "anthropic/claude-dev"} resp))
67+
;; Messenger events captured by TestMessenger
68+
(let [msgs (get (h/messages) :rewrite-content-received)]
69+
;; Ensure order and content types
70+
(is (= 5 (count msgs)))
71+
(is (match? [{:rewrite-id "rw-1" :content {:type :started}}
72+
{:rewrite-id "rw-1" :content {:type :reasoning}}
73+
{:rewrite-id "rw-1" :content {:type :text :text "Hello"}}
74+
{:rewrite-id "rw-1" :content {:type :text :text ", world!"}}
75+
{:rewrite-id "rw-1" :content {:type :finished :total-time-ms number?}}]
76+
msgs))))))
77+
78+
(deftest prompt-instructions-and-contexts-test
79+
(testing "Passes instructions from build-rewrite-instructions and uses file context when path provided"
80+
(h/reset-components!)
81+
;; Prepare DB model capabilities for assertion
82+
(swap! (h/db*) assoc :models {"openai/gpt-test" {:max-output-tokens 1024}})
83+
(let [captured-instr-args* (atom nil)
84+
api-opts* (atom nil)
85+
finished? (promise)
86+
resp
87+
(with-redefs [llm-api/sync-or-async-prompt!
88+
(fn [opts]
89+
(reset! api-opts* opts)
90+
((:on-first-response-received opts) {:type :text})
91+
((:on-message-received opts) {:type :finish})
92+
(deliver finished? true))
93+
f.prompt/build-rewrite-instructions
94+
(fn [text path full-text range cfg]
95+
(reset! captured-instr-args* {:text text :path path :full-text full-text :range range :config cfg})
96+
"MY-INSTR")
97+
f.login/maybe-renew-auth-token! (fn [& _] nil)
98+
llm-api/refine-file-context (fn [path _] (str "CTX:" path))]
99+
(h/config! {:env "test" :rewrite {:model "openai/gpt-test"}})
100+
(let [r (f.rewrite/prompt {:id "rw-2"
101+
:prompt "Do it"
102+
:text "T"
103+
:path (h/file-path "/tmp/file.txt")
104+
:range {:start {:line 10 :character 2}
105+
:end {:line 12 :character 5}}}
106+
(h/db*) (h/config) (h/messenger) (h/metrics))]
107+
(deref finished? 2000 nil)
108+
r))]
109+
(is (= {:status "prompting" :model "openai/gpt-test"} resp))
110+
(is (true? (deref finished? 2000 false)))
111+
;; build-rewrite-instructions received expected args
112+
(is (match? {:text "T"
113+
:path (h/file-path "/tmp/file.txt")
114+
:full-text (str "CTX:" (h/file-path "/tmp/file.txt"))
115+
:range {:start {:line 10 :character 2}
116+
:end {:line 12 :character 5}}}
117+
@captured-instr-args*))
118+
;; llm-api called with provider/model split and our instructions
119+
(is (= "openai" (:provider @api-opts*)))
120+
(is (= "gpt-test" (:model @api-opts*)))
121+
(is (= "MY-INSTR" (:instructions @api-opts*)))
122+
(is (= [{:role "user" :content [{:type :text :text "Do it"}]}]
123+
(:user-messages @api-opts*)))
124+
(is (= {:max-output-tokens 1024}
125+
(:model-capabilities @api-opts*))))))
126+
127+
(deftest prompt-default-model-and-auth-renew-test
128+
(testing "Falls back to default model when rewrite.model not set and calls auth renew with provider"
129+
(h/reset-components!)
130+
;; Prepare DB auth and models
131+
(swap! (h/db*) assoc-in [:auth "google"] {:token "abc"})
132+
(swap! (h/db*) assoc :models {"google/gemini-dev" {:reason? true}})
133+
(let [renew-called* (atom nil)
134+
api-opts* (atom nil)
135+
finished? (promise)
136+
resp
137+
(with-redefs [llm-api/sync-or-async-prompt!
138+
(fn [opts]
139+
(reset! api-opts* opts)
140+
((:on-first-response-received opts) {:type :text})
141+
((:on-message-received opts) {:type :finish})
142+
(deliver finished? true))
143+
f.prompt/build-rewrite-instructions (constantly "INSTR")
144+
f.login/maybe-renew-auth-token!
145+
(fn [{:keys [provider]} _ctx]
146+
(reset! renew-called* provider))
147+
llm-api/default-model (constantly "google/gemini-dev")]
148+
(h/config! {:env "test"})
149+
(let [r (f.rewrite/prompt {:id "rw-3"
150+
:prompt "X"
151+
:text "Y"}
152+
(h/db*) (h/config) (h/messenger) (h/metrics))]
153+
(deref finished? 2000 nil)
154+
r))]
155+
(is (= {:status "prompting" :model "google/gemini-dev"} resp))
156+
(is (true? (deref finished? 2000 false)))
157+
(is (= "google" @renew-called*))
158+
(is (= "google" (:provider @api-opts*)))
159+
(is (= "gemini-dev" (:model @api-opts*)))
160+
(is (= {:token "abc"} (:provider-auth @api-opts*))))))

test/eca/test_helper.clj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
(when windows?
1515
(second (re-find #"^([A-Za-z]).+" (System/getProperty "user.dir")))))
1616

17-
18-
1917
(defn file-path [path]
2018
(cond-> path windows?
2119
(-> (string/replace-first #"^/" (str windows-drive-letter ":\\\\"))
@@ -31,6 +29,7 @@
3129
(defrecord TestMessenger [messages* diagnostics*]
3230
messenger/IMessenger
3331
(chat-content-received [_ data] (swap! messages* update :chat-content-received (fnil conj []) data))
32+
(rewrite-content-received [_ data] (swap! messages* update :rewrite-content-received (fnil conj []) data))
3433
(config-updated [_ data] (swap! messages* update :config-updated (fnil conj []) data))
3534
(tool-server-updated [_ data] (swap! messages* update :tool-server-update (fnil conj []) data))
3635
(showMessage [_ data] (swap! messages* update :show-message (fnil conj []) data))

0 commit comments

Comments
 (0)