Skip to content

Commit b828d68

Browse files
committed
Add tests for server and request
1 parent 88a8200 commit b828d68

File tree

2 files changed

+278
-0
lines changed

2 files changed

+278
-0
lines changed

server/src/lsp4clj/server.clj

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
(ns lsp4clj.server
22
(:require
33
[clojure.core.async :as async]
4+
[clojure.pprint :as pprint]
45
[lsp4clj.json-rpc :as json-rpc]
56
[lsp4clj.json-rpc.messages :as json-rpc.messages]
67
[lsp4clj.protocols.endpoint :as protocols.endpoint]
@@ -50,6 +51,10 @@
5051
(deliver p ::cancelled)
5152
true))))
5253

54+
;; Avoid error: java.lang.IllegalArgumentException: Multiple methods in multimethod 'simple-dispatch' match dispatch value: class lsp4clj.server.PendingRequest -> interface clojure.lang.IPersistentMap and interface clojure.lang.IDeref, and neither is preferred
55+
;; Only when CIDER is running? See https://github.com/thi-ng/color/issues/10
56+
(prefer-method pprint/simple-dispatch clojure.lang.IDeref clojure.lang.IPersistentMap)
57+
5358
(defn pending-request
5459
"Returns an object representing a pending JSON-RPC request to a remote
5560
endpoint. Deref the object to get the response.

server/test/lsp4clj/server_test.clj

+273
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
(ns lsp4clj.server-test
2+
(:require
3+
[clojure.core.async :as async]
4+
[clojure.test :refer [deftest is testing]]
5+
[lsp4clj.server :as server]))
6+
7+
(defn take-or-timeout
8+
([ch]
9+
(take-or-timeout ch 100))
10+
([ch timeout-ms]
11+
(take-or-timeout ch timeout-ms :timeout))
12+
([ch timeout-ms timeout-val]
13+
(let [timeout (async/timeout timeout-ms)
14+
[result ch] (async/alts!! [ch timeout])]
15+
(if (= ch timeout)
16+
timeout-val
17+
result))))
18+
19+
(defn assert-no-take [ch]
20+
(is (= :nothing (take-or-timeout ch 500 :nothing))))
21+
22+
(defn assert-take [ch]
23+
(let [result (take-or-timeout ch)]
24+
(is (not= :timeout result))
25+
result))
26+
27+
(def fixed-clock
28+
(-> (java.time.LocalDateTime/of 2022 03 05 13 35 23 0)
29+
(.toInstant java.time.ZoneOffset/UTC)
30+
(java.time.Clock/fixed (java.time.ZoneId/systemDefault))))
31+
32+
(deftest should-process-messages-received-before-start
33+
(let [input (async/chan 3)
34+
output (async/chan 3)
35+
server (server/chan-server {:output output
36+
:input input
37+
:parallelism 1})]
38+
(async/put! input {:id 1 :method "foo" :params {}})
39+
(let [join (server/start server nil)]
40+
(assert-take output)
41+
(server/shutdown server)
42+
(server/exit server)
43+
@join)))
44+
45+
(deftest should-process-sent-messages-before-closing
46+
(let [input (async/chan 3)
47+
output (async/chan 3)
48+
server (server/chan-server {:output output
49+
:input input
50+
:parallelism 1})
51+
join (server/start server nil)]
52+
(async/put! input {:id 2
53+
:method "bar"
54+
:params {}})
55+
(server/shutdown server)
56+
(server/exit server)
57+
(assert-take output)
58+
@join))
59+
60+
(deftest should-close-when-asked-to
61+
(let [input (async/chan 3)
62+
output (async/chan 3)
63+
server (server/chan-server {:output output
64+
:input input
65+
:parallelism 1})
66+
join (server/start server nil)]
67+
(server/shutdown server)
68+
(server/exit server)
69+
(is (= :done @join))
70+
;; output also closes
71+
(is (nil? (take-or-timeout output)))))
72+
73+
(deftest should-close-when-input-closes
74+
(let [input (async/chan 3)
75+
output (async/chan 3)
76+
server (server/chan-server {:output output
77+
:input input
78+
:parallelism 1})
79+
join (server/start server nil)]
80+
(async/close! input)
81+
(is (= :done @join))
82+
;; output also closes
83+
(is (nil? (take-or-timeout output)))))
84+
85+
(deftest should-receive-responses
86+
(let [input (async/chan 3)
87+
output (async/chan 3)
88+
server (server/chan-server {:output output
89+
:input input
90+
:parallelism 1})
91+
join (server/start server nil)
92+
req (server/send-request server "req" {:body "foo"})
93+
client-rcvd-msg (assert-take output)]
94+
(async/put! input {:id (:id client-rcvd-msg)
95+
:result {:processed true}})
96+
(is (= {:processed true} @req))
97+
(server/shutdown server)
98+
(server/exit server)
99+
@join))
100+
101+
(deftest should-respond-to-requests
102+
(let [input (async/chan 3)
103+
output (async/chan 3)
104+
server (server/chan-server {:output output
105+
:input input
106+
:parallelism 1})
107+
join (server/start server nil)]
108+
(async/put! input {:id 1 :method "foo" :params {}})
109+
(is (= 1 (:id (assert-take output))))
110+
(server/shutdown server)
111+
(server/exit server)
112+
@join))
113+
114+
(deftest should-reply-with-method-not-found-for-unexpected-messages
115+
(let [input (async/chan 3)
116+
output (async/chan 3)
117+
server (server/chan-server {:output output
118+
:input input
119+
:parallelism 1})
120+
join (server/start server nil)]
121+
(async/put! input {:id 1 :method "foo" :params {}})
122+
(is (= {:jsonrpc "2.0"
123+
:id 1
124+
:error {:code -32601, :message "Method not found", :data {:method "foo"}}}
125+
(assert-take output)))
126+
(server/shutdown server)
127+
(server/exit server)
128+
@join))
129+
130+
(deftest should-cancel-if-no-response-received
131+
(let [input (async/chan 3)
132+
output (async/chan 3)
133+
server (server/chan-server {:output output
134+
:input input
135+
:parallelism 1})
136+
join (server/start server nil)
137+
req (server/send-request server "req" {:body "foo"})]
138+
;; client receives message, but doesn't reply
139+
(assert-take output)
140+
(is (= :expected-timeout (server/deref-or-cancel req 100 :expected-timeout)))
141+
(is (= {:jsonrpc "2.0", :method "$/cancelRequest", :params {:id 1}}
142+
(assert-take output)))
143+
(server/shutdown server)
144+
(server/exit server)
145+
@join))
146+
147+
(deftest should-not-cancel-after-client-replies
148+
(let [input (async/chan 3)
149+
output (async/chan 3)
150+
server (server/chan-server {:output output
151+
:input input
152+
:parallelism 1})
153+
join (server/start server nil)
154+
req (server/send-request server "req" {:body "foo"})
155+
client-rcvd-msg (assert-take output)]
156+
(async/put! input {:id (:id client-rcvd-msg)
157+
:result {:processed true}})
158+
(is (= {:processed true} (server/deref-or-cancel req 1000 :test-timeout)))
159+
(assert-no-take output)
160+
(is (not (future-cancel req)))
161+
(server/shutdown server)
162+
(server/exit server)
163+
@join))
164+
165+
(deftest should-send-only-one-cancellation
166+
(let [input (async/chan 3)
167+
output (async/chan 3)
168+
server (server/chan-server {:output output
169+
:input input
170+
:parallelism 1})
171+
join (server/start server nil)
172+
req (server/send-request server "req" {:body "foo"})]
173+
(assert-take output)
174+
(is (future-cancel req))
175+
(is (= "$/cancelRequest" (:method (assert-take output))))
176+
(is (not (future-cancel req)))
177+
(assert-no-take output)
178+
(server/shutdown server)
179+
(server/exit server)
180+
@join))
181+
182+
(deftest request-should-behave-like-a-clojure-future
183+
(testing "before being handled"
184+
(let [input (async/chan 3)
185+
output (async/chan 3)
186+
server (server/chan-server {:output output
187+
:input input
188+
:parallelism 1})
189+
join (server/start server nil)
190+
req (server/send-request server "req" {:body "foo"})]
191+
(is (not (realized? req)))
192+
(is (not (future-done? req)))
193+
(is (not (future-cancelled? req)))
194+
(server/shutdown server)
195+
(server/exit server)
196+
@join))
197+
(testing "after response"
198+
(let [input (async/chan 3)
199+
output (async/chan 3)
200+
server (server/chan-server {:output output
201+
:input input
202+
:parallelism 1})
203+
join (server/start server nil)
204+
req (server/send-request server "req" {:body "foo"})
205+
client-rcvd-msg (assert-take output)]
206+
(async/put! input {:id (:id client-rcvd-msg)
207+
:result {:processed true}})
208+
(is (= {:processed true} (server/deref-or-cancel req 1000 :test-timeout)))
209+
(is (realized? req))
210+
(is (future-done? req))
211+
(is (not (future-cancelled? req)))
212+
(server/shutdown server)
213+
(server/exit server)
214+
@join))
215+
(testing "after cancellation"
216+
(let [input (async/chan 3)
217+
output (async/chan 3)
218+
server (server/chan-server {:output output
219+
:input input
220+
:parallelism 3})
221+
join (server/start server nil)
222+
req (server/send-request server "req" {:body "foo"})]
223+
(future-cancel req)
224+
(is (realized? req))
225+
(is (future-done? req))
226+
(is (future-cancelled? req))
227+
(server/shutdown server)
228+
(server/exit server)
229+
@join)))
230+
231+
(deftest request-should-behave-like-a-java-future
232+
(testing "before being handled"
233+
(let [input (async/chan 3)
234+
output (async/chan 3)
235+
server (server/chan-server {:output output
236+
:input input
237+
:parallelism 1})
238+
join (server/start server nil)
239+
req (server/send-request server "req" {:body "foo"})]
240+
(is (thrown? java.util.concurrent.TimeoutException
241+
(.get req 500 java.util.concurrent.TimeUnit/MILLISECONDS)))
242+
(server/shutdown server)
243+
(server/exit server)
244+
@join))
245+
(testing "after response"
246+
(let [input (async/chan 3)
247+
output (async/chan 3)
248+
server (server/chan-server {:output output
249+
:input input
250+
:parallelism 1})
251+
join (server/start server nil)
252+
req (server/send-request server "req" {:body "foo"})
253+
client-rcvd-msg (assert-take output)]
254+
(async/put! input {:id (:id client-rcvd-msg)
255+
:result {:processed true}})
256+
(is (= {:processed true} (.get req 100 java.util.concurrent.TimeUnit/MILLISECONDS)))
257+
(server/shutdown server)
258+
(server/exit server)
259+
@join))
260+
(testing "after cancellation"
261+
(let [input (async/chan 3)
262+
output (async/chan 3)
263+
server (server/chan-server {:output output
264+
:input input
265+
:parallelism 3})
266+
join (server/start server nil)
267+
req (server/send-request server "req" {:body "foo"})]
268+
(future-cancel req)
269+
(is (thrown? java.util.concurrent.CancellationException
270+
(.get req 100 java.util.concurrent.TimeUnit/MILLISECONDS)))
271+
(server/shutdown server)
272+
(server/exit server)
273+
@join)))

0 commit comments

Comments
 (0)