AtlatestRepositorysigil-web
1
(import (sigil test)2
(sigil web routes)3
(sigil http request)4
(sigil http response))6
;; ============================================================7
;; Test Helpers8
;; ============================================================10
(define (make-test-request method path)11
(http-request method: method path: path headers: #{} body: #f))13
(define (ok-handler request)14
(http-response status: 200 body: "ok"))16
(define (echo-path-handler request)17
(http-response status: 200 body: (http-request-path request)))19
;; ============================================================20
;; route record21
;; ============================================================23
(test-group "route record"24
(test "constructor and accessors"25
(let ((r (route method: 'GET pattern: "/foo" handler: ok-handler)))26
(assert-equal 'GET (route-method r))27
(assert-equal "/foo" (route-pattern r))28
(assert-equal ok-handler (route-handler r))))30
(test "route with ANY method"31
(let ((r (route method: 'ANY pattern: "/*path" handler: ok-handler)))32
(assert-equal 'ANY (route-method r)))))34
;; ============================================================35
;; router - literal paths36
;; ============================================================38
(test-group "router - literal paths"39
(test "exact match"40
(let* ((app (router (route method: GET pattern: "/" handler: ok-handler)))41
(res (app (make-test-request 'GET "/"))))42
(assert-true (http-response? res))43
(assert-equal 200 (http-response-status res))))45
(test "no match returns #f"46
(let* ((app (router (route method: GET pattern: "/foo" handler: ok-handler)))47
(res (app (make-test-request 'GET "/bar"))))48
(assert-false res)))50
(test "multi-segment path"51
(let* ((app (router (route method: GET pattern: "/api/users" handler: ok-handler)))52
(res (app (make-test-request 'GET "/api/users"))))53
(assert-true (http-response? res))54
(assert-equal 200 (http-response-status res))))56
(test "method mismatch"57
(let* ((app (router (route method: GET pattern: "/" handler: ok-handler)))58
(res (app (make-test-request 'POST "/"))))59
(assert-false res)))61
(test "ANY matches all methods"62
(let ((app (router (route method: ANY pattern: "/any" handler: ok-handler))))63
(assert-true (http-response? (app (make-test-request 'GET "/any"))))64
(assert-true (http-response? (app (make-test-request 'POST "/any"))))65
(assert-true (http-response? (app (make-test-request 'DELETE "/any")))))))67
;; ============================================================68
;; router - named parameters69
;; ============================================================71
(test-group "router - named parameters"72
(test "single :id parameter"73
(let* ((handler (lambda (req)74
(http-response status: 20075
body: (path-param req "id"))))76
(app (router (route method: GET pattern: "/users/:id" handler: handler)))77
(res (app (make-test-request 'GET "/users/42"))))78
(assert-true (http-response? res))79
(assert-equal "42" (http-response-body res))))81
(test "multiple parameters"82
(let* ((handler (lambda (req)83
(http-response status: 20084
body: (string-append85
(path-param req "uid") ":"86
(path-param req "pid")))))87
(app (router (route method: GET pattern: "/users/:uid/posts/:pid" handler: handler)))88
(res (app (make-test-request 'GET "/users/alice/posts/99"))))89
(assert-equal "alice:99" (http-response-body res))))91
(test "path-params returns full alist"92
(let* ((handler (lambda (req)93
(let ((params (path-params req)))94
(http-response status: 20095
body: (number->string (length params))))))96
(app (router (route method: GET pattern: "/a/:x/b/:y" handler: handler)))97
(res (app (make-test-request 'GET "/a/1/b/2"))))98
(assert-equal "2" (http-response-body res))))100
(test "path-param returns #f for missing param"101
(let* ((handler (lambda (req)102
(http-response status: 200103
body: (if (path-param req "missing") "found" "not-found"))))104
(app (router (route method: GET pattern: "/x/:id" handler: handler)))105
(res (app (make-test-request 'GET "/x/1"))))106
(assert-equal "not-found" (http-response-body res)))))108
;; ============================================================109
;; router - splat parameters110
;; ============================================================112
(test-group "router - splat parameters"113
(test "*path captures rest of path"114
(let* ((handler (lambda (req)115
(http-response status: 200116
body: (path-param req "path"))))117
(app (router (route method: GET pattern: "/static/*path" handler: handler)))118
(res (app (make-test-request 'GET "/static/css/style.css"))))119
(assert-equal "css/style.css" (http-response-body res))))121
(test "splat captures single segment"122
(let* ((handler (lambda (req)123
(http-response status: 200124
body: (path-param req "file"))))125
(app (router (route method: GET pattern: "/files/*file" handler: handler)))126
(res (app (make-test-request 'GET "/files/readme.txt"))))127
(assert-equal "readme.txt" (http-response-body res))))129
(test "splat captures empty rest"130
(let* ((handler (lambda (req)131
(http-response status: 200132
body: (path-param req "rest"))))133
(app (router (route method: GET pattern: "/api/*rest" handler: handler)))134
(res (app (make-test-request 'GET "/api/"))))135
(assert-equal "" (http-response-body res)))))137
;; ============================================================138
;; router - first match wins139
;; ============================================================141
(test-group "router - first match wins"142
(test "first matching route wins"143
(let* ((handler1 (lambda (req) (http-response status: 200 body: "first")))144
(handler2 (lambda (req) (http-response status: 200 body: "second")))145
(app (router (route method: GET pattern: "/x" handler: handler1)146
(route method: GET pattern: "/x" handler: handler2)))147
(res (app (make-test-request 'GET "/x"))))148
(assert-equal "first" (http-response-body res))))150
(test "method differentiation"151
(let* ((get-handler (lambda (req) (http-response status: 200 body: "get")))152
(post-handler (lambda (req) (http-response status: 200 body: "post")))153
(app (router (route method: GET pattern: "/x" handler: get-handler)154
(route method: POST pattern: "/x" handler: post-handler)))155
(get-res (app (make-test-request 'GET "/x")))156
(post-res (app (make-test-request 'POST "/x"))))157
(assert-equal "get" (http-response-body get-res))158
(assert-equal "post" (http-response-body post-res)))))160
;; ============================================================161
;; routes combinator162
;; ============================================================164
(test-group "routes combinator"165
(test "tries handlers in order, returns first non-#f"166
(let* ((h1 (lambda (req) #f))167
(h2 (lambda (req) (http-response status: 200 body: "h2")))168
(h3 (lambda (req) (http-response status: 200 body: "h3")))169
(app (routes h1 h2 h3))170
(res (app (make-test-request 'GET "/"))))171
(assert-equal "h2" (http-response-body res))))173
(test "all #f returns #f"174
(let* ((h1 (lambda (req) #f))175
(h2 (lambda (req) #f))176
(app (routes h1 h2))177
(res (app (make-test-request 'GET "/"))))178
(assert-false res)))180
(test "first handler succeeds"181
(let* ((h1 (lambda (req) (http-response status: 200 body: "first")))182
(h2 (lambda (req) (http-response status: 200 body: "second")))183
(app (routes h1 h2))184
(res (app (make-test-request 'GET "/"))))185
(assert-equal "first" (http-response-body res)))))187
(run-tests)