AtlatestRepositorycourier

courier / tree / testtest-poller.sgl

1(import (sigil test)
2 (sigil string)
3 (courier poller))
4
5;; ============================================================
6;; Poller event wire format (child stdout protocol)
7;; ============================================================
8
9(test-group "make-poller-message-event"
10 (test "message event round-trips through JSON"
11 (let ((event (parse-poller-event
12 (make-poller-message-event
13 "hello leader" "daviwil" "12345" "67890"))))
14 (assert-equal "message" (dict-ref event type:))
15 (assert-equal "hello leader" (dict-ref event text:))
16 (assert-equal "daviwil" (dict-ref event sender:))
17 (assert-equal "12345" (dict-ref event sender_id:))
18 (assert-equal "67890" (dict-ref event chat_id:))))
20 (test "event is a single line"
21 ;; The stdout protocol is newline-delimited; an embedded newline
22 ;; in the JSON itself would split one event into two bad lines.
23 (let ((json (make-poller-message-event "a" "b" "c" "d")))
24 (assert-false (string-contains? json "\n"))))
26 (test "message text with newlines stays one line"
27 ;; Newlines inside the text must be escaped by json-encode, not
28 ;; emitted literally.
29 (let* ((json (make-poller-message-event "line1\nline2" "s" "1" "2"))
30 (event (parse-poller-event json)))
31 (assert-false (string-contains? json "\n"))
32 (assert-equal "line1\nline2" (dict-ref event text:)))))
34(test-group "parse-poller-event"
35 (test "heartbeat event parses"
36 (let ((event (parse-poller-event "{\"type\":\"heartbeat\"}")))
37 (assert-equal "heartbeat" (dict-ref event type:))))
39 (test "hello event carries pid"
40 (let ((event (parse-poller-event "{\"type\":\"hello\",\"pid\":4242}")))
41 (assert-equal "hello" (dict-ref event type:))
42 (assert-equal 4242 (dict-ref event pid:))))
44 (test "garbage line returns #f instead of raising"
45 ;; A corrupt child line must never crash the supervisor's
46 ;; event loop.
47 (assert-false (parse-poller-event "not json at all"))
48 (assert-false (parse-poller-event "")))
50 (test "non-object JSON returns #f"
51 (assert-false (parse-poller-event "[1,2,3]"))
52 (assert-false (parse-poller-event "42"))))
54;; ============================================================
55;; Watchdog staleness decision
56;; ============================================================
58(test-group "poller-stale?"
59 (test "fresh output is not stale"
60 (assert-false (poller-stale? 100 99)))
62 (test "output at the threshold is not stale"
63 ;; Threshold is 30s; exactly 30s of silence is still alive
64 ;; (heartbeat cadence is ~2s, so a healthy child never gets here)
65 (assert-false (poller-stale? 130 100)))
67 (test "silence past the threshold is stale"
68 (assert-true (poller-stale? 131 100))))
70;; ============================================================
71;; Supervisor restart backoff
72;; ============================================================
74(test-group "poller-next-restart-delay"
75 (test "crash-looping doubles the delay"
76 (assert-equal 2 (poller-next-restart-delay 1 0))
77 (assert-equal 4 (poller-next-restart-delay 2 5)))
79 (test "delay is capped"
80 (assert-equal 60 (poller-next-restart-delay 60 0))
81 (assert-equal 60 (poller-next-restart-delay 40 0)))
83 (test "healthy uptime resets the backoff"
84 (assert-equal 1 (poller-next-restart-delay 60 61))
85 (assert-equal 1 (poller-next-restart-delay 8 3600))))
87(run-tests)