@ultimat3/cli 30.4.0 → 30.6.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 +5 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/cli",
|
|
3
|
-
"version": "30.
|
|
3
|
+
"version": "30.6.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.6.0",
|
|
57
|
+
"@ultimat3/admin": "30.6.0",
|
|
58
|
+
"@ultimat3/ai": "30.6.0",
|
|
59
|
+
"@ultimat3/auth": "30.6.0",
|
|
60
|
+
"@ultimat3/cache": "30.6.0",
|
|
61
|
+
"@ultimat3/core": "30.6.0",
|
|
62
|
+
"@ultimat3/db": "30.6.0",
|
|
63
|
+
"@ultimat3/entity": "30.6.0",
|
|
64
|
+
"@ultimat3/flags": "30.6.0",
|
|
65
|
+
"@ultimat3/http": "30.6.0",
|
|
66
|
+
"@ultimat3/i18n": "30.6.0",
|
|
67
|
+
"@ultimat3/jobs": "30.6.0",
|
|
68
|
+
"@ultimat3/mail": "30.6.0",
|
|
69
|
+
"@ultimat3/manifest": "30.6.0",
|
|
70
|
+
"@ultimat3/mcp": "30.6.0",
|
|
71
|
+
"@ultimat3/money": "30.6.0",
|
|
72
|
+
"@ultimat3/notify": "30.6.0",
|
|
73
|
+
"@ultimat3/policy": "30.6.0",
|
|
74
|
+
"@ultimat3/pwa": "30.6.0",
|
|
75
|
+
"@ultimat3/query": "30.6.0",
|
|
76
|
+
"@ultimat3/realtime": "30.6.0",
|
|
77
|
+
"@ultimat3/render": "30.6.0",
|
|
78
|
+
"@ultimat3/schema": "30.6.0",
|
|
79
|
+
"@ultimat3/seo": "30.6.0",
|
|
80
|
+
"@ultimat3/storage": "30.6.0",
|
|
81
|
+
"@ultimat3/testing": "30.6.0",
|
|
82
|
+
"@ultimat3/time": "30.6.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` |
|
|
@@ -558,7 +559,7 @@ What the rendered result is then held to: [Interface rules](Interface-Rules).
|
|
|
558
559
|
| `mail` | `outbox()` · `lastTo(address)` · `failOnce(mail)` | the preload |
|
|
559
560
|
| `network` | `offline()` · `drop()` · `online()` · `state()` | the preload |
|
|
560
561
|
| `push` | `subscribe(actorId, { locale })` · `sent()` — each delivery decrypted, as the device shows it · `answerOnce(status)` | the preload |
|
|
561
|
-
| `runJobs` | enqueue+drain, then `enqueue()` `drain()` `due()` `inFlight()` `depth()` → [A job under `runJobs`](#a-job-under-runjobs) | the preload |
|
|
562
|
+
| `runJobs` | enqueue+drain, then `enqueue()` `drain()` `tick()` `due()` `inFlight()` `depth()` → [A job under `runJobs`](#a-job-under-runjobs) | the preload |
|
|
562
563
|
| `statements` | `all()` · `count(fingerprint?)` · `shapes()` — and an N+1 fails the test | the preload |
|
|
563
564
|
| `page` | `goto` · `gotoStreamed` · `getByRole` · `evaluate` · `waitForServiceWorker` | a browser driver |
|
|
564
565
|
| `budget` | `jsBytes(route)` off the built output | a browser driver |
|
|
@@ -994,6 +995,7 @@ shape, same unreached code. `As of 2026-11`.
|
|
|
994
995
|
| Actor | the first of `x policy explain`'s actors the policy allows, in the primitive's tenant. None allowed is `refused`, by design |
|
|
995
996
|
| Input | the override, then an id field whose stem names a seeded entity (`postId` → `posts`), then the schema's minimal draw. A wrong guess shows as `unreached` |
|
|
996
997
|
| A job's org | the field(s) its `tenant(input)` reads — any name, nested, left out of the draw, or under a parent the draw left out (a scoped backfill's `payload.input.orgId`) — are set to the tenant it is enqueued in, found by reading them: A's for the control, B's for the tenant proof (B's own job, carrying A's ids). A `tenant` no input field moves (one fixed org) is `unreached`, `X_JOB_TENANT_MISMATCH`, its `fix:` the declaration: `tenant: (input) => input.orgId` |
|
|
998
|
+
| A job's run | its own execution alone: enqueued on a queue holding nothing else, run by one worker pass. What it enqueues is left queued, as an action's is — an `enqueue` effect on its record, searched by the tenant proof, never run in its room. Its `statements`, effects, verdict and scale and plan sweeps are its own body's: a job that queues one child per row is flat, a body that reads each row is still `X_QUERY_SCALES_WITH_ROWS`. A child is judged as its own primitive. The crash proof too: its clean and killed runs claim only the job's own row, a child compared as its `enqueue:` effect |
|
|
997
999
|
| A job's failure | judged by the action rule: a coded refusal under 500 (an app's 409) is `unreached` under its own code, never a defect; an uncoded throw or a coded 5xx is `X_CONTRACT_DEFECT` |
|
|
998
1000
|
| Async input | an override's `input` may be `async`: it is awaited inside the call's request, as the call's actor, so it can mint what the action checks (a one-time token, a passkey assertion). `x drill`'s load skips an `async` input and says why |
|
|
999
1001
|
| Room | network sealed, mail captured, jobs in memory, clock frozen — from before the app loads and the scratch database boots, as `bun test`'s preload does, so the embedded Postgres never sees its clock jump. A call out is refused and counted, so the primitive is `unreached` |
|
|
@@ -1001,7 +1003,7 @@ shape, same unreached code. `As of 2026-11`.
|
|
|
1001
1003
|
| 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
1004
|
| 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
1005
|
| 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 |
|
|
1006
|
+
| 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
1007
|
| 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
1008
|
|
|
1007
1009
|
```bash
|
|
@@ -1063,7 +1065,7 @@ actor, after the tenant proof. Source: `packages/testing/src/exercise-crash.ts`,
|
|
|
1063
1065
|
|
|
1064
1066
|
| `crash.verdict` | When | Red |
|
|
1065
1067
|
|---|---|---|
|
|
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
|
|
1068
|
+
| `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
1069
|
| `held` | otherwise; `boundaries` and `reruns` (lost answers — at-least-once, never red) counted | no |
|
|
1068
1070
|
| `unproven` | the proof itself threw: `reason` | no |
|
|
1069
1071
|
|