AtlatestRepositorycourier

courier / tree / testtest-poller-offset.sgl

1(import (sigil test)
2 (sigil string)
3 (sigil fs)
4 (sigil path)
5 (sigil process)
6 (courier poller))
7
8;; ============================================================
9;; getUpdates offset persistence (the second-spam-cause fix)
10;; ============================================================
11;;
12;; The poller's getUpdates offset (tg-client last-update-id) must
13;; survive a process restart, or a fresh child starts at offset 0 and
14;; re-delivers Telegram's entire ~24h backlog -- the leader then acks
15;; each re-delivered message, an outbound storm. These tests cover the
16;; durable-offset primitives: round-trip, and the "cold start" signals
17;; (missing / non-positive / garbage file) that trigger a silent drain
18;; instead of delivering a stale backlog.
20;; Point the relay dir (and thus the offset file) at a fresh temp dir.
21;; offset-file-path is <dirname(relay-dir)>/telegram-offset.
22(define (with-temp-offset thunk)
23 (let ((tmp (make-temp-directory)))
24 (setenv! "COURIER_RELAY_DIR" (path-join tmp "relays"))
25 (let ((result (thunk tmp)))
26 (setenv! "COURIER_RELAY_DIR" "")
27 (guard (e (else #f)) (delete-directory tmp))
28 result)))
30(test-group "offset-file-path"
31 (test "sits beside the relay dir, not inside it"
32 (with-temp-offset
33 (lambda (tmp)
34 ;; dirname(<tmp>/relays) == <tmp>
35 (assert-equal (path-join tmp "telegram-offset")
36 (offset-file-path))))))
38(test-group "load-persisted-offset — cold-start signals"
39 (test "missing file -> #f (cold start: drain the backlog)"
40 (with-temp-offset
41 (lambda (tmp)
42 (assert-false (load-persisted-offset)))))
44 (test "garbage file -> #f (corrupt: treat as cold start)"
45 (with-temp-offset
46 (lambda (tmp)
47 (write-file-string (offset-file-path) "not-a-number")
48 (assert-false (load-persisted-offset)))))
50 (test "zero -> #f (offset 0 means no offset -> would re-fetch backlog)"
51 (with-temp-offset
52 (lambda (tmp)
53 (write-file-string (offset-file-path) "0")
54 (assert-false (load-persisted-offset)))))
56 (test "empty file -> #f"
57 (with-temp-offset
58 (lambda (tmp)
59 (write-file-string (offset-file-path) "")
60 (assert-false (load-persisted-offset))))))
62(test-group "save-offset! / load-persisted-offset round-trip"
63 (test "a saved positive offset reloads exactly"
64 (with-temp-offset
65 (lambda (tmp)
66 (save-offset! 42)
67 (assert-equal 42 (load-persisted-offset)))))
69 (test "re-saving overwrites (offset only advances forward)"
70 (with-temp-offset
71 (lambda (tmp)
72 (save-offset! 42)
73 (save-offset! 100)
74 (assert-equal 100 (load-persisted-offset)))))
76 (test "large update_id round-trips (no precision loss)"
77 (with-temp-offset
78 (lambda (tmp)
79 (save-offset! 999999999)
80 (assert-equal 999999999 (load-persisted-offset)))))
82 (test "trailing whitespace in the file is tolerated"
83 (with-temp-offset
84 (lambda (tmp)
85 (write-file-string (offset-file-path) "57\n")
86 (assert-equal 57 (load-persisted-offset))))))
88;; ============================================================
89;; Sentinel-zero hole (the 2026-07-02 live storm)
90;; ============================================================
91;;
92;; A count=0 cold-start drain leaves the offset at 0. Persisting 0 seeds
93;; Telegram's "no offset / send me everything" sentinel; a later start
94;; that trusts it re-fetches and re-delivers the whole backlog. So
95;; save-offset! must NEVER write a non-positive value, and must clear any
96;; existing file so the next start cold-drains instead.
98(test-group "save-offset! — never persists the sentinel 0"
99 (test "saving 0 writes NO file"
100 (with-temp-offset
101 (lambda (tmp)
102 (save-offset! 0)
103 (assert-false (file-exists? (offset-file-path)))
104 (assert-false (load-persisted-offset)))))
106 (test "saving 0 CLEARS a previously-persisted offset"
107 (with-temp-offset
108 (lambda (tmp)
109 (save-offset! 99)
110 (assert-equal 99 (load-persisted-offset))
111 ;; e.g. a later empty-backlog cold start
112 (save-offset! 0)
113 (assert-false (file-exists? (offset-file-path)))
114 (assert-false (load-persisted-offset)))))
116 (test "saving a negative value writes NO file"
117 (with-temp-offset
118 (lambda (tmp)
119 (save-offset! -3)
120 (assert-false (file-exists? (offset-file-path)))
121 (assert-false (load-persisted-offset)))))
123 (test "a positive offset still persists and reloads"
124 (with-temp-offset
125 (lambda (tmp)
126 (save-offset! 7)
127 (assert-true (file-exists? (offset-file-path)))
128 (assert-equal 7 (load-persisted-offset))))))
130(run-tests)