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_config.bash
DELETED
|
@@ -1,438 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# Tests for config.js - Configuration management
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
set -euo pipefail
|
|
7
|
-
|
|
8
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
-
source "$SCRIPT_DIR/test_helper.bash"
|
|
10
|
-
|
|
11
|
-
PLUGIN_DIR="$(dirname "$SCRIPT_DIR")/plugin"
|
|
12
|
-
|
|
13
|
-
echo "Testing config.js module..."
|
|
14
|
-
echo ""
|
|
15
|
-
|
|
16
|
-
# =============================================================================
|
|
17
|
-
# File Structure Tests
|
|
18
|
-
# =============================================================================
|
|
19
|
-
|
|
20
|
-
test_config_file_exists() {
|
|
21
|
-
assert_file_exists "$PLUGIN_DIR/config.js"
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
test_config_js_syntax() {
|
|
25
|
-
if ! command -v node &>/dev/null; then
|
|
26
|
-
echo "SKIP: node not available"
|
|
27
|
-
return 0
|
|
28
|
-
fi
|
|
29
|
-
node --check "$PLUGIN_DIR/config.js" 2>&1 || {
|
|
30
|
-
echo "config.js has syntax errors"
|
|
31
|
-
return 1
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
# =============================================================================
|
|
36
|
-
# Export Tests
|
|
37
|
-
# =============================================================================
|
|
38
|
-
|
|
39
|
-
test_config_exports_load_config() {
|
|
40
|
-
grep -q "export.*function loadConfig\|export.*loadConfig" "$PLUGIN_DIR/config.js" || {
|
|
41
|
-
echo "loadConfig export not found in config.js"
|
|
42
|
-
return 1
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
test_config_exports_get_callback_host() {
|
|
47
|
-
grep -q "export.*function getCallbackHost\|export.*getCallbackHost" "$PLUGIN_DIR/config.js" || {
|
|
48
|
-
echo "getCallbackHost export not found in config.js"
|
|
49
|
-
return 1
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
# =============================================================================
|
|
54
|
-
# Implementation Tests
|
|
55
|
-
# =============================================================================
|
|
56
|
-
|
|
57
|
-
test_config_reads_file() {
|
|
58
|
-
grep -q "readFileSync\|existsSync" "$PLUGIN_DIR/config.js" || {
|
|
59
|
-
echo "File reading not found in config.js"
|
|
60
|
-
return 1
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
test_config_uses_separate_config_file() {
|
|
65
|
-
# Must use opencode-pilot (not opencode-ntfy) config path
|
|
66
|
-
grep -q "opencode-pilot.*config.json" "$PLUGIN_DIR/config.js" || {
|
|
67
|
-
echo "Expected config path ~/.config/opencode-pilot/config.json not found in config.js"
|
|
68
|
-
return 1
|
|
69
|
-
}
|
|
70
|
-
# Ensure old path is NOT present
|
|
71
|
-
if grep -q "opencode-ntfy" "$PLUGIN_DIR/config.js"; then
|
|
72
|
-
echo "ERROR: Old opencode-ntfy path still present in config.js"
|
|
73
|
-
return 1
|
|
74
|
-
fi
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
test_config_parses_json() {
|
|
78
|
-
grep -q "JSON.parse" "$PLUGIN_DIR/config.js" || {
|
|
79
|
-
echo "JSON parsing not found in config.js"
|
|
80
|
-
return 1
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
test_config_supports_env_vars() {
|
|
85
|
-
grep -q "process.env" "$PLUGIN_DIR/config.js" || {
|
|
86
|
-
echo "Environment variable support not found in config.js"
|
|
87
|
-
return 1
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
test_config_has_all_fields() {
|
|
92
|
-
local required_fields=(
|
|
93
|
-
"topic"
|
|
94
|
-
"server"
|
|
95
|
-
"authToken"
|
|
96
|
-
"callbackHost"
|
|
97
|
-
"callbackPort"
|
|
98
|
-
"idleDelayMs"
|
|
99
|
-
"errorNotify"
|
|
100
|
-
"errorDebounceMs"
|
|
101
|
-
"retryNotifyFirst"
|
|
102
|
-
"retryNotifyAfter"
|
|
103
|
-
"debug"
|
|
104
|
-
"debugPath"
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
for field in "${required_fields[@]}"; do
|
|
108
|
-
if ! grep -q "$field" "$PLUGIN_DIR/config.js"; then
|
|
109
|
-
echo "Config field '$field' not found in config.js"
|
|
110
|
-
return 1
|
|
111
|
-
fi
|
|
112
|
-
done
|
|
113
|
-
return 0
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
test_config_has_defaults() {
|
|
117
|
-
# Check for default values
|
|
118
|
-
grep -q "https://ntfy.sh" "$PLUGIN_DIR/config.js" || {
|
|
119
|
-
echo "Default server not found in config.js"
|
|
120
|
-
return 1
|
|
121
|
-
}
|
|
122
|
-
grep -q "4097" "$PLUGIN_DIR/config.js" || {
|
|
123
|
-
echo "Default port not found in config.js"
|
|
124
|
-
return 1
|
|
125
|
-
}
|
|
126
|
-
grep -q "300000" "$PLUGIN_DIR/config.js" || {
|
|
127
|
-
echo "Default idle delay not found in config.js"
|
|
128
|
-
return 1
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
# =============================================================================
|
|
133
|
-
# Functional Tests (requires Node.js)
|
|
134
|
-
# =============================================================================
|
|
135
|
-
|
|
136
|
-
test_config_load_returns_defaults() {
|
|
137
|
-
if ! command -v node &>/dev/null; then
|
|
138
|
-
echo "SKIP: node not available"
|
|
139
|
-
return 0
|
|
140
|
-
fi
|
|
141
|
-
|
|
142
|
-
local result
|
|
143
|
-
result=$(unset NTFY_TOPIC NTFY_SERVER NTFY_TOKEN; node --experimental-vm-modules -e "
|
|
144
|
-
// Clear env vars
|
|
145
|
-
delete process.env.NTFY_TOPIC;
|
|
146
|
-
delete process.env.NTFY_SERVER;
|
|
147
|
-
delete process.env.NTFY_TOKEN;
|
|
148
|
-
|
|
149
|
-
import { loadConfig } from './plugin/config.js';
|
|
150
|
-
|
|
151
|
-
const config = loadConfig();
|
|
152
|
-
|
|
153
|
-
if (config.server !== 'https://ntfy.sh') {
|
|
154
|
-
console.log('FAIL: Expected default server https://ntfy.sh, got ' + config.server);
|
|
155
|
-
process.exit(1);
|
|
156
|
-
}
|
|
157
|
-
if (config.callbackPort !== 4097) {
|
|
158
|
-
console.log('FAIL: Expected default port 4097, got ' + config.callbackPort);
|
|
159
|
-
process.exit(1);
|
|
160
|
-
}
|
|
161
|
-
if (config.idleDelayMs !== 300000) {
|
|
162
|
-
console.log('FAIL: Expected default idle delay 300000, got ' + config.idleDelayMs);
|
|
163
|
-
process.exit(1);
|
|
164
|
-
}
|
|
165
|
-
console.log('PASS');
|
|
166
|
-
" 2>&1) || {
|
|
167
|
-
echo "Functional test failed: $result"
|
|
168
|
-
return 1
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
172
|
-
echo "$result"
|
|
173
|
-
return 1
|
|
174
|
-
fi
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
test_config_env_overrides_defaults() {
|
|
178
|
-
if ! command -v node &>/dev/null; then
|
|
179
|
-
echo "SKIP: node not available"
|
|
180
|
-
return 0
|
|
181
|
-
fi
|
|
182
|
-
|
|
183
|
-
local result
|
|
184
|
-
result=$(NTFY_TOPIC="my-topic" NTFY_SERVER="https://custom.ntfy.sh" node --experimental-vm-modules -e "
|
|
185
|
-
import { loadConfig } from './plugin/config.js';
|
|
186
|
-
|
|
187
|
-
const config = loadConfig();
|
|
188
|
-
|
|
189
|
-
if (config.topic !== 'my-topic') {
|
|
190
|
-
console.log('FAIL: Expected topic my-topic, got ' + config.topic);
|
|
191
|
-
process.exit(1);
|
|
192
|
-
}
|
|
193
|
-
if (config.server !== 'https://custom.ntfy.sh') {
|
|
194
|
-
console.log('FAIL: Expected server https://custom.ntfy.sh, got ' + config.server);
|
|
195
|
-
process.exit(1);
|
|
196
|
-
}
|
|
197
|
-
console.log('PASS');
|
|
198
|
-
" 2>&1) || {
|
|
199
|
-
echo "Functional test failed: $result"
|
|
200
|
-
return 1
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
204
|
-
echo "$result"
|
|
205
|
-
return 1
|
|
206
|
-
fi
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
test_config_get_callback_host_from_config() {
|
|
210
|
-
if ! command -v node &>/dev/null; then
|
|
211
|
-
echo "SKIP: node not available"
|
|
212
|
-
return 0
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
local result
|
|
216
|
-
result=$(node --experimental-vm-modules -e "
|
|
217
|
-
import { getCallbackHost } from './plugin/config.js';
|
|
218
|
-
|
|
219
|
-
const config = { callbackHost: 'myhost.ts.net' };
|
|
220
|
-
const host = getCallbackHost(config);
|
|
221
|
-
|
|
222
|
-
if (host !== 'myhost.ts.net') {
|
|
223
|
-
console.log('FAIL: Expected myhost.ts.net, got ' + host);
|
|
224
|
-
process.exit(1);
|
|
225
|
-
}
|
|
226
|
-
console.log('PASS');
|
|
227
|
-
" 2>&1) || {
|
|
228
|
-
echo "Functional test failed: $result"
|
|
229
|
-
return 1
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
233
|
-
echo "$result"
|
|
234
|
-
return 1
|
|
235
|
-
fi
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
test_config_get_callback_host_returns_null_when_not_configured() {
|
|
239
|
-
if ! command -v node &>/dev/null; then
|
|
240
|
-
echo "SKIP: node not available"
|
|
241
|
-
return 0
|
|
242
|
-
fi
|
|
243
|
-
|
|
244
|
-
local result
|
|
245
|
-
result=$(node --experimental-vm-modules -e "
|
|
246
|
-
import { getCallbackHost } from './plugin/config.js';
|
|
247
|
-
|
|
248
|
-
const config = { callbackHost: null };
|
|
249
|
-
const host = getCallbackHost(config);
|
|
250
|
-
|
|
251
|
-
if (host !== null) {
|
|
252
|
-
console.log('FAIL: Expected null, got ' + host);
|
|
253
|
-
process.exit(1);
|
|
254
|
-
}
|
|
255
|
-
console.log('PASS');
|
|
256
|
-
" 2>&1) || {
|
|
257
|
-
echo "Functional test failed: $result"
|
|
258
|
-
return 1
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
262
|
-
echo "$result"
|
|
263
|
-
return 1
|
|
264
|
-
fi
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
test_config_debug_defaults_to_false() {
|
|
268
|
-
if ! command -v node &>/dev/null; then
|
|
269
|
-
echo "SKIP: node not available"
|
|
270
|
-
return 0
|
|
271
|
-
fi
|
|
272
|
-
|
|
273
|
-
local result
|
|
274
|
-
result=$(node --experimental-vm-modules -e "
|
|
275
|
-
// Clear debug env var
|
|
276
|
-
delete process.env.NTFY_DEBUG;
|
|
277
|
-
|
|
278
|
-
import { loadConfig } from './plugin/config.js';
|
|
279
|
-
|
|
280
|
-
const config = loadConfig();
|
|
281
|
-
|
|
282
|
-
if (config.debug !== false) {
|
|
283
|
-
console.log('FAIL: Expected debug to default to false, got ' + config.debug);
|
|
284
|
-
process.exit(1);
|
|
285
|
-
}
|
|
286
|
-
console.log('PASS');
|
|
287
|
-
" 2>&1) || {
|
|
288
|
-
echo "Functional test failed: $result"
|
|
289
|
-
return 1
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
293
|
-
echo "$result"
|
|
294
|
-
return 1
|
|
295
|
-
fi
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
test_config_debug_env_override() {
|
|
299
|
-
if ! command -v node &>/dev/null; then
|
|
300
|
-
echo "SKIP: node not available"
|
|
301
|
-
return 0
|
|
302
|
-
fi
|
|
303
|
-
|
|
304
|
-
local result
|
|
305
|
-
result=$(NTFY_DEBUG=true node --experimental-vm-modules -e "
|
|
306
|
-
import { loadConfig } from './plugin/config.js';
|
|
307
|
-
|
|
308
|
-
const config = loadConfig();
|
|
309
|
-
|
|
310
|
-
if (config.debug !== true) {
|
|
311
|
-
console.log('FAIL: Expected debug to be true from env, got ' + config.debug);
|
|
312
|
-
process.exit(1);
|
|
313
|
-
}
|
|
314
|
-
console.log('PASS');
|
|
315
|
-
" 2>&1) || {
|
|
316
|
-
echo "Functional test failed: $result"
|
|
317
|
-
return 1
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
321
|
-
echo "$result"
|
|
322
|
-
return 1
|
|
323
|
-
fi
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
test_config_debug_path_defaults_to_null() {
|
|
327
|
-
if ! command -v node &>/dev/null; then
|
|
328
|
-
echo "SKIP: node not available"
|
|
329
|
-
return 0
|
|
330
|
-
fi
|
|
331
|
-
|
|
332
|
-
local result
|
|
333
|
-
result=$(node --experimental-vm-modules -e "
|
|
334
|
-
// Clear debug path env var
|
|
335
|
-
delete process.env.NTFY_DEBUG_PATH;
|
|
336
|
-
|
|
337
|
-
import { loadConfig } from './plugin/config.js';
|
|
338
|
-
|
|
339
|
-
const config = loadConfig();
|
|
340
|
-
|
|
341
|
-
if (config.debugPath !== null) {
|
|
342
|
-
console.log('FAIL: Expected debugPath to default to null, got ' + config.debugPath);
|
|
343
|
-
process.exit(1);
|
|
344
|
-
}
|
|
345
|
-
console.log('PASS');
|
|
346
|
-
" 2>&1) || {
|
|
347
|
-
echo "Functional test failed: $result"
|
|
348
|
-
return 1
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
352
|
-
echo "$result"
|
|
353
|
-
return 1
|
|
354
|
-
fi
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
test_config_debug_path_env_override() {
|
|
358
|
-
if ! command -v node &>/dev/null; then
|
|
359
|
-
echo "SKIP: node not available"
|
|
360
|
-
return 0
|
|
361
|
-
fi
|
|
362
|
-
|
|
363
|
-
local result
|
|
364
|
-
result=$(NTFY_DEBUG_PATH="/custom/debug.log" node --experimental-vm-modules -e "
|
|
365
|
-
import { loadConfig } from './plugin/config.js';
|
|
366
|
-
|
|
367
|
-
const config = loadConfig();
|
|
368
|
-
|
|
369
|
-
if (config.debugPath !== '/custom/debug.log') {
|
|
370
|
-
console.log('FAIL: Expected debugPath to be /custom/debug.log, got ' + config.debugPath);
|
|
371
|
-
process.exit(1);
|
|
372
|
-
}
|
|
373
|
-
console.log('PASS');
|
|
374
|
-
" 2>&1) || {
|
|
375
|
-
echo "Functional test failed: $result"
|
|
376
|
-
return 1
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if ! echo "$result" | grep -q "PASS"; then
|
|
380
|
-
echo "$result"
|
|
381
|
-
return 1
|
|
382
|
-
fi
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
# =============================================================================
|
|
386
|
-
# Run Tests
|
|
387
|
-
# =============================================================================
|
|
388
|
-
|
|
389
|
-
echo "File Structure Tests:"
|
|
390
|
-
|
|
391
|
-
for test_func in \
|
|
392
|
-
test_config_file_exists \
|
|
393
|
-
test_config_js_syntax
|
|
394
|
-
do
|
|
395
|
-
run_test "${test_func#test_}" "$test_func"
|
|
396
|
-
done
|
|
397
|
-
|
|
398
|
-
echo ""
|
|
399
|
-
echo "Export Tests:"
|
|
400
|
-
|
|
401
|
-
for test_func in \
|
|
402
|
-
test_config_exports_load_config \
|
|
403
|
-
test_config_exports_get_callback_host
|
|
404
|
-
do
|
|
405
|
-
run_test "${test_func#test_}" "$test_func"
|
|
406
|
-
done
|
|
407
|
-
|
|
408
|
-
echo ""
|
|
409
|
-
echo "Implementation Tests:"
|
|
410
|
-
|
|
411
|
-
for test_func in \
|
|
412
|
-
test_config_reads_file \
|
|
413
|
-
test_config_uses_separate_config_file \
|
|
414
|
-
test_config_parses_json \
|
|
415
|
-
test_config_supports_env_vars \
|
|
416
|
-
test_config_has_all_fields \
|
|
417
|
-
test_config_has_defaults
|
|
418
|
-
do
|
|
419
|
-
run_test "${test_func#test_}" "$test_func"
|
|
420
|
-
done
|
|
421
|
-
|
|
422
|
-
echo ""
|
|
423
|
-
echo "Functional Tests:"
|
|
424
|
-
|
|
425
|
-
for test_func in \
|
|
426
|
-
test_config_load_returns_defaults \
|
|
427
|
-
test_config_env_overrides_defaults \
|
|
428
|
-
test_config_get_callback_host_from_config \
|
|
429
|
-
test_config_get_callback_host_returns_null_when_not_configured \
|
|
430
|
-
test_config_debug_defaults_to_false \
|
|
431
|
-
test_config_debug_env_override \
|
|
432
|
-
test_config_debug_path_defaults_to_null \
|
|
433
|
-
test_config_debug_path_env_override
|
|
434
|
-
do
|
|
435
|
-
run_test "${test_func#test_}" "$test_func"
|
|
436
|
-
done
|
|
437
|
-
|
|
438
|
-
print_summary
|
package/test/test_helper.bash
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# Test helper functions for opencode-ntfy tests
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
# Test counters
|
|
7
|
-
TESTS_RUN=0
|
|
8
|
-
TESTS_PASSED=0
|
|
9
|
-
TESTS_FAILED=0
|
|
10
|
-
|
|
11
|
-
# Colors
|
|
12
|
-
RED='\033[0;31m'
|
|
13
|
-
GREEN='\033[0;32m'
|
|
14
|
-
YELLOW='\033[0;33m'
|
|
15
|
-
NC='\033[0m' # No Color
|
|
16
|
-
|
|
17
|
-
# Setup test environment
|
|
18
|
-
setup_test_env() {
|
|
19
|
-
export TEST_DIR=$(mktemp -d)
|
|
20
|
-
export HOME="$TEST_DIR/home"
|
|
21
|
-
mkdir -p "$HOME"
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
# Cleanup test environment
|
|
25
|
-
cleanup_test_env() {
|
|
26
|
-
if [[ -n "$TEST_DIR" ]] && [[ -d "$TEST_DIR" ]]; then
|
|
27
|
-
rm -rf "$TEST_DIR"
|
|
28
|
-
fi
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
# Assert equality
|
|
32
|
-
assert_equals() {
|
|
33
|
-
local expected="$1"
|
|
34
|
-
local actual="$2"
|
|
35
|
-
local message="${3:-Values should be equal}"
|
|
36
|
-
|
|
37
|
-
if [[ "$expected" == "$actual" ]]; then
|
|
38
|
-
return 0
|
|
39
|
-
else
|
|
40
|
-
echo " Expected: $expected"
|
|
41
|
-
echo " Actual: $actual"
|
|
42
|
-
return 1
|
|
43
|
-
fi
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
# Assert string contains
|
|
47
|
-
assert_contains() {
|
|
48
|
-
local haystack="$1"
|
|
49
|
-
local needle="$2"
|
|
50
|
-
local message="${3:-String should contain substring}"
|
|
51
|
-
|
|
52
|
-
if [[ "$haystack" == *"$needle"* ]]; then
|
|
53
|
-
return 0
|
|
54
|
-
else
|
|
55
|
-
echo " String: $haystack"
|
|
56
|
-
echo " Should contain: $needle"
|
|
57
|
-
return 1
|
|
58
|
-
fi
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
# Assert command succeeds
|
|
62
|
-
assert_success() {
|
|
63
|
-
local cmd="$1"
|
|
64
|
-
local message="${2:-Command should succeed}"
|
|
65
|
-
|
|
66
|
-
if eval "$cmd" >/dev/null 2>&1; then
|
|
67
|
-
return 0
|
|
68
|
-
else
|
|
69
|
-
echo " Command failed: $cmd"
|
|
70
|
-
return 1
|
|
71
|
-
fi
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
# Assert command fails
|
|
75
|
-
assert_failure() {
|
|
76
|
-
local cmd="$1"
|
|
77
|
-
local message="${2:-Command should fail}"
|
|
78
|
-
|
|
79
|
-
if eval "$cmd" >/dev/null 2>&1; then
|
|
80
|
-
echo " Command should have failed: $cmd"
|
|
81
|
-
return 1
|
|
82
|
-
else
|
|
83
|
-
return 0
|
|
84
|
-
fi
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
# Assert file exists
|
|
88
|
-
assert_file_exists() {
|
|
89
|
-
local file="$1"
|
|
90
|
-
local message="${2:-File should exist}"
|
|
91
|
-
|
|
92
|
-
if [[ -f "$file" ]]; then
|
|
93
|
-
return 0
|
|
94
|
-
else
|
|
95
|
-
echo " File does not exist: $file"
|
|
96
|
-
return 1
|
|
97
|
-
fi
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
# Run a single test
|
|
101
|
-
run_test() {
|
|
102
|
-
local test_name="$1"
|
|
103
|
-
local test_func="$2"
|
|
104
|
-
|
|
105
|
-
TESTS_RUN=$((TESTS_RUN + 1))
|
|
106
|
-
|
|
107
|
-
printf " %-50s " "$test_name"
|
|
108
|
-
|
|
109
|
-
# Run test in subshell to isolate failures
|
|
110
|
-
local output
|
|
111
|
-
if output=$($test_func 2>&1); then
|
|
112
|
-
echo -e "${GREEN}PASS${NC}"
|
|
113
|
-
TESTS_PASSED=$((TESTS_PASSED + 1))
|
|
114
|
-
else
|
|
115
|
-
echo -e "${RED}FAIL${NC}"
|
|
116
|
-
if [[ -n "$output" ]]; then
|
|
117
|
-
echo "$output" | sed 's/^/ /'
|
|
118
|
-
fi
|
|
119
|
-
TESTS_FAILED=$((TESTS_FAILED + 1))
|
|
120
|
-
fi
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
# Print test summary
|
|
124
|
-
print_summary() {
|
|
125
|
-
echo ""
|
|
126
|
-
echo "========================================"
|
|
127
|
-
echo "Tests run: $TESTS_RUN"
|
|
128
|
-
echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}"
|
|
129
|
-
if [[ $TESTS_FAILED -gt 0 ]]; then
|
|
130
|
-
echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}"
|
|
131
|
-
else
|
|
132
|
-
echo "Tests failed: $TESTS_FAILED"
|
|
133
|
-
fi
|
|
134
|
-
echo "========================================"
|
|
135
|
-
|
|
136
|
-
if [[ $TESTS_FAILED -gt 0 ]]; then
|
|
137
|
-
return 1
|
|
138
|
-
fi
|
|
139
|
-
return 0
|
|
140
|
-
}
|