Commit98927edbRecorded15 Feb 2026Repositorysigil-jwt

test: Add JWT encode/decode/expiration tests for sigil-jwt package

Message

Tests jwt-encode structure, roundtrip encode/decode with claim recovery, wrong secret rejection, malformed token handling, expired/valid token expiration, and jwt-decode-unsafe.

Changed
 test/test-jwt.sgl | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)
Diff
test/test-jwt.sgladded
@@ -0,0 +1,79 @@
+1
(import (sigil test)
+2
(sigil jwt)
+3
(sigil time))
+4
+5
;; ============================================================
+6
;; jwt-encode
+7
;; ============================================================
+8
+9
(test-group "jwt-encode"
+10
(test "produces 3 dot-separated parts"
+11
(let ((token (jwt-encode '((sub . "user1")) "secret")))
+12
(assert-true (string? token))
+13
;; Count dots
+14
(let ((parts (let loop ((s token) (parts '()) (current ""))
+15
(if (= (string-length s) 0)
+16
(reverse (cons current parts))
+17
(if (char=? (string-ref s 0) #\.)
+18
(loop (substring s 1 (string-length s))
+19
(cons current parts)
+20
"")
+21
(loop (substring s 1 (string-length s))
+22
parts
+23
(string-append current (string (string-ref s 0)))))))))
+24
(assert-equal 3 (length parts))))))
+25
+26
;; ============================================================
+27
;; jwt-decode roundtrip
+28
;; ============================================================
+29
+30
(test-group "jwt-decode"
+31
(test "roundtrip encode/decode recovers claims"
+32
(let* ((claims '((sub . "user123") (role . "admin")))
+33
(token (jwt-encode claims "my-secret"))
+34
(decoded (jwt-decode token "my-secret")))
+35
(assert-true (dict? decoded))
+36
(assert-equal "user123" (dict-ref decoded sub:))
+37
(assert-equal "admin" (dict-ref decoded role:))))
+38
+39
(test "wrong secret returns #f"
+40
(let* ((token (jwt-encode '((sub . "user")) "correct-secret"))
+41
(decoded (jwt-decode token "wrong-secret")))
+42
(assert-false decoded)))
+43
+44
(test "malformed token returns #f"
+45
(assert-false (jwt-decode "not.a.valid-token" "secret"))
+46
(assert-false (jwt-decode "onlyonepart" "secret"))))
+47
+48
;; ============================================================
+49
;; Expiration
+50
;; ============================================================
+51
+52
(test-group "expiration"
+53
(test "expired token rejected"
+54
(let* ((claims (list (cons 'sub "user")
+55
(cons 'exp (- (floor (current-second)) 3600))))
+56
(token (jwt-encode claims "secret"))
+57
(decoded (jwt-decode token "secret")))
+58
(assert-false decoded)))
+59
+60
(test "valid future token accepted"
+61
(let* ((claims (list (cons 'sub "user")
+62
(cons 'exp (+ (floor (current-second)) 3600))))
+63
(token (jwt-encode claims "secret"))
+64
(decoded (jwt-decode token "secret")))
+65
(assert-true (dict? decoded))
+66
(assert-equal "user" (dict-ref decoded sub:)))))
+67
+68
;; ============================================================
+69
;; jwt-decode-unsafe
+70
;; ============================================================
+71
+72
(test-group "jwt-decode-unsafe"
+73
(test "decodes without verifying signature"
+74
(let* ((token (jwt-encode '((sub . "peek")) "secret"))
+75
(decoded (jwt-decode-unsafe token)))
+76
(assert-true (dict? decoded))
+77
(assert-equal "peek" (dict-ref decoded sub:)))))
+78
+79
(run-tests)