opencode-pilot 0.1.0 → 0.2.1
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/server.js +44 -1381
- package/test/unit/paths.test.js +4 -46
- 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/service/io.opencode.ntfy.plist +0 -29
- package/test/run_tests.bash +0 -34
- package/test/test_actions.bash +0 -263
- package/test/test_cli.bash +0 -161
- package/test/test_config.bash +0 -438
- package/test/test_helper.bash +0 -140
- package/test/test_logger.bash +0 -401
- package/test/test_notifier.bash +0 -310
- package/test/test_plist.bash +0 -125
- package/test/test_plugin.bash +0 -952
- package/test/test_poll_service.bash +0 -179
- package/test/test_poller.bash +0 -120
- package/test/test_readiness.bash +0 -313
- package/test/test_repo_config.bash +0 -406
- package/test/test_service.bash +0 -1342
- package/test/unit/config.test.js +0 -86
package/test/test_plugin.bash
DELETED
|
@@ -1,952 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# Tests for opencode-ntfy plugin
|
|
4
|
-
#
|
|
5
|
-
# These tests verify plugin file structure and JavaScript syntax.
|
|
6
|
-
# Pure function tests will be added as modules are implemented.
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
-
source "$SCRIPT_DIR/test_helper.bash"
|
|
13
|
-
|
|
14
|
-
PLUGIN_DIR="$(dirname "$SCRIPT_DIR")/plugin"
|
|
15
|
-
|
|
16
|
-
echo "Testing opencode-ntfy plugin..."
|
|
17
|
-
echo ""
|
|
18
|
-
|
|
19
|
-
# =============================================================================
|
|
20
|
-
# Plugin File Structure Tests
|
|
21
|
-
# =============================================================================
|
|
22
|
-
|
|
23
|
-
test_plugin_index_exists() {
|
|
24
|
-
assert_file_exists "$PLUGIN_DIR/index.js"
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
test_plugin_notifier_exists() {
|
|
28
|
-
assert_file_exists "$PLUGIN_DIR/notifier.js"
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
# =============================================================================
|
|
32
|
-
# JavaScript Syntax Validation Tests
|
|
33
|
-
# =============================================================================
|
|
34
|
-
|
|
35
|
-
test_index_js_syntax() {
|
|
36
|
-
if ! command -v node &>/dev/null; then
|
|
37
|
-
echo "SKIP: node not available"
|
|
38
|
-
return 0
|
|
39
|
-
fi
|
|
40
|
-
node --check "$PLUGIN_DIR/index.js" 2>&1 || {
|
|
41
|
-
echo "index.js has syntax errors"
|
|
42
|
-
return 1
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
test_notifier_js_syntax() {
|
|
47
|
-
if ! command -v node &>/dev/null; then
|
|
48
|
-
echo "SKIP: node not available"
|
|
49
|
-
return 0
|
|
50
|
-
fi
|
|
51
|
-
node --check "$PLUGIN_DIR/notifier.js" 2>&1 || {
|
|
52
|
-
echo "notifier.js has syntax errors"
|
|
53
|
-
return 1
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
# =============================================================================
|
|
58
|
-
# Configuration Tests
|
|
59
|
-
# =============================================================================
|
|
60
|
-
|
|
61
|
-
test_index_imports_config() {
|
|
62
|
-
# Verify index.js imports config from config.js
|
|
63
|
-
grep -q "import.*loadConfig.*from.*config" "$PLUGIN_DIR/index.js" || {
|
|
64
|
-
echo "loadConfig import not found in index.js"
|
|
65
|
-
return 1
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
test_index_uses_load_config() {
|
|
70
|
-
# Verify index.js calls loadConfig()
|
|
71
|
-
grep -q "loadConfig()" "$PLUGIN_DIR/index.js" || {
|
|
72
|
-
echo "loadConfig() call not found in index.js"
|
|
73
|
-
return 1
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
# =============================================================================
|
|
78
|
-
# Idle Notification Behavior Tests
|
|
79
|
-
# =============================================================================
|
|
80
|
-
|
|
81
|
-
test_handles_session_status_events() {
|
|
82
|
-
# Verify the event handler checks for session.status events
|
|
83
|
-
grep -q "session.status" "$PLUGIN_DIR/index.js" || {
|
|
84
|
-
echo "session.status event handling not found"
|
|
85
|
-
return 1
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
test_handles_idle_status() {
|
|
90
|
-
# Verify idle status triggers timer
|
|
91
|
-
grep -q "idle" "$PLUGIN_DIR/index.js" || {
|
|
92
|
-
echo "idle status handling not found"
|
|
93
|
-
return 1
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
test_handles_busy_status() {
|
|
98
|
-
# Verify busy status cancels timer
|
|
99
|
-
grep -q "busy" "$PLUGIN_DIR/index.js" || {
|
|
100
|
-
echo "busy status handling not found"
|
|
101
|
-
return 1
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
test_uses_settimeout_for_idle() {
|
|
106
|
-
# Verify setTimeout is used for idle delay
|
|
107
|
-
grep -q "setTimeout" "$PLUGIN_DIR/index.js" || {
|
|
108
|
-
echo "setTimeout not found for idle delay"
|
|
109
|
-
return 1
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
test_uses_cleartimeout_for_busy() {
|
|
114
|
-
# Verify clearTimeout is used when busy
|
|
115
|
-
grep -q "clearTimeout" "$PLUGIN_DIR/index.js" || {
|
|
116
|
-
echo "clearTimeout not found for busy cancel"
|
|
117
|
-
return 1
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
test_imports_send_notification_from_notifier() {
|
|
122
|
-
# Verify index.js imports sendNotification from notifier.js
|
|
123
|
-
grep -q "sendNotification" "$PLUGIN_DIR/index.js" || {
|
|
124
|
-
echo "sendNotification not imported/used in index.js"
|
|
125
|
-
return 1
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
test_uses_configured_server_and_topic() {
|
|
130
|
-
# Verify notification uses config.server and config.topic
|
|
131
|
-
grep -q 'config\.server' "$PLUGIN_DIR/index.js" && \
|
|
132
|
-
grep -q 'config\.topic' "$PLUGIN_DIR/index.js" || {
|
|
133
|
-
echo "Notification should use config.server and config.topic"
|
|
134
|
-
return 1
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
test_uses_configured_idle_delay() {
|
|
139
|
-
# Verify timeout uses config.idleDelayMs
|
|
140
|
-
grep -q 'config\.idleDelayMs' "$PLUGIN_DIR/index.js" || {
|
|
141
|
-
echo "Timer should use config.idleDelayMs"
|
|
142
|
-
return 1
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
test_uses_directory_param_not_process_cwd() {
|
|
147
|
-
# Should use the directory parameter from OpenCode, not process.cwd()
|
|
148
|
-
# This ensures devcontainer temp dirs don't show up in notifications
|
|
149
|
-
grep -q 'directory' "$PLUGIN_DIR/index.js" || {
|
|
150
|
-
echo "directory parameter not used in index.js"
|
|
151
|
-
return 1
|
|
152
|
-
}
|
|
153
|
-
# Should NOT use process.cwd() for directory name in code (comments are ok)
|
|
154
|
-
# Look for actual usage like: basename(process.cwd()) or = process.cwd()
|
|
155
|
-
if grep -E 'basename\(process\.cwd\(\)\)|=\s*process\.cwd\(\)' "$PLUGIN_DIR/index.js"; then
|
|
156
|
-
echo "Should not use process.cwd() for directory - use directory param instead"
|
|
157
|
-
return 1
|
|
158
|
-
fi
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
test_idle_notification_shows_repo_context() {
|
|
162
|
-
# Idle notification should include repo name for context
|
|
163
|
-
# Title should be "Idle (repo)" not just "OpenCode"
|
|
164
|
-
grep -q 'Idle' "$PLUGIN_DIR/index.js" || {
|
|
165
|
-
echo "Idle notification title should include 'Idle'"
|
|
166
|
-
return 1
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
# =============================================================================
|
|
171
|
-
# Logging Tests
|
|
172
|
-
# =============================================================================
|
|
173
|
-
# NOTE: Logging tests removed - all console output is now suppressed to avoid TUI interference
|
|
174
|
-
|
|
175
|
-
# =============================================================================
|
|
176
|
-
# Plugin Export Structure Tests
|
|
177
|
-
# =============================================================================
|
|
178
|
-
|
|
179
|
-
test_index_exports_notify() {
|
|
180
|
-
# Plugin should only use default export to prevent double-loading by OpenCode
|
|
181
|
-
# (Issue #34: Having both named and default export caused plugin to load twice)
|
|
182
|
-
grep -q "export default Notify" "$PLUGIN_DIR/index.js" || {
|
|
183
|
-
echo "Default Notify export not found in index.js"
|
|
184
|
-
return 1
|
|
185
|
-
}
|
|
186
|
-
# Ensure named export is NOT present (would cause double-loading)
|
|
187
|
-
if grep -q "export const Notify" "$PLUGIN_DIR/index.js"; then
|
|
188
|
-
echo "Named export 'export const Notify' found - should only use default export"
|
|
189
|
-
return 1
|
|
190
|
-
fi
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
test_index_has_default_export() {
|
|
194
|
-
grep -q "export default" "$PLUGIN_DIR/index.js" || {
|
|
195
|
-
echo "Default export not found in index.js"
|
|
196
|
-
return 1
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
test_notifier_exports_send_notification() {
|
|
201
|
-
grep -q "export.*sendNotification" "$PLUGIN_DIR/notifier.js" || {
|
|
202
|
-
echo "sendNotification export not found in notifier.js"
|
|
203
|
-
return 1
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
# =============================================================================
|
|
208
|
-
# Retry Event Handling Tests (Issue #7)
|
|
209
|
-
# =============================================================================
|
|
210
|
-
|
|
211
|
-
test_handles_retry_status() {
|
|
212
|
-
# Verify retry status is handled within session.status events
|
|
213
|
-
grep -q "retry" "$PLUGIN_DIR/index.js" || {
|
|
214
|
-
echo "retry status handling not found"
|
|
215
|
-
return 1
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
test_tracks_retry_count() {
|
|
220
|
-
# Verify retry counter is tracked
|
|
221
|
-
grep -q "retryCount" "$PLUGIN_DIR/index.js" || {
|
|
222
|
-
echo "retryCount variable not found"
|
|
223
|
-
return 1
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
test_uses_retry_notify_first_config() {
|
|
228
|
-
# Verify retryNotifyFirst config is used
|
|
229
|
-
grep -q "retryNotifyFirst" "$PLUGIN_DIR/index.js" || {
|
|
230
|
-
echo "retryNotifyFirst config not used"
|
|
231
|
-
return 1
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
test_uses_retry_notify_after_config() {
|
|
236
|
-
# Verify retryNotifyAfter config is used
|
|
237
|
-
grep -q "retryNotifyAfter" "$PLUGIN_DIR/index.js" || {
|
|
238
|
-
echo "retryNotifyAfter config not used"
|
|
239
|
-
return 1
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
test_sends_retry_notification_with_priority() {
|
|
244
|
-
# Verify retry notifications use high priority (4)
|
|
245
|
-
grep -q "priority.*4\|4.*priority" "$PLUGIN_DIR/index.js" || {
|
|
246
|
-
echo "High priority (4) not found for retry notifications"
|
|
247
|
-
return 1
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
# =============================================================================
|
|
252
|
-
# Error Event Handling Tests (Issue #7)
|
|
253
|
-
# =============================================================================
|
|
254
|
-
|
|
255
|
-
test_handles_session_error() {
|
|
256
|
-
# Verify session.error events are handled
|
|
257
|
-
grep -q "session\.error" "$PLUGIN_DIR/index.js" || {
|
|
258
|
-
echo "session.error event handling not found"
|
|
259
|
-
return 1
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
test_uses_error_notify_config() {
|
|
264
|
-
# Verify errorNotify config is used
|
|
265
|
-
grep -q "errorNotify" "$PLUGIN_DIR/index.js" || {
|
|
266
|
-
echo "errorNotify config not used"
|
|
267
|
-
return 1
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
test_uses_error_debounce_config() {
|
|
272
|
-
# Verify errorDebounceMs config is used
|
|
273
|
-
grep -q "errorDebounceMs" "$PLUGIN_DIR/index.js" || {
|
|
274
|
-
echo "errorDebounceMs config not used"
|
|
275
|
-
return 1
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
test_tracks_last_error_time() {
|
|
280
|
-
# Verify last error timestamp is tracked for debouncing
|
|
281
|
-
grep -q "lastErrorTime" "$PLUGIN_DIR/index.js" || {
|
|
282
|
-
echo "lastErrorTime variable not found"
|
|
283
|
-
return 1
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
test_sends_error_notification_with_urgent_priority() {
|
|
288
|
-
# Verify error notifications use urgent priority (5)
|
|
289
|
-
grep -q "priority.*5\|5.*priority" "$PLUGIN_DIR/index.js" || {
|
|
290
|
-
echo "Urgent priority (5) not found for error notifications"
|
|
291
|
-
return 1
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
# =============================================================================
|
|
298
|
-
# Counter Reset Tests (Issue #7)
|
|
299
|
-
# =============================================================================
|
|
300
|
-
|
|
301
|
-
test_resets_retry_counter_on_status_change() {
|
|
302
|
-
# Verify retry counter is reset when status changes
|
|
303
|
-
grep -q "retryCount.*=.*0\|retryCount = 0" "$PLUGIN_DIR/index.js" || {
|
|
304
|
-
echo "Retry counter reset not found"
|
|
305
|
-
return 1
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
# =============================================================================
|
|
310
|
-
# Cancellation Handling Tests
|
|
311
|
-
# =============================================================================
|
|
312
|
-
|
|
313
|
-
test_handles_canceled_status() {
|
|
314
|
-
# Verify canceled status is handled (no notification on cancel)
|
|
315
|
-
grep -q "canceled" "$PLUGIN_DIR/index.js" || {
|
|
316
|
-
echo "canceled status handling not found"
|
|
317
|
-
return 1
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
test_sets_canceled_flag_on_canceled_status() {
|
|
322
|
-
# Verify a flag is set when session is canceled
|
|
323
|
-
grep -q "wasCanceled\|sessionCanceled\|canceled.*true\|isCanceled" "$PLUGIN_DIR/index.js" || {
|
|
324
|
-
echo "canceled flag not found"
|
|
325
|
-
return 1
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
test_shutdown_checks_canceled_before_notification() {
|
|
330
|
-
# Verify shutdown handler respects canceled state
|
|
331
|
-
# The idle timer should be cancelled without sending notification
|
|
332
|
-
grep -q "shutdown" "$PLUGIN_DIR/index.js" || {
|
|
333
|
-
echo "shutdown handler not found"
|
|
334
|
-
return 1
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
# =============================================================================
|
|
339
|
-
# Console Output Suppression Tests
|
|
340
|
-
# =============================================================================
|
|
341
|
-
|
|
342
|
-
test_no_console_log_calls() {
|
|
343
|
-
# Plugin should not use console.log (interferes with TUI)
|
|
344
|
-
if grep -q 'console\.log' "$PLUGIN_DIR/index.js"; then
|
|
345
|
-
echo "console.log found - should use silent or file-based logging"
|
|
346
|
-
return 1
|
|
347
|
-
fi
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
test_no_console_warn_calls() {
|
|
351
|
-
# Plugin should not use console.warn (interferes with TUI)
|
|
352
|
-
if grep -q 'console\.warn' "$PLUGIN_DIR/index.js"; then
|
|
353
|
-
echo "console.warn found - should use silent or file-based logging"
|
|
354
|
-
return 1
|
|
355
|
-
fi
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
test_no_console_error_calls() {
|
|
359
|
-
# Plugin should not use console.error (interferes with TUI)
|
|
360
|
-
if grep -q 'console\.error' "$PLUGIN_DIR/index.js"; then
|
|
361
|
-
echo "console.error found - should use silent or file-based logging"
|
|
362
|
-
return 1
|
|
363
|
-
fi
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
# =============================================================================
|
|
367
|
-
# Debug Logging Tests
|
|
368
|
-
# =============================================================================
|
|
369
|
-
|
|
370
|
-
test_index_imports_logger() {
|
|
371
|
-
grep -q "import.*logger\|from.*logger" "$PLUGIN_DIR/index.js" || {
|
|
372
|
-
echo "logger import not found in index.js"
|
|
373
|
-
return 1
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
test_index_initializes_logger() {
|
|
378
|
-
grep -q "initLogger" "$PLUGIN_DIR/index.js" || {
|
|
379
|
-
echo "initLogger call not found in index.js"
|
|
380
|
-
return 1
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
test_index_uses_debug_for_events() {
|
|
385
|
-
# Plugin should log events received
|
|
386
|
-
grep -q "debug.*[Ee]vent" "$PLUGIN_DIR/index.js" || {
|
|
387
|
-
echo "debug logging for events not found in index.js"
|
|
388
|
-
return 1
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
test_index_uses_debug_for_idle_timer() {
|
|
393
|
-
# Plugin should log idle timer start/cancel
|
|
394
|
-
grep -q "debug.*[Ii]dle\|debug.*timer" "$PLUGIN_DIR/index.js" || {
|
|
395
|
-
echo "debug logging for idle timer not found in index.js"
|
|
396
|
-
return 1
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
test_index_uses_debug_for_initialization() {
|
|
401
|
-
# Plugin should log initialization with key config
|
|
402
|
-
grep -q "debug.*[Ii]nitial\|debug.*[Pp]lugin" "$PLUGIN_DIR/index.js" || {
|
|
403
|
-
echo "debug logging for initialization not found in index.js"
|
|
404
|
-
return 1
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
# =============================================================================
|
|
409
|
-
# Notification Suppression Logging Tests (Issue #7)
|
|
410
|
-
# =============================================================================
|
|
411
|
-
# NOTE: Console output suppression tests removed - all logging disabled to avoid TUI interference
|
|
412
|
-
|
|
413
|
-
# =============================================================================
|
|
414
|
-
# OpenCode Runtime Integration Tests
|
|
415
|
-
# =============================================================================
|
|
416
|
-
# These tests verify the plugin doesn't hang opencode on startup and
|
|
417
|
-
# works correctly in the real OpenCode runtime.
|
|
418
|
-
#
|
|
419
|
-
# Tests run if opencode is installed and plugin is configured.
|
|
420
|
-
# Skipped in CI unless explicitly enabled.
|
|
421
|
-
|
|
422
|
-
# Helper to check if we can run integration tests
|
|
423
|
-
can_run_integration_tests() {
|
|
424
|
-
# Check opencode is installed
|
|
425
|
-
if ! command -v opencode &>/dev/null; then
|
|
426
|
-
return 1
|
|
427
|
-
fi
|
|
428
|
-
|
|
429
|
-
# Check plugin is installed (use REAL_HOME since we may have changed HOME)
|
|
430
|
-
local plugin_path="${REAL_HOME:-$HOME}/.config/opencode/plugins/opencode-ntfy/index.js"
|
|
431
|
-
if [[ ! -f "$plugin_path" ]]; then
|
|
432
|
-
return 1
|
|
433
|
-
fi
|
|
434
|
-
|
|
435
|
-
return 0
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
# Setup isolated environment for integration tests
|
|
439
|
-
# Uses temp directory for opencode data while symlinking config from real HOME
|
|
440
|
-
setup_integration_env() {
|
|
441
|
-
REAL_HOME="$HOME"
|
|
442
|
-
INTEGRATION_TEST_DIR=$(mktemp -d)
|
|
443
|
-
export HOME="$INTEGRATION_TEST_DIR"
|
|
444
|
-
|
|
445
|
-
# Create necessary directories
|
|
446
|
-
mkdir -p "$HOME/.config"
|
|
447
|
-
mkdir -p "$HOME/.local/share"
|
|
448
|
-
|
|
449
|
-
# Symlink opencode config (contains plugin registrations and auth)
|
|
450
|
-
ln -s "$REAL_HOME/.config/opencode" "$HOME/.config/opencode"
|
|
451
|
-
|
|
452
|
-
# Symlink opencode-ntfy config
|
|
453
|
-
if [[ -d "$REAL_HOME/.config/opencode-ntfy" ]]; then
|
|
454
|
-
ln -s "$REAL_HOME/.config/opencode-ntfy" "$HOME/.config/opencode-ntfy"
|
|
455
|
-
fi
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
# Cleanup isolated environment after integration tests
|
|
459
|
-
cleanup_integration_env() {
|
|
460
|
-
if [[ -n "${INTEGRATION_TEST_DIR:-}" ]] && [[ -d "$INTEGRATION_TEST_DIR" ]]; then
|
|
461
|
-
rm -rf "$INTEGRATION_TEST_DIR"
|
|
462
|
-
fi
|
|
463
|
-
if [[ -n "${REAL_HOME:-}" ]]; then
|
|
464
|
-
export HOME="$REAL_HOME"
|
|
465
|
-
fi
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
# Cross-platform timeout wrapper
|
|
469
|
-
# Uses perl alarm which works on macOS and Linux
|
|
470
|
-
run_with_timeout() {
|
|
471
|
-
local timeout_secs="$1"
|
|
472
|
-
shift
|
|
473
|
-
perl -e "alarm $timeout_secs; exec @ARGV" "$@" 2>&1
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
# Run opencode and capture output (with timeout)
|
|
477
|
-
run_opencode() {
|
|
478
|
-
local prompt="$1"
|
|
479
|
-
local timeout="${2:-60}"
|
|
480
|
-
|
|
481
|
-
run_with_timeout "$timeout" opencode run --format json "$prompt"
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
test_opencode_starts_within_timeout() {
|
|
485
|
-
if ! can_run_integration_tests; then
|
|
486
|
-
echo "SKIP: opencode not installed or plugin not configured"
|
|
487
|
-
return 0
|
|
488
|
-
fi
|
|
489
|
-
|
|
490
|
-
setup_integration_env
|
|
491
|
-
|
|
492
|
-
# CRITICAL: Verify opencode starts within 10 seconds
|
|
493
|
-
# This catches plugin initialization hangs that would block startup indefinitely.
|
|
494
|
-
|
|
495
|
-
local start_time end_time elapsed output
|
|
496
|
-
start_time=$(date +%s)
|
|
497
|
-
|
|
498
|
-
# Use a short timeout - if plugin hangs, this will fail
|
|
499
|
-
output=$(run_with_timeout 10 opencode run --format json "Say hi" 2>&1)
|
|
500
|
-
local exit_code=$?
|
|
501
|
-
|
|
502
|
-
end_time=$(date +%s)
|
|
503
|
-
elapsed=$((end_time - start_time))
|
|
504
|
-
|
|
505
|
-
cleanup_integration_env
|
|
506
|
-
|
|
507
|
-
if [[ $exit_code -ne 0 ]]; then
|
|
508
|
-
# Check if it was a timeout (exit code 142 = SIGALRM)
|
|
509
|
-
if [[ $exit_code -eq 142 ]] || [[ "$output" == *"Alarm clock"* ]]; then
|
|
510
|
-
echo "FAIL: opencode startup timed out after ${elapsed}s (plugin may be hanging)"
|
|
511
|
-
echo "Output: $output"
|
|
512
|
-
return 1
|
|
513
|
-
fi
|
|
514
|
-
# Skip on model configuration errors (not a plugin issue)
|
|
515
|
-
if [[ "$output" == *"ModelNotFoundError"* ]] || [[ "$output" == *"ProviderModelNotFoundError"* ]]; then
|
|
516
|
-
echo "SKIP: model configuration error (not a plugin issue)"
|
|
517
|
-
return 0
|
|
518
|
-
fi
|
|
519
|
-
echo "opencode run failed (exit $exit_code): $output"
|
|
520
|
-
return 1
|
|
521
|
-
fi
|
|
522
|
-
|
|
523
|
-
# Verify we got a response
|
|
524
|
-
if ! echo "$output" | grep -q '"type"'; then
|
|
525
|
-
# Skip on model configuration errors (not a plugin issue)
|
|
526
|
-
if [[ "$output" == *"ModelNotFoundError"* ]] || [[ "$output" == *"ProviderModelNotFoundError"* ]]; then
|
|
527
|
-
echo "SKIP: model configuration error (not a plugin issue)"
|
|
528
|
-
return 0
|
|
529
|
-
fi
|
|
530
|
-
echo "No valid JSON output from opencode"
|
|
531
|
-
echo "Output: $output"
|
|
532
|
-
return 1
|
|
533
|
-
fi
|
|
534
|
-
|
|
535
|
-
return 0
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
test_opencode_plugin_loads() {
|
|
539
|
-
if ! can_run_integration_tests; then
|
|
540
|
-
echo "SKIP: opencode not installed or plugin not configured"
|
|
541
|
-
return 0
|
|
542
|
-
fi
|
|
543
|
-
|
|
544
|
-
setup_integration_env
|
|
545
|
-
|
|
546
|
-
# Plugin should load without errors - check opencode doesn't report plugin failures
|
|
547
|
-
local output
|
|
548
|
-
output=$(run_opencode "Say hello" 30) || {
|
|
549
|
-
cleanup_integration_env
|
|
550
|
-
echo "opencode run failed: $output"
|
|
551
|
-
return 1
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
cleanup_integration_env
|
|
555
|
-
|
|
556
|
-
# Check that output doesn't contain plugin error messages
|
|
557
|
-
if echo "$output" | grep -qi "plugin.*error\|failed to load"; then
|
|
558
|
-
echo "Plugin load error detected"
|
|
559
|
-
echo "Output: $output"
|
|
560
|
-
return 1
|
|
561
|
-
fi
|
|
562
|
-
|
|
563
|
-
return 0
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
test_opencode_ntfy_disabled_without_topic() {
|
|
567
|
-
if ! can_run_integration_tests; then
|
|
568
|
-
echo "SKIP: opencode not installed or plugin not configured"
|
|
569
|
-
return 0
|
|
570
|
-
fi
|
|
571
|
-
|
|
572
|
-
setup_integration_env
|
|
573
|
-
|
|
574
|
-
# Without NTFY_TOPIC, plugin should disable gracefully (not crash)
|
|
575
|
-
# Unset NTFY_TOPIC temporarily
|
|
576
|
-
local old_topic="${NTFY_TOPIC:-}"
|
|
577
|
-
unset NTFY_TOPIC
|
|
578
|
-
|
|
579
|
-
local output
|
|
580
|
-
output=$(run_opencode "Say hi" 30)
|
|
581
|
-
local exit_code=$?
|
|
582
|
-
|
|
583
|
-
# Restore
|
|
584
|
-
if [[ -n "$old_topic" ]]; then
|
|
585
|
-
export NTFY_TOPIC="$old_topic"
|
|
586
|
-
fi
|
|
587
|
-
|
|
588
|
-
cleanup_integration_env
|
|
589
|
-
|
|
590
|
-
if [[ $exit_code -ne 0 ]]; then
|
|
591
|
-
echo "opencode failed without NTFY_TOPIC: $output"
|
|
592
|
-
return 1
|
|
593
|
-
fi
|
|
594
|
-
|
|
595
|
-
return 0
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
# =============================================================================
|
|
599
|
-
# Run Tests
|
|
600
|
-
# =============================================================================
|
|
601
|
-
|
|
602
|
-
echo "Plugin File Structure Tests:"
|
|
603
|
-
|
|
604
|
-
for test_func in \
|
|
605
|
-
test_plugin_index_exists \
|
|
606
|
-
test_plugin_notifier_exists
|
|
607
|
-
do
|
|
608
|
-
run_test "${test_func#test_}" "$test_func"
|
|
609
|
-
done
|
|
610
|
-
|
|
611
|
-
echo ""
|
|
612
|
-
echo "JavaScript Syntax Validation Tests:"
|
|
613
|
-
|
|
614
|
-
for test_func in \
|
|
615
|
-
test_index_js_syntax \
|
|
616
|
-
test_notifier_js_syntax
|
|
617
|
-
do
|
|
618
|
-
run_test "${test_func#test_}" "$test_func"
|
|
619
|
-
done
|
|
620
|
-
|
|
621
|
-
echo ""
|
|
622
|
-
echo "Configuration Tests:"
|
|
623
|
-
|
|
624
|
-
for test_func in \
|
|
625
|
-
test_index_imports_config \
|
|
626
|
-
test_index_uses_load_config
|
|
627
|
-
do
|
|
628
|
-
run_test "${test_func#test_}" "$test_func"
|
|
629
|
-
done
|
|
630
|
-
|
|
631
|
-
echo ""
|
|
632
|
-
echo "Idle Notification Behavior Tests:"
|
|
633
|
-
|
|
634
|
-
for test_func in \
|
|
635
|
-
test_handles_session_status_events \
|
|
636
|
-
test_handles_idle_status \
|
|
637
|
-
test_handles_busy_status \
|
|
638
|
-
test_uses_settimeout_for_idle \
|
|
639
|
-
test_uses_cleartimeout_for_busy \
|
|
640
|
-
test_imports_send_notification_from_notifier \
|
|
641
|
-
test_uses_configured_server_and_topic \
|
|
642
|
-
test_uses_configured_idle_delay \
|
|
643
|
-
test_uses_directory_param_not_process_cwd \
|
|
644
|
-
test_idle_notification_shows_repo_context
|
|
645
|
-
do
|
|
646
|
-
run_test "${test_func#test_}" "$test_func"
|
|
647
|
-
done
|
|
648
|
-
|
|
649
|
-
# Logging tests removed - console output is now fully suppressed
|
|
650
|
-
|
|
651
|
-
echo ""
|
|
652
|
-
echo "Plugin Export Structure Tests:"
|
|
653
|
-
|
|
654
|
-
for test_func in \
|
|
655
|
-
test_index_exports_notify \
|
|
656
|
-
test_index_has_default_export \
|
|
657
|
-
test_notifier_exports_send_notification
|
|
658
|
-
do
|
|
659
|
-
run_test "${test_func#test_}" "$test_func"
|
|
660
|
-
done
|
|
661
|
-
|
|
662
|
-
echo ""
|
|
663
|
-
echo "Retry Event Handling Tests (Issue #7):"
|
|
664
|
-
|
|
665
|
-
for test_func in \
|
|
666
|
-
test_handles_retry_status \
|
|
667
|
-
test_tracks_retry_count \
|
|
668
|
-
test_uses_retry_notify_first_config \
|
|
669
|
-
test_uses_retry_notify_after_config \
|
|
670
|
-
test_sends_retry_notification_with_priority
|
|
671
|
-
do
|
|
672
|
-
run_test "${test_func#test_}" "$test_func"
|
|
673
|
-
done
|
|
674
|
-
|
|
675
|
-
echo ""
|
|
676
|
-
echo "Error Event Handling Tests (Issue #7):"
|
|
677
|
-
|
|
678
|
-
for test_func in \
|
|
679
|
-
test_handles_session_error \
|
|
680
|
-
test_uses_error_notify_config \
|
|
681
|
-
test_uses_error_debounce_config \
|
|
682
|
-
test_tracks_last_error_time \
|
|
683
|
-
test_sends_error_notification_with_urgent_priority
|
|
684
|
-
do
|
|
685
|
-
run_test "${test_func#test_}" "$test_func"
|
|
686
|
-
done
|
|
687
|
-
|
|
688
|
-
echo ""
|
|
689
|
-
echo "Counter Reset Tests (Issue #7):"
|
|
690
|
-
|
|
691
|
-
for test_func in \
|
|
692
|
-
test_resets_retry_counter_on_status_change
|
|
693
|
-
do
|
|
694
|
-
run_test "${test_func#test_}" "$test_func"
|
|
695
|
-
done
|
|
696
|
-
|
|
697
|
-
echo ""
|
|
698
|
-
echo "Cancellation Handling Tests:"
|
|
699
|
-
|
|
700
|
-
for test_func in \
|
|
701
|
-
test_handles_canceled_status \
|
|
702
|
-
test_sets_canceled_flag_on_canceled_status \
|
|
703
|
-
test_shutdown_checks_canceled_before_notification
|
|
704
|
-
do
|
|
705
|
-
run_test "${test_func#test_}" "$test_func"
|
|
706
|
-
done
|
|
707
|
-
|
|
708
|
-
echo ""
|
|
709
|
-
echo "Console Output Suppression Tests:"
|
|
710
|
-
|
|
711
|
-
for test_func in \
|
|
712
|
-
test_no_console_log_calls \
|
|
713
|
-
test_no_console_warn_calls \
|
|
714
|
-
test_no_console_error_calls
|
|
715
|
-
do
|
|
716
|
-
run_test "${test_func#test_}" "$test_func"
|
|
717
|
-
done
|
|
718
|
-
|
|
719
|
-
echo ""
|
|
720
|
-
echo "Debug Logging Tests:"
|
|
721
|
-
|
|
722
|
-
for test_func in \
|
|
723
|
-
test_index_imports_logger \
|
|
724
|
-
test_index_initializes_logger \
|
|
725
|
-
test_index_uses_debug_for_events \
|
|
726
|
-
test_index_uses_debug_for_idle_timer \
|
|
727
|
-
test_index_uses_debug_for_initialization
|
|
728
|
-
do
|
|
729
|
-
run_test "${test_func#test_}" "$test_func"
|
|
730
|
-
done
|
|
731
|
-
|
|
732
|
-
# Removed notification suppression logging tests - console output is now fully suppressed
|
|
733
|
-
|
|
734
|
-
# =============================================================================
|
|
735
|
-
# Per-Conversation State Tracking Tests (Issue #34)
|
|
736
|
-
# =============================================================================
|
|
737
|
-
|
|
738
|
-
test_uses_conversations_map_for_state() {
|
|
739
|
-
# Plugin should track state per-conversation using a Map, not global variables
|
|
740
|
-
grep -q "conversations\|Map()" "$PLUGIN_DIR/index.js" || {
|
|
741
|
-
echo "Conversations Map not found in index.js"
|
|
742
|
-
return 1
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
test_extracts_session_id_from_event() {
|
|
747
|
-
# Plugin should extract session ID from event properties for routing
|
|
748
|
-
grep -q "event\.properties.*id\|properties.*info.*id" "$PLUGIN_DIR/index.js" || {
|
|
749
|
-
echo "Session ID extraction from event not found"
|
|
750
|
-
return 1
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
test_stores_idle_timer_per_conversation() {
|
|
755
|
-
# Each conversation should have its own idle timer
|
|
756
|
-
# Look for pattern like conversations.get(sessionId).idleTimer or similar
|
|
757
|
-
grep -q "idleTimer" "$PLUGIN_DIR/index.js" || {
|
|
758
|
-
echo "idleTimer not found in index.js"
|
|
759
|
-
return 1
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
test_captures_session_id_in_idle_timer_closure() {
|
|
764
|
-
# The sessionId should be captured when setting the timer, not read later
|
|
765
|
-
# This ensures the notification URL points to the correct conversation
|
|
766
|
-
# Pattern: use local variable in setTimeout callback, not external reference
|
|
767
|
-
grep -A 30 "setTimeout" "$PLUGIN_DIR/index.js" | grep -q "session" || {
|
|
768
|
-
echo "Session ID not captured in idle timer"
|
|
769
|
-
return 1
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
test_clears_conversation_state_on_cancel() {
|
|
774
|
-
# When a conversation is canceled, its state should be cleaned up
|
|
775
|
-
grep -q "canceled" "$PLUGIN_DIR/index.js" && \
|
|
776
|
-
grep -q "delete\|clear" "$PLUGIN_DIR/index.js" || {
|
|
777
|
-
echo "Conversation cleanup on cancel not found"
|
|
778
|
-
return 1
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
# =============================================================================
|
|
783
|
-
# Session Tracking and Open Session Action Tests (Issue #27)
|
|
784
|
-
# =============================================================================
|
|
785
|
-
|
|
786
|
-
test_tracks_current_session_id() {
|
|
787
|
-
# Plugin should track current session ID from session events
|
|
788
|
-
grep -q "currentSessionId\|sessionId" "$PLUGIN_DIR/index.js" || {
|
|
789
|
-
echo "session ID tracking not found in index.js"
|
|
790
|
-
return 1
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
test_extracts_session_id_from_status_event() {
|
|
795
|
-
# Plugin should extract session ID from session.status events for per-conversation tracking
|
|
796
|
-
grep -q "session\.status\|properties.*info.*id" "$PLUGIN_DIR/index.js" || {
|
|
797
|
-
echo "Session ID extraction from status event not found"
|
|
798
|
-
return 1
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
# =============================================================================
|
|
805
|
-
# Devcontainer Path Parsing Tests
|
|
806
|
-
# =============================================================================
|
|
807
|
-
|
|
808
|
-
test_parses_devcontainer_clone_path() {
|
|
809
|
-
# When directory is a devcontainer clone, should extract repo and branch
|
|
810
|
-
# Path format: /Users/foo/.cache/devcontainer-clones/{repo}/{branch}
|
|
811
|
-
grep -q "devcontainer-clones" "$PLUGIN_DIR/index.js" || {
|
|
812
|
-
echo "devcontainer-clones path parsing not found in index.js"
|
|
813
|
-
return 1
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
test_notification_shows_repo_and_branch() {
|
|
818
|
-
# Notification title should show both repo name and branch when in devcontainer
|
|
819
|
-
# e.g., "Idle (opencode-ntfy/fix-something)" instead of just "Idle (fix-something)"
|
|
820
|
-
grep -q "parseRepoInfo\|getRepoName" "$PLUGIN_DIR/index.js" || {
|
|
821
|
-
echo "Repo info parsing function not found in index.js"
|
|
822
|
-
return 1
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
test_regular_directory_shows_basename_only() {
|
|
827
|
-
# For regular directories (not devcontainer clones), should show just basename
|
|
828
|
-
# e.g., "/Users/foo/code/myrepo" -> "myrepo"
|
|
829
|
-
grep -q "basename" "$PLUGIN_DIR/index.js" || {
|
|
830
|
-
echo "basename fallback not found in index.js"
|
|
831
|
-
return 1
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
# =============================================================================
|
|
836
|
-
# Session Ownership Verification Tests (Issue #50)
|
|
837
|
-
# =============================================================================
|
|
838
|
-
# These tests verify the plugin only processes events for sessions that
|
|
839
|
-
# belong to its OpenCode instance, preventing duplicate notifications when
|
|
840
|
-
# multiple OpenCode instances are running.
|
|
841
|
-
|
|
842
|
-
test_verifies_session_ownership_before_processing() {
|
|
843
|
-
# Plugin should verify session exists in its OpenCode instance before processing events
|
|
844
|
-
# This prevents duplicate notifications from multiple plugin instances
|
|
845
|
-
grep -q "verifySessionOwnership\|session\.get\|verifiedSessions" "$PLUGIN_DIR/index.js" || {
|
|
846
|
-
echo "Session ownership verification not found in index.js"
|
|
847
|
-
return 1
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
test_caches_verified_sessions() {
|
|
852
|
-
# Plugin should cache verified sessions to avoid repeated API calls
|
|
853
|
-
grep -q "verifiedSessions\|ownedSessions\|sessionCache" "$PLUGIN_DIR/index.js" || {
|
|
854
|
-
echo "Verified sessions cache not found in index.js"
|
|
855
|
-
return 1
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
test_tracks_rejected_sessions() {
|
|
860
|
-
# Plugin should track sessions that don't belong to it to avoid repeated lookups
|
|
861
|
-
grep -q "rejectedSessions\|foreignSessions\|notOurs" "$PLUGIN_DIR/index.js" || {
|
|
862
|
-
echo "Rejected sessions tracking not found in index.js"
|
|
863
|
-
return 1
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
test_uses_client_session_get_for_verification() {
|
|
868
|
-
# Plugin should use client.session.get() to verify session ownership
|
|
869
|
-
grep -q "client\.session\.get\|session\.get" "$PLUGIN_DIR/index.js" || {
|
|
870
|
-
echo "client.session.get() call not found in index.js"
|
|
871
|
-
return 1
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
test_skips_events_for_foreign_sessions() {
|
|
876
|
-
# Events for sessions that don't belong to this instance should be skipped
|
|
877
|
-
# Look for early return when session is not owned
|
|
878
|
-
grep -q "rejectedSessions\|foreignSessions\|not.*ours\|skip.*event" "$PLUGIN_DIR/index.js" || {
|
|
879
|
-
echo "Foreign session skip logic not found in index.js"
|
|
880
|
-
return 1
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
test_cleans_up_session_caches_on_shutdown() {
|
|
885
|
-
# Shutdown handler should clear session ownership caches to prevent memory leaks
|
|
886
|
-
grep -A 20 "shutdown:" "$PLUGIN_DIR/index.js" | grep -q "verifiedSessions.clear\|rejectedSessions.clear" || {
|
|
887
|
-
echo "Session cache cleanup not found in shutdown handler"
|
|
888
|
-
return 1
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
echo ""
|
|
893
|
-
echo "Session Ownership Verification Tests (Issue #50):"
|
|
894
|
-
|
|
895
|
-
for test_func in \
|
|
896
|
-
test_verifies_session_ownership_before_processing \
|
|
897
|
-
test_caches_verified_sessions \
|
|
898
|
-
test_tracks_rejected_sessions \
|
|
899
|
-
test_uses_client_session_get_for_verification \
|
|
900
|
-
test_skips_events_for_foreign_sessions \
|
|
901
|
-
test_cleans_up_session_caches_on_shutdown
|
|
902
|
-
do
|
|
903
|
-
run_test "${test_func#test_}" "$test_func"
|
|
904
|
-
done
|
|
905
|
-
|
|
906
|
-
echo ""
|
|
907
|
-
echo "Per-Conversation State Tracking Tests (Issue #34):"
|
|
908
|
-
|
|
909
|
-
for test_func in \
|
|
910
|
-
test_uses_conversations_map_for_state \
|
|
911
|
-
test_extracts_session_id_from_event \
|
|
912
|
-
test_stores_idle_timer_per_conversation \
|
|
913
|
-
test_captures_session_id_in_idle_timer_closure \
|
|
914
|
-
test_clears_conversation_state_on_cancel
|
|
915
|
-
do
|
|
916
|
-
run_test "${test_func#test_}" "$test_func"
|
|
917
|
-
done
|
|
918
|
-
|
|
919
|
-
echo ""
|
|
920
|
-
echo "Session Tracking Tests:"
|
|
921
|
-
|
|
922
|
-
for test_func in \
|
|
923
|
-
test_tracks_current_session_id \
|
|
924
|
-
test_extracts_session_id_from_status_event
|
|
925
|
-
do
|
|
926
|
-
run_test "${test_func#test_}" "$test_func"
|
|
927
|
-
done
|
|
928
|
-
|
|
929
|
-
echo ""
|
|
930
|
-
echo "Devcontainer Path Parsing Tests:"
|
|
931
|
-
|
|
932
|
-
for test_func in \
|
|
933
|
-
test_parses_devcontainer_clone_path \
|
|
934
|
-
test_notification_shows_repo_and_branch \
|
|
935
|
-
test_regular_directory_shows_basename_only
|
|
936
|
-
do
|
|
937
|
-
run_test "${test_func#test_}" "$test_func"
|
|
938
|
-
done
|
|
939
|
-
|
|
940
|
-
echo ""
|
|
941
|
-
echo "OpenCode Runtime Integration Tests:"
|
|
942
|
-
|
|
943
|
-
for test_func in \
|
|
944
|
-
test_opencode_starts_within_timeout \
|
|
945
|
-
test_opencode_plugin_loads \
|
|
946
|
-
test_opencode_ntfy_disabled_without_topic
|
|
947
|
-
do
|
|
948
|
-
# Don't use setup/teardown for integration tests - use real HOME
|
|
949
|
-
run_test "${test_func#test_}" "$test_func"
|
|
950
|
-
done
|
|
951
|
-
|
|
952
|
-
print_summary
|