aboutsummaryrefslogtreecommitdiff
path: root/tests/test-telega-config--server-death.el
blob: 1151fa7e51129b3103a24b0719d98d5157a81e61 (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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
;;; test-telega-config--server-death.el --- Tests for the telega-server death alert -*- lexical-binding: t; -*-

;;; Commentary:
;; Tests for the telega-server death sentinel.  telega's own sentinel only
;; calls `message' on an abnormal exit, which scrolls away unseen -- so a
;; dead server reads as a quiet Telegram.  These pin the alert that replaces
;; that silence.

;;; Code:

(require 'ert)
(require 'cl-lib)

(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
(require 'telega-config)

;; -- cj/--telega-server-death-p ----------------------------------------------

(ert-deftest test-telega-config-death-p-nonzero-status-is-death ()
  "Normal: a non-zero exit status is an abnormal death."
  (should (cj/--telega-server-death-p 1))
  (should (cj/--telega-server-death-p 139)))

(ert-deftest test-telega-config-death-p-segv-signal-is-death ()
  "Normal: a signal number (SIGSEGV is 11) counts as death.
This is the observed failure -- the server segfaults rather than exiting."
  (should (cj/--telega-server-death-p 11)))

(ert-deftest test-telega-config-death-p-zero-is-clean-exit ()
  "Boundary: exit status 0 is a clean shutdown and must not alert.
Quitting telega deliberately would otherwise page on every exit."
  (should-not (cj/--telega-server-death-p 0)))

(ert-deftest test-telega-config-death-p-nil-is-not-death ()
  "Boundary: nil status (no status available) is not a death."
  (should-not (cj/--telega-server-death-p nil)))

(ert-deftest test-telega-config-death-p-non-integer-is-not-death ()
  "Error: a non-integer status returns nil rather than signalling.
`process-exit-status' is documented to return an integer, but this runs
inside telega's sentinel where an error would disrupt telega's own cleanup."
  (should-not (cj/--telega-server-death-p "segfault"))
  (should-not (cj/--telega-server-death-p 'exited)))

;; -- cj/--telega-server-exit-status ------------------------------------------

(ert-deftest test-telega-config-exit-status-nil-for-non-process ()
  "Error: a nil or non-process argument yields nil, not a signal.
The sentinel hands us whatever it has; a bad object must not break telega."
  (should-not (cj/--telega-server-exit-status nil))
  (should-not (cj/--telega-server-exit-status "not-a-process")))

;; -- cj/--telega-server-death-body -------------------------------------------

(ert-deftest test-telega-config-death-body-names-status-and-effect ()
  "Normal: the body carries the status and says coverage is affected.
The status distinguishes a segfault from a clean kill; the effect is why
the reader should care."
  (let ((body (cj/--telega-server-death-body 11 "segmentation fault\n")))
    (should (string-match-p "11" body))
    (should (string-match-p "Telegram" body))))

(ert-deftest test-telega-config-death-body-strips-trailing-newline ()
  "Boundary: a sentinel event string ends in a newline; it must not survive.
A trailing newline in a desktop notification body renders as dead space."
  (let ((body (cj/--telega-server-death-body 11 "segmentation fault\n")))
    (should-not (string-suffix-p "\n" body))))

(ert-deftest test-telega-config-death-body-handles-empty-event ()
  "Boundary: an empty or nil event still produces a usable body."
  (should (stringp (cj/--telega-server-death-body 11 nil)))
  (should (stringp (cj/--telega-server-death-body 11 "")))
  (should (string-match-p "11" (cj/--telega-server-death-body 11 nil))))

(ert-deftest test-telega-config-death-body-handles-unicode-event ()
  "Boundary: a non-ASCII event string passes through intact."
  (let ((body (cj/--telega-server-death-body 1 "ошибка сервера\n")))
    (should (string-match-p "ошибка" body))))

;; -- cj/--telega-server-notify-death (the advice) ----------------------------

(defmacro test-telega-config--with-status (status captured &rest body)
  "Run BODY with the exit status forced to STATUS.
Notification calls are captured into CAPTURED as (TITLE . BODY) pairs."
  (declare (indent 2))
  `(let ((,captured nil))
     (cl-letf (((symbol-function 'cj/--telega-server-exit-status)
                (lambda (&rest _) ,status))
               ((symbol-function 'cj/--telega-server-send-notification)
                (lambda (title body) (push (cons title body) ,captured))))
       ,@body)))

(ert-deftest test-telega-config-notify-fires-on-abnormal-exit ()
  "Normal: an abnormal exit sends exactly one notification."
  (test-telega-config--with-status 11 sent
    (cj/--telega-server-notify-death nil "segmentation fault\n")
    (should (= 1 (length sent)))
    (should (string-match-p "telega" (downcase (car (car sent)))))))

(ert-deftest test-telega-config-notify-silent-on-clean-exit ()
  "Boundary: a clean exit sends nothing.
Deliberately quitting telega must not page."
  (test-telega-config--with-status 0 sent
    (cj/--telega-server-notify-death nil "finished\n")
    (should-not sent)))

(ert-deftest test-telega-config-notify-silent-without-status ()
  "Boundary: no recoverable status sends nothing rather than a bare alert."
  (test-telega-config--with-status nil sent
    (cj/--telega-server-notify-death nil "gone\n")
    (should-not sent)))

(ert-deftest test-telega-config-notify-contains-notifier-errors ()
  "Error: a failing notifier must not escape into telega's sentinel.
This runs as :after advice on `telega-server--sentinel'; an error here
would abort telega's own status handling and its relogin path."
  (cl-letf (((symbol-function 'cj/--telega-server-exit-status)
             (lambda (&rest _) 11))
            ((symbol-function 'cj/--telega-server-send-notification)
             (lambda (&rest _) (error "notifier unavailable"))))
    (should (progn (cj/--telega-server-notify-death nil "segfault\n") t))))

(ert-deftest test-telega-config-notify-advice-is-named-and-removable ()
  "Normal: the sentinel advice is installed by name, so it can be removed.
An anonymous lambda can't be `advice-remove'd by reference, which strands
the old advice in a live daemon after the source stops installing it."
  (should (fboundp 'cj/--telega-server-notify-death))
  (should (symbolp 'cj/--telega-server-notify-death)))

(provide 'test-telega-config--server-death)
;;; test-telega-config--server-death.el ends here