opencode-pilot 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +8 -1
- package/.releaserc.cjs +10 -1
- package/AGENTS.md +6 -12
- package/README.md +31 -25
- package/bin/opencode-pilot +47 -209
- package/examples/config.yaml +2 -9
- package/package.json +6 -6
- package/plugin/index.js +45 -245
- package/service/{io.opencode.ntfy.plist → io.opencode.pilot.plist} +5 -5
- package/service/server.js +44 -1381
- package/test/run_tests.bash +1 -1
- package/test/test_actions.bash +21 -36
- package/test/test_cli.bash +20 -24
- package/test/test_plist.bash +11 -12
- package/test/test_poller.bash +20 -20
- package/test/test_repo_config.bash +19 -233
- package/test/test_service.bash +48 -1095
- package/test/unit/paths.test.js +16 -43
- package/test/unit/plugin.test.js +46 -0
- package/dist/opencode-ntfy.tar.gz +0 -0
- package/plugin/config.js +0 -76
- package/plugin/logger.js +0 -125
- package/plugin/notifier.js +0 -110
- package/test/test_config.bash +0 -438
- package/test/test_logger.bash +0 -401
- package/test/test_notifier.bash +0 -310
- package/test/test_plugin.bash +0 -952
- package/test/unit/config.test.js +0 -86
package/test/test_logger.bash
DELETED
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# Tests for logger.js - Optional debug logging module
|
|
4
|
-
# Writes to ~/.config/opencode-ntfy/debug.log when enabled
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
set -euo pipefail
|
|
8
|
-
|
|
9
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
-
source "$SCRIPT_DIR/test_helper.bash"
|
|
11
|
-
|
|
12
|
-
PLUGIN_DIR="$(dirname "$SCRIPT_DIR")/plugin"
|
|
13
|
-
|
|
14
|
-
echo "Testing logger.js module..."
|
|
15
|
-
echo ""
|
|
16
|
-
|
|
17
|
-
# =============================================================================
|
|
18
|
-
# File Structure Tests
|
|
19
|
-
# =============================================================================
|
|
20
|
-
|
|
21
|
-
test_logger_file_exists() {
|
|
22
|
-
assert_file_exists "$PLUGIN_DIR/logger.js"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
test_logger_js_syntax() {
|
|
26
|
-
if ! command -v node &>/dev/null; then
|
|
27
|
-
echo "SKIP: node not available"
|
|
28
|
-
return 0
|
|
29
|
-
fi
|
|
30
|
-
node --check "$PLUGIN_DIR/logger.js" 2>&1 || {
|
|
31
|
-
echo "logger.js has syntax errors"
|
|
32
|
-
return 1
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
# =============================================================================
|
|
37
|
-
# Export Tests
|
|
38
|
-
# =============================================================================
|
|
39
|
-
|
|
40
|
-
test_logger_exports_debug() {
|
|
41
|
-
grep -q "export.*function debug\|export.*debug" "$PLUGIN_DIR/logger.js" || {
|
|
42
|
-
echo "debug export not found in logger.js"
|
|
43
|
-
return 1
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
test_logger_exports_init_logger() {
|
|
48
|
-
grep -q "export.*function initLogger\|export.*initLogger" "$PLUGIN_DIR/logger.js" || {
|
|
49
|
-
echo "initLogger export not found in logger.js"
|
|
50
|
-
return 1
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
# =============================================================================
|
|
55
|
-
# Implementation Tests
|
|
56
|
-
# =============================================================================
|
|
57
|
-
|
|
58
|
-
test_logger_uses_append_file_sync() {
|
|
59
|
-
# Should use appendFileSync for simplicity (low volume writes)
|
|
60
|
-
grep -q "appendFileSync" "$PLUGIN_DIR/logger.js" || {
|
|
61
|
-
echo "appendFileSync not found in logger.js"
|
|
62
|
-
return 1
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
test_logger_uses_config_dir_by_default() {
|
|
67
|
-
# Default log path should be in ~/.config/opencode-ntfy/
|
|
68
|
-
grep -q "opencode-ntfy\|config.*debug\|debugPath" "$PLUGIN_DIR/logger.js" || {
|
|
69
|
-
echo "Config directory path not found in logger.js"
|
|
70
|
-
return 1
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
test_logger_includes_timestamp() {
|
|
75
|
-
# Log entries should include ISO 8601 timestamp
|
|
76
|
-
grep -q "toISOString\|ISO\|Date\|timestamp" "$PLUGIN_DIR/logger.js" || {
|
|
77
|
-
echo "Timestamp formatting not found in logger.js"
|
|
78
|
-
return 1
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
test_logger_has_rotation_logic() {
|
|
83
|
-
# Should have log rotation or size limiting
|
|
84
|
-
grep -q "rotate\|size\|maxSize\|MAX_SIZE\|truncate\|unlink" "$PLUGIN_DIR/logger.js" || {
|
|
85
|
-
echo "Log rotation logic not found in logger.js"
|
|
86
|
-
return 1
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
test_logger_checks_enabled_flag() {
|
|
91
|
-
# Should check if debug mode is enabled before writing
|
|
92
|
-
grep -q "enabled\|isEnabled\|debugEnabled" "$PLUGIN_DIR/logger.js" || {
|
|
93
|
-
echo "Enabled flag check not found in logger.js"
|
|
94
|
-
return 1
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
test_logger_handles_errors_silently() {
|
|
99
|
-
# Should catch errors to avoid crashing the plugin
|
|
100
|
-
grep -q "catch\|try" "$PLUGIN_DIR/logger.js" || {
|
|
101
|
-
echo "Error handling not found in logger.js"
|
|
102
|
-
return 1
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
test_logger_no_console_output() {
|
|
107
|
-
# Should not use console.log (interferes with TUI)
|
|
108
|
-
if grep -q 'console\.log\|console\.error\|console\.warn' "$PLUGIN_DIR/logger.js"; then
|
|
109
|
-
echo "Console output found - should be silent"
|
|
110
|
-
return 1
|
|
111
|
-
fi
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
test_logger_creates_directory_if_missing() {
|
|
115
|
-
# Should create log directory if it doesn't exist
|
|
116
|
-
grep -q "mkdirSync\|mkdir" "$PLUGIN_DIR/logger.js" || {
|
|
117
|
-
echo "Directory creation not found in logger.js"
|
|
118
|
-
return 1
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
# =============================================================================
|
|
123
|
-
# Functional Tests (requires Node.js)
|
|
124
|
-
# =============================================================================
|
|
125
|
-
|
|
126
|
-
test_logger_disabled_by_default() {
|
|
127
|
-
if ! command -v node &>/dev/null; then
|
|
128
|
-
echo "SKIP: node not available"
|
|
129
|
-
return 0
|
|
130
|
-
fi
|
|
131
|
-
|
|
132
|
-
local result
|
|
133
|
-
result=$(node --experimental-vm-modules -e "
|
|
134
|
-
import { debug, initLogger } from './plugin/logger.js';
|
|
135
|
-
|
|
136
|
-
// Initialize without debug enabled
|
|
137
|
-
initLogger({ debug: false });
|
|
138
|
-
|
|
139
|
-
// debug() should be a no-op when disabled
|
|
140
|
-
debug('test message');
|
|
141
|
-
|
|
142
|
-
console.log('PASS');
|
|
143
|
-
" 2>&1) || {
|
|
144
|
-
echo "Functional test failed: $result"
|
|
145
|
-
return 1
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
149
|
-
echo "$result"
|
|
150
|
-
return 1
|
|
151
|
-
fi
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
test_logger_writes_when_enabled() {
|
|
155
|
-
if ! command -v node &>/dev/null; then
|
|
156
|
-
echo "SKIP: node not available"
|
|
157
|
-
return 0
|
|
158
|
-
fi
|
|
159
|
-
|
|
160
|
-
local test_dir
|
|
161
|
-
test_dir=$(mktemp -d)
|
|
162
|
-
local log_file="$test_dir/debug.log"
|
|
163
|
-
|
|
164
|
-
local result
|
|
165
|
-
result=$(node --experimental-vm-modules -e "
|
|
166
|
-
import { debug, initLogger } from './plugin/logger.js';
|
|
167
|
-
import { existsSync, readFileSync } from 'fs';
|
|
168
|
-
|
|
169
|
-
// Initialize with debug enabled and custom path
|
|
170
|
-
initLogger({ debug: true, debugPath: '$log_file' });
|
|
171
|
-
|
|
172
|
-
// Write a test message
|
|
173
|
-
debug('test message');
|
|
174
|
-
|
|
175
|
-
// Verify file was created and contains message
|
|
176
|
-
if (!existsSync('$log_file')) {
|
|
177
|
-
console.log('FAIL: Log file not created');
|
|
178
|
-
process.exit(1);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const content = readFileSync('$log_file', 'utf8');
|
|
182
|
-
if (!content.includes('test message')) {
|
|
183
|
-
console.log('FAIL: Log file does not contain message');
|
|
184
|
-
console.log('Content:', content);
|
|
185
|
-
process.exit(1);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
console.log('PASS');
|
|
189
|
-
" 2>&1)
|
|
190
|
-
local exit_code=$?
|
|
191
|
-
|
|
192
|
-
# Cleanup
|
|
193
|
-
rm -rf "$test_dir"
|
|
194
|
-
|
|
195
|
-
if [[ $exit_code -ne 0 ]]; then
|
|
196
|
-
echo "Functional test failed: $result"
|
|
197
|
-
return 1
|
|
198
|
-
fi
|
|
199
|
-
|
|
200
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
201
|
-
echo "$result"
|
|
202
|
-
return 1
|
|
203
|
-
fi
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
test_logger_includes_timestamp_in_output() {
|
|
207
|
-
if ! command -v node &>/dev/null; then
|
|
208
|
-
echo "SKIP: node not available"
|
|
209
|
-
return 0
|
|
210
|
-
fi
|
|
211
|
-
|
|
212
|
-
local test_dir
|
|
213
|
-
test_dir=$(mktemp -d)
|
|
214
|
-
local log_file="$test_dir/debug.log"
|
|
215
|
-
|
|
216
|
-
local result
|
|
217
|
-
result=$(node --experimental-vm-modules -e "
|
|
218
|
-
import { debug, initLogger } from './plugin/logger.js';
|
|
219
|
-
import { readFileSync } from 'fs';
|
|
220
|
-
|
|
221
|
-
initLogger({ debug: true, debugPath: '$log_file' });
|
|
222
|
-
debug('test message');
|
|
223
|
-
|
|
224
|
-
const content = readFileSync('$log_file', 'utf8');
|
|
225
|
-
|
|
226
|
-
// Should have ISO 8601 timestamp format: [2025-01-02T12:00:00.000Z]
|
|
227
|
-
if (!/\\[\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(content)) {
|
|
228
|
-
console.log('FAIL: Timestamp not in expected format');
|
|
229
|
-
console.log('Content:', content);
|
|
230
|
-
process.exit(1);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
console.log('PASS');
|
|
234
|
-
" 2>&1)
|
|
235
|
-
local exit_code=$?
|
|
236
|
-
|
|
237
|
-
rm -rf "$test_dir"
|
|
238
|
-
|
|
239
|
-
if [[ $exit_code -ne 0 ]]; then
|
|
240
|
-
echo "Functional test failed: $result"
|
|
241
|
-
return 1
|
|
242
|
-
fi
|
|
243
|
-
|
|
244
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
245
|
-
echo "$result"
|
|
246
|
-
return 1
|
|
247
|
-
fi
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
test_logger_rotates_large_files() {
|
|
251
|
-
if ! command -v node &>/dev/null; then
|
|
252
|
-
echo "SKIP: node not available"
|
|
253
|
-
return 0
|
|
254
|
-
fi
|
|
255
|
-
|
|
256
|
-
local test_dir
|
|
257
|
-
test_dir=$(mktemp -d)
|
|
258
|
-
local log_file="$test_dir/debug.log"
|
|
259
|
-
|
|
260
|
-
local result
|
|
261
|
-
result=$(node --experimental-vm-modules -e "
|
|
262
|
-
import { debug, initLogger, MAX_LOG_SIZE } from './plugin/logger.js';
|
|
263
|
-
import { writeFileSync, statSync, existsSync } from 'fs';
|
|
264
|
-
|
|
265
|
-
initLogger({ debug: true, debugPath: '$log_file' });
|
|
266
|
-
|
|
267
|
-
// Create a file larger than MAX_LOG_SIZE
|
|
268
|
-
// Write more than the max size to trigger rotation
|
|
269
|
-
const largeContent = 'x'.repeat(MAX_LOG_SIZE + 1000);
|
|
270
|
-
writeFileSync('$log_file', largeContent);
|
|
271
|
-
|
|
272
|
-
// This write should trigger rotation
|
|
273
|
-
debug('after rotation');
|
|
274
|
-
|
|
275
|
-
const stats = statSync('$log_file');
|
|
276
|
-
|
|
277
|
-
// File should be smaller than the large content we wrote
|
|
278
|
-
// (rotation should have cleared/truncated it)
|
|
279
|
-
if (stats.size >= largeContent.length) {
|
|
280
|
-
console.log('FAIL: File was not rotated, size:', stats.size);
|
|
281
|
-
process.exit(1);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
console.log('PASS');
|
|
285
|
-
" 2>&1)
|
|
286
|
-
local exit_code=$?
|
|
287
|
-
|
|
288
|
-
rm -rf "$test_dir"
|
|
289
|
-
|
|
290
|
-
if [[ $exit_code -ne 0 ]]; then
|
|
291
|
-
echo "Functional test failed: $result"
|
|
292
|
-
return 1
|
|
293
|
-
fi
|
|
294
|
-
|
|
295
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
296
|
-
echo "$result"
|
|
297
|
-
return 1
|
|
298
|
-
fi
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
test_logger_respects_env_var() {
|
|
302
|
-
if ! command -v node &>/dev/null; then
|
|
303
|
-
echo "SKIP: node not available"
|
|
304
|
-
return 0
|
|
305
|
-
fi
|
|
306
|
-
|
|
307
|
-
local test_dir
|
|
308
|
-
test_dir=$(mktemp -d)
|
|
309
|
-
local log_file="$test_dir/debug.log"
|
|
310
|
-
|
|
311
|
-
local result
|
|
312
|
-
result=$(NTFY_DEBUG=true NTFY_DEBUG_PATH="$log_file" node --experimental-vm-modules -e "
|
|
313
|
-
import { debug, initLogger } from './plugin/logger.js';
|
|
314
|
-
import { existsSync, readFileSync } from 'fs';
|
|
315
|
-
|
|
316
|
-
// Initialize from environment (no explicit config)
|
|
317
|
-
initLogger({});
|
|
318
|
-
|
|
319
|
-
debug('env var test');
|
|
320
|
-
|
|
321
|
-
if (!existsSync('$log_file')) {
|
|
322
|
-
console.log('FAIL: Log file not created from env var');
|
|
323
|
-
process.exit(1);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const content = readFileSync('$log_file', 'utf8');
|
|
327
|
-
if (!content.includes('env var test')) {
|
|
328
|
-
console.log('FAIL: Message not written');
|
|
329
|
-
process.exit(1);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
console.log('PASS');
|
|
333
|
-
" 2>&1)
|
|
334
|
-
local exit_code=$?
|
|
335
|
-
|
|
336
|
-
rm -rf "$test_dir"
|
|
337
|
-
|
|
338
|
-
if [[ $exit_code -ne 0 ]]; then
|
|
339
|
-
echo "Functional test failed: $result"
|
|
340
|
-
return 1
|
|
341
|
-
fi
|
|
342
|
-
|
|
343
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
344
|
-
echo "$result"
|
|
345
|
-
return 1
|
|
346
|
-
fi
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
# =============================================================================
|
|
350
|
-
# Run Tests
|
|
351
|
-
# =============================================================================
|
|
352
|
-
|
|
353
|
-
echo "File Structure Tests:"
|
|
354
|
-
|
|
355
|
-
for test_func in \
|
|
356
|
-
test_logger_file_exists \
|
|
357
|
-
test_logger_js_syntax
|
|
358
|
-
do
|
|
359
|
-
run_test "${test_func#test_}" "$test_func"
|
|
360
|
-
done
|
|
361
|
-
|
|
362
|
-
echo ""
|
|
363
|
-
echo "Export Tests:"
|
|
364
|
-
|
|
365
|
-
for test_func in \
|
|
366
|
-
test_logger_exports_debug \
|
|
367
|
-
test_logger_exports_init_logger
|
|
368
|
-
do
|
|
369
|
-
run_test "${test_func#test_}" "$test_func"
|
|
370
|
-
done
|
|
371
|
-
|
|
372
|
-
echo ""
|
|
373
|
-
echo "Implementation Tests:"
|
|
374
|
-
|
|
375
|
-
for test_func in \
|
|
376
|
-
test_logger_uses_append_file_sync \
|
|
377
|
-
test_logger_uses_config_dir_by_default \
|
|
378
|
-
test_logger_includes_timestamp \
|
|
379
|
-
test_logger_has_rotation_logic \
|
|
380
|
-
test_logger_checks_enabled_flag \
|
|
381
|
-
test_logger_handles_errors_silently \
|
|
382
|
-
test_logger_no_console_output \
|
|
383
|
-
test_logger_creates_directory_if_missing
|
|
384
|
-
do
|
|
385
|
-
run_test "${test_func#test_}" "$test_func"
|
|
386
|
-
done
|
|
387
|
-
|
|
388
|
-
echo ""
|
|
389
|
-
echo "Functional Tests:"
|
|
390
|
-
|
|
391
|
-
for test_func in \
|
|
392
|
-
test_logger_disabled_by_default \
|
|
393
|
-
test_logger_writes_when_enabled \
|
|
394
|
-
test_logger_includes_timestamp_in_output \
|
|
395
|
-
test_logger_rotates_large_files \
|
|
396
|
-
test_logger_respects_env_var
|
|
397
|
-
do
|
|
398
|
-
run_test "${test_func#test_}" "$test_func"
|
|
399
|
-
done
|
|
400
|
-
|
|
401
|
-
print_summary
|
package/test/test_notifier.bash
DELETED
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# Tests for notifier.js - ntfy HTTP client module
|
|
4
|
-
# Issue #3: Notifier: ntfy HTTP client with all notification types
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
set -euo pipefail
|
|
8
|
-
|
|
9
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
-
source "$SCRIPT_DIR/test_helper.bash"
|
|
11
|
-
|
|
12
|
-
PLUGIN_DIR="$(dirname "$SCRIPT_DIR")/plugin"
|
|
13
|
-
|
|
14
|
-
echo "Testing notifier.js module..."
|
|
15
|
-
echo ""
|
|
16
|
-
|
|
17
|
-
# =============================================================================
|
|
18
|
-
# sendNotification Function Tests
|
|
19
|
-
# =============================================================================
|
|
20
|
-
|
|
21
|
-
test_notifier_exports_send_notification() {
|
|
22
|
-
grep -q "export.*function sendNotification" "$PLUGIN_DIR/notifier.js" || \
|
|
23
|
-
grep -q "export async function sendNotification" "$PLUGIN_DIR/notifier.js" || {
|
|
24
|
-
echo "sendNotification export not found in notifier.js"
|
|
25
|
-
return 1
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
test_send_notification_uses_fetch() {
|
|
30
|
-
# sendNotification should use native fetch() for HTTP requests
|
|
31
|
-
grep -q "fetch(" "$PLUGIN_DIR/notifier.js" || {
|
|
32
|
-
echo "fetch() not found in notifier.js - should use native fetch"
|
|
33
|
-
return 1
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
test_send_notification_posts_to_ntfy() {
|
|
38
|
-
# Should POST to the ntfy server URL
|
|
39
|
-
grep -q "method.*POST\|POST.*method" "$PLUGIN_DIR/notifier.js" || {
|
|
40
|
-
echo "POST method not found in notifier.js"
|
|
41
|
-
return 1
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
test_send_notification_sends_json() {
|
|
46
|
-
# Should send JSON content type
|
|
47
|
-
grep -q "application/json" "$PLUGIN_DIR/notifier.js" || {
|
|
48
|
-
echo "JSON content type not found in notifier.js"
|
|
49
|
-
return 1
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
test_send_notification_includes_topic() {
|
|
54
|
-
# Body should include topic
|
|
55
|
-
grep -q "topic" "$PLUGIN_DIR/notifier.js" || {
|
|
56
|
-
echo "topic not used in notifier.js"
|
|
57
|
-
return 1
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
test_send_notification_includes_title() {
|
|
62
|
-
# Body should include title
|
|
63
|
-
grep -q "title" "$PLUGIN_DIR/notifier.js" || {
|
|
64
|
-
echo "title not used in notifier.js"
|
|
65
|
-
return 1
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
test_send_notification_includes_message() {
|
|
70
|
-
# Body should include message
|
|
71
|
-
grep -q "message" "$PLUGIN_DIR/notifier.js" || {
|
|
72
|
-
echo "message not used in notifier.js"
|
|
73
|
-
return 1
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
test_send_notification_handles_optional_priority() {
|
|
78
|
-
# Priority is optional (1-5)
|
|
79
|
-
grep -q "priority" "$PLUGIN_DIR/notifier.js" || {
|
|
80
|
-
echo "priority not handled in notifier.js"
|
|
81
|
-
return 1
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
test_send_notification_handles_optional_tags() {
|
|
86
|
-
# Tags are optional (emoji tags)
|
|
87
|
-
grep -q "tags" "$PLUGIN_DIR/notifier.js" || {
|
|
88
|
-
echo "tags not handled in notifier.js"
|
|
89
|
-
return 1
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
test_send_notification_catches_errors() {
|
|
94
|
-
# Should catch network errors gracefully
|
|
95
|
-
grep -q "catch" "$PLUGIN_DIR/notifier.js" || {
|
|
96
|
-
echo "Error handling (catch) not found in notifier.js"
|
|
97
|
-
return 1
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
test_send_notification_handles_errors_silently() {
|
|
102
|
-
# Should handle errors silently (no console output to avoid TUI interference)
|
|
103
|
-
grep -q "catch" "$PLUGIN_DIR/notifier.js" || {
|
|
104
|
-
echo "Error handling (catch) not found in notifier.js"
|
|
105
|
-
return 1
|
|
106
|
-
}
|
|
107
|
-
# Should NOT have console output
|
|
108
|
-
if grep -q "console\.\(error\|warn\|log\)" "$PLUGIN_DIR/notifier.js"; then
|
|
109
|
-
echo "Console output found - should be silent to avoid TUI interference"
|
|
110
|
-
return 1
|
|
111
|
-
fi
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
test_send_notification_supports_auth_token() {
|
|
115
|
-
# Should support optional auth token for ntfy Bearer auth
|
|
116
|
-
grep -q "token\|Token\|auth\|Auth\|Bearer" "$PLUGIN_DIR/notifier.js" || {
|
|
117
|
-
echo "Auth token support not found in notifier.js"
|
|
118
|
-
return 1
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
test_send_notification_uses_bearer_auth() {
|
|
123
|
-
# Should use Authorization: Bearer header when token provided
|
|
124
|
-
grep -q "Authorization.*Bearer\|Bearer.*Authorization" "$PLUGIN_DIR/notifier.js" || {
|
|
125
|
-
echo "Bearer auth header not found in notifier.js"
|
|
126
|
-
return 1
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
# =============================================================================
|
|
133
|
-
# Deduplication Tests
|
|
134
|
-
# =============================================================================
|
|
135
|
-
|
|
136
|
-
test_notifier_has_deduplication() {
|
|
137
|
-
# Should have deduplication to prevent duplicate notifications
|
|
138
|
-
grep -q "isDuplicate\|dedupe\|Dedupe" "$PLUGIN_DIR/notifier.js" || {
|
|
139
|
-
echo "Deduplication logic not found in notifier.js"
|
|
140
|
-
return 1
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
test_notifier_deduplicates_same_notification() {
|
|
145
|
-
# Same notification sent twice within window should be deduplicated
|
|
146
|
-
if ! command -v node &>/dev/null; then
|
|
147
|
-
echo "SKIP: node not available"
|
|
148
|
-
return 0
|
|
149
|
-
fi
|
|
150
|
-
|
|
151
|
-
local result
|
|
152
|
-
result=$(node --experimental-vm-modules -e "
|
|
153
|
-
// Import the module to access the dedupe functions
|
|
154
|
-
import { sendNotification } from './plugin/notifier.js';
|
|
155
|
-
|
|
156
|
-
// Mock fetch to count calls
|
|
157
|
-
let fetchCount = 0;
|
|
158
|
-
global.fetch = async () => { fetchCount++; return { ok: true }; };
|
|
159
|
-
|
|
160
|
-
// Send same notification twice
|
|
161
|
-
await sendNotification({ server: 'http://test', topic: 'test', title: 'Test', message: 'Hello' });
|
|
162
|
-
await sendNotification({ server: 'http://test', topic: 'test', title: 'Test', message: 'Hello' });
|
|
163
|
-
|
|
164
|
-
if (fetchCount !== 1) {
|
|
165
|
-
console.log('FAIL: Expected 1 fetch call (deduplicated), got ' + fetchCount);
|
|
166
|
-
process.exit(1);
|
|
167
|
-
}
|
|
168
|
-
console.log('PASS');
|
|
169
|
-
" 2>&1) || {
|
|
170
|
-
echo "Functional test failed: $result"
|
|
171
|
-
return 1
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
175
|
-
echo "$result"
|
|
176
|
-
return 1
|
|
177
|
-
fi
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
test_notifier_allows_different_notifications() {
|
|
181
|
-
# Different notifications should not be deduplicated
|
|
182
|
-
if ! command -v node &>/dev/null; then
|
|
183
|
-
echo "SKIP: node not available"
|
|
184
|
-
return 0
|
|
185
|
-
fi
|
|
186
|
-
|
|
187
|
-
local result
|
|
188
|
-
result=$(node --experimental-vm-modules -e "
|
|
189
|
-
import { sendNotification } from './plugin/notifier.js';
|
|
190
|
-
|
|
191
|
-
// Mock fetch to count calls
|
|
192
|
-
let fetchCount = 0;
|
|
193
|
-
global.fetch = async () => { fetchCount++; return { ok: true }; };
|
|
194
|
-
|
|
195
|
-
// Send different notifications
|
|
196
|
-
await sendNotification({ server: 'http://test', topic: 'test', title: 'Test', message: 'Hello 1' });
|
|
197
|
-
await sendNotification({ server: 'http://test', topic: 'test', title: 'Test', message: 'Hello 2' });
|
|
198
|
-
|
|
199
|
-
if (fetchCount !== 2) {
|
|
200
|
-
console.log('FAIL: Expected 2 fetch calls (different messages), got ' + fetchCount);
|
|
201
|
-
process.exit(1);
|
|
202
|
-
}
|
|
203
|
-
console.log('PASS');
|
|
204
|
-
" 2>&1) || {
|
|
205
|
-
echo "Functional test failed: $result"
|
|
206
|
-
return 1
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
210
|
-
echo "$result"
|
|
211
|
-
return 1
|
|
212
|
-
fi
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
# =============================================================================
|
|
216
|
-
# Debug Logging Tests
|
|
217
|
-
# =============================================================================
|
|
218
|
-
|
|
219
|
-
test_notifier_imports_logger() {
|
|
220
|
-
grep -q "import.*logger\|from.*logger" "$PLUGIN_DIR/notifier.js" || {
|
|
221
|
-
echo "logger import not found in notifier.js"
|
|
222
|
-
return 1
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
test_notifier_logs_send_notification() {
|
|
227
|
-
# Should log notification send attempts
|
|
228
|
-
grep -q "debug.*[Nn]otification\|debug.*[Ss]end" "$PLUGIN_DIR/notifier.js" || {
|
|
229
|
-
echo "debug logging for notification send not found in notifier.js"
|
|
230
|
-
return 1
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
test_notifier_logs_deduplication() {
|
|
235
|
-
# Should log when deduplicating
|
|
236
|
-
grep -q "debug.*[Dd]edupe\|debug.*[Ss]kip\|debug.*[Dd]uplicate" "$PLUGIN_DIR/notifier.js" || {
|
|
237
|
-
echo "debug logging for deduplication not found in notifier.js"
|
|
238
|
-
return 1
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
# =============================================================================
|
|
243
|
-
# No-Implementation Check (should throw until implemented)
|
|
244
|
-
# =============================================================================
|
|
245
|
-
|
|
246
|
-
test_notifier_not_implemented_placeholder_removed() {
|
|
247
|
-
# After implementation, the "Not implemented" error should be removed
|
|
248
|
-
if grep -q "throw new Error.*Not implemented" "$PLUGIN_DIR/notifier.js"; then
|
|
249
|
-
echo "notifier.js still has 'Not implemented' placeholder"
|
|
250
|
-
return 1
|
|
251
|
-
fi
|
|
252
|
-
return 0
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
# =============================================================================
|
|
256
|
-
# Run Tests
|
|
257
|
-
# =============================================================================
|
|
258
|
-
|
|
259
|
-
echo "sendNotification Function Tests:"
|
|
260
|
-
|
|
261
|
-
for test_func in \
|
|
262
|
-
test_notifier_exports_send_notification \
|
|
263
|
-
test_send_notification_uses_fetch \
|
|
264
|
-
test_send_notification_posts_to_ntfy \
|
|
265
|
-
test_send_notification_sends_json \
|
|
266
|
-
test_send_notification_includes_topic \
|
|
267
|
-
test_send_notification_includes_title \
|
|
268
|
-
test_send_notification_includes_message \
|
|
269
|
-
test_send_notification_handles_optional_priority \
|
|
270
|
-
test_send_notification_handles_optional_tags \
|
|
271
|
-
test_send_notification_catches_errors \
|
|
272
|
-
test_send_notification_handles_errors_silently \
|
|
273
|
-
test_send_notification_supports_auth_token \
|
|
274
|
-
test_send_notification_uses_bearer_auth
|
|
275
|
-
do
|
|
276
|
-
run_test "${test_func#test_}" "$test_func"
|
|
277
|
-
done
|
|
278
|
-
|
|
279
|
-
echo ""
|
|
280
|
-
echo "Deduplication Tests:"
|
|
281
|
-
|
|
282
|
-
for test_func in \
|
|
283
|
-
test_notifier_has_deduplication \
|
|
284
|
-
test_notifier_deduplicates_same_notification \
|
|
285
|
-
test_notifier_allows_different_notifications
|
|
286
|
-
do
|
|
287
|
-
run_test "${test_func#test_}" "$test_func"
|
|
288
|
-
done
|
|
289
|
-
|
|
290
|
-
echo ""
|
|
291
|
-
echo "Debug Logging Tests:"
|
|
292
|
-
|
|
293
|
-
for test_func in \
|
|
294
|
-
test_notifier_imports_logger \
|
|
295
|
-
test_notifier_logs_send_notification \
|
|
296
|
-
test_notifier_logs_deduplication
|
|
297
|
-
do
|
|
298
|
-
run_test "${test_func#test_}" "$test_func"
|
|
299
|
-
done
|
|
300
|
-
|
|
301
|
-
echo ""
|
|
302
|
-
echo "Implementation Status Tests:"
|
|
303
|
-
|
|
304
|
-
for test_func in \
|
|
305
|
-
test_notifier_not_implemented_placeholder_removed
|
|
306
|
-
do
|
|
307
|
-
run_test "${test_func#test_}" "$test_func"
|
|
308
|
-
done
|
|
309
|
-
|
|
310
|
-
print_summary
|