socket_spec.fnl

Branches > main > latest > files / nvim / pack / plugins / opt / conjure / fnl / conjure-spec / client / guile / socket_spec.fnl

(local {: autoload } (require :conjure.nfnl.module))
(local {: describe : it : before_each} (require :plenary.busted))
(local a (autoload :conjure.nfnl.core))
(local assert (autoload :luassert.assert))
(local guile (autoload :conjure.client.guile.socket))
(local config (autoload :conjure.config))
(require :conjure-spec.assertions)

(local mock-socket (require :conjure-spec.client.guile.mock-socket))
(local mock-tsc (require :conjure-spec.mock-tree-sitter-completions))
(local mock-log (require :conjure-spec.mock-log))

(tset package.loaded "conjure.remote.socket" mock-socket)
(tset package.loaded "conjure.tree-sitter-completions" mock-tsc)
(tset package.loaded "conjure.log" mock-log)

(local completion-code-define-match "%(define%* %(%%conjure:get%-guile%-completions")

(fn set-repl-connected 
  [repl]
  (tset repl :status :connected))

(fn set-repl-busy
  [repl]
  (tset repl :current "some command"))

(describe "conjure.client.guile.socket"
  (fn []
    (tset package.loaded "conjure.remote.socket" mock-socket)
    (tset guile :valid-str? (fn [_] true))

    (describe "context extraction"
      (fn [] 
        (it "returns nil for hello world"
            (fn []
              (assert.are.equal nil (guile.context "(print \"Hello World\")"))))
        (it "returns (my-module) for (define-module (my-module))"
            (fn []
              (assert.are.equal "(my-module)" (guile.context "(define-module (my-module))"))))
        (it "returns (my-module) for (define-module\\n(my-module))"
            (fn []
              (assert.are.equal "(my-module)" (guile.context "(define-module\n(my-module))"))))
        (it "returns (my-module spaces) for (define-module ( my-module  spaces   ))"
            (fn []
              (assert.are.equal "(my-module spaces)" (guile.context "(define-module ( my-module  spaces   ))"))))
        (it "returns nil for ;(define-module (my-module))"
            (fn []
              (assert.are.equal nil (guile.context ";(define-module (my-module))"))))
        (it "returns nil for (define-m;odule (my-module))"
            (fn []
              (assert.are.equal nil (guile.context "(define-m;odule (my-module))"))))
        (it "returns (another-module) for ;\\n(define-module ( another-module ))"
            (fn []
              (assert.are.equal "(another-module)" (guile.context ";\n(define-module ( another-module ))"))))
        (it "returns (a-module specification) for ;\\n(define-module\\n;some comments\\n( a-module\\n; more comments\\n specification))"
            (fn []
              (assert.are.equal "(a-module specification)" (guile.context ";\n(define-module\n;some comments\n( a-module\n; more comments\n specification))"))))))

    (describe "parse-guile-result"
      (fn []
        (it "returns a result in the simple happy path"
          (fn []
            (assert.are.same
              {:done? true
               :error? false
               :result "1234"}
              (guile.parse-guile-result "$1 = 1234\nscheme@(guile-user)> "))))

        (it "handles single line output from display, missing a newline"
          (fn []
            (let [stray-output []
                  capture-stray-output (fn [output]
                                         (table.insert stray-output output))]
              (assert.are.same
                {:done? true
                 :error? false
                 :result nil}
                (guile.parse-guile-result "hischeme@(guile-user)> " capture-stray-output))
              (assert.are.same
                [["; (out) hi"]]
                stray-output))))

        (it "prompts with an error number report as an error"
          (fn []
            (assert.are.same
              {:done? true
               :error? true}
              (guile.parse-guile-result "scheme@(guile-user) [1]> "))))

        (it "handles multiple return values"
          (fn []
            (let [stray-output []
                  capture-stray-output (fn [output]
                                        (table.insert stray-output output))]
              (assert.are.same
                {:done? true
                 :error? false
                 :result "(values 10 20 30)"}
                (guile.parse-guile-result "hi\n$1 = 10\n$2 = 20\n$3 = 30\nscheme@(guile-user)> " capture-stray-output))
              (assert.are.same
                [["; (out) hi"]]
                stray-output))))
        
        (it "return values with stray output that lacks a new line are parsed correctly"
          (fn []
            (let [stray-output []
                  capture-stray-output (fn [output]
                                        (table.insert stray-output output))]
              (assert.are.same
                {:done? true
                 :error? false
                 :result "(values 10 20 30)"}
                (guile.parse-guile-result "hi$1 = 10\n$2 = 20\n$3 = 30\nscheme@(guile-user)> " capture-stray-output))
              (assert.are.same
                [["; (out) hi"]]
                stray-output))))))

    (describe "eval-str" 
      (fn []
        (config.merge {:client {:guile {:socket
                        {:pipename "fake-pipe" :host_port nil}}}}
                      {:overwrite? true})
        (it "does eval string when valid-str? returns true"
            (fn []
              (let [expected-code "(valid form)"
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl (mock-socket.build-mock-repl spy-send)]
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.eval-str {:code expected-code :context nil})
                (guile.disconnect)

                (assert.are.equal (.. ",m (guile-user)\n" expected-code) (. calls 3)))))

        (it "does not eval string when valid-str? returns false"
            (fn []
              (let [calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl (mock-socket.build-mock-repl spy-send)]
                (tset guile :valid-str? (fn [_] false))
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.eval-str {:code "(some invalid form" :context nil})
                (guile.disconnect)

                (assert.same [] calls)
                (tset guile :valid-str? (fn [_] true)))))))

    (describe "module initialization"
      (fn []
        (config.merge {:client {:guile {:socket
                        {:pipename "fake-pipe" :host_port nil}}}}
                      {:overwrite? true})
        (it "initializes (guile-user) when eval-str called on new repl in nil context"
            (fn []
              (let [
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl (mock-socket.build-mock-repl spy-send)
                    expected-code "(print \"Hello world\")"]
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.eval-str {:code expected-code :context nil})
                (guile.disconnect)

                (assert.are.equal ",m (guile-user)\n,import (guile)" (. calls 1))
                (assert.has-substring completion-code-define-match (. calls 2))
                (assert.are.equal (.. ",m (guile-user)\n" expected-code) (. calls 3)))))

        (it "initializes (guile-user) once when eval-str called twice on repl in nil context"
            (fn []
              (let [
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl (mock-socket.build-mock-repl spy-send)
                    expected-code "(print \"Hello second call\")"]
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.eval-str {:code "(first-call)" :context nil})
                (guile.eval-str {:code expected-code :context nil})
                (guile.disconnect)

                (assert.are.equal (.. ",m (guile-user)\n" expected-code) (. calls 4)))))

        (it "initializes (guile-user) again when eval-str disconnect eval-str is called in nil context"
            (fn []
              (let [
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl (mock-socket.build-mock-repl spy-send)
                    expected-code "(print \"Hello second call\")"]
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.eval-str {:code "(first-call)" :context nil})
                (guile.disconnect)
                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.eval-str {:code expected-code :context nil})
                (guile.disconnect)

                (assert.are.equal ",m (guile-user)\n,import (guile)" (. calls 4))
                (assert.has-substring completion-code-define-match (. calls 5))
                (assert.are.equal (.. ",m (guile-user)\n" expected-code) (. calls 6)))))

        (it "initializes (a-module) when eval-str in (guile-user) then eval-str in (a-module)"
            (fn []
              (let [
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl {:send spy-send :status nil :destroy (fn [])}
                    expected-module "a-module"
                    expected-code "(print \"Hello second call\")"]
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.eval-str {:code "(first-call)" :context nil})
                (guile.eval-str {:code expected-code :context expected-module})
                (guile.disconnect)

                (assert.are.equal (..  ",m " expected-module "\n,import (guile)") (. calls 4))
                (assert.has-substring completion-code-define-match (. calls 5))
                (assert.are.equal (.. ",m " expected-module "\n" expected-code) (. calls 6)))))))

    (describe "completions"
      (fn [] 
        (before_each 
          (fn []
            (config.merge {:client {:guile {:socket
                             {:pipename "fake-pipe" :host_port nil}}}}
                          {:overwrite? true})))

        (it "Does not execute completions in REPL when not connected"
            (fn []
              (let [
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl (mock-socket.build-mock-repl spy-send)
                    callback-results  []
                    mock-callback (fn [result] (table.insert callback-results result))]
                (mock-socket.set-mock-repl mock-repl)

                (guile.completions {:cb mock-callback :prefix "something"})

                (assert.same [] calls)
                (assert.same [] (. callback-results 1)))))

        (it "Gets built-in results for define when execute completions and REPL not connected"
            (fn []
              (let [
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl (mock-socket.build-mock-repl spy-send)
                    callback-results  []
                    mock-callback (fn [result] (table.insert callback-results result))]
                (mock-socket.set-mock-repl mock-repl)

                (guile.completions {:cb mock-callback :prefix "define"})

                (assert.same "define" (. (. callback-results 1) 1)))))

        (it "Executes completions in REPL for prefix dela with result delay and dela-something"
            (fn []
              (let [
                    calls []
                    spy-send (fn [call callback] (table.insert calls {:code call :callback callback}))
                    mock-repl {:send spy-send :status nil :destroy (fn [])}
                    callback-results  []
                    mock-callback (fn [result] (table.insert callback-results result))]
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.completions {:cb mock-callback :prefix "dela"})
                (let [completion-call (. calls 3)]
                  ((. completion-call :callback) [{:out "(\"dela-something\")"}])
                  (guile.disconnect)

                  (assert.same ["delay" "dela-something"] (. callback-results 1))))))

        (it "Executes completions with tree sitter results given prefix nil with result delalex as first result"
            (fn []
              (let [
                    calls []
                    spy-send (fn [call callback] (table.insert calls {:code call :callback callback}))
                    mock-repl {:send spy-send :status nil :destroy (fn [])}
                    callback-results  []
                    mock-callback (fn [result] (table.insert callback-results result))]
                (mock-tsc.set-mock-completions ["delalex"])
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.completions {:cb mock-callback :prefix nil})
                ((. (. calls 3) :callback) [{:out "(\"dela-something\")"}])
                (guile.disconnect)

                (assert.are.equal "delalex" (a.get-in callback-results [1 1])))))


        (it "Executes completions with tree sitter results given prefix dela with result delay dela-something and delalex"
            (fn []
              (let [
                    calls []
                    spy-send (fn [call callback] (table.insert calls {:code call :callback callback}))
                    mock-repl {:send spy-send :status nil :destroy (fn [])}
                    callback-results  []
                    mock-callback (fn [result] (table.insert callback-results result))]
                (mock-tsc.set-mock-completions ["delalex"])
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.completions {:cb mock-callback :prefix "dela"})
                ((. (. calls 3) :callback) [{:out "(\"dela-something\")"}])
                (guile.disconnect)

                (assert.same ["delalex" "delay" "dela-something"] (. callback-results 1)))))

        (it "Deduplicates results when built-in tree sitter and repl results given prefix are all delay"
            (fn []
              (let [
                    calls []
                    spy-send (fn [call callback] (table.insert calls {:code call :callback callback}))
                    mock-repl {:send spy-send :status nil :destroy (fn [])}
                    expected-code "%(%%conjure:get%-guile%-completions \"dela\"%)"
                    callback-results  []
                    mock-callback (fn [result] (table.insert callback-results result))]
                (mock-tsc.set-mock-completions ["delay"])
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.completions {:cb mock-callback :prefix "dela"})
                ((. (. calls 3) :callback) [{:out "(\"delay\")"}])
                (guile.disconnect)

                (assert.same ["delay"] (. callback-results 1)))))

        (it "Puts last completion first for prefix fu with results fun func and future"
            (fn []
              (let [
                    sent-callbacks []
                    spy-send (fn [_ callback] (table.insert sent-callbacks callback))
                    mock-repl (mock-socket.build-mock-repl spy-send)
                    callback-results  []
                    mock-callback (fn [result] (table.insert callback-results result))]
                (mock-tsc.set-mock-completions [])
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.completions {:cb mock-callback :prefix "fu"})
                ((. sent-callbacks 3) [{:out "(\"fun\" \"func\" \"future\")"}])
                (guile.disconnect)

                (assert.same ["future" "fun" "func"] (. callback-results 1)))))))

    (describe "enable completions config setting"
      (fn [] 
        (it "Does not load completion code when completions disabled in config"
            (fn []
              (config.merge {:client {:guile {:socket
                               {:pipename "fake-pipe" :host_port nil
                                :enable_completions false }}}}
                            {:overwrite? true})
              (let [
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl {:send spy-send :status nil :destroy (fn [])}
                    expected-code "(print \"Hello world\")"]
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.eval-str {:code expected-code :context nil})
                (guile.disconnect)

                (assert.are.equal ",m (guile-user)\n,import (guile)" (. calls 1))
                (assert.are.equal (.. ",m (guile-user)\n" expected-code) (. calls 2)))))

        (it "Does load completion code when completions enabled in config"
            (fn []
              (config.merge {:client {:guile {:socket
                               {:pipename "fake-pipe" :host_port nil
                                :enable_completions true}}}}
                            {:overwrite? true})
              (let [
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl (mock-socket.build-mock-repl spy-send)
                    expected-code "(print \"Hello world\")"]
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.eval-str {:code expected-code :context nil})
                (guile.disconnect)

                (assert.are.equal ",m (guile-user)\n,import (guile)" (. calls 1))
                (assert.has-substring completion-code-define-match (. calls 2))
                (assert.are.equal (.. ",m (guile-user)\n" expected-code) (. calls 3)))))

        (it "Does not execute completions in REPL when connected but completions disabled"
            (fn []
              (config.merge {:client {:guile {:socket
                               {:pipename "fake-pipe" :host_port nil
                                :enable_completions false}}}}
                            {:overwrite? true})
              (let [
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl {:send spy-send :status nil :destroy (fn [])}
                    callback-results  []
                    mock-callback (fn [result] (table.insert callback-results result))]
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (guile.completions {:cb mock-callback :prefix "define"})
                (guile.disconnect)

                (assert.same [] calls)
                (assert.same [] (. callback-results 1)))))

        (it "Does not execute completions in REPL when connected but busy"
            (fn []
              (config.merge {:client {:guile {:socket
                               {:pipename "fake-pipe" :host_port nil
                                :enable_completions true}}}}
                            {:overwrite? true})
              (let [
                    calls []
                    spy-send (fn [call] (table.insert calls call))
                    mock-repl {:send spy-send :status nil :destroy (fn [])}
                    callback-results  []
                    fake-callback (fn [result] (table.insert callback-results result))]
                (mock-socket.set-mock-repl mock-repl)

                (guile.connect {})
                (set-repl-connected mock-repl)
                (set-repl-busy mock-repl)
                (guile.completions {:cb fake-callback :prefix "something"})
                (guile.disconnect)

                (assert.same [] calls)
                (assert.same [] (. callback-results 1)))))))))