blob: 488b0234c1ea1ffdbeba33349ea36b3ccde43b41 (
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
|
#!/usr/bin/env bats
#
# Tests for claude-templates/.ai/scripts/task-review-staleness.sh —
# counts top-level todo.org tasks whose review has gone stale.
#
# Strategy: write a synthetic todo.org into a temp dir per test, with
# LAST_REVIEWED dates generated relative to the real `date` (never
# hardcoded). Run the real script against it and assert the count it
# prints on stdout.
#
# Staleness rule under test:
# - A qualifying task is a depth-2 (**) heading with a TODO/DOING/VERIFY
# keyword and an [#A]/[#B]/[#C] priority cookie.
# - It is stale when LAST_REVIEWED is missing/malformed (NIL → oldest),
# or when its age strictly exceeds the threshold (age > N days).
# - age == N exactly is fresh (the spec's wording is ">N days").
# The script under test is always the sibling-of-parent of this test file
# (scripts/task-review-staleness.sh next to scripts/tests/). This holds in
# both the canonical claude-templates/ tree and the rsync'd project mirror,
# so the suite runs identically from either location.
SCRIPT="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)/task-review-staleness.sh"
setup() {
TEST_DIR="$(mktemp -d -t task-review-bats.XXXXXX)"
TODO="$TEST_DIR/todo.org"
TODAY="$(date +%F)"
D5="$(date -d '5 days ago' +%F)"
D30="$(date -d '30 days ago' +%F)"
D31="$(date -d '31 days ago' +%F)"
D40="$(date -d '40 days ago' +%F)"
}
teardown() {
rm -rf "$TEST_DIR"
}
# Emit a qualifying task with an explicit LAST_REVIEWED date.
task_reviewed() {
local keyword="$1" prio="$2" title="$3" date="$4"
printf '** %s [#%s] %s\n:PROPERTIES:\n:LAST_REVIEWED: %s\n:END:\nBody.\n\n' \
"$keyword" "$prio" "$title" "$date" >> "$TODO"
}
# Emit a qualifying task with no PROPERTIES drawer at all.
task_unreviewed() {
local keyword="$1" prio="$2" title="$3"
printf '** %s [#%s] %s\nBody.\n\n' "$keyword" "$prio" "$title" >> "$TODO"
}
# ---- Normal cases ----------------------------------------------------
@test "staleness: empty file reports zero" {
: > "$TODO"
run bash "$SCRIPT" "$TODO" 30
[ "$status" -eq 0 ]
[ "$output" = "0" ]
}
@test "staleness: all tasks fresh reports zero" {
task_reviewed TODO A "Fresh one" "$D5"
task_reviewed TODO B "Fresh two" "$D5"
task_reviewed DOING A "Fresh three" "$TODAY"
run bash "$SCRIPT" "$TODO" 30
[ "$status" -eq 0 ]
[ "$output" = "0" ]
}
@test "staleness: all tasks stale reports full count" {
task_reviewed TODO A "Stale one" "$D40"
task_reviewed TODO B "Stale two" "$D40"
task_reviewed VERIFY C "Stale three" "$D40"
run bash "$SCRIPT" "$TODO" 30
[ "$status" -eq 0 ]
[ "$output" = "3" ]
}
@test "staleness: mixed fresh, stale, and unreviewed counts only the latter two" {
task_reviewed TODO A "Fresh" "$D5"
task_reviewed TODO B "Stale" "$D40"
task_unreviewed DOING A "Never reviewed"
run bash "$SCRIPT" "$TODO" 30
[ "$status" -eq 0 ]
[ "$output" = "2" ]
}
# ---- Boundary cases --------------------------------------------------
@test "staleness: age exactly equal to threshold is fresh" {
task_reviewed TODO A "Exactly at cutoff" "$D30"
run bash "$SCRIPT" "$TODO" 30
[ "$status" -eq 0 ]
[ "$output" = "0" ]
}
@test "staleness: age one day past threshold is stale" {
task_reviewed TODO A "One day over" "$D31"
run bash "$SCRIPT" "$TODO" 30
[ "$status" -eq 0 ]
[ "$output" = "1" ]
}
@test "staleness: unreviewed task (no drawer) counts as stale" {
task_unreviewed TODO A "Never reviewed"
run bash "$SCRIPT" "$TODO" 30
[ "$status" -eq 0 ]
[ "$output" = "1" ]
}
@test "staleness: threshold of 7 is softer than 30 on the same list" {
task_reviewed TODO A "Reviewed five days ago" "$D5"
task_reviewed TODO B "Reviewed thirty-one days ago" "$D31"
run bash "$SCRIPT" "$TODO" 7
[ "$status" -eq 0 ]
[ "$output" = "1" ]
}
# ---- Error / exclusion cases -----------------------------------------
@test "staleness: DONE and CANCELLED tasks are excluded even when old" {
task_reviewed DONE A "Shipped long ago" "$D40"
task_reviewed CANCELLED B "Abandoned long ago" "$D40"
run bash "$SCRIPT" "$TODO" 30
[ "$status" -eq 0 ]
[ "$output" = "0" ]
}
@test "staleness: deeper headings and cookie-less headings are excluded" {
# Depth-3 child with an old review date — not a review unit.
printf '*** TODO [#A] Child task\n:PROPERTIES:\n:LAST_REVIEWED: %s\n:END:\n\n' "$D40" >> "$TODO"
# Depth-2 but no priority cookie — not a review unit.
printf '** TODO Cookie-less task\nBody.\n\n' >> "$TODO"
run bash "$SCRIPT" "$TODO" 30
[ "$status" -eq 0 ]
[ "$output" = "0" ]
}
@test "staleness: malformed LAST_REVIEWED is treated as stale" {
task_reviewed TODO A "Bad date" "not-a-date"
run bash "$SCRIPT" "$TODO" 30
[ "$status" -eq 0 ]
[ "$output" = "1" ]
}
@test "staleness: missing todo file exits non-zero" {
run bash "$SCRIPT" "$TEST_DIR/does-not-exist.org" 30
[ "$status" -ne 0 ]
}
# ---- --list mode -----------------------------------------------------
@test "staleness --list: orders oldest-reviewed first, unreviewed before dated" {
task_reviewed TODO A "Reviewed recently" "$D5"
task_reviewed TODO B "Reviewed long ago" "$D40"
task_unreviewed DOING A "Never reviewed"
run bash "$SCRIPT" --list "$TODO" 10
[ "$status" -eq 0 ]
[[ "${lines[0]}" == *"Never reviewed"* ]]
[[ "${lines[1]}" == *"Reviewed long ago"* ]]
[[ "${lines[2]}" == *"Reviewed recently"* ]]
}
@test "staleness --list: takes only the requested count" {
task_unreviewed TODO A "First"
task_reviewed TODO B "Second" "$D40"
task_reviewed TODO C "Third" "$D30"
run bash "$SCRIPT" --list "$TODO" 2
[ "$status" -eq 0 ]
[ "${#lines[@]}" -eq 2 ]
}
@test "staleness --list: count larger than available returns all candidates" {
task_unreviewed TODO A "Only one"
run bash "$SCRIPT" --list "$TODO" 10
[ "$status" -eq 0 ]
[ "${#lines[@]}" -eq 1 ]
}
@test "staleness --list: excludes DONE, deeper headings, and cookie-less headings" {
task_reviewed DONE A "Shipped" "$D40"
printf '*** TODO [#A] Child task\n\n' >> "$TODO"
printf '** TODO Cookie-less\n\n' >> "$TODO"
task_unreviewed TODO B "Real one"
run bash "$SCRIPT" --list "$TODO" 10
[ "$status" -eq 0 ]
[ "${#lines[@]}" -eq 1 ]
[[ "${lines[0]}" == *"Real one"* ]]
}
@test "staleness --list: emits line, review value, and heading tab-separated" {
task_unreviewed TODO A "Never reviewed"
run bash "$SCRIPT" --list "$TODO" 10
[ "$status" -eq 0 ]
line_no=$(printf '%s' "${lines[0]}" | cut -f1)
value=$(printf '%s' "${lines[0]}" | cut -f2)
heading=$(printf '%s' "${lines[0]}" | cut -f3)
[ "$line_no" = "1" ]
[ "$value" = "NONE" ]
[ "$heading" = "** TODO [#A] Never reviewed" ]
}
@test "staleness --list: empty file produces no output" {
: > "$TODO"
run bash "$SCRIPT" --list "$TODO" 10
[ "$status" -eq 0 ]
[ -z "$output" ]
}
@test "staleness --list: missing file exits non-zero" {
run bash "$SCRIPT" --list "$TEST_DIR/nope.org" 10
[ "$status" -ne 0 ]
}
|