aboutsummaryrefslogtreecommitdiff
path: root/tests/test-slack-config--notify.el
blob: d830ae29df03c422da5a8693256470adf0a3b698 (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
;;; test-slack-config--notify.el --- Slack notification hardening tests -*- lexical-binding: t; -*-

;;; Commentary:
;; The config audit found `cj/slack-notify' missing signel's hardening: no
;; body truncation (giant toasts), no whitespace collapse, no sound gating,
;; and no `notifications-notify' fallback when the notify script is absent
;; (the raw `start-process' error was swallowed by the condition-case, so
;; the notification silently vanished).  This mirrors signel's shape in
;; place; the shared cj/messenger-notify extraction belongs to the
;; messenger-unification task.
;;
;; The slack package's own predicates (`slack-im-p', `slack-message-minep',
;; `slack-message-mentioned-p') are package boundaries and are mocked; the
;; formatter and routing logic run real.

;;; Code:

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

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

;;; ------------------------------ body formatter -----------------------------

(ert-deftest test-slack-config-format-notify-body-collapses-whitespace ()
  "Normal: whitespace runs (including newlines) become single spaces."
  (should (equal (cj/slack--format-notify-body "a  b\nc\t\td")
                 "a b c d")))

(ert-deftest test-slack-config-format-notify-body-truncates-long ()
  "Boundary: text over the max truncates to max length ending in an ellipsis."
  (let ((long (make-string 500 ?x)))
    (let ((formatted (cj/slack--format-notify-body long)))
      (should (= (length formatted) cj/slack--notify-body-max))
      (should (string-suffix-p "…" formatted)))))

(ert-deftest test-slack-config-format-notify-body-empty ()
  "Boundary: empty and whitespace-only input format to the empty string."
  (should (equal (cj/slack--format-notify-body "") ""))
  (should (equal (cj/slack--format-notify-body " \n\t ") "")))

;;; ----------------------------- delivery routing ----------------------------

(ert-deftest test-slack-config-send-notification-script-silent-by-default ()
  "Normal: with the notify script on PATH and sound off, --silent is passed."
  (let ((argv nil) (cj/slack-notify-sound nil))
    (cl-letf (((symbol-function 'executable-find)
               (lambda (prog &rest _) (when (equal prog "notify") "/bin/notify")))
              ((symbol-function 'start-process)
               (lambda (_name _buf &rest args) (setq argv args))))
      (cj/slack--send-notification "Slack: general" "hello"))
    (should (equal argv '("/bin/notify" "info" "Slack: general" "hello" "--silent")))))

(ert-deftest test-slack-config-send-notification-sound-enabled ()
  "Boundary: with sound enabled, --silent is not passed."
  (let ((argv nil) (cj/slack-notify-sound t))
    (cl-letf (((symbol-function 'executable-find)
               (lambda (prog &rest _) (when (equal prog "notify") "/bin/notify")))
              ((symbol-function 'start-process)
               (lambda (_name _buf &rest args) (setq argv args))))
      (cj/slack--send-notification "Slack: general" "hello"))
    (should (equal argv '("/bin/notify" "info" "Slack: general" "hello")))))

(ert-deftest test-slack-config-send-notification-fallback-without-script ()
  "Error: with no notify script, delivery falls back to notifications-notify."
  (let ((fallback nil))
    (cl-letf (((symbol-function 'executable-find) (lambda (&rest _) nil))
              ((symbol-function 'notifications-notify)
               (lambda (&rest args) (setq fallback args))))
      (cj/slack--send-notification "Slack: general" "hello"))
    (should (equal (plist-get fallback :title) "Slack: general"))
    (should (equal (plist-get fallback :body) "hello"))))

;;; --------------------------- predicate wiring ------------------------------

(defmacro test-slack-notify--with-message (minep im-p mentioned-p &rest body)
  "Run BODY with the slack package predicates mocked to the given values.
Also mocks room/body accessors and captures delivery into `sent'."
  (declare (indent 3))
  `(let ((sent nil))
     (cl-letf (((symbol-function 'slack-message-minep) (lambda (&rest _) ,minep))
               ((symbol-function 'slack-im-p) (lambda (&rest _) ,im-p))
               ((symbol-function 'slack-message-mentioned-p) (lambda (&rest _) ,mentioned-p))
               ((symbol-function 'slack-room-display-name) (lambda (&rest _) "general"))
               ((symbol-function 'slack-message-body) (lambda (&rest _) "the message"))
               ((symbol-function 'cj/slack--send-notification)
                (lambda (title body) (setq sent (list title body)))))
       (cj/slack-notify 'msg 'room 'team)
       ,@body)))

(ert-deftest test-slack-config-notify-dm-notifies ()
  "Normal: a DM from someone else raises a notification."
  (test-slack-notify--with-message nil t nil
    (should (equal sent '("Slack: general" "the message")))))

(ert-deftest test-slack-config-notify-mention-notifies ()
  "Normal: an @mention in a channel raises a notification."
  (test-slack-notify--with-message nil nil t
    (should (equal sent '("Slack: general" "the message")))))

(ert-deftest test-slack-config-notify-own-message-silent ()
  "Boundary: your own message never notifies, even in a DM."
  (test-slack-notify--with-message t t t
    (should-not sent)))

(ert-deftest test-slack-config-notify-plain-channel-silent ()
  "Boundary: a channel message with no mention stays silent."
  (test-slack-notify--with-message nil nil nil
    (should-not sent)))

(provide 'test-slack-config--notify)
;;; test-slack-config--notify.el ends here