88 lines
3.4 KiB
Text
88 lines
3.4 KiB
Text
# Integration test: jolt.nrepl server + client over a real TCP/bencode wire.
|
|
#
|
|
# The server runs in a subprocess (`jolt nrepl PORT`) so the client (this
|
|
# process) isn't affected by the server's accept-loop fiber, which leaves the
|
|
# shared ctx's current-ns pointing at jolt.nrepl. The client uses the jolt.nrepl
|
|
# Clojure API, exercising both halves of the implementation.
|
|
|
|
(use ../../src/jolt/api)
|
|
(use ../../src/jolt/types)
|
|
|
|
(def port "17888")
|
|
|
|
# Watchdog: never let a hang stall CI — bail out after 30s.
|
|
(ev/spawn (ev/sleep 30) (eprint "nrepl-test: watchdog fired (possible hang)") (os/exit 1))
|
|
|
|
(print "Starting jolt.nrepl server subprocess on port " port " ...")
|
|
(def proc (os/spawn ["janet" "src/jolt/main.janet" "nrepl" port] :p {:out :pipe :err :pipe}))
|
|
|
|
# Wait until the server accepts connections (poll up to ~5s).
|
|
(var ready false)
|
|
(var tries 0)
|
|
(while (and (not ready) (< tries 50))
|
|
(let [r (protect (net/connect "127.0.0.1" port))]
|
|
(if (r 0) (do (:close (r 1)) (set ready true))
|
|
(do (ev/sleep 0.1) (++ tries)))))
|
|
(assert ready "nREPL server did not start")
|
|
|
|
(def ctx (init))
|
|
(ctx-set-current-ns ctx "user")
|
|
(load-string ctx "(require '[jolt.nrepl])")
|
|
(load-string ctx (string "(def c (jolt.nrepl/connect {:port " port "}))"))
|
|
|
|
(defn ev [e] (eval-string ctx e))
|
|
(var fails 0)
|
|
(defn check [label expr expected]
|
|
(let [got (ev expr)]
|
|
(if (= got expected)
|
|
(print " ok " label)
|
|
(do (++ fails) (printf " FAIL %s: want %q, got %q" label expected got)))))
|
|
|
|
# describe advertises ops
|
|
(check "describe has ops"
|
|
"(boolean (get (first (jolt.nrepl/request c {\"op\" \"describe\"})) \"ops\"))" true)
|
|
|
|
# clone yields a session id
|
|
(ev "(def s (jolt.nrepl/client-clone c))")
|
|
(check "clone session is string" "(string? s)" true)
|
|
|
|
# eval returns a value
|
|
(check "eval (+ 1 2)" "(some #(get % \"value\") (jolt.nrepl/client-eval c \"(+ 1 2)\" s))" "3")
|
|
|
|
# defs persist across evals in the session
|
|
(ev "(jolt.nrepl/client-eval c \"(def yy 21)\" s)")
|
|
(check "def then use" "(some #(get % \"value\") (jolt.nrepl/client-eval c \"(* yy 2)\" s))" "42")
|
|
|
|
# stdout is captured and streamed as an out message
|
|
(check "println captured as out"
|
|
"(some #(get % \"out\") (jolt.nrepl/client-eval c \"(do (println \\\"hi\\\") 9)\" s))" "hi\n")
|
|
|
|
# the response carries the current ns
|
|
(check "ns field reported"
|
|
"(some #(get % \"ns\") (jolt.nrepl/client-eval c \"(+ 1 1)\" s))" "user")
|
|
|
|
# eval error -> eval-error status, and the connection keeps working afterward
|
|
(check "eval error status"
|
|
"(boolean (some #(let [st (get % \"status\")] (and (sequential? st) (some (fn [x] (= \"eval-error\" x)) st))) (jolt.nrepl/client-eval c \"(/ 1 :z)\" s)))"
|
|
true)
|
|
(check "still alive after error"
|
|
"(some #(get % \"value\") (jolt.nrepl/client-eval c \"(+ 5 5)\" s))" "10")
|
|
|
|
# multiple forms in one eval -> a value per form (values arrive as strings)
|
|
(check "multiple forms"
|
|
"(= [\"2\" \"4\"] (mapv #(get % \"value\") (filter #(get % \"value\") (jolt.nrepl/client-eval c \"(+ 1 1) (+ 2 2)\" s))))"
|
|
true)
|
|
|
|
# unknown op -> error/unknown-op/done
|
|
(check "unknown op status"
|
|
"(let [st (get (first (jolt.nrepl/request c {\"op\" \"nope\"})) \"status\")] (and (some #(= \"unknown-op\" %) st) true))"
|
|
true)
|
|
|
|
# clean up
|
|
(ev "(jolt.nrepl/client-close c)")
|
|
(os/proc-kill proc true)
|
|
(when (os/stat ".nrepl-port") (os/rm ".nrepl-port"))
|
|
|
|
(if (> fails 0)
|
|
(do (eprint "nrepl-test: " fails " failing check(s)") (os/exit 1))
|
|
(do (print "\nAll nREPL tests passed!") (os/exit 0)))
|