aboutsummaryrefslogtreecommitdiffstats
path: root/mail-overview/test-mail-notify.sh
blob: 9fc21a9a7eadcc9036ae816d7f44c85a598f5eb9 (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
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/bin/bash
#
# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The one runnable check for mail-notify.sh. No framework: it sources the
# script as a library and asserts on the pure functions, the ones that take
# text and print text. Nothing here calls notmuch, dunstify or inotify, which
# is why it runs in milliseconds and needs no mail to arrive.
#
# Usage: ./test-mail-notify.sh   (exit 0 = all passed)

set -u

MAIL_NOTIFY_LIB=1 . "$(dirname "$0")/mail-notify.sh"

pass=0
fail=0

# Compares two strings and reports. Multi-line values are printed with their
# newlines intact, because half the assertions here are about line structure.
check() {
    local name="$1" want="$2" got="$3"
    if [[ "$want" == "$got" ]]; then
        pass=$((pass + 1))
    else
        fail=$((fail + 1))
        printf 'FAIL: %s\n  want: %s\n  got:  %s\n' "$name" "$want" "$got"
    fi
}

check "library guard does not run main" "loaded" "loaded"

# A config with the two traps in it: a key containing dots, and a folder
# value containing a bracket. Both are real shapes from qtmaildir.conf, with
# placeholder names.
read -r -d '' fixture <<'EOF'
[general]
theme = dark

[account.simple]
label = Simple
color = #112233

[account.provider-first.last]
folder = [Gmail]/Bozze
label = Dotted
color = #445566

[account.nolabel]
color = #778899

[ui]
label = NotAnAccount
EOF

check "keys run to the bracket, not the first dot" \
    "simple provider-first.last nolabel" \
    "$(parse_accounts <<<"$fixture" | cut -f1 | tr '\n' ' ' | sed 's/ $//')"

check "a bracket in a value does not end the section" \
    "Dotted" \
    "$(parse_accounts <<<"$fixture" | awk -F'\t' '$1=="provider-first.last"{print $2}')"

check "a missing label falls back to the key" \
    "nolabel" \
    "$(parse_accounts <<<"$fixture" | awk -F'\t' '$1=="nolabel"{print $2}')"

check "a non-account section is not an account" \
    "" \
    "$(parse_accounts <<<"$fixture" | awk -F'\t' '$1=="ui"{print $2}')"

# The shape notmuch search --format=json actually returns, trimmed to the two
# fields the body uses.
rows_json='[
  {"authors":"Alice Example","subject":"First subject"},
  {"authors":"Bob Example","subject":"Second subject"},
  {"authors":"Carol Example","subject":"Third subject"}
]'

check "a body lists author and subject per row" \
    "• Alice Example — First subject

• Bob Example — Second subject

• Carol Example — Third subject" \
    "$(build_body "$rows_json" 3)"

check "a batch bigger than the rows shown is elided" \
    "• Alice Example — First subject

• Bob Example — Second subject

• Carol Example — Third subject
+7 more" \
    "$(build_body "$rows_json" 10)"

check "markup characters are escaped, not rendered" \
    "• A &amp; B — &lt;script&gt;" \
    "$(build_body '[{"authors":"A & B","subject":"<script>"}]' 1)"

check "a missing subject says so rather than printing nothing" \
    "• Alice Example — (no subject)" \
    "$(build_body '[{"authors":"Alice Example","subject":null}]' 1)"

check "malformed json yields an empty body rather than an error" \
    "" \
    "$(build_body 'not json at all' 1 2>/dev/null)"

# A scratch state file, removed at exit. Never the real one: that is the
# user's live notification position, and a test must not move it.
tmpstate="$(mktemp)"
trap 'rm -f "$tmpstate"' EXIT
STATE="$tmpstate"

rm -f "$tmpstate"
check "a missing state file reports no previous revision" \
    "" "$(read_prev_rev "uuid-a")"

write_state "uuid-a" 500
check "a revision written is a revision read back" \
    "500" "$(read_prev_rev "uuid-a")"

check "a different database UUID discards the revision" \
    "" "$(read_prev_rev "uuid-b")"

printf 'uuid-a garbage\n' > "$tmpstate"
check "an unparseable state file reports no previous revision" \
    "" "$(read_prev_rev "uuid-a")"

write_state "uuid-a" 600
check "a rewrite replaces rather than appends" \
    "1" "$(wc -l < "$tmpstate")"

printf '\n%d passed, %d failed\n' "$pass" "$fail"
[[ "$fail" -eq 0 ]]