Skip to content

Commit 04f8b57

Browse files
committed
test: add graceful-shutdown e2e test
Also, rework e2e tests to use ephemeral ports.
1 parent b0bd84e commit 04f8b57

File tree

18 files changed

+221
-111
lines changed

18 files changed

+221
-111
lines changed

web-server-test/tests/web-server/e2e/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ These tests spin up real web servers in order to ensure that the
44
system works end-to-end. Each subfolder is expected to contain two
55
files: `server.rkt` and `tests.rkt`.
66

7-
Each `server.rkt` module must provide a function called `start` that
8-
takes a port, starts a web server on that port and returns a function
9-
that can be used to stop the server.
7+
Each `server.rkt` module must provide a function called `start` starts a
8+
web server on an open port and returns a procedure that can be used to
9+
stop the server and the port the server is listening on.
1010

1111
Each `tests.rkt` module must provide a function called `make-tests`
12-
that takes a port and returns a rackunit `test-suite`.
12+
that takes a port and a procedure that stops the server when called and
13+
returns a rackunit `test-suite`.

web-server-test/tests/web-server/e2e/all-e2e-tests.rkt

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,39 @@
11
#lang racket/base
22

33
(require racket/path
4-
racket/tcp
54
rackunit)
65

76
(provide all-e2e-tests)
87

9-
(define here
10-
(simplify-path
11-
(build-path (syntax-source #'here) 'up)))
12-
13-
(define (wait-for-local-port port)
14-
(let loop ([attempts 1])
15-
(sync (system-idle-evt))
16-
(with-handlers ([exn:fail?
17-
(lambda (e)
18-
(if (> attempts 99)
19-
(raise e)
20-
(loop (add1 attempts))))])
21-
(define-values (in out)
22-
(tcp-connect "127.0.0.1" port))
23-
(close-output-port out)
24-
(close-input-port in))))
8+
(define here (path-only (syntax-source #'here)))
259

2610
(define all-e2e-tests
2711
(make-test-suite
2812
"e2e"
2913

3014
(for/list ([test-path (in-list (directory-list here))]
31-
[port (in-naturals 9111)]
3215
#:when (directory-exists? test-path)
3316
#:unless (equal? #"compiled" (path->bytes test-path)))
3417
(define server-mod-path (build-path test-path "server.rkt"))
3518
(define tests-mod-path (build-path test-path "tests.rkt"))
36-
(define stopper #f)
19+
(define stop-box (box void))
20+
(define port-box (box #f))
3721
(make-test-suite
3822
(path->string (file-name-from-path test-path))
3923
#:before
40-
(lambda _
41-
(define start
42-
(dynamic-require server-mod-path 'start))
43-
44-
(set! stopper (start port))
45-
(wait-for-local-port port))
24+
(lambda ()
25+
(define start (dynamic-require server-mod-path 'start))
26+
(let-values ([(stop port) (start)])
27+
(set-box! stop-box stop)
28+
(set-box! port-box port)))
4629
#:after
47-
(lambda _
48-
(stopper))
49-
30+
(lambda ()
31+
((unbox stop-box)))
5032
(let ([make-tests (dynamic-require tests-mod-path 'make-tests)])
51-
(list (make-tests port)))))))
33+
(list
34+
(make-tests
35+
(λ () (unbox port-box))
36+
(λ () (unbox stop-box)))))))))
5237

5338
(module+ test
5439
(require rackunit/text-ui)

web-server-test/tests/web-server/e2e/echo/server.rkt

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#lang racket/base
22

3-
(require web-server/servlet
3+
(require racket/async-channel
4+
web-server/servlet
45
web-server/servlet-dispatch
56
web-server/web-server)
67

@@ -15,7 +16,16 @@
1516
(lambda (out)
1617
(display msg out))))
1718

18-
(define (start port)
19-
(serve
20-
#:port port
21-
#:dispatch (dispatch/servlet echo)))
19+
(define (start)
20+
(define confirmation-ch
21+
(make-async-channel))
22+
(define stop
23+
(serve
24+
#:port 0
25+
#:dispatch (dispatch/servlet echo)
26+
#:confirmation-channel confirmation-ch))
27+
(define port-or-exn
28+
(sync confirmation-ch))
29+
(when (exn:fail? port-or-exn)
30+
(raise port-or-exn))
31+
(values stop port-or-exn))

web-server-test/tests/web-server/e2e/echo/tests.rkt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
(define get-response
1010
(compose1 port->string get-pure-port string->url))
1111

12-
(define (make-tests port)
12+
(define (make-tests get-port _get-stop)
1313
(define (make-uri [path "/"])
14-
(format "http://127.0.0.1:~a/~a" port path))
14+
(format "http://127.0.0.1:~a/~a" (get-port) path))
1515

1616
(test-suite
1717
"echo"
Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#lang racket/base
22

33
(require openssl/sha1
4+
racket/async-channel
45
racket/port
6+
web-server/safety-limits
57
web-server/servlet
68
web-server/servlet-dispatch
7-
web-server/safety-limits
89
web-server/web-server)
910

1011
(provide start)
@@ -18,15 +19,24 @@
1819
(for ([h (in-list hashes)])
1920
(displayln h out)))))
2021

21-
(define (start port)
22+
(define (start)
2223
;; We're testing file limits and those end up raising exceptions in
2324
;; the request-handling threads which get reported to stderr so we
2425
;; need to drop those messages in order for drdr not to fail.
2526
(parameterize ([current-error-port (open-output-nowhere)])
26-
(serve
27-
#:port port
28-
#:dispatch (dispatch/servlet file-upload)
29-
#:safety-limits (make-safety-limits
30-
#:max-form-data-files 2
31-
#:max-form-data-file-length 500
32-
#:form-data-file-memory-threshold 250))))
27+
(define confirmation-ch
28+
(make-async-channel))
29+
(define stop
30+
(serve
31+
#:port 0
32+
#:dispatch (dispatch/servlet file-upload)
33+
#:confirmation-channel confirmation-ch
34+
#:safety-limits (make-safety-limits
35+
#:max-form-data-files 2
36+
#:max-form-data-file-length 500
37+
#:form-data-file-memory-threshold 250)))
38+
(define port-or-exn
39+
(sync confirmation-ch))
40+
(when (exn:fail? port-or-exn)
41+
(raise port-or-exn))
42+
(values stop port-or-exn)))

web-server-test/tests/web-server/e2e/file-upload/tests.rkt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
#lang racket/base
22

3-
(require net/url
4-
openssl/sha1
5-
racket/list
3+
(require openssl/sha1
64
racket/port
5+
racket/random
76
racket/string
87
racket/tcp
9-
racket/random
108
rackunit)
119

1210
(provide make-tests)
1311

14-
(define (make-tests port)
12+
(define (make-tests get-port _get-stop)
1513
(define (upload-files . ins)
1614
(define-values (in out)
17-
(tcp-connect "127.0.0.1" port))
15+
(tcp-connect "127.0.0.1" (get-port)))
1816

1917
(define boundary
2018
(sha1-bytes (open-input-bytes (crypto-random-bytes 32))))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#lang racket/base
2+
3+
(require racket/async-channel
4+
web-server/safety-limits
5+
web-server/servlet
6+
web-server/servlet-dispatch
7+
web-server/web-server)
8+
9+
(provide start)
10+
11+
(define (app _req)
12+
(response/output
13+
(lambda (out)
14+
(for ([idx (in-range 5)])
15+
(displayln idx out)
16+
(sleep 1)))))
17+
18+
(define (start)
19+
(define confirmation-ch
20+
(make-async-channel))
21+
(define stop
22+
(serve
23+
#:port 0
24+
#:dispatch (dispatch/servlet app)
25+
#:confirmation-channel confirmation-ch
26+
#:safety-limits
27+
(make-safety-limits
28+
#:max-concurrent 1
29+
#:max-waiting 10
30+
#:shutdown-grace-period 6)))
31+
(define port-or-exn
32+
(sync confirmation-ch))
33+
(when (exn:fail? port-or-exn)
34+
(raise port-or-exn))
35+
(values stop port-or-exn))
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#lang racket/base
2+
3+
(require net/http-client
4+
racket/port
5+
rackunit)
6+
7+
(provide make-tests)
8+
9+
(define (make-tests get-port get-stop)
10+
(test-suite
11+
"graceful-shutdown"
12+
13+
(test-case "waits for in-progress connections on stop"
14+
(define hc (http-conn-open "127.0.0.1" #:port (get-port)))
15+
(define-values (status _headers in)
16+
(http-conn-sendrecv! hc "/"))
17+
((get-stop))
18+
(check-equal? status #"HTTP/1.1 200 OK")
19+
(check-equal? (port->bytes in) #"0\n1\n2\n3\n4\n"))))
Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
#lang racket/base
22

3-
(require web-server/servlet
3+
(require racket/async-channel
4+
web-server/servlet
45
web-server/servlet-dispatch
56
web-server/web-server)
67

78
(provide start)
89

9-
(define (start port)
10-
(serve
11-
#:port port
12-
#:dispatch (dispatch/servlet
13-
(lambda (_req)
14-
(response/output
15-
#:headers (list (make-header #"X-Example" #"Found"))
16-
(lambda (out)
17-
(displayln "hello" out)))))))
10+
(define (start)
11+
(define confirmation-ch
12+
(make-async-channel))
13+
(define stop
14+
(serve
15+
#:port 0
16+
#:dispatch (dispatch/servlet
17+
(lambda (_req)
18+
(response/output
19+
#:headers (list (make-header #"X-Example" #"Found"))
20+
(lambda (out)
21+
(displayln "hello" out)))))
22+
#:confirmation-channel confirmation-ch))
23+
(define port-or-exn
24+
(sync confirmation-ch))
25+
(when (exn:fail? port-or-exn)
26+
(raise port-or-exn))
27+
(values stop port-or-exn))

web-server-test/tests/web-server/e2e/head/tests.rkt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
(provide make-tests)
77

8-
(define (make-tests port)
8+
(define (make-tests get-port _get-stop)
99
;; net/url and net/http-client both ignore the body of HEAD requests
1010
;; if present so we can't use them to test this.
1111
(define (request method path)
1212
(define-values (in out)
13-
(tcp-connect "127.0.0.1" port))
13+
(tcp-connect "127.0.0.1" (get-port)))
1414

1515
(display (format "~a ~a HTTP/1.1\r\n" method path) out)
1616
(display "\r\n" out)

0 commit comments

Comments
 (0)