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.
@@ -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
@@ -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