Commit2171ec52Recorded9 Jul 2026Repositorysigil-nrepl
Expand nREPL server regression coverage
Changed
src/sigil/nrepl.sgl | 8 ++++++++
test/test-server.sgl | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 129 insertions(+), 2 deletions(-)Diff
src/sigil/nrepl.sglmodified
@@ -240,6 +240,7 @@
240
;; Dispatch on operation 241
(case op 242
((eval) (handle-eval client id args))+243
((abort interrupt) (handle-abort client id args)) 244
((complete) (handle-complete client id args)) 245
((doc) (handle-doc client id args)) 246
((describe) (handle-describe client id args))@@ -470,6 +471,13 @@
471
(clear-debug-mode! client) 472
(make-response* client id 'ok ':in-debugger #f ':value "Returning to REPL")) 473
+474
(define (handle-abort client id args)+475
(let ((target-id (get-arg args ':target-id #f)))+476
(if (not target-id)+477
(make-error-response* client id "missing-target-id" "No :target-id provided")+478
(make-error-response* client id "not-found"+479
(format "No running eval for request id ~a" target-id)))))+480
481
;; complete - return completions for a prefix 482
;; Includes both value bindings and syntax/macro bindings 483
(define (handle-complete client id args)test/test-server.sglmodified
@@ -24,6 +24,9 @@
24
(define (send-request sock request) 25
(socket-write sock (encode-test-message request))) 26
+27
(define (send-bytes sock bytes)+28
(socket-write sock bytes))+29
30
(define (read-at-least sock n acc attempts) 31
(cond 32
((<= attempts 0) #f)@@ -69,6 +72,12 @@
72
(pump server 20) 73
(read-response sock)) 74
+75
(define (list-contains? items value)+76
(cond+77
((null? items) #f)+78
((equal? (car items) value) #t)+79
(else (list-contains? (cdr items) value))))+80
81
(define (with-server thunk) 82
(let ((server (nrepl-start *test-port*))) 83
(dynamic-wind@@ -117,8 +126,7 @@
126
(let ((frames-resp (request-response server sock 127
'(request :id "frames" :op debug-frames)))) 128
(assert-eq (response-ref frames-resp ':status #f) 'ok)−120
(assert-true (response-ref frames-resp ':in-debugger #f)−121
)+129
(assert-true (response-ref frames-resp ':in-debugger #f)) 130
(assert-true (string? (response-ref frames-resp ':frames #f)))) 131
(let ((restarts-resp (request-response server sock 132
'(request :id "restarts" :op debug-restarts))))@@ -134,4 +142,115 @@
142
(assert-equal (response-ref ok-resp ':value #f) "3")) 143
(socket-close sock)))))) 144
+145
(test-group "nrepl advertised ops"+146
(test "ping and info round-trip"+147
(with-server+148
(lambda (server)+149
(let ((sock (tcp-connect "127.0.0.1" *test-port*)))+150
(pump server 5)+151
(let ((ping-resp (request-response server sock+152
'(request :id "ping" :op ping))))+153
(assert-eq (response-ref ping-resp ':status #f) 'ok)+154
(assert-true (response-ref ping-resp ':pong #f)))+155
(let ((info-resp (request-response server sock+156
'(request :id "info" :op info))))+157
(assert-eq (response-ref info-resp ':status #f) 'ok)+158
(assert-equal (response-ref info-resp ':module #f) "(sigil user)"))+159
(socket-close sock)))))+160
+161
(test "complete, doc, describe, macroexpand, and modules round-trip"+162
(with-server+163
(lambda (server)+164
(let ((sock (tcp-connect "127.0.0.1" *test-port*)))+165
(pump server 5)+166
(let ((complete-resp (request-response server sock+167
'(request :id "complete" :op complete :prefix "ca"))))+168
(assert-eq (response-ref complete-resp ':status #f) 'ok)+169
(assert-true (list? (response-ref complete-resp ':completions #f))))+170
(let ((doc-resp (request-response server sock+171
'(request :id "doc" :op doc :symbol "car"))))+172
(assert-eq (response-ref doc-resp ':status #f) 'ok))+173
(let ((describe-resp (request-response server sock+174
'(request :id "describe" :op describe :symbol "car"))))+175
(assert-eq (response-ref describe-resp ':status #f) 'ok)+176
(assert-equal (response-ref describe-resp ':name #f) "car"))+177
(let ((macro-resp (request-response server sock+178
'(request :id "macro" :op macroexpand :code "(when #t 1)"))))+179
(assert-eq (response-ref macro-resp ':status #f) 'ok)+180
(assert-true (string? (response-ref macro-resp ':expansion #f))))+181
(let ((modules-resp (request-response server sock+182
'(request :id "modules" :op modules))))+183
(assert-eq (response-ref modules-resp ':status #f) 'ok)+184
(assert-equal (response-ref modules-resp ':current #f) "(sigil user)")+185
(assert-true (list? (response-ref modules-resp ':modules #f))))+186
(socket-close sock)))))+187
+188
(test "abort and interrupt validate provisional target-id protocol"+189
(with-server+190
(lambda (server)+191
(let ((sock (tcp-connect "127.0.0.1" *test-port*)))+192
(pump server 5)+193
(let ((missing-resp (request-response server sock+194
'(request :id "abort-missing" :op abort))))+195
(assert-eq (response-ref missing-resp ':status #f) 'error)+196
(assert-equal (response-ref missing-resp ':code #f) "missing-target-id"))+197
(let ((not-found-resp (request-response server sock+198
'(request :id "abort-unknown" :op interrupt :target-id "nope"))))+199
(assert-eq (response-ref not-found-resp ':status #f) 'error)+200
(assert-equal (response-ref not-found-resp ':code #f) "not-found"))+201
(socket-close sock))))))+202
+203
(test-group "nrepl framing"+204
(test "partial message reassembly waits for a complete frame"+205
(with-server+206
(lambda (server)+207
(let* ((sock (tcp-connect "127.0.0.1" *test-port*))+208
(bytes (encode-test-message+209
'(request :id "partial" :op eval :code "(+ 2 3)")))+210
(split 7))+211
(pump server 5)+212
(send-bytes sock (bytevector-copy bytes 0 split))+213
(pump server 10)+214
(assert-false (socket-ready? sock 20))+215
(send-bytes sock (bytevector-copy bytes split))+216
(pump server 20)+217
(let ((resp (read-response sock)))+218
(assert-eq (response-ref resp ':status #f) 'ok)+219
(assert-equal (response-ref resp ':value #f) "5"))+220
(socket-close sock))))))+221
+222
(test-group "nrepl eval semantics"+223
(test "module context is applied per request"+224
(with-server+225
(lambda (server)+226
(let ((sock (tcp-connect "127.0.0.1" *test-port*)))+227
(pump server 5)+228
(let ((resp (request-response server sock+229
'(request :id "module-eval" :op eval :module "(sigil user)" :code "(+ 6 7)"))))+230
(assert-eq (response-ref resp ':status #f) 'ok)+231
(assert-equal (response-ref resp ':module #f) "(sigil user)")+232
(assert-equal (response-ref resp ':value #f) "13"))+233
(socket-close sock)))))+234
+235
(test "redefinition takes effect at name-based call sites"+236
(with-server+237
(lambda (server)+238
(let ((sock (tcp-connect "127.0.0.1" *test-port*)))+239
(pump server 5)+240
(request-response server sock+241
'(request :id "def-1" :op eval :code "(define (nrepl-test-base) 1)"))+242
(request-response server sock+243
'(request :id "def-caller" :op eval :code "(define (nrepl-test-caller) (nrepl-test-base))"))+244
(let ((first (request-response server sock+245
'(request :id "first-call" :op eval :code "(nrepl-test-caller)"))))+246
(assert-eq (response-ref first ':status #f) 'ok)+247
(assert-equal (response-ref first ':value #f) "1"))+248
(request-response server sock+249
'(request :id "def-2" :op eval :code "(define (nrepl-test-base) 2)"))+250
(let ((second (request-response server sock+251
'(request :id "second-call" :op eval :code "(nrepl-test-caller)"))))+252
(assert-eq (response-ref second ':status #f) 'ok)+253
(assert-equal (response-ref second ':value #f) "2"))+254
(socket-close sock))))))+255
256
(run-tests)