@ultimat3/cli 30.4.0 → 30.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +28 -28
- package/src/exercise-findings.ts +27 -11
- package/wiki/Testing.md +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/cli",
|
|
3
|
-
"version": "30.
|
|
3
|
+
"version": "30.5.0",
|
|
4
4
|
"description": "The `x` binary: new, dev, build, verify, generate, db, mcp, doctor, deploy",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ultimate",
|
|
@@ -53,33 +53,33 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@babel/core": "^8.0.7",
|
|
56
|
-
"@ultimat3/action": "30.
|
|
57
|
-
"@ultimat3/admin": "30.
|
|
58
|
-
"@ultimat3/ai": "30.
|
|
59
|
-
"@ultimat3/auth": "30.
|
|
60
|
-
"@ultimat3/cache": "30.
|
|
61
|
-
"@ultimat3/core": "30.
|
|
62
|
-
"@ultimat3/db": "30.
|
|
63
|
-
"@ultimat3/entity": "30.
|
|
64
|
-
"@ultimat3/flags": "30.
|
|
65
|
-
"@ultimat3/http": "30.
|
|
66
|
-
"@ultimat3/i18n": "30.
|
|
67
|
-
"@ultimat3/jobs": "30.
|
|
68
|
-
"@ultimat3/mail": "30.
|
|
69
|
-
"@ultimat3/manifest": "30.
|
|
70
|
-
"@ultimat3/mcp": "30.
|
|
71
|
-
"@ultimat3/money": "30.
|
|
72
|
-
"@ultimat3/notify": "30.
|
|
73
|
-
"@ultimat3/policy": "30.
|
|
74
|
-
"@ultimat3/pwa": "30.
|
|
75
|
-
"@ultimat3/query": "30.
|
|
76
|
-
"@ultimat3/realtime": "30.
|
|
77
|
-
"@ultimat3/render": "30.
|
|
78
|
-
"@ultimat3/schema": "30.
|
|
79
|
-
"@ultimat3/seo": "30.
|
|
80
|
-
"@ultimat3/storage": "30.
|
|
81
|
-
"@ultimat3/testing": "30.
|
|
82
|
-
"@ultimat3/time": "30.
|
|
56
|
+
"@ultimat3/action": "30.5.0",
|
|
57
|
+
"@ultimat3/admin": "30.5.0",
|
|
58
|
+
"@ultimat3/ai": "30.5.0",
|
|
59
|
+
"@ultimat3/auth": "30.5.0",
|
|
60
|
+
"@ultimat3/cache": "30.5.0",
|
|
61
|
+
"@ultimat3/core": "30.5.0",
|
|
62
|
+
"@ultimat3/db": "30.5.0",
|
|
63
|
+
"@ultimat3/entity": "30.5.0",
|
|
64
|
+
"@ultimat3/flags": "30.5.0",
|
|
65
|
+
"@ultimat3/http": "30.5.0",
|
|
66
|
+
"@ultimat3/i18n": "30.5.0",
|
|
67
|
+
"@ultimat3/jobs": "30.5.0",
|
|
68
|
+
"@ultimat3/mail": "30.5.0",
|
|
69
|
+
"@ultimat3/manifest": "30.5.0",
|
|
70
|
+
"@ultimat3/mcp": "30.5.0",
|
|
71
|
+
"@ultimat3/money": "30.5.0",
|
|
72
|
+
"@ultimat3/notify": "30.5.0",
|
|
73
|
+
"@ultimat3/policy": "30.5.0",
|
|
74
|
+
"@ultimat3/pwa": "30.5.0",
|
|
75
|
+
"@ultimat3/query": "30.5.0",
|
|
76
|
+
"@ultimat3/realtime": "30.5.0",
|
|
77
|
+
"@ultimat3/render": "30.5.0",
|
|
78
|
+
"@ultimat3/schema": "30.5.0",
|
|
79
|
+
"@ultimat3/seo": "30.5.0",
|
|
80
|
+
"@ultimat3/storage": "30.5.0",
|
|
81
|
+
"@ultimat3/testing": "30.5.0",
|
|
82
|
+
"@ultimat3/time": "30.5.0",
|
|
83
83
|
"babel-preset-solid": "^1.9.16"
|
|
84
84
|
}
|
|
85
85
|
}
|
package/src/exercise-findings.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
renderFixShellArg,
|
|
13
13
|
} from '@ultimat3/core';
|
|
14
14
|
import type { ExerciseRecord } from '@ultimat3/testing';
|
|
15
|
-
import { planSweepFindings, tenantProofFindings } from '@ultimat3/testing';
|
|
15
|
+
import { crashEffectPlace, planSweepFindings, tenantProofFindings } from '@ultimat3/testing';
|
|
16
16
|
import type { Finding } from './output';
|
|
17
17
|
import { renderTable } from './table';
|
|
18
18
|
|
|
@@ -44,6 +44,20 @@ export const exerciseCounts = (records: readonly ExerciseRecord[]): ExerciseCoun
|
|
|
44
44
|
};
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* A 5xx's own words — the cause testing recorded (credential values already masked there) and the
|
|
49
|
+
* fields a validation failure names. The statement is inside the cause, so it is not said twice.
|
|
50
|
+
*/
|
|
51
|
+
const defectOf = (record: ExerciseRecord): string => {
|
|
52
|
+
const defect = record.defect;
|
|
53
|
+
if (defect === undefined) return '';
|
|
54
|
+
const fields =
|
|
55
|
+
defect.fields === undefined
|
|
56
|
+
? ''
|
|
57
|
+
: ` at ${defect.fields.map((field) => renderCauseValue(field)).join(', ')}`;
|
|
58
|
+
return ` — cause: ${renderCauseValue(defect.cause)}${fields}`;
|
|
59
|
+
};
|
|
60
|
+
|
|
47
61
|
/**
|
|
48
62
|
* `X_CONTRACT_EXERCISE_UNREACHED`, `@ultimat3/action`'s code for the same silence one level up: a
|
|
49
63
|
* primitive no run got to as an allowed actor. The fix is the override the pass printed, or the
|
|
@@ -56,7 +70,7 @@ const unreachedFinding = (record: ExerciseRecord): Finding => {
|
|
|
56
70
|
const why = record.reason === undefined ? '' : ` (${renderCauseValue(record.reason)})`;
|
|
57
71
|
return {
|
|
58
72
|
code: 'X_CONTRACT_EXERCISE_UNREACHED',
|
|
59
|
-
cause: `${record.kind} ${renderCauseValue(record.name)} never reached its handler as an allowed actor in the exercise pass${stopped}${why}`,
|
|
73
|
+
cause: `${record.kind} ${renderCauseValue(record.name)} never reached its handler as an allowed actor in the exercise pass${stopped}${why}${defectOf(record)}`,
|
|
60
74
|
fix:
|
|
61
75
|
record.fix ??
|
|
62
76
|
`defineExercise({ ${override} }) # paste the entry into ${EXERCISE_FILE}, or { off: '<why>' }; then: x exercise ${name} --json`,
|
|
@@ -75,27 +89,29 @@ const crashFindingsOf = (record: ExerciseRecord): readonly CrashFinding[] =>
|
|
|
75
89
|
|
|
76
90
|
/**
|
|
77
91
|
* `X_JOB_CRASH_DIVERGED`, testing's code: a reached job, killed at a step boundary, made an effect
|
|
78
|
-
*
|
|
79
|
-
*
|
|
92
|
+
* a different number of times than a clean run. Where it ran — outside every `step.run`, or inside
|
|
93
|
+
* one whose body re-runs — is testing's `crashEffectPlace`, the phrase its error uses too. One
|
|
94
|
+
* finding per effect and place, every boundary that showed it named in the cause.
|
|
80
95
|
*/
|
|
81
96
|
const crashFindings = (record: ExerciseRecord): readonly Finding[] => {
|
|
82
97
|
const groups = new Map<string, CrashFinding[]>();
|
|
83
98
|
for (const one of crashFindingsOf(record)) {
|
|
84
|
-
const key = `${one.effect}\u0000${one.after ?? ''}`;
|
|
99
|
+
const key = `${one.effect}\u0000${one.after ?? ''}\u0000${one.inside ?? ''}`;
|
|
85
100
|
groups.set(key, [...(groups.get(key) ?? []), one]);
|
|
86
101
|
}
|
|
87
102
|
const job = renderFixShellArg(record.name, '<job>');
|
|
88
103
|
return [...groups.values()].map((group): Finding => {
|
|
89
104
|
const [first] = group as [CrashFinding, ...CrashFinding[]];
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
105
|
+
const { place, remedy } = crashEffectPlace(first);
|
|
106
|
+
const how =
|
|
107
|
+
first.killed > first.clean
|
|
108
|
+
? 'the retry after a crash makes it again'
|
|
109
|
+
: 'the crash skipped it';
|
|
94
110
|
const at = group.map((one) => JSON.stringify(one.boundary)).join(', ');
|
|
95
111
|
return {
|
|
96
112
|
code: 'X_JOB_CRASH_DIVERGED',
|
|
97
|
-
cause: `job ${renderCauseValue(record.name)} killed at ${at} made ${renderCauseValue(first.effect)} ${first.killed}× against ${first.clean}× in a clean run:
|
|
98
|
-
fix: `x exercise ${job} --json # in job(${renderFixLiteral(record.name, '<job>')})
|
|
113
|
+
cause: `job ${renderCauseValue(record.name)} killed at ${at} made ${renderCauseValue(first.effect)} ${first.killed}× against ${first.clean}× in a clean run: ${place}, so ${how}`,
|
|
114
|
+
fix: `x exercise ${job} --json # in job(${renderFixLiteral(record.name, '<job>')}), for ${renderFixLiteral(first.effect, '<effect>')}: ${remedy}`,
|
|
99
115
|
docs: ERROR_DOCS_URL,
|
|
100
116
|
at: `${record.kind} ${record.name}`,
|
|
101
117
|
};
|
package/wiki/Testing.md
CHANGED
|
@@ -92,6 +92,7 @@ Any test that can pass twice and fail the third time is worse than no test — i
|
|
|
92
92
|
| **Frozen clock** | time starts at a fixed instant. `clock.advance('3d')` moves it, and it also drives `step.sleep` and cron in tests |
|
|
93
93
|
| **Seeded RNG** | `Math.random`, `crypto.randomUUID`, and Bun's RNG are seeded per test file from its path — reproducible, distinct across files |
|
|
94
94
|
| **Sealed network** | a `fetch`, a `new WebSocket(…)` or a `Bun.connect(…)` to anywhere not explicitly mocked or allowed **fails the test** with `X_TEST_NETWORK_SEALED`, naming the URL and the fix — the three dials share one gate, so the allow-list and `offline()` apply to all of them — `offline()` refuses a loopback dial too (`X_TEST_NETWORK_OFFLINE`). A dial that goes around those three globals (a `node:net` socket, `Bun.udpSocket`) is not sealed |
|
|
95
|
+
| **Sealed names** | `Bun.dns.lookup`, `dns.lookup` and `dns.promises.lookup` ask the same gate: a host a `mockFetch` route names resolves to `MOCKED_HOST_ADDRESS` (`11.11.11.11`, public, so an SSRF screen such as `screenDial` passes it and the dial it pins still meets the mock by its `Host`); this machine, an address literal and an `allowHost` name resolve for real; offline refuses every name (`X_TEST_NETWORK_OFFLINE`); any other name is `X_TEST_NETWORK_SEALED`. A RegExp route names a host it matches at the origin or spells in its source. Not covered: `lookup` bound by a named ESM import (`import { lookup } from 'node:dns'`) before the seal, and `dns.resolve*` |
|
|
95
96
|
| **Fixed timezone + locale** | `UTC` and `en-US` unless a test declares otherwise; a tz-dependent bug fails deterministically |
|
|
96
97
|
| **Ordered concurrency** | job workers in tests run deterministically; `runJobs()` drains the queue synchronously |
|
|
97
98
|
| **Per-test reset** | before EVERY test, not every file, the preload puts the jobs event bus back (`installPerTestReset()`): an answer one test published never resumes the next test's waiting run, with or without `runJobs`. A test that never loaded `@ultimat3/jobs` pays nothing. `As of 2026-10` |
|
|
@@ -1001,7 +1002,7 @@ shape, same unreached code. `As of 2026-11`.
|
|
|
1001
1002
|
| Overrides | `apps/web/exercise.ts`: `export const exercise = defineExercise({ publishPost: { actor, input }, sendInvoice: { off: '<why>' } })`. Each value is `.contract({ exercise })`'s option. An unknown name is `X_DECLARATION_UNKNOWN`; a blank `off` is `X_CONTRACT_EXERCISE_UNDECLARED` |
|
|
1002
1003
|
| A handler that calls a vendor | `defineExercise({ … }, { setup: () => { mockFetch('https://api.vendor.test/*', handler); } })` — or `setup: () => cassette(import.meta.dir, 'exercise')`. Runs once in the exercise process, after the seal and before the first row; what it returns is disposed after the pass. A call a setup route answers is reached, not refused. `allowHost` in it is `X_EXERCISE_SETUP_UNSEALED` |
|
|
1003
1004
|
| Rows a generated value cannot satisfy | a cross-column `c.satisfies(…)` reads `X_SEED_GRAPH_UNSATISFIED`: `defineExercise({ … }, { factories: { bosses: defineFactory(bosses, { defaults: (i) => ({ … }) }) } })`, keyed by entity name, the graph's keys kept. A column that follows from a key or reference the graph picked: `derive: (row) => ({ scopeKey: … })` beside `build`/`reset`, reading `row.ownerOrgId`. A partial unique whose columns the factory builds is the database's to judge. An unknown name is `X_DECLARATION_UNKNOWN` |
|
|
1004
|
-
| Report | `.x/exercise.json` and `--json`: `{ seed, counts, records[] }`, each record `{ kind, name, outcome, code?, reason?, statements, effects, ms, override?, fix?, tenant?, crash? }` — `fix` where no override reaches it. The fields before `tenant` are A's call. The human table's `tenant` and `crash` columns are the verdicts, `-` where none ran |
|
|
1005
|
+
| Report | `.x/exercise.json` and `--json`: `{ seed, counts, records[] }`, each record `{ kind, name, outcome, code?, reason?, defect?, statements, effects, ms, override?, fix?, tenant?, crash? }` — `fix` where no override reaches it; `defect` on a 5xx: `{ cause, statement?, fields? }`, the error's own cause (a value the input held under a credential key or a `.sensitive()` field masked `[redacted]`), the SQL a database refusal names and the paths a validation failure names, all carried into the `X_CONTRACT_EXERCISE_UNREACHED` finding. The fields before `tenant` are A's call. The human table's `tenant` and `crash` columns are the verdicts, `-` where none ran |
|
|
1005
1006
|
| Gate | the `exercise` step, after `job`, runs `x exercise --json` as a child and is red on its findings: `X_CONTRACT_EXERCISE_UNREACHED`, `X_TENANT_LEAK`, `X_TENANT_WRITE_CROSSED`, `X_JOB_CRASH_DIVERGED`, and — on a whole pass, not `x exercise <name>` — `X_ENTITY_TENANT_UNDECLARED`. `unreached` is never sampled or skipped: it is a finding whose `fix:` is the entry to paste |
|
|
1006
1007
|
|
|
1007
1008
|
```bash
|
|
@@ -1063,7 +1064,7 @@ actor, after the tenant proof. Source: `packages/testing/src/exercise-crash.ts`,
|
|
|
1063
1064
|
|
|
1064
1065
|
| `crash.verdict` | When | Red |
|
|
1065
1066
|
|---|---|---|
|
|
1066
|
-
| `diverged` | an effect made more often by a killed run: `findings[]` of `{ boundary, mode, effect, clean, killed, after? }` | yes: `X_JOB_CRASH_DIVERGED`, one per effect and the step it
|
|
1067
|
+
| `diverged` | an effect made more often by a killed run: `findings[]` of `{ boundary, mode, effect, clean, killed, after?, inside? }` — `inside` the step whose body made it (`@ultimat3/jobs`' `currentStep()`), `after` the last step completed before it. An effect inside a step that did not complete, made FEWER times by a kill that spent one of its attempts, is not a divergence | yes: `X_JOB_CRASH_DIVERGED`, one per effect and place, every boundary named. Inside a step the fix keys its call on `${jobId}:<step>`; outside every step it wraps it in `step.run` |
|
|
1067
1068
|
| `held` | otherwise; `boundaries` and `reruns` (lost answers — at-least-once, never red) counted | no |
|
|
1068
1069
|
| `unproven` | the proof itself threw: `reason` | no |
|
|
1069
1070
|
|