@xano/cli 1.0.6-beta.10 → 1.0.6-beta.11
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/README.md +43 -1
- package/dist/commands/sandbox/unit_test/run/index.js +17 -11
- package/dist/commands/sandbox/unit_test/run_all/index.js +9 -1
- package/dist/commands/sandbox/workflow_test/run/index.js +12 -9
- package/dist/commands/sandbox/workflow_test/run_all/index.js +9 -1
- package/dist/commands/tenant/unit_test/run/index.js +19 -11
- package/dist/commands/tenant/unit_test/run_all/index.js +13 -1
- package/dist/commands/tenant/workflow_test/run/index.js +14 -9
- package/dist/commands/tenant/workflow_test/run_all/index.js +13 -1
- package/dist/commands/unit_test/run/index.js +19 -11
- package/dist/commands/unit_test/run_all/index.js +13 -1
- package/dist/commands/workflow_test/run/index.js +15 -10
- package/dist/commands/workflow_test/run_all/index.js +15 -3
- package/oclif.manifest.json +4156 -4156
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -487,7 +487,7 @@ Every `run_all` command takes `--concurrency` (default `1`):
|
|
|
487
487
|
|
|
488
488
|
```bash
|
|
489
489
|
xano unit_test run_all --concurrency 4
|
|
490
|
-
xano tenant unit_test run_all my-tenant --concurrency 8
|
|
490
|
+
xano tenant unit_test run_all --tenant my-tenant --concurrency 8
|
|
491
491
|
```
|
|
492
492
|
|
|
493
493
|
Output stays in test order regardless of concurrency, so a parallel run is still diffable against a sequential one, and the JSON `results` array keeps a stable order.
|
|
@@ -496,6 +496,48 @@ Output stays in test order regardless of concurrency, so a parallel run is still
|
|
|
496
496
|
|
|
497
497
|
**The default is 1 on purpose.** These tests execute against a shared workspace database, so tests that touch the same tables or records can interfere with each other when run at the same time — producing failures that look like flakiness rather than a real regression. Raise `--concurrency` once you know your tests are independent.
|
|
498
498
|
|
|
499
|
+
#### Exit codes
|
|
500
|
+
|
|
501
|
+
Every `run` and `run_all` command — on all four surfaces (`unit_test`, `workflow_test`, `sandbox`, `tenant`) — uses the same exit codes:
|
|
502
|
+
|
|
503
|
+
| Code | Meaning |
|
|
504
|
+
|------|---------|
|
|
505
|
+
| `0` | All tests passed, **or** there were no tests to run |
|
|
506
|
+
| `1` | At least one test failed |
|
|
507
|
+
| `2` | The command could not run the tests at all — bad workspace, auth failure, unreadable profile, or the test-list request failed |
|
|
508
|
+
|
|
509
|
+
**This is identical for `-o json` and summary output.** Adding `-o json` to get machine-readable output does not change the exit code, so a CI job can rely on it in either mode.
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
xano workflow_test run_all -o json || echo "tests failed or the command errored"
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**One asymmetry worth knowing about.** In `run`, a non-2xx response from the run endpoint is a CLI error and exits `2`. In `run_all`, a non-2xx response for an *individual* test is recorded as a failed test result with an `API error <status>` message and contributes to exit `1` — because `run_all`'s job is to finish the batch and report a roll-up rather than abort on one test's transport error. Exit `2` in `run_all` is reserved for failures that stop the batch from starting at all.
|
|
516
|
+
|
|
517
|
+
If you need to tell a backend outage apart from a genuine assertion failure, the exit code alone will not do it — inspect `results[].message` for the `API error` prefix:
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
xano workflow_test run_all -o json | jq '[.results[] | select(.message // "" | startswith("API error"))] | length'
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
#### JSON output
|
|
524
|
+
|
|
525
|
+
`run` returns the API's result object unchanged. Treat **any** `status` other than `"ok"` as a failure — several distinct non-`ok` values are returned in practice, so match on "not `ok`" rather than enumerating known failure values:
|
|
526
|
+
|
|
527
|
+
```json
|
|
528
|
+
{ "status": "exception", "timing": 0.02, "message": "Precondition failed." }
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
`run_all` returns a roll-up. A branch with no tests is a normal state — freshly created branches routinely have none — and still emits valid JSON rather than plain text, so `JSON.parse` is always safe:
|
|
532
|
+
|
|
533
|
+
```json
|
|
534
|
+
{ "passed": 0, "failed": 0, "results": [], "total_timing": 0 }
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
The `total_timing` field is currently emitted by `workflow_test run_all` only; the `unit_test`, `tenant`, and `sandbox` variants omit it. The empty-suite envelope always carries the same keys as that command's populated envelope.
|
|
538
|
+
|
|
539
|
+
> **Changed in this release:** a failing `run` in summary mode previously exited `2` and printed a spurious `EEXIT: 1` line; it now exits `1` with clean output. A failing `run` with `-o json` previously exited `0`, which silently hid failures from CI. If you match on exit code `2` specifically to detect test failures, switch to a non-zero check.
|
|
540
|
+
|
|
499
541
|
### Tenants
|
|
500
542
|
|
|
501
543
|
Manage tenants, their environment variables, backups, deployments, and clusters.
|
|
@@ -46,24 +46,30 @@ Result: PASS
|
|
|
46
46
|
this.error(message);
|
|
47
47
|
}
|
|
48
48
|
const result = (await response.json());
|
|
49
|
+
// Anything other than 'ok' is a failure -- the API has been observed
|
|
50
|
+
// returning several distinct non-ok statuses, so never enumerate them.
|
|
51
|
+
const passed = result.status === 'ok';
|
|
49
52
|
if (flags.output === 'json') {
|
|
50
53
|
this.log(JSON.stringify(result, null, 2));
|
|
51
54
|
}
|
|
55
|
+
else if (passed) {
|
|
56
|
+
this.log('Result: PASS');
|
|
57
|
+
}
|
|
52
58
|
else {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const failedExpects = result.results?.filter((r) => r.status === 'fail') ?? [];
|
|
59
|
-
for (const expect of failedExpects) {
|
|
60
|
-
if (expect.message) {
|
|
61
|
-
this.log(` Error: ${expect.message}`);
|
|
62
|
-
}
|
|
59
|
+
this.log('Result: FAIL');
|
|
60
|
+
const failedExpects = result.results?.filter((r) => r.status === 'fail') ?? [];
|
|
61
|
+
for (const expect of failedExpects) {
|
|
62
|
+
if (expect.message) {
|
|
63
|
+
this.log(` Error: ${expect.message}`);
|
|
63
64
|
}
|
|
64
|
-
this.exit(1);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
// Set the code rather than throwing via this.exit(): the exit must apply
|
|
68
|
+
// in both output modes, and process.exitCode cannot be swallowed by the
|
|
69
|
+
// surrounding catch. Mirrors what run_all already does.
|
|
70
|
+
if (!passed) {
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
}
|
|
67
73
|
}
|
|
68
74
|
catch (error) {
|
|
69
75
|
if (error instanceof Error && 'oclif' in error)
|
|
@@ -78,7 +78,15 @@ Results: 4 passed, 1 failed
|
|
|
78
78
|
},
|
|
79
79
|
});
|
|
80
80
|
if (tests.length === 0) {
|
|
81
|
-
|
|
81
|
+
// An empty suite is a normal state (fresh branches have no tests) and
|
|
82
|
+
// is a success, not a failure. Under -o json it must still be parseable,
|
|
83
|
+
// with the same keys the populated path emits below.
|
|
84
|
+
if (flags.output === 'json') {
|
|
85
|
+
this.log(JSON.stringify({ failed: 0, passed: 0, results: [] }, null, 2));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.log('No unit tests found');
|
|
89
|
+
}
|
|
82
90
|
return;
|
|
83
91
|
}
|
|
84
92
|
if (flags.output === 'summary') {
|
|
@@ -46,22 +46,25 @@ Result: PASS (0.25s)
|
|
|
46
46
|
this.error(message);
|
|
47
47
|
}
|
|
48
48
|
const result = (await response.json());
|
|
49
|
+
// Anything other than 'ok' is a failure -- the API has been observed
|
|
50
|
+
// returning several distinct non-ok statuses, so never enumerate them.
|
|
51
|
+
const passed = result.status === 'ok';
|
|
49
52
|
if (flags.output === 'json') {
|
|
50
53
|
this.log(JSON.stringify(result, null, 2));
|
|
51
54
|
}
|
|
52
55
|
else {
|
|
53
56
|
const timing = result.timing ? ` (${result.timing}s)` : '';
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
else {
|
|
58
|
-
this.log(`Result: FAIL${timing}`);
|
|
59
|
-
if (result.message) {
|
|
60
|
-
this.log(` Error: ${result.message}`);
|
|
61
|
-
}
|
|
62
|
-
this.exit(1);
|
|
57
|
+
this.log(`Result: ${passed ? 'PASS' : 'FAIL'}${timing}`);
|
|
58
|
+
if (!passed && result.message) {
|
|
59
|
+
this.log(` Error: ${result.message}`);
|
|
63
60
|
}
|
|
64
61
|
}
|
|
62
|
+
// Set the code rather than throwing via this.exit(): the exit must apply
|
|
63
|
+
// in both output modes, and process.exitCode cannot be swallowed by the
|
|
64
|
+
// surrounding catch. Mirrors what run_all already does.
|
|
65
|
+
if (!passed) {
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
}
|
|
65
68
|
}
|
|
66
69
|
catch (error) {
|
|
67
70
|
if (error instanceof Error && 'oclif' in error)
|
|
@@ -71,7 +71,15 @@ Results: 2 passed, 1 failed
|
|
|
71
71
|
},
|
|
72
72
|
});
|
|
73
73
|
if (tests.length === 0) {
|
|
74
|
-
|
|
74
|
+
// An empty suite is a normal state (fresh branches have no tests) and
|
|
75
|
+
// is a success, not a failure. Under -o json it must still be parseable,
|
|
76
|
+
// with the same keys the populated path emits below.
|
|
77
|
+
if (flags.output === 'json') {
|
|
78
|
+
this.log(JSON.stringify({ failed: 0, passed: 0, results: [] }, null, 2));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.log('No workflow tests found');
|
|
82
|
+
}
|
|
75
83
|
return;
|
|
76
84
|
}
|
|
77
85
|
if (flags.output === 'summary') {
|
|
@@ -68,26 +68,34 @@ Result: PASS
|
|
|
68
68
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
69
69
|
}
|
|
70
70
|
const result = (await response.json());
|
|
71
|
+
// Anything other than 'ok' is a failure -- the API has been observed
|
|
72
|
+
// returning several distinct non-ok statuses, so never enumerate them.
|
|
73
|
+
const passed = result.status === 'ok';
|
|
71
74
|
if (flags.output === 'json') {
|
|
72
75
|
this.log(JSON.stringify(result, null, 2));
|
|
73
76
|
}
|
|
77
|
+
else if (passed) {
|
|
78
|
+
this.log('Result: PASS');
|
|
79
|
+
}
|
|
74
80
|
else {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const failedExpects = result.results?.filter((r) => r.status === 'fail') ?? [];
|
|
81
|
-
for (const expect of failedExpects) {
|
|
82
|
-
if (expect.message) {
|
|
83
|
-
this.log(` Error: ${expect.message}`);
|
|
84
|
-
}
|
|
81
|
+
this.log('Result: FAIL');
|
|
82
|
+
const failedExpects = result.results?.filter((r) => r.status === 'fail') ?? [];
|
|
83
|
+
for (const expect of failedExpects) {
|
|
84
|
+
if (expect.message) {
|
|
85
|
+
this.log(` Error: ${expect.message}`);
|
|
85
86
|
}
|
|
86
|
-
this.exit(1);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
+
// Set the code rather than throwing via this.exit(): the exit must apply
|
|
90
|
+
// in both output modes, and process.exitCode cannot be swallowed by the
|
|
91
|
+
// surrounding catch. Mirrors what run_all already does.
|
|
92
|
+
if (!passed) {
|
|
93
|
+
process.exitCode = 1;
|
|
94
|
+
}
|
|
89
95
|
}
|
|
90
96
|
catch (error) {
|
|
97
|
+
if (error instanceof Error && 'oclif' in error)
|
|
98
|
+
throw error;
|
|
91
99
|
if (error instanceof Error) {
|
|
92
100
|
this.error(`Failed to run unit test: ${error.message}`);
|
|
93
101
|
}
|
|
@@ -95,7 +95,15 @@ Results: 4 passed, 1 failed
|
|
|
95
95
|
},
|
|
96
96
|
});
|
|
97
97
|
if (tests.length === 0) {
|
|
98
|
-
|
|
98
|
+
// An empty suite is a normal state (fresh branches have no tests) and
|
|
99
|
+
// is a success, not a failure. Under -o json it must still be parseable,
|
|
100
|
+
// with the same keys the populated path emits below.
|
|
101
|
+
if (flags.output === 'json') {
|
|
102
|
+
this.log(JSON.stringify({ failed: 0, passed: 0, results: [] }, null, 2));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
this.log('No unit tests found');
|
|
106
|
+
}
|
|
99
107
|
return;
|
|
100
108
|
}
|
|
101
109
|
if (flags.output === 'summary') {
|
|
@@ -162,6 +170,8 @@ Results: 4 passed, 1 failed
|
|
|
162
170
|
}
|
|
163
171
|
}
|
|
164
172
|
catch (error) {
|
|
173
|
+
if (error instanceof Error && 'oclif' in error)
|
|
174
|
+
throw error;
|
|
165
175
|
const message = error instanceof Error ? error.message : String(error);
|
|
166
176
|
results.push({
|
|
167
177
|
message,
|
|
@@ -202,6 +212,8 @@ Results: 4 passed, 1 failed
|
|
|
202
212
|
}
|
|
203
213
|
}
|
|
204
214
|
catch (error) {
|
|
215
|
+
if (error instanceof Error && 'oclif' in error)
|
|
216
|
+
throw error;
|
|
205
217
|
if (error instanceof Error) {
|
|
206
218
|
this.error(`Failed to run unit tests: ${error.message}`);
|
|
207
219
|
}
|
|
@@ -61,24 +61,29 @@ Result: PASS (0.25s)
|
|
|
61
61
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
62
62
|
}
|
|
63
63
|
const result = (await response.json());
|
|
64
|
+
// Anything other than 'ok' is a failure -- the API has been observed
|
|
65
|
+
// returning several distinct non-ok statuses, so never enumerate them.
|
|
66
|
+
const passed = result.status === 'ok';
|
|
64
67
|
if (flags.output === 'json') {
|
|
65
68
|
this.log(JSON.stringify(result, null, 2));
|
|
66
69
|
}
|
|
67
70
|
else {
|
|
68
71
|
const timing = result.timing ? ` (${result.timing}s)` : '';
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
else {
|
|
73
|
-
this.log(`Result: FAIL${timing}`);
|
|
74
|
-
if (result.message) {
|
|
75
|
-
this.log(` Error: ${result.message}`);
|
|
76
|
-
}
|
|
77
|
-
this.exit(1);
|
|
72
|
+
this.log(`Result: ${passed ? 'PASS' : 'FAIL'}${timing}`);
|
|
73
|
+
if (!passed && result.message) {
|
|
74
|
+
this.log(` Error: ${result.message}`);
|
|
78
75
|
}
|
|
79
76
|
}
|
|
77
|
+
// Set the code rather than throwing via this.exit(): the exit must apply
|
|
78
|
+
// in both output modes, and process.exitCode cannot be swallowed by the
|
|
79
|
+
// surrounding catch. Mirrors what run_all already does.
|
|
80
|
+
if (!passed) {
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
}
|
|
80
83
|
}
|
|
81
84
|
catch (error) {
|
|
85
|
+
if (error instanceof Error && 'oclif' in error)
|
|
86
|
+
throw error;
|
|
82
87
|
if (error instanceof Error) {
|
|
83
88
|
this.error(`Failed to run workflow test: ${error.message}`);
|
|
84
89
|
}
|
|
@@ -85,7 +85,15 @@ Results: 2 passed, 1 failed
|
|
|
85
85
|
},
|
|
86
86
|
});
|
|
87
87
|
if (tests.length === 0) {
|
|
88
|
-
|
|
88
|
+
// An empty suite is a normal state (fresh branches have no tests) and
|
|
89
|
+
// is a success, not a failure. Under -o json it must still be parseable,
|
|
90
|
+
// with the same keys the populated path emits below.
|
|
91
|
+
if (flags.output === 'json') {
|
|
92
|
+
this.log(JSON.stringify({ failed: 0, passed: 0, results: [] }, null, 2));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
this.log('No workflow tests found');
|
|
96
|
+
}
|
|
89
97
|
return;
|
|
90
98
|
}
|
|
91
99
|
if (flags.output === 'summary') {
|
|
@@ -146,6 +154,8 @@ Results: 2 passed, 1 failed
|
|
|
146
154
|
}
|
|
147
155
|
}
|
|
148
156
|
catch (error) {
|
|
157
|
+
if (error instanceof Error && 'oclif' in error)
|
|
158
|
+
throw error;
|
|
149
159
|
const message = error instanceof Error ? error.message : String(error);
|
|
150
160
|
results.push({
|
|
151
161
|
message,
|
|
@@ -184,6 +194,8 @@ Results: 2 passed, 1 failed
|
|
|
184
194
|
}
|
|
185
195
|
}
|
|
186
196
|
catch (error) {
|
|
197
|
+
if (error instanceof Error && 'oclif' in error)
|
|
198
|
+
throw error;
|
|
187
199
|
if (error instanceof Error) {
|
|
188
200
|
this.error(`Failed to run workflow tests: ${error.message}`);
|
|
189
201
|
}
|
|
@@ -63,26 +63,34 @@ Result: PASS
|
|
|
63
63
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
64
64
|
}
|
|
65
65
|
const result = await response.json();
|
|
66
|
+
// Anything other than 'ok' is a failure -- the API has been observed
|
|
67
|
+
// returning several distinct non-ok statuses, so never enumerate them.
|
|
68
|
+
const passed = result.status === 'ok';
|
|
66
69
|
if (flags.output === 'json') {
|
|
67
70
|
this.log(JSON.stringify(result, null, 2));
|
|
68
71
|
}
|
|
72
|
+
else if (passed) {
|
|
73
|
+
this.log('Result: PASS');
|
|
74
|
+
}
|
|
69
75
|
else {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const failedExpects = result.results?.filter(r => r.status === 'fail') ?? [];
|
|
76
|
-
for (const expect of failedExpects) {
|
|
77
|
-
if (expect.message) {
|
|
78
|
-
this.log(` Error: ${expect.message}`);
|
|
79
|
-
}
|
|
76
|
+
this.log('Result: FAIL');
|
|
77
|
+
const failedExpects = result.results?.filter(r => r.status === 'fail') ?? [];
|
|
78
|
+
for (const expect of failedExpects) {
|
|
79
|
+
if (expect.message) {
|
|
80
|
+
this.log(` Error: ${expect.message}`);
|
|
80
81
|
}
|
|
81
|
-
this.exit(1);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
+
// Set the code rather than throwing via this.exit(): the exit must apply
|
|
85
|
+
// in both output modes, and process.exitCode cannot be swallowed by the
|
|
86
|
+
// surrounding catch. Mirrors what run_all already does.
|
|
87
|
+
if (!passed) {
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
}
|
|
84
90
|
}
|
|
85
91
|
catch (error) {
|
|
92
|
+
if (error instanceof Error && 'oclif' in error)
|
|
93
|
+
throw error;
|
|
86
94
|
if (error instanceof Error) {
|
|
87
95
|
this.error(`Failed to run unit test: ${error.message}`);
|
|
88
96
|
}
|
|
@@ -91,7 +91,15 @@ Results: 2 passed, 1 failed
|
|
|
91
91
|
},
|
|
92
92
|
});
|
|
93
93
|
if (tests.length === 0) {
|
|
94
|
-
|
|
94
|
+
// An empty suite is a normal state (fresh branches have no tests) and
|
|
95
|
+
// is a success, not a failure. Under -o json it must still be parseable,
|
|
96
|
+
// with the same keys the populated path emits below.
|
|
97
|
+
if (flags.output === 'json') {
|
|
98
|
+
this.log(JSON.stringify({ failed: 0, passed: 0, results: [] }, null, 2));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
this.log('No unit tests found');
|
|
102
|
+
}
|
|
95
103
|
return;
|
|
96
104
|
}
|
|
97
105
|
if (flags.output === 'summary') {
|
|
@@ -161,6 +169,8 @@ Results: 2 passed, 1 failed
|
|
|
161
169
|
}
|
|
162
170
|
}
|
|
163
171
|
catch (error) {
|
|
172
|
+
if (error instanceof Error && 'oclif' in error)
|
|
173
|
+
throw error;
|
|
164
174
|
const message = error instanceof Error ? error.message : String(error);
|
|
165
175
|
results.push({
|
|
166
176
|
message,
|
|
@@ -202,6 +212,8 @@ Results: 2 passed, 1 failed
|
|
|
202
212
|
}
|
|
203
213
|
}
|
|
204
214
|
catch (error) {
|
|
215
|
+
if (error instanceof Error && 'oclif' in error)
|
|
216
|
+
throw error;
|
|
205
217
|
if (error instanceof Error) {
|
|
206
218
|
this.error(`Failed to run unit tests: ${error.message}`);
|
|
207
219
|
}
|
|
@@ -55,24 +55,29 @@ Result: PASS (1.234s)
|
|
|
55
55
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
56
56
|
}
|
|
57
57
|
const result = await response.json();
|
|
58
|
+
// Anything other than 'ok' is a failure -- the API has been observed
|
|
59
|
+
// returning several distinct non-ok statuses, so never enumerate them.
|
|
60
|
+
const passed = result.status === 'ok';
|
|
58
61
|
if (flags.output === 'json') {
|
|
59
62
|
this.log(JSON.stringify(result, null, 2));
|
|
60
63
|
}
|
|
61
64
|
else {
|
|
62
|
-
const timing = `(${result.timing.toFixed(3)}s)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
else {
|
|
67
|
-
this.log(`Result: FAIL ${timing}`);
|
|
68
|
-
if (result.message) {
|
|
69
|
-
this.log(` Error: ${result.message}`);
|
|
70
|
-
}
|
|
71
|
-
this.exit(1);
|
|
65
|
+
const timing = typeof result.timing === 'number' ? ` (${result.timing.toFixed(3)}s)` : '';
|
|
66
|
+
this.log(`Result: ${passed ? 'PASS' : 'FAIL'}${timing}`);
|
|
67
|
+
if (!passed && result.message) {
|
|
68
|
+
this.log(` Error: ${result.message}`);
|
|
72
69
|
}
|
|
73
70
|
}
|
|
71
|
+
// Set the code rather than throwing via this.exit(): the exit must apply
|
|
72
|
+
// in both output modes, and process.exitCode cannot be swallowed by the
|
|
73
|
+
// surrounding catch. Mirrors what run_all already does.
|
|
74
|
+
if (!passed) {
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
}
|
|
74
77
|
}
|
|
75
78
|
catch (error) {
|
|
79
|
+
if (error instanceof Error && 'oclif' in error)
|
|
80
|
+
throw error;
|
|
76
81
|
if (error instanceof Error) {
|
|
77
82
|
this.error(`Failed to run workflow test: ${error.message}`);
|
|
78
83
|
}
|
|
@@ -82,7 +82,15 @@ Results: 2 passed, 1 failed (2.691s total)
|
|
|
82
82
|
},
|
|
83
83
|
});
|
|
84
84
|
if (tests.length === 0) {
|
|
85
|
-
|
|
85
|
+
// An empty suite is a normal state (fresh branches have no tests) and
|
|
86
|
+
// is a success, not a failure. Under -o json it must still be parseable,
|
|
87
|
+
// with the same keys the populated path emits below.
|
|
88
|
+
if (flags.output === 'json') {
|
|
89
|
+
this.log(JSON.stringify({ failed: 0, passed: 0, results: [], total_timing: 0 }, null, 2));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
this.log('No workflow tests found');
|
|
93
|
+
}
|
|
86
94
|
return;
|
|
87
95
|
}
|
|
88
96
|
if (flags.output === 'summary') {
|
|
@@ -134,9 +142,9 @@ Results: 2 passed, 1 failed (2.691s total)
|
|
|
134
142
|
timing: runResult.timing,
|
|
135
143
|
};
|
|
136
144
|
results.push(result);
|
|
137
|
-
totalTiming += runResult.timing;
|
|
145
|
+
totalTiming += runResult.timing ?? 0;
|
|
138
146
|
if (flags.output === 'summary') {
|
|
139
|
-
const timing = `(${runResult.timing.toFixed(3)}s)`;
|
|
147
|
+
const timing = `(${(runResult.timing ?? 0).toFixed(3)}s)`;
|
|
140
148
|
if (passed) {
|
|
141
149
|
log(`PASS ${test.name} ${timing}`);
|
|
142
150
|
}
|
|
@@ -149,6 +157,8 @@ Results: 2 passed, 1 failed (2.691s total)
|
|
|
149
157
|
}
|
|
150
158
|
}
|
|
151
159
|
catch (error) {
|
|
160
|
+
if (error instanceof Error && 'oclif' in error)
|
|
161
|
+
throw error;
|
|
152
162
|
const message = error instanceof Error ? error.message : String(error);
|
|
153
163
|
results.push({
|
|
154
164
|
message,
|
|
@@ -189,6 +199,8 @@ Results: 2 passed, 1 failed (2.691s total)
|
|
|
189
199
|
}
|
|
190
200
|
}
|
|
191
201
|
catch (error) {
|
|
202
|
+
if (error instanceof Error && 'oclif' in error)
|
|
203
|
+
throw error;
|
|
192
204
|
if (error instanceof Error) {
|
|
193
205
|
this.error(`Failed to run workflow tests: ${error.message}`);
|
|
194
206
|
}
|