@lovrozagar/oat 0.1.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/LICENSE +21 -0
- package/README.md +401 -0
- package/bin/oat.js +2 -0
- package/dist/cli.js +386 -0
- package/dist/config/define-config.d.ts +165 -0
- package/dist/config/define-config.d.ts.map +1 -0
- package/dist/config/define-config.js +24 -0
- package/dist/config/define-config.js.map +1 -0
- package/dist/config/load.d.ts +10 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +61 -0
- package/dist/config/load.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +9 -0
- package/dist/reference/defects.d.ts +254 -0
- package/dist/reference/defects.d.ts.map +1 -0
- package/dist/reference/defects.js +265 -0
- package/dist/reference/defects.js.map +1 -0
- package/dist/reference/dialect.d.ts +104 -0
- package/dist/reference/dialect.d.ts.map +1 -0
- package/dist/reference/dialect.js +174 -0
- package/dist/reference/dialect.js.map +1 -0
- package/dist/reference/http.d.ts +79 -0
- package/dist/reference/http.d.ts.map +1 -0
- package/dist/reference/http.js +889 -0
- package/dist/reference/http.js.map +1 -0
- package/dist/reference/model.d.ts +56 -0
- package/dist/reference/model.d.ts.map +1 -0
- package/dist/reference/model.js +156 -0
- package/dist/reference/model.js.map +1 -0
- package/dist/reference/query.d.ts +45 -0
- package/dist/reference/query.d.ts.map +1 -0
- package/dist/reference/query.js +353 -0
- package/dist/reference/query.js.map +1 -0
- package/dist/reference/spec.d.ts +13 -0
- package/dist/reference/spec.d.ts.map +1 -0
- package/dist/reference/spec.js +547 -0
- package/dist/reference/spec.js.map +1 -0
- package/dist/reference/store-api.d.ts +59 -0
- package/dist/reference/store-api.d.ts.map +1 -0
- package/dist/reference/store-api.js +76 -0
- package/dist/reference/store-api.js.map +1 -0
- package/dist/reference/stores/memory.d.ts +27 -0
- package/dist/reference/stores/memory.d.ts.map +1 -0
- package/dist/reference/stores/memory.js +97 -0
- package/dist/reference/stores/memory.js.map +1 -0
- package/dist/reference/stores/postgres.d.ts +61 -0
- package/dist/reference/stores/postgres.d.ts.map +1 -0
- package/dist/reference/stores/postgres.js +406 -0
- package/dist/reference/stores/postgres.js.map +1 -0
- package/dist/reference/stores/sqlite-driver.d.ts +54 -0
- package/dist/reference/stores/sqlite-driver.d.ts.map +1 -0
- package/dist/reference/stores/sqlite-driver.js +99 -0
- package/dist/reference/stores/sqlite-driver.js.map +1 -0
- package/dist/reference/stores/sqlite.d.ts +68 -0
- package/dist/reference/stores/sqlite.d.ts.map +1 -0
- package/dist/reference/stores/sqlite.js +464 -0
- package/dist/reference/stores/sqlite.js.map +1 -0
- package/dist/report/console.d.ts +33 -0
- package/dist/report/console.d.ts.map +1 -0
- package/dist/report/console.js +237 -0
- package/dist/report/console.js.map +1 -0
- package/dist/report/matrix.d.ts +131 -0
- package/dist/report/matrix.d.ts.map +1 -0
- package/dist/report/matrix.js +865 -0
- package/dist/report/matrix.js.map +1 -0
- package/dist/report/render.d.ts +66 -0
- package/dist/report/render.d.ts.map +1 -0
- package/dist/report/render.js +423 -0
- package/dist/report/render.js.map +1 -0
- package/dist/runtime/async.d.ts +34 -0
- package/dist/runtime/async.d.ts.map +1 -0
- package/dist/runtime/async.js +130 -0
- package/dist/runtime/async.js.map +1 -0
- package/dist/runtime/auth.d.ts +46 -0
- package/dist/runtime/auth.d.ts.map +1 -0
- package/dist/runtime/auth.js +236 -0
- package/dist/runtime/auth.js.map +1 -0
- package/dist/runtime/checks.d.ts +89 -0
- package/dist/runtime/checks.d.ts.map +1 -0
- package/dist/runtime/checks.js +3400 -0
- package/dist/runtime/checks.js.map +1 -0
- package/dist/runtime/client.d.ts +65 -0
- package/dist/runtime/client.d.ts.map +1 -0
- package/dist/runtime/client.js +122 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/finding.d.ts +50 -0
- package/dist/runtime/finding.d.ts.map +1 -0
- package/dist/runtime/finding.js +50 -0
- package/dist/runtime/finding.js.map +1 -0
- package/dist/runtime/fixture.d.ts +20 -0
- package/dist/runtime/fixture.d.ts.map +1 -0
- package/dist/runtime/fixture.js +175 -0
- package/dist/runtime/fixture.js.map +1 -0
- package/dist/runtime/progress.d.ts +41 -0
- package/dist/runtime/progress.d.ts.map +1 -0
- package/dist/runtime/progress.js +186 -0
- package/dist/runtime/progress.js.map +1 -0
- package/dist/runtime/run.d.ts +69 -0
- package/dist/runtime/run.d.ts.map +1 -0
- package/dist/runtime/run.js +575 -0
- package/dist/runtime/run.js.map +1 -0
- package/dist/runtime/teardown.d.ts +38 -0
- package/dist/runtime/teardown.d.ts.map +1 -0
- package/dist/runtime/teardown.js +129 -0
- package/dist/runtime/teardown.js.map +1 -0
- package/dist/runtime/validate.d.ts +31 -0
- package/dist/runtime/validate.d.ts.map +1 -0
- package/dist/runtime/validate.js +111 -0
- package/dist/runtime/validate.js.map +1 -0
- package/dist/runtime/world.d.ts +49 -0
- package/dist/runtime/world.d.ts.map +1 -0
- package/dist/runtime/world.js +119 -0
- package/dist/runtime/world.js.map +1 -0
- package/dist/spec/collection.d.ts +31 -0
- package/dist/spec/collection.d.ts.map +1 -0
- package/dist/spec/collection.js +121 -0
- package/dist/spec/collection.js.map +1 -0
- package/dist/spec/conventions.d.ts +103 -0
- package/dist/spec/conventions.d.ts.map +1 -0
- package/dist/spec/conventions.js +275 -0
- package/dist/spec/conventions.js.map +1 -0
- package/dist/spec/extensions.d.ts +74 -0
- package/dist/spec/extensions.d.ts.map +1 -0
- package/dist/spec/extensions.js +335 -0
- package/dist/spec/extensions.js.map +1 -0
- package/dist/spec/graph.d.ts +89 -0
- package/dist/spec/graph.d.ts.map +1 -0
- package/dist/spec/graph.js +318 -0
- package/dist/spec/graph.js.map +1 -0
- package/dist/spec/load.d.ts +36 -0
- package/dist/spec/load.d.ts.map +1 -0
- package/dist/spec/load.js +166 -0
- package/dist/spec/load.js.map +1 -0
- package/dist/spec/types.d.ts +57 -0
- package/dist/spec/types.d.ts.map +1 -0
- package/dist/spec/types.js +24 -0
- package/dist/spec/types.js.map +1 -0
- package/labs/README.md +130 -0
- package/labs/annotated-openapi.yaml +311 -0
- package/labs/anyrow.config.ts +107 -0
- package/labs/demo.mjs +124 -0
- package/labs/local.config.ts +41 -0
- package/labs/minimal.config.ts +32 -0
- package/package.json +80 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run orchestrator: model → world → checks → findings.
|
|
3
|
+
*
|
|
4
|
+
* The world is seeded once and outlives every check. A seeding failure is recorded once and
|
|
5
|
+
* everything downstream is reported BLOCKED against that single cause, rather than re-failing
|
|
6
|
+
* per case — which is what made prior attempts produce dozens of failures from one broken fixture.
|
|
7
|
+
*/
|
|
8
|
+
import { buildModel } from "../spec/graph.js";
|
|
9
|
+
import { dereference, loadSpec } from "../spec/load.js";
|
|
10
|
+
import { createPrincipal } from "./auth.js";
|
|
11
|
+
import { CHECKS } from "./checks.js";
|
|
12
|
+
import { Client } from "./client.js";
|
|
13
|
+
import { FindingCollector } from "./finding.js";
|
|
14
|
+
import { Ledger } from "./teardown.js";
|
|
15
|
+
import { SchemaValidator } from "./validate.js";
|
|
16
|
+
import { SeedError, fillPath, resolveScope, seedCohort } from "./world.js";
|
|
17
|
+
function readPointer(body, pointer) {
|
|
18
|
+
const path = pointer.replace(/^\$\.?/, "").split(".").filter(Boolean);
|
|
19
|
+
let node = body;
|
|
20
|
+
for (const segment of path) {
|
|
21
|
+
if (node === null || typeof node !== "object")
|
|
22
|
+
return undefined;
|
|
23
|
+
node = node[segment];
|
|
24
|
+
}
|
|
25
|
+
return node;
|
|
26
|
+
}
|
|
27
|
+
function sameTenant(a, b) {
|
|
28
|
+
const keys = [...new Set([...Object.keys(a), ...Object.keys(b)])];
|
|
29
|
+
if (keys.length === 0)
|
|
30
|
+
return true;
|
|
31
|
+
return keys.every((key) => a[key] === b[key]);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Removes principals the run provisioned.
|
|
35
|
+
*
|
|
36
|
+
* Where a flow registers a throwaway account, the account itself is a fixture — and usually the
|
|
37
|
+
* only handle that can remove everything it created, since per-record deletes are owner-scoped
|
|
38
|
+
* and the credential dies with the run.
|
|
39
|
+
*/
|
|
40
|
+
async function teardownPrincipals(principals, hooks, findings) {
|
|
41
|
+
const teardown = hooks.teardownPrincipal;
|
|
42
|
+
const addresses = principals
|
|
43
|
+
.map((principal) => principal?.runtime?.address)
|
|
44
|
+
.filter((address) => typeof address === "string" && address !== "");
|
|
45
|
+
if (addresses.length === 0)
|
|
46
|
+
return;
|
|
47
|
+
if (teardown === undefined) {
|
|
48
|
+
findings.gap("world.teardown", "principal", `${addresses.length} principal(s) provisioned by this run were not removed`, `oat registered ${addresses.join(", ")} to run this test and has no way to delete them. ` +
|
|
49
|
+
"Supply a teardownPrincipal hook, or these accounts accumulate on every run.");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
for (const address of addresses) {
|
|
53
|
+
try {
|
|
54
|
+
await teardown(address);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
findings.gap("world.teardown", "principal", `could not remove provisioned principal ${address}`, error instanceof Error ? error.message : String(error));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolves a principal to something that can produce headers on demand.
|
|
63
|
+
*
|
|
64
|
+
* Static headers stay static; acquired credentials refresh themselves. Returning a function
|
|
65
|
+
* rather than a snapshot is what lets a long run outlive a short-lived token — a five-minute TTL
|
|
66
|
+
* would otherwise turn the back half of every run into spurious 401s.
|
|
67
|
+
*/
|
|
68
|
+
async function resolvePrincipal(principal, model, client, hooks) {
|
|
69
|
+
const configured = principal.roots ?? {};
|
|
70
|
+
/* A principal without a flow authenticates by static header — a long-lived API key needs no
|
|
71
|
+
* acquisition at all, and forcing one would be ceremony. */
|
|
72
|
+
if (principal.auth === undefined) {
|
|
73
|
+
return {
|
|
74
|
+
headers: () => principal.headers ?? {},
|
|
75
|
+
id: principal.id,
|
|
76
|
+
inviteAs: principal.inviteAs,
|
|
77
|
+
rank: principal.rank ?? 0,
|
|
78
|
+
role: principal.role,
|
|
79
|
+
roots: configured,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const runtime = await createPrincipal(principal.id, principal.auth, {
|
|
83
|
+
client,
|
|
84
|
+
hooks,
|
|
85
|
+
model,
|
|
86
|
+
principalId: principal.id,
|
|
87
|
+
});
|
|
88
|
+
/* A flow that provisions a tenant produces its own roots — the run then needs no fixture
|
|
89
|
+
* identifiers configured at all. */
|
|
90
|
+
const discovered = {};
|
|
91
|
+
for (const [param, key] of Object.entries(principal.rootsFromFlow ?? {})) {
|
|
92
|
+
const value = runtime.scope[key];
|
|
93
|
+
if (value !== undefined)
|
|
94
|
+
discovered[param] = value;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
headers: () => ({ ...principal.headers, ...runtime.headers() }),
|
|
98
|
+
id: principal.id,
|
|
99
|
+
inviteAs: principal.inviteAs,
|
|
100
|
+
rank: principal.rank ?? 0,
|
|
101
|
+
role: principal.role,
|
|
102
|
+
roots: { ...discovered, ...configured },
|
|
103
|
+
runtime,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** Reads whatever the list endpoint already returns, for degraded read-only coverage. */
|
|
107
|
+
async function readExisting(listOp, client, headers, roots) {
|
|
108
|
+
/* Seed with every known root, not only the list route's own parameters. Sibling routes for
|
|
109
|
+
* the same entity are often scoped differently — a global list beside a tenant-scoped item
|
|
110
|
+
* route — and a scope built from the list alone leaves those unresolvable. */
|
|
111
|
+
const scope = { ...roots };
|
|
112
|
+
for (const param of listOp.pathParams) {
|
|
113
|
+
if (scope[param] === undefined)
|
|
114
|
+
return { records: [], scope };
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const exchange = await client.get(fillPath(listOp.path, scope), { headers, query: { limit: 50 } });
|
|
118
|
+
if (exchange.status >= 300)
|
|
119
|
+
return { records: [], scope };
|
|
120
|
+
const body = exchange.responseBody;
|
|
121
|
+
const key = listOp.collection?.key ?? null;
|
|
122
|
+
const items = Array.isArray(body)
|
|
123
|
+
? body
|
|
124
|
+
: body !== null && typeof body === "object" && key !== null
|
|
125
|
+
? (body[key] ?? [])
|
|
126
|
+
: [];
|
|
127
|
+
return { records: Array.isArray(items) ? items : [], scope };
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return { records: [], scope };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function testableEntities(model, only) {
|
|
134
|
+
return [...model.entities.values()]
|
|
135
|
+
.filter((entity) => entity.list !== undefined && entity.create !== undefined && entity.trackable)
|
|
136
|
+
.filter((entity) => only === undefined || only.length === 0 || only.includes(entity.name))
|
|
137
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
138
|
+
}
|
|
139
|
+
export async function run(options) {
|
|
140
|
+
const startedAt = Date.now();
|
|
141
|
+
const findings = new FindingCollector();
|
|
142
|
+
let last;
|
|
143
|
+
let currentPhase = "load";
|
|
144
|
+
let currentEntity;
|
|
145
|
+
let currentCheck;
|
|
146
|
+
let currentEntityIndex;
|
|
147
|
+
let entityTotal;
|
|
148
|
+
const defectCount = () => findings.findings.filter((f) => f.verdict !== "BLOCKED" && f.verdict !== "COVERAGE_GAP").length;
|
|
149
|
+
const publish = (snap) => {
|
|
150
|
+
options.onProgress?.(snap);
|
|
151
|
+
};
|
|
152
|
+
const tick = (partial) => {
|
|
153
|
+
currentPhase = partial.phase;
|
|
154
|
+
const snap = {
|
|
155
|
+
elapsedMs: Date.now() - startedAt,
|
|
156
|
+
findings: defectCount(),
|
|
157
|
+
phase: partial.phase,
|
|
158
|
+
requests: partial.requests ?? 0,
|
|
159
|
+
};
|
|
160
|
+
if (partial.entity !== undefined)
|
|
161
|
+
snap.entity = partial.entity;
|
|
162
|
+
if (partial.entityIndex !== undefined)
|
|
163
|
+
snap.entityIndex = partial.entityIndex;
|
|
164
|
+
if (partial.entityTotal !== undefined)
|
|
165
|
+
snap.entityTotal = partial.entityTotal;
|
|
166
|
+
if (partial.check !== undefined)
|
|
167
|
+
snap.check = partial.check;
|
|
168
|
+
if (partial.message !== undefined)
|
|
169
|
+
snap.message = partial.message;
|
|
170
|
+
if (last !== undefined)
|
|
171
|
+
snap.last = last;
|
|
172
|
+
publish(snap);
|
|
173
|
+
};
|
|
174
|
+
tick({ message: options.spec, phase: "load", requests: 0 });
|
|
175
|
+
const raw = await loadSpec(options.spec, options.baseUrl);
|
|
176
|
+
const { doc } = dereference(raw);
|
|
177
|
+
const model = buildModel(doc);
|
|
178
|
+
const client = new Client(options.baseUrl, options.globalHeaders ?? {}, options.maxInFlight ?? 4, (exchange) => {
|
|
179
|
+
last = {
|
|
180
|
+
at: Date.now(),
|
|
181
|
+
durationMs: exchange.durationMs,
|
|
182
|
+
method: exchange.method,
|
|
183
|
+
status: exchange.status,
|
|
184
|
+
url: exchange.url,
|
|
185
|
+
};
|
|
186
|
+
const snap = {
|
|
187
|
+
elapsedMs: Date.now() - startedAt,
|
|
188
|
+
findings: defectCount(),
|
|
189
|
+
last,
|
|
190
|
+
phase: currentPhase,
|
|
191
|
+
requests: client.transcript.length,
|
|
192
|
+
};
|
|
193
|
+
if (currentCheck !== undefined)
|
|
194
|
+
snap.check = currentCheck;
|
|
195
|
+
if (currentEntity !== undefined)
|
|
196
|
+
snap.entity = currentEntity;
|
|
197
|
+
if (currentEntityIndex !== undefined)
|
|
198
|
+
snap.entityIndex = currentEntityIndex;
|
|
199
|
+
if (entityTotal !== undefined)
|
|
200
|
+
snap.entityTotal = entityTotal;
|
|
201
|
+
publish(snap);
|
|
202
|
+
});
|
|
203
|
+
const validator = new SchemaValidator();
|
|
204
|
+
const ledger = new Ledger();
|
|
205
|
+
const seed = options.seed ?? 1;
|
|
206
|
+
if (options.principals[0] === undefined)
|
|
207
|
+
throw new Error("oat: at least one principal is required");
|
|
208
|
+
const hooks = options.hooks ?? {};
|
|
209
|
+
const resolved = [];
|
|
210
|
+
for (const principal of options.principals) {
|
|
211
|
+
resolved.push(await resolvePrincipal(principal, model, client, hooks));
|
|
212
|
+
}
|
|
213
|
+
tick({
|
|
214
|
+
message: `${resolved.length} principal(s)`,
|
|
215
|
+
phase: "auth",
|
|
216
|
+
requests: client.transcript.length,
|
|
217
|
+
});
|
|
218
|
+
const alpha = resolved[0];
|
|
219
|
+
/* Isolation peer: first principal whose roots are a different tenant — not "whoever is
|
|
220
|
+
* second in the array". A same-tenant viewer sitting at index 1 must not steal that slot. */
|
|
221
|
+
const peer = resolved.slice(1).find((candidate) => !sameTenant(alpha.roots, candidate.roots));
|
|
222
|
+
const entitiesTested = [];
|
|
223
|
+
const checksRun = new Set();
|
|
224
|
+
const checksSkipped = [];
|
|
225
|
+
const checksSuppressed = [];
|
|
226
|
+
const testEntity = async (entity) => {
|
|
227
|
+
const listOp = model.byOperationId.get(entity.list ?? "");
|
|
228
|
+
const createOp = model.byOperationId.get(entity.create ?? "");
|
|
229
|
+
if (listOp === undefined || createOp === undefined)
|
|
230
|
+
return;
|
|
231
|
+
currentEntity = entity.name;
|
|
232
|
+
currentCheck = undefined;
|
|
233
|
+
currentPhase = "seed";
|
|
234
|
+
tick({
|
|
235
|
+
entity: entity.name,
|
|
236
|
+
entityIndex: currentEntityIndex,
|
|
237
|
+
entityTotal,
|
|
238
|
+
message: "seeding",
|
|
239
|
+
phase: "seed",
|
|
240
|
+
requests: client.transcript.length,
|
|
241
|
+
});
|
|
242
|
+
for (const principal of resolved)
|
|
243
|
+
await principal.runtime?.refreshIfStale();
|
|
244
|
+
const rootValues = { ...options.roots, ...alpha.roots };
|
|
245
|
+
let scope;
|
|
246
|
+
let records;
|
|
247
|
+
let degraded = false;
|
|
248
|
+
try {
|
|
249
|
+
scope = await resolveScope(createOp, model, client, {
|
|
250
|
+
authHeaders: alpha.headers,
|
|
251
|
+
roots: rootValues,
|
|
252
|
+
seed,
|
|
253
|
+
});
|
|
254
|
+
/* Carry every known root, not only what the create route happened to need. Sibling
|
|
255
|
+
* routes for one entity are frequently scoped differently — a global create beside a
|
|
256
|
+
* tenant-scoped item route — and a scope built from create alone leaves those
|
|
257
|
+
* unresolvable, which shows up as a wall of "could not complete" gaps. */
|
|
258
|
+
scope.values = { ...rootValues, ...scope.values };
|
|
259
|
+
const cohort = await seedCohort(createOp, model, client, {
|
|
260
|
+
authHeaders: alpha.headers,
|
|
261
|
+
...(options.cohortSize === undefined ? {} : { cohortSize: options.cohortSize }),
|
|
262
|
+
roots: rootValues,
|
|
263
|
+
seed,
|
|
264
|
+
}, scope);
|
|
265
|
+
records = cohort.records;
|
|
266
|
+
/* Ancestors first, then the cohort — the unwind reverses this, so children are always
|
|
267
|
+
* removed before the parents they hang from. */
|
|
268
|
+
for (const ancestor of scope.created) {
|
|
269
|
+
ledger.record(ancestor.entity, ancestor.id, scope.values);
|
|
270
|
+
}
|
|
271
|
+
for (const record of records) {
|
|
272
|
+
ledger.record(entity.name, String(record[entity.identity ?? "id"]), scope.values);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
const cause = error instanceof SeedError ? error.cause_ : "unknown";
|
|
277
|
+
const status = error instanceof SeedError ? error.status : undefined;
|
|
278
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
279
|
+
/* A create that fails with 5xx is not a fixture problem — it is the defect. Reporting
|
|
280
|
+
* it as merely "blocked" buries the most serious thing oat found. */
|
|
281
|
+
if (status !== undefined && status >= 500) {
|
|
282
|
+
findings.backend("create.does-not-error", entity.name, `creating a "${entity.name}" fails with a server error`, `${message}. The request body was generated from the documented schema, so either ` +
|
|
283
|
+
"the handler rejects input the document permits, or it is failing outright. " +
|
|
284
|
+
"Everything downstream of this entity is untestable until it is fixed.", client.transcript.filter((e) => e.status >= 500).slice(-1));
|
|
285
|
+
}
|
|
286
|
+
/* Fall back to whatever already exists. A backend whose create is broken can still have
|
|
287
|
+
* a working list, and read-only coverage beats no coverage — this is exactly the state
|
|
288
|
+
* in which a read-path bug is most likely to be sitting undiscovered. */
|
|
289
|
+
const existing = await readExisting(listOp, client, alpha.headers(), {
|
|
290
|
+
...options.roots,
|
|
291
|
+
...alpha.roots,
|
|
292
|
+
});
|
|
293
|
+
if (existing.records.length === 0) {
|
|
294
|
+
findings.blocked("world.seed", entity.name, `could not seed "${entity.name}"`, `${cause}: ${message}`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
findings.gap("world.seed", entity.name, `seeding "${entity.name}" failed; running read-only checks against existing records`, `${cause}: ${message}. Write-path and lifecycle checks are skipped for this entity.`);
|
|
298
|
+
scope = { created: [], values: existing.scope };
|
|
299
|
+
records = existing.records;
|
|
300
|
+
degraded = true;
|
|
301
|
+
}
|
|
302
|
+
const actorOf = async (principal, seedOffset) => {
|
|
303
|
+
const roots = { ...options.roots, ...principal.roots };
|
|
304
|
+
try {
|
|
305
|
+
const next = await resolveScope(listOp, model, client, {
|
|
306
|
+
authHeaders: principal.headers,
|
|
307
|
+
roots,
|
|
308
|
+
seed: seed + seedOffset,
|
|
309
|
+
});
|
|
310
|
+
return {
|
|
311
|
+
headers: principal.headers,
|
|
312
|
+
id: principal.id,
|
|
313
|
+
inviteAs: principal.inviteAs,
|
|
314
|
+
rank: principal.rank,
|
|
315
|
+
role: principal.role,
|
|
316
|
+
roots,
|
|
317
|
+
scope: { ...roots, ...next.values },
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
return {
|
|
322
|
+
headers: principal.headers,
|
|
323
|
+
id: principal.id,
|
|
324
|
+
inviteAs: principal.inviteAs,
|
|
325
|
+
rank: principal.rank,
|
|
326
|
+
role: principal.role,
|
|
327
|
+
roots,
|
|
328
|
+
scope: roots,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
const actors = [
|
|
333
|
+
{
|
|
334
|
+
headers: alpha.headers,
|
|
335
|
+
id: alpha.id,
|
|
336
|
+
inviteAs: alpha.inviteAs,
|
|
337
|
+
rank: alpha.rank,
|
|
338
|
+
role: alpha.role,
|
|
339
|
+
roots: { ...options.roots, ...alpha.roots },
|
|
340
|
+
scope: scope.values,
|
|
341
|
+
},
|
|
342
|
+
];
|
|
343
|
+
for (let i = 1; i < resolved.length; i++) {
|
|
344
|
+
const principal = resolved[i];
|
|
345
|
+
if (principal === undefined)
|
|
346
|
+
continue;
|
|
347
|
+
actors.push(await actorOf(principal, i));
|
|
348
|
+
}
|
|
349
|
+
const isolation = actors.find((actor) => !sameTenant(actors[0]?.roots ?? {}, actor.roots));
|
|
350
|
+
const altScope = isolation?.scope;
|
|
351
|
+
const altAuth = isolation?.headers;
|
|
352
|
+
/*
|
|
353
|
+
* Anything a check creates is registered for teardown automatically.
|
|
354
|
+
*
|
|
355
|
+
* Several checks POST directly — replaying an idempotency key, probing a declared
|
|
356
|
+
* invalidation, sending a body that validation should have rejected — and those records
|
|
357
|
+
* were invisible to the ledger, so oat left them behind in the backend under test while
|
|
358
|
+
* reporting that it had cleaned up everything it made. Recording centrally rather than at
|
|
359
|
+
* each call site means a new check cannot forget: the wrapper sees every request.
|
|
360
|
+
*/
|
|
361
|
+
const trackingClient = createOp === undefined || degraded
|
|
362
|
+
? client
|
|
363
|
+
: new Proxy(client, {
|
|
364
|
+
get(target, property, receiver) {
|
|
365
|
+
if (property !== "request")
|
|
366
|
+
return Reflect.get(target, property, receiver);
|
|
367
|
+
return async (method, path, options) => {
|
|
368
|
+
const exchange = await target.request(method, path, options ?? {});
|
|
369
|
+
if (method.toUpperCase() !== "POST" || exchange.status >= 300)
|
|
370
|
+
return exchange;
|
|
371
|
+
const body = exchange.responseBody;
|
|
372
|
+
if (body === null || typeof body !== "object")
|
|
373
|
+
return exchange;
|
|
374
|
+
const id = body[entity.identity ?? "id"];
|
|
375
|
+
if (typeof id !== "string" && typeof id !== "number")
|
|
376
|
+
return exchange;
|
|
377
|
+
/* Recording the same id twice is harmless — the unwind tolerates a 404
|
|
378
|
+
* on an already-removed record — and missing one is not. */
|
|
379
|
+
ledger.record(entity.name, String(id), scope.values);
|
|
380
|
+
return exchange;
|
|
381
|
+
};
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
const ctx = {
|
|
385
|
+
actors,
|
|
386
|
+
altAuth,
|
|
387
|
+
altScope,
|
|
388
|
+
asyncOps: model.operations.filter((op) => op.entity === entity.name && op.async !== null),
|
|
389
|
+
effectOps: model.operations.filter((op) => op.entity === entity.name && op.effects.length > 0),
|
|
390
|
+
auth: alpha.headers,
|
|
391
|
+
client: trackingClient,
|
|
392
|
+
collectionKey: listOp.collection?.key ?? null,
|
|
393
|
+
/* In degraded mode oat did not write these records, so it has no oracle for them —
|
|
394
|
+
* every write-path check must sit out rather than assert against data it did not
|
|
395
|
+
* create. */
|
|
396
|
+
createOp: degraded ? undefined : createOp,
|
|
397
|
+
deleteOp: degraded ? undefined : model.byOperationId.get(entity.delete ?? ""),
|
|
398
|
+
entityName: entity.name,
|
|
399
|
+
findings,
|
|
400
|
+
identity: entity.identity ?? "id",
|
|
401
|
+
invite: entity.invite,
|
|
402
|
+
listOp,
|
|
403
|
+
model,
|
|
404
|
+
query: listOp.query,
|
|
405
|
+
readOp: model.byOperationId.get(entity.read ?? ""),
|
|
406
|
+
records,
|
|
407
|
+
scope: scope.values,
|
|
408
|
+
/* Taken from any operation on the entity, not just the list: authors naturally put
|
|
409
|
+
* x-soft-delete on the delete route, and a tag that exists but is only read from
|
|
410
|
+
* list made softdelete.absent-from-default-list stand down against a real document. */
|
|
411
|
+
softDelete: model.operations.find((op) => op.entity === entity.name && op.softDelete !== null)
|
|
412
|
+
?.softDelete ?? listOp.softDelete,
|
|
413
|
+
seed,
|
|
414
|
+
updateOp: degraded ? undefined : model.byOperationId.get(entity.update ?? ""),
|
|
415
|
+
validator,
|
|
416
|
+
};
|
|
417
|
+
entitiesTested.push(entity.name);
|
|
418
|
+
const runOne = async (check) => {
|
|
419
|
+
checksRun.add(check.id);
|
|
420
|
+
currentCheck = check.id;
|
|
421
|
+
currentPhase = "test";
|
|
422
|
+
tick({
|
|
423
|
+
check: check.id,
|
|
424
|
+
entity: entity.name,
|
|
425
|
+
entityIndex: currentEntityIndex,
|
|
426
|
+
entityTotal,
|
|
427
|
+
phase: "test",
|
|
428
|
+
requests: client.transcript.length,
|
|
429
|
+
});
|
|
430
|
+
try {
|
|
431
|
+
await check.run(ctx);
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
findings.gap(check.id, entity.name, `check "${check.id}" could not complete`, error instanceof Error ? error.message : String(error));
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
/* Cascade suppression: a check whose premise is already known broken would report a
|
|
438
|
+
* consequence, not a defect. One root cause, one finding. */
|
|
439
|
+
/*
|
|
440
|
+
* Cascade suppression, transitively.
|
|
441
|
+
*
|
|
442
|
+
* A check whose premise is known broken reports a consequence, not a defect. The subtle
|
|
443
|
+
* part is that suppression has to propagate: if A is suppressed because B failed, then C
|
|
444
|
+
* — which depends on A — must be suppressed too. Consulting only *fired* findings misses
|
|
445
|
+
* this, because A never fired; it was skipped. C then runs against the same broken premise
|
|
446
|
+
* and reports the root cause a second time under its own name.
|
|
447
|
+
*
|
|
448
|
+
* So a check is suppressed when any dependency either failed outright or was itself
|
|
449
|
+
* suppressed, and the reason carried forward names the original cause rather than the
|
|
450
|
+
* intermediate link — which is what the reader has to fix.
|
|
451
|
+
*/
|
|
452
|
+
const suppressedBy = new Map();
|
|
453
|
+
const suppressed = (check) => {
|
|
454
|
+
for (const dependency of check.dependsOn ?? []) {
|
|
455
|
+
const failed = findings.findings.some((f) => f.check === dependency && f.entity === entity.name);
|
|
456
|
+
const inherited = suppressedBy.get(dependency);
|
|
457
|
+
if (!failed && inherited === undefined)
|
|
458
|
+
continue;
|
|
459
|
+
const because = failed ? dependency : inherited;
|
|
460
|
+
suppressedBy.set(check.id, because);
|
|
461
|
+
checksSuppressed.push({ because, check: check.id, entity: entity.name });
|
|
462
|
+
return true;
|
|
463
|
+
}
|
|
464
|
+
return false;
|
|
465
|
+
};
|
|
466
|
+
/*
|
|
467
|
+
* Read-only checks accumulate into a batch and fire together; a mutating check flushes the
|
|
468
|
+
* batch and then runs alone. A batch also flushes when the next check depends on something
|
|
469
|
+
* already inside it, so suppression never has to consult a finding that has not landed yet.
|
|
470
|
+
*
|
|
471
|
+
* This is where a live run's time actually goes — dozens of independent GETs against one
|
|
472
|
+
* entity, each paying full network latency for no reason.
|
|
473
|
+
*/
|
|
474
|
+
let batch = [];
|
|
475
|
+
const flush = async () => {
|
|
476
|
+
if (batch.length === 0)
|
|
477
|
+
return;
|
|
478
|
+
const pending = batch;
|
|
479
|
+
batch = [];
|
|
480
|
+
await Promise.all(pending.map(runOne));
|
|
481
|
+
};
|
|
482
|
+
for (const check of CHECKS) {
|
|
483
|
+
if (!check.applicable(ctx)) {
|
|
484
|
+
/* Recorded, not dropped: on an API shaped unlike the fixture this is most of the
|
|
485
|
+
* suite, and a silent skip reads exactly like a clean result. */
|
|
486
|
+
checksSkipped.push({
|
|
487
|
+
check: check.id,
|
|
488
|
+
entity: entity.name,
|
|
489
|
+
needs: check.needs ?? "an unstated precondition",
|
|
490
|
+
});
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
if (check.mutates === true) {
|
|
494
|
+
await flush();
|
|
495
|
+
if (suppressed(check))
|
|
496
|
+
continue;
|
|
497
|
+
await runOne(check);
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (check.dependsOn?.some((d) => batch.some((queued) => queued.id === d)) === true) {
|
|
501
|
+
await flush();
|
|
502
|
+
}
|
|
503
|
+
if (suppressed(check))
|
|
504
|
+
continue;
|
|
505
|
+
batch.push(check);
|
|
506
|
+
}
|
|
507
|
+
await flush();
|
|
508
|
+
};
|
|
509
|
+
/*
|
|
510
|
+
* Entities run in parallel; checks within an entity stay strictly sequential.
|
|
511
|
+
*
|
|
512
|
+
* The ordering inside an entity is load-bearing — cascade suppression consults findings
|
|
513
|
+
* already reported for it, so running those concurrently would let a root cause and its
|
|
514
|
+
* consequences race and both be reported. Across entities there is no such coupling, and the
|
|
515
|
+
* run is almost entirely network wait.
|
|
516
|
+
*/
|
|
517
|
+
const queue = testableEntities(model, options.only);
|
|
518
|
+
entityTotal = queue.length;
|
|
519
|
+
const lanes = Math.max(1, Math.min(options.concurrency ?? 1, queue.length));
|
|
520
|
+
let cursor = 0;
|
|
521
|
+
await Promise.all(Array.from({ length: lanes }, async () => {
|
|
522
|
+
while (cursor < queue.length) {
|
|
523
|
+
const index = cursor;
|
|
524
|
+
const entity = queue[cursor++];
|
|
525
|
+
if (entity === undefined)
|
|
526
|
+
break;
|
|
527
|
+
currentEntityIndex = index + 1;
|
|
528
|
+
await testEntity(entity);
|
|
529
|
+
}
|
|
530
|
+
}));
|
|
531
|
+
/* Unwind after every check has run, never per case: a check may legitimately depend on records
|
|
532
|
+
* another one created, and tearing down early turns that into a phantom defect. */
|
|
533
|
+
currentPhase = "teardown";
|
|
534
|
+
currentCheck = undefined;
|
|
535
|
+
tick({
|
|
536
|
+
message: `${ledger.size} record(s)`,
|
|
537
|
+
phase: "teardown",
|
|
538
|
+
requests: client.transcript.length,
|
|
539
|
+
});
|
|
540
|
+
const teardown = options.keepFixtures === true || ledger.size === 0
|
|
541
|
+
? null
|
|
542
|
+
: await ledger.unwind(model, client, alpha.headers, (done, total, item) => {
|
|
543
|
+
if (done % 25 !== 0 && done !== total)
|
|
544
|
+
return;
|
|
545
|
+
tick({
|
|
546
|
+
entity: item.entity,
|
|
547
|
+
message: `${done}/${total} ${item.entity} ${item.id}`,
|
|
548
|
+
phase: "teardown",
|
|
549
|
+
requests: client.transcript.length,
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
if (teardown !== null && teardown.unsupported.length > 0) {
|
|
553
|
+
findings.gap("world.teardown", teardown.unsupported.join(", "), "records created during the run could not be removed", `no delete operation is reachable for ${teardown.unsupported.join(", ")}, so this run left ` +
|
|
554
|
+
"fixtures behind. Declare x-cleanup to name the route that removes them.");
|
|
555
|
+
}
|
|
556
|
+
await teardownPrincipals(resolved, hooks, findings);
|
|
557
|
+
tick({
|
|
558
|
+
message: "done",
|
|
559
|
+
phase: "done",
|
|
560
|
+
requests: client.transcript.length,
|
|
561
|
+
});
|
|
562
|
+
return {
|
|
563
|
+
checksRun: [...checksRun].sort(),
|
|
564
|
+
checksSkipped,
|
|
565
|
+
checksSuppressed,
|
|
566
|
+
inconclusive: findings.inconclusive,
|
|
567
|
+
client,
|
|
568
|
+
created: ledger.size,
|
|
569
|
+
entitiesTested,
|
|
570
|
+
findings: findings.findings,
|
|
571
|
+
model,
|
|
572
|
+
teardown,
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/runtime/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Teardown of everything a run created.
|
|
3
|
+
*
|
|
4
|
+
* A tester that leaves its fixtures behind is a tester nobody runs twice against anything real.
|
|
5
|
+
* Records are unwound newest-first so children go before the parents they depend on, and every
|
|
6
|
+
* failure is collected rather than thrown — a teardown that aborts halfway is worse than one that
|
|
7
|
+
* reports exactly what it could not remove.
|
|
8
|
+
*/
|
|
9
|
+
import type { SpecModel } from "../spec/graph.ts";
|
|
10
|
+
import type { Client } from "./client.ts";
|
|
11
|
+
export interface Disposable {
|
|
12
|
+
entity: string;
|
|
13
|
+
id: string;
|
|
14
|
+
/** Path parameters in scope when the record was created. */
|
|
15
|
+
scope: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
export interface TeardownReport {
|
|
18
|
+
removed: number;
|
|
19
|
+
failed: Array<{
|
|
20
|
+
entity: string;
|
|
21
|
+
id: string;
|
|
22
|
+
reason: string;
|
|
23
|
+
}>;
|
|
24
|
+
unsupported: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Registry of what a run brought into existence. Entities are recorded in creation order so the
|
|
28
|
+
* unwind can reverse it.
|
|
29
|
+
*/
|
|
30
|
+
export declare class Ledger {
|
|
31
|
+
private readonly items;
|
|
32
|
+
record(entity: string, id: string, scope: Record<string, string>): void;
|
|
33
|
+
get size(): number;
|
|
34
|
+
entries(): readonly Disposable[];
|
|
35
|
+
unwind(model: SpecModel, client: Client, headers: () => Record<string, string>, onItem?: (done: number, total: number, item: Disposable) => void): Promise<TeardownReport>;
|
|
36
|
+
}
|
|
37
|
+
export declare function renderTeardown(report: TeardownReport, created: number): string[];
|
|
38
|
+
//# sourceMappingURL=teardown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teardown.d.ts","sourceRoot":"","sources":["../../src/runtime/teardown.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAkB,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAIzC,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;IACV,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC7B;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC7D,WAAW,EAAE,MAAM,EAAE,CAAA;CACrB;AAED;;;GAGG;AACH,qBAAa,MAAM;IAClB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IAEzC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAKvE,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,IAAI,SAAS,UAAU,EAAE;IAI1B,MAAM,CACX,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,GAC9D,OAAO,CAAC,cAAc,CAAC;CA2D1B;AAuBD,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAqBhF"}
|