summaryrefslogtreecommitdiff
path: root/tests/testlib.scm
blob: 33ac2e210a6281f9252cb83e2fc313b2eaaa72ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
;; http-client test library.  This adds some helpers for setting up
;; fake connections and logging the requests and responses.

;; TODO: Test HTTPS somehow?

(import test uri-common intarweb srfi-1 srfi-18 (chicken tcp)
        (chicken string) (chicken io) (chicken file) (chicken format))

;; From intarweb
(define-syntax test-error*
  (syntax-rules ()
    ((_ ?msg (?error-type ...) ?expr)
     (let-syntax ((expression:
                   (syntax-rules ()
                     ((_ ?expr)
                      (condition-case (begin ?expr "<no error thrown>")
                                      ((?error-type ...) '(?error-type ...))
                                      (exn () (##sys#slot exn 1)))))))
       (test ?msg '(?error-type ...) (expression: ?expr))))
    ((_ ?msg ?error-type ?expr)
     (test-error* ?msg (?error-type) ?expr))
    ((_ ?error-type ?expr)
     (test-error* (sprintf "~S" '?expr) ?error-type ?expr))))

(define-record log request body)

(define server-port #f)

(server-connector (lambda (uri proxy)
                    (tcp-connect "localhost" server-port)) )

;; These need to be reasonably high to avoid lots of errors on slow
;; VMs and some OSes (FreeBSD in particular?), see also Salmonella.
;; At least 100 seems to be too low, so we aim high and set it to 500.
(tcp-read-timeout 500)
(tcp-write-timeout 500)

;; Set up a number of fake connections to a "server", with predefined
;; responses for each (expected) request.
(define (with-server-responses thunk . responses)
  (let* ((response-count (length responses))
         (logs '())
         (listener (tcp-listen 0 0 "localhost"))
         (server-thread
          (thread-start!
           (make-thread
            (lambda ()
              (let lp ()
                (if (null? responses)
                    (tcp-close listener)
                    (receive (in out) (tcp-accept listener)
                      (let* ((req (read-request in))
                             (h (request-headers req))
                             (log (make-log req #f))
                             (response (car responses)))

                        (when ((request-has-message-body?) req)
                          (let* ((len (header-value 'content-length h))
                                 (body (read-string len (request-port req))))
                            (log-body-set! log body)))
                        (set! logs (cons log logs))
                        (set! responses (cdr responses))
                        (display response out)
                        (close-output-port out)
                        (lp))))))
            'server-thread))))

    (set! server-port (tcp-listener-port listener))

    ;; TODO: Figure out how to ensure connections get closed correctly
    (dynamic-wind
        void
        thunk
        (lambda ()
          (handle-exceptions exn (thread-terminate! server-thread)
            ;; To close idle connections here to catch a regression
            ;; where we would loop endlessly...
            (close-idle-connections!)
            (thread-join! server-thread 0)) ))
    
    ;; Return the accumulated logs if all went well
    (if (not (= (length logs) response-count))
        (error (sprintf "Not enough requests.  Expected ~A responses, but logged ~A requests!" response-count (length logs)))
        (reverse logs)) ))

(define (with-server-response thunk response)
  (car (with-server-responses thunk response)))