@lovrozagar/oat 0.12.20 → 0.13.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/README.md +6 -1
- package/dist/cli.js +1 -0
- package/dist/config/define-config.d.ts +77 -0
- package/dist/config/define-config.d.ts.map +1 -1
- package/dist/config/define-config.js.map +1 -1
- package/dist/config/validate.d.ts.map +1 -1
- package/dist/config/validate.js +1 -0
- package/dist/config/validate.js.map +1 -1
- package/dist/conformance/suite.d.ts.map +1 -1
- package/dist/conformance/suite.js +3 -0
- package/dist/conformance/suite.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/reference/defects.d.ts +7 -0
- package/dist/reference/defects.d.ts.map +1 -1
- package/dist/reference/defects.js +7 -0
- package/dist/reference/defects.js.map +1 -1
- package/dist/reference/http.js +1 -1
- package/dist/reference/http.js.map +1 -1
- package/dist/report/render.js +3 -3
- package/dist/report/render.js.map +1 -1
- package/dist/runtime/auth.d.ts +12 -0
- package/dist/runtime/auth.d.ts.map +1 -1
- package/dist/runtime/auth.js +5 -5
- package/dist/runtime/auth.js.map +1 -1
- package/dist/runtime/check-costs.d.ts.map +1 -1
- package/dist/runtime/check-costs.js +1 -0
- package/dist/runtime/check-costs.js.map +1 -1
- package/dist/runtime/check-ids.d.ts +1 -1
- package/dist/runtime/check-ids.d.ts.map +1 -1
- package/dist/runtime/check-ids.js +1 -0
- package/dist/runtime/check-ids.js.map +1 -1
- package/dist/runtime/checks.d.ts.map +1 -1
- package/dist/runtime/checks.js +199 -48
- package/dist/runtime/checks.js.map +1 -1
- package/dist/runtime/client.d.ts +2 -0
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +6 -1
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/contract.d.ts +52 -0
- package/dist/runtime/contract.d.ts.map +1 -0
- package/dist/runtime/contract.js +247 -0
- package/dist/runtime/contract.js.map +1 -0
- package/dist/runtime/disposable.d.ts +26 -10
- package/dist/runtime/disposable.d.ts.map +1 -1
- package/dist/runtime/disposable.js +287 -145
- package/dist/runtime/disposable.js.map +1 -1
- package/dist/runtime/effects.d.ts +1 -1
- package/dist/runtime/effects.d.ts.map +1 -1
- package/dist/runtime/evidence.d.ts +21 -0
- package/dist/runtime/evidence.d.ts.map +1 -0
- package/dist/runtime/evidence.js +168 -0
- package/dist/runtime/evidence.js.map +1 -0
- package/dist/runtime/path.d.ts +6 -0
- package/dist/runtime/path.d.ts.map +1 -1
- package/dist/runtime/path.js +46 -0
- package/dist/runtime/path.js.map +1 -1
- package/dist/runtime/run.d.ts +3 -1
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +97 -14
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/scenario.d.ts +35 -0
- package/dist/runtime/scenario.d.ts.map +1 -0
- package/dist/runtime/scenario.js +319 -0
- package/dist/runtime/scenario.js.map +1 -0
- package/dist/runtime/scope.d.ts +28 -0
- package/dist/runtime/scope.d.ts.map +1 -1
- package/dist/runtime/scope.js +31 -4
- package/dist/runtime/scope.js.map +1 -1
- package/dist/runtime/teardown.d.ts +3 -0
- package/dist/runtime/teardown.d.ts.map +1 -1
- package/dist/runtime/teardown.js +7 -1
- package/dist/runtime/teardown.js.map +1 -1
- package/dist/runtime/transcript.d.ts.map +1 -1
- package/dist/runtime/transcript.js +9 -5
- package/dist/runtime/transcript.js.map +1 -1
- package/dist/spec/collection.d.ts +2 -1
- package/dist/spec/collection.d.ts.map +1 -1
- package/dist/spec/collection.js +13 -2
- package/dist/spec/collection.js.map +1 -1
- package/dist/spec/extensions.d.ts +6 -0
- package/dist/spec/extensions.d.ts.map +1 -1
- package/dist/spec/extensions.js +9 -1
- package/dist/spec/extensions.js.map +1 -1
- package/dist/spec/graph.d.ts +8 -0
- package/dist/spec/graph.d.ts.map +1 -1
- package/dist/spec/graph.js +44 -2
- package/dist/spec/graph.js.map +1 -1
- package/dist/spec/tag-schema.d.ts.map +1 -1
- package/dist/spec/tag-schema.js +2 -1
- package/dist/spec/tag-schema.js.map +1 -1
- package/docs/configuration.md +43 -1
- package/docs/reports.md +19 -1
- package/docs/tags.md +7 -2
- package/package.json +14 -14
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The exchanges behind a matrix pass. A check that passed proved a property; the report also
|
|
3
|
+
* names the call and the later exchange that shows it, so a reader can audit the claim without
|
|
4
|
+
* trusting oat. Bodies are read through the client, which may have moved older ones to disk.
|
|
5
|
+
*/
|
|
6
|
+
const READS = new Set(["GET", "HEAD"]);
|
|
7
|
+
/* How far either side of a call to look. Bounded, so a long run does not reread its whole journal. */
|
|
8
|
+
const WINDOW = 400;
|
|
9
|
+
function ok(exchange) {
|
|
10
|
+
return exchange.status >= 200 && exchange.status < 300;
|
|
11
|
+
}
|
|
12
|
+
function pathOf(url) {
|
|
13
|
+
try {
|
|
14
|
+
return new URL(url).pathname;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return url;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function walk(node, depth, into, idsOnly) {
|
|
21
|
+
if (depth > 4 || node === null || typeof node !== "object")
|
|
22
|
+
return;
|
|
23
|
+
for (const [key, value] of Object.entries(node)) {
|
|
24
|
+
if (typeof value === "string" && value.length >= 6) {
|
|
25
|
+
if (!idsOnly || key === "id" || key.endsWith("_id"))
|
|
26
|
+
into.push(value);
|
|
27
|
+
}
|
|
28
|
+
else
|
|
29
|
+
walk(value, depth + 1, into, idsOnly);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Distinctive values a call put into the world: ids it returned, then long strings it sent. */
|
|
33
|
+
function tokensOf(exchange) {
|
|
34
|
+
const ids = [];
|
|
35
|
+
const sent = [];
|
|
36
|
+
walk(exchange.responseBody, 0, ids, true);
|
|
37
|
+
walk(exchange.requestBody, 0, sent, false);
|
|
38
|
+
return [...new Set([...ids, ...sent])];
|
|
39
|
+
}
|
|
40
|
+
/** A JSON path (`[*]` for arrays) to a string node equal to `value`, or null. */
|
|
41
|
+
export function pathTo(body, value, prefix = "$", depth = 0) {
|
|
42
|
+
if (depth > 6 || body === null || typeof body !== "object")
|
|
43
|
+
return null;
|
|
44
|
+
if (Array.isArray(body)) {
|
|
45
|
+
for (const item of body) {
|
|
46
|
+
if (item === value)
|
|
47
|
+
return `${prefix}[*]`;
|
|
48
|
+
const hit = pathTo(item, value, `${prefix}[*]`, depth + 1);
|
|
49
|
+
if (hit !== null)
|
|
50
|
+
return hit;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
for (const [key, item] of Object.entries(body)) {
|
|
55
|
+
if (item === value)
|
|
56
|
+
return `${prefix}.${key}`;
|
|
57
|
+
const hit = pathTo(item, value, `${prefix}.${key}`, depth + 1);
|
|
58
|
+
if (hit !== null)
|
|
59
|
+
return hit;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/** `ancestor` is `path` or a path above it, segment by segment. */
|
|
64
|
+
function isAncestor(ancestor, path) {
|
|
65
|
+
return ancestor === path || path.startsWith(`${ancestor}/`);
|
|
66
|
+
}
|
|
67
|
+
async function infer(op, client) {
|
|
68
|
+
const transcript = client.transcript;
|
|
69
|
+
const at = new Map(transcript.map((exchange, index) => [exchange.seq, index]));
|
|
70
|
+
const cache = new Map();
|
|
71
|
+
const whole = async (exchange) => {
|
|
72
|
+
const known = cache.get(exchange.seq);
|
|
73
|
+
if (known !== undefined)
|
|
74
|
+
return known;
|
|
75
|
+
const hydrated = await client.hydrate(exchange);
|
|
76
|
+
cache.set(exchange.seq, hydrated);
|
|
77
|
+
return hydrated;
|
|
78
|
+
};
|
|
79
|
+
const calls = transcript.filter((exchange) => exchange.operationId === op.operationId && ok(exchange));
|
|
80
|
+
const last = calls.at(-1);
|
|
81
|
+
if (last === undefined)
|
|
82
|
+
return undefined;
|
|
83
|
+
const method = op.method.toUpperCase();
|
|
84
|
+
for (const stored of calls.toReversed()) {
|
|
85
|
+
const index = at.get(stored.seq) ?? 0;
|
|
86
|
+
const call = await whole(stored);
|
|
87
|
+
if (method === "DELETE") {
|
|
88
|
+
const segments = pathOf(call.url).split("/");
|
|
89
|
+
const id = decodeURIComponent(segments.pop() ?? "");
|
|
90
|
+
const collection = segments.join("/");
|
|
91
|
+
for (const read of transcript.slice(index + 1, index + 1 + WINDOW)) {
|
|
92
|
+
if (!READS.has(read.method.toUpperCase()))
|
|
93
|
+
continue;
|
|
94
|
+
if ((read.status === 404 || read.status === 410) && pathOf(read.url) === pathOf(call.url)) {
|
|
95
|
+
return { call: call.seq, observation: { seq: read.seq, status: read.status } };
|
|
96
|
+
}
|
|
97
|
+
if (ok(read) && id !== "" && pathOf(read.url) === collection) {
|
|
98
|
+
if (pathTo((await whole(read)).responseBody, id) === null) {
|
|
99
|
+
return { call: call.seq, observation: { absent: true, path: "$", seq: read.seq, value: id } };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/* A nested item with no read of its own (a column inside its table) shows through an
|
|
104
|
+
* ancestor: the same ancestor read held the id before the call and lacks it after. */
|
|
105
|
+
const before = transcript.slice(Math.max(0, index - WINDOW), index);
|
|
106
|
+
for (const read of transcript.slice(index + 1, index + 1 + WINDOW)) {
|
|
107
|
+
if (id === "" || !READS.has(read.method.toUpperCase()) || !ok(read))
|
|
108
|
+
continue;
|
|
109
|
+
const path = pathOf(read.url);
|
|
110
|
+
if (!isAncestor(path, collection))
|
|
111
|
+
continue;
|
|
112
|
+
if (pathTo((await whole(read)).responseBody, id) !== null)
|
|
113
|
+
continue;
|
|
114
|
+
for (const prior of before) {
|
|
115
|
+
if (!READS.has(prior.method.toUpperCase()) || !ok(prior) || pathOf(prior.url) !== path)
|
|
116
|
+
continue;
|
|
117
|
+
if (pathTo((await whole(prior)).responseBody, id) !== null) {
|
|
118
|
+
return { call: call.seq, observation: { absent: true, path: "$", seq: read.seq, value: id } };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (READS.has(method)) {
|
|
125
|
+
for (const write of transcript.slice(Math.max(0, index - WINDOW), index)) {
|
|
126
|
+
if (READS.has(write.method.toUpperCase()) || !ok(write))
|
|
127
|
+
continue;
|
|
128
|
+
for (const value of tokensOf(await whole(write))) {
|
|
129
|
+
const path = pathTo(call.responseBody, value);
|
|
130
|
+
if (path !== null)
|
|
131
|
+
return { call: call.seq, observation: { path, seq: call.seq, value } };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const values = tokensOf(call);
|
|
137
|
+
if (values.length === 0)
|
|
138
|
+
continue;
|
|
139
|
+
for (const read of transcript.slice(index + 1, index + 1 + WINDOW)) {
|
|
140
|
+
if (!READS.has(read.method.toUpperCase()) || !ok(read))
|
|
141
|
+
continue;
|
|
142
|
+
const body = (await whole(read)).responseBody;
|
|
143
|
+
for (const value of values) {
|
|
144
|
+
const path = pathTo(body, value);
|
|
145
|
+
if (path !== null)
|
|
146
|
+
return { call: call.seq, observation: { path, seq: read.seq, value } };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { call: last.seq };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Evidence for every operation a check graded without naming its exchanges: the call, and the
|
|
154
|
+
* first nearby exchange that holds a value the call put into the world. A write no later read
|
|
155
|
+
* shows keeps only its call, and the report calls that pass `contract`.
|
|
156
|
+
*/
|
|
157
|
+
export async function proveFromTranscript(input) {
|
|
158
|
+
for (const op of input.model.operations) {
|
|
159
|
+
if ((input.grades.ran.get(op.operationId)?.size ?? 0) === 0)
|
|
160
|
+
continue;
|
|
161
|
+
if (input.grades.evidence.get(op.operationId)?.observation !== undefined)
|
|
162
|
+
continue;
|
|
163
|
+
const evidence = await infer(op, input.client);
|
|
164
|
+
if (evidence !== undefined)
|
|
165
|
+
input.grades.prove(op.operationId, evidence);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/runtime/evidence.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/path.d.ts
CHANGED
|
@@ -4,4 +4,10 @@
|
|
|
4
4
|
* Auth flows, async receipts, x-wait and invite pointers all name values this way.
|
|
5
5
|
*/
|
|
6
6
|
export declare function readPath(body: unknown, pointer: string): unknown;
|
|
7
|
+
/**
|
|
8
|
+
* Every node `path` names, fanning out at `[*]` and narrowing at `[?(@.field==value)]`:
|
|
9
|
+
* `$.items[*].name` is each item's name, `$.items[?(@.id=='a')].name` the name of the item whose
|
|
10
|
+
* id is `a`. A path with neither names at most one node, the same one `readPath` returns.
|
|
11
|
+
*/
|
|
12
|
+
export declare function readAll(body: unknown, path: string): unknown[];
|
|
7
13
|
//# sourceMappingURL=path.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/runtime/path.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/runtime/path.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAoBhE;AAgCD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAuB9D"}
|
package/dist/runtime/path.js
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
export function readPath(body, pointer) {
|
|
7
7
|
if (pointer.startsWith("/"))
|
|
8
8
|
return readJsonPointer(body, pointer);
|
|
9
|
+
/* A filter names items, not one node: the first match is the one a saved value means. */
|
|
10
|
+
if (FILTER_RE.test(pointer))
|
|
11
|
+
return readAll(body, pointer)[0];
|
|
9
12
|
let node = body;
|
|
10
13
|
/* `$.messages[0].id` and `$.messages.0.id` name the same node: brackets are read as segments. */
|
|
11
14
|
for (const segment of pointer
|
|
@@ -41,4 +44,47 @@ function readJsonPointer(body, pointer) {
|
|
|
41
44
|
}
|
|
42
45
|
return node;
|
|
43
46
|
}
|
|
47
|
+
/* `[?(@.field==value)]`: a quoted string, a number, true, false or null. */
|
|
48
|
+
const FILTER_RE = /\[\?\(@\.([A-Za-z0-9_]+)==(?:'([^']*)'|"([^"]*)"|([^)\]]+))\)\]/;
|
|
49
|
+
function filterValue(match) {
|
|
50
|
+
if (match[2] !== undefined)
|
|
51
|
+
return match[2];
|
|
52
|
+
if (match[3] !== undefined)
|
|
53
|
+
return match[3];
|
|
54
|
+
const literal = (match[4] ?? "").trim();
|
|
55
|
+
if (literal === "true")
|
|
56
|
+
return true;
|
|
57
|
+
if (literal === "false")
|
|
58
|
+
return false;
|
|
59
|
+
if (literal === "null")
|
|
60
|
+
return null;
|
|
61
|
+
const number = Number(literal);
|
|
62
|
+
return Number.isNaN(number) ? literal : number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Every node `path` names, fanning out at `[*]` and narrowing at `[?(@.field==value)]`:
|
|
66
|
+
* `$.items[*].name` is each item's name, `$.items[?(@.id=='a')].name` the name of the item whose
|
|
67
|
+
* id is `a`. A path with neither names at most one node, the same one `readPath` returns.
|
|
68
|
+
*/
|
|
69
|
+
export function readAll(body, path) {
|
|
70
|
+
const star = path.indexOf("[*]");
|
|
71
|
+
const filter = FILTER_RE.exec(path);
|
|
72
|
+
if (star < 0 && filter === null) {
|
|
73
|
+
const node = readPath(body, path);
|
|
74
|
+
return node === undefined ? [] : [node];
|
|
75
|
+
}
|
|
76
|
+
const fansOut = filter === null || (star >= 0 && star < filter.index);
|
|
77
|
+
const at = fansOut ? star : (filter?.index ?? 0);
|
|
78
|
+
const head = path.slice(0, at);
|
|
79
|
+
const rest = path.slice(at + (fansOut ? 3 : (filter?.[0].length ?? 0)));
|
|
80
|
+
const list = head === "$" || head === "" ? body : readPath(body, head);
|
|
81
|
+
if (!Array.isArray(list))
|
|
82
|
+
return [];
|
|
83
|
+
const kept = fansOut || filter === null
|
|
84
|
+
? list
|
|
85
|
+
: list.filter((item) => item !== null &&
|
|
86
|
+
typeof item === "object" &&
|
|
87
|
+
item[filter[1] ?? ""] === filterValue(filter));
|
|
88
|
+
return kept.flatMap((item) => (rest === "" ? [item] : readAll(item, `$${rest}`)));
|
|
89
|
+
}
|
|
44
90
|
//# sourceMappingURL=path.js.map
|
package/dist/runtime/path.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/runtime/path.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,UAAU,QAAQ,CAAC,IAAa,EAAE,OAAe;IACtD,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAClE,IAAI,IAAI,GAAY,IAAI,CAAA;IACxB,iGAAiG;IACjG,KAAK,MAAM,OAAO,IAAI,OAAO;SAC3B,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC;SAC5B,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACnB,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QAC1C,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YACzB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;gBACpB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YACd,CAAC,CAAE,IAAgC,CAAC,OAAO,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,SAAS,eAAe,CAAC,IAAa,EAAE,OAAe;IACtD,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAA;IAChC,IAAI,IAAI,GAAY,IAAI,CAAA;IACxB,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAC3D,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QAC1C,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YACzB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;gBACpB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YACd,CAAC,CAAE,IAAgC,CAAC,OAAO,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,IAAI,CAAA;AACZ,CAAC"}
|
|
1
|
+
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/runtime/path.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/run.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* per case — which is what made prior attempts produce dozens of failures from one broken fixture.
|
|
7
7
|
*/
|
|
8
8
|
import { type EntityModel, type SpecModel } from "../spec/graph.ts";
|
|
9
|
-
import type { EntityConfig, Hooks, OriginSpec, OutOfBandConfig, PayloadPolicy, Principal, ProfileSpec, QueryCapabilities, RateLimitSpec, Uploads } from "../config/define-config.ts";
|
|
9
|
+
import type { EntityConfig, Hooks, OriginSpec, OutOfBandConfig, PayloadPolicy, Principal, ProfileSpec, QueryCapabilities, RateLimitSpec, Scenario, Uploads } from "../config/define-config.ts";
|
|
10
10
|
import { type NetworkKind } from "./network.ts";
|
|
11
11
|
import { type PersistedPrincipal } from "./principals.ts";
|
|
12
12
|
import { Client } from "./client.ts";
|
|
@@ -52,6 +52,8 @@ export interface RunOptions {
|
|
|
52
52
|
onProgress?: ProgressHandler;
|
|
53
53
|
/** Extra hosts with their own OpenAPI. Auth stays on the primary. */
|
|
54
54
|
origins?: OriginSpec[];
|
|
55
|
+
/** Declared flows for operations the matrix cannot reach. */
|
|
56
|
+
scenarios?: Scenario[];
|
|
55
57
|
/** Backoff for `resolveOutOfBand` / `resolvePrincipalAuth`. */
|
|
56
58
|
outOfBand?: OutOfBandConfig;
|
|
57
59
|
/** How much of the payload catalog to send; see `OatConfig.payloads`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/runtime/run.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAc,KAAK,WAAW,EAAuB,KAAK,SAAS,EAAqB,MAAM,kBAAkB,CAAA;AAEvH,OAAO,KAAK,EACX,YAAY,EACZ,KAAK,EACL,UAAU,EACV,eAAe,EACf,aAAa,EACb,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,OAAO,EACP,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAIN,KAAK,WAAW,EAMhB,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,KAAK,kBAAkB,EAA2C,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/runtime/run.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/run.js
CHANGED
|
@@ -17,6 +17,9 @@ import { persistedToPrincipal, snapshotPrincipal } from "./principals.js";
|
|
|
17
17
|
import { CHECKS } from "./checks.js";
|
|
18
18
|
import { runPublicGets } from "./one-shot.js";
|
|
19
19
|
import { runDisposableOps, signupOperationId } from "./disposable.js";
|
|
20
|
+
import { assertScenarios, runScenarios } from "./scenario.js";
|
|
21
|
+
import { confirmDeletes, runContractCalls } from "./contract.js";
|
|
22
|
+
import { proveFromTranscript } from "./evidence.js";
|
|
20
23
|
import { rootReadIds, ROOT_READ_CHECK_ID, runRootRead } from "./root-read.js";
|
|
21
24
|
import { entityIsSelf, runSelfIdentity, SELF_CHECK_ID, selfOperationIds } from "./self.js";
|
|
22
25
|
import { AuthRefreshError, Client, } from "./client.js";
|
|
@@ -369,6 +372,8 @@ export async function run(options) {
|
|
|
369
372
|
exclusion: { profile: profile.name, reason: (op) => excludedByProfile(op, profile.spec) },
|
|
370
373
|
origins: new Map([...originClients].map(([id, origin]) => [id, origin.model])),
|
|
371
374
|
});
|
|
375
|
+
/* A scenario that names an operation it never calls is a config error, caught before signup. */
|
|
376
|
+
assertScenarios(options.scenarios ?? [], (id) => model.byOperationId.has(id));
|
|
372
377
|
const grades = new GradeLedger();
|
|
373
378
|
tick({
|
|
374
379
|
message: runScope.mode === "full"
|
|
@@ -484,6 +489,7 @@ export async function run(options) {
|
|
|
484
489
|
return op !== undefined && excludedByProfile(op, profile.spec) !== null;
|
|
485
490
|
};
|
|
486
491
|
const excludedIds = new Set();
|
|
492
|
+
const scenarioOwned = new Set((options.scenarios ?? []).flatMap((scenario) => scenario.covers));
|
|
487
493
|
const excludeOp = (entity, op) => {
|
|
488
494
|
if (op === undefined)
|
|
489
495
|
return false;
|
|
@@ -611,7 +617,21 @@ export async function run(options) {
|
|
|
611
617
|
let records;
|
|
612
618
|
let degraded = false;
|
|
613
619
|
let uniqueAdopted = false;
|
|
614
|
-
if (createOp !== undefined &&
|
|
620
|
+
if (createOp !== undefined && scenarioOwned.has(createOp.operationId)) {
|
|
621
|
+
/* A scenario owns this create: it holds what the create needs (a mailed token, a paid
|
|
622
|
+
* session), and a generated body would only be refused. The matrix reads what exists;
|
|
623
|
+
* with nothing to read, the entity is the scenario's alone. */
|
|
624
|
+
const existing = await readExisting(listOp, model, seeding, alpha.headers(), {
|
|
625
|
+
...options.roots,
|
|
626
|
+
...alpha.roots,
|
|
627
|
+
});
|
|
628
|
+
if (existing.records.length === 0)
|
|
629
|
+
return;
|
|
630
|
+
scope = { created: [], values: existing.scope };
|
|
631
|
+
records = existing.records;
|
|
632
|
+
degraded = true;
|
|
633
|
+
}
|
|
634
|
+
else if (createOp !== undefined && excludeOp(entity, createOp)) {
|
|
615
635
|
/* Same fallback a failed create takes below: read-only coverage against whatever
|
|
616
636
|
* already exists beats no coverage, and it is exactly the state most likely to hide a
|
|
617
637
|
* read-path bug. The gap finding excludeOp already reported names the reason. */
|
|
@@ -1241,23 +1261,31 @@ export async function run(options) {
|
|
|
1241
1261
|
mode: runScope.mode,
|
|
1242
1262
|
model,
|
|
1243
1263
|
nonce,
|
|
1264
|
+
...(options.principals[0]?.auth === undefined ? {} : { authFlow: options.principals[0].auth }),
|
|
1265
|
+
hooks,
|
|
1266
|
+
outOfBand,
|
|
1244
1267
|
...(registerOperationId === undefined ? {} : { registerOperationId }),
|
|
1245
1268
|
...(hooks.resolveInput === undefined ? {} : { resolveInput: hooks.resolveInput }),
|
|
1269
|
+
...(options.principals[0]?.rootsFromFlow === undefined
|
|
1270
|
+
? {}
|
|
1271
|
+
: { rootsFromFlow: options.principals[0].rootsFromFlow }),
|
|
1246
1272
|
seed,
|
|
1247
1273
|
});
|
|
1248
1274
|
/* Unwind after every check has run, never per case: a check may legitimately depend on records
|
|
1249
1275
|
* another one created, and tearing down early turns that into a phantom defect. */
|
|
1250
|
-
currentPhase = "teardown";
|
|
1251
|
-
currentCheck = undefined;
|
|
1252
|
-
tick({
|
|
1253
|
-
message: `${ledger.size} record(s)`,
|
|
1254
|
-
phase: "teardown",
|
|
1255
|
-
requests: client.transcript.length,
|
|
1256
|
-
});
|
|
1257
1276
|
let teardown = null;
|
|
1258
|
-
|
|
1277
|
+
const unwindLedger = async () => {
|
|
1278
|
+
currentPhase = "teardown";
|
|
1279
|
+
currentCheck = undefined;
|
|
1280
|
+
tick({
|
|
1281
|
+
message: `${ledger.size} record(s)`,
|
|
1282
|
+
phase: "teardown",
|
|
1283
|
+
requests: client.transcript.length,
|
|
1284
|
+
});
|
|
1285
|
+
if (options.keepFixtures === true || ledger.size === 0)
|
|
1286
|
+
return;
|
|
1259
1287
|
try {
|
|
1260
|
-
|
|
1288
|
+
const report = await ledger.unwind(model, client.view({ purpose: "teardown" }), fallbackDeleters, (done, total, item) => {
|
|
1261
1289
|
if (done % 25 !== 0 && done !== total)
|
|
1262
1290
|
return;
|
|
1263
1291
|
tick({
|
|
@@ -1267,13 +1295,68 @@ export async function run(options) {
|
|
|
1267
1295
|
requests: client.transcript.length,
|
|
1268
1296
|
});
|
|
1269
1297
|
});
|
|
1298
|
+
teardown = {
|
|
1299
|
+
failed: [...(teardown?.failed ?? []), ...report.failed],
|
|
1300
|
+
removed: (teardown?.removed ?? 0) + report.removed,
|
|
1301
|
+
unsupported: [...new Set([...(teardown?.unsupported ?? []), ...report.unsupported])],
|
|
1302
|
+
};
|
|
1270
1303
|
}
|
|
1271
1304
|
catch (error) {
|
|
1272
1305
|
findings.blocked("world.teardown", "run", `teardown stopped on an unexpected error with ${ledger.size} record(s) ledgered`, errorText(error));
|
|
1273
1306
|
}
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1307
|
+
};
|
|
1308
|
+
/* The matrix's fixtures go before the scenarios: a scenario sets its own scene, and a record a
|
|
1309
|
+
* check left (a pending invite) would refuse the scenario's own as a duplicate. */
|
|
1310
|
+
await unwindLedger();
|
|
1311
|
+
await runScenarios({
|
|
1312
|
+
checksRun,
|
|
1313
|
+
client,
|
|
1314
|
+
defaultPrincipal: alpha.id,
|
|
1315
|
+
findings,
|
|
1316
|
+
grades,
|
|
1317
|
+
hooks,
|
|
1318
|
+
inScope: (operationId) => runScope.inScope(operationId),
|
|
1319
|
+
mode: runScope.mode,
|
|
1320
|
+
model,
|
|
1321
|
+
originClients,
|
|
1322
|
+
outOfBand: options.outOfBand ?? {},
|
|
1323
|
+
principals: new Map(resolved.map((principal) => [principal.id, { headers: principal.headers, roots: principal.roots }])),
|
|
1324
|
+
scenarios: options.scenarios ?? [],
|
|
1325
|
+
});
|
|
1326
|
+
await proveFromTranscript({ client, grades, model });
|
|
1327
|
+
await confirmDeletes({
|
|
1328
|
+
auth: alpha.headers,
|
|
1329
|
+
client,
|
|
1330
|
+
findings,
|
|
1331
|
+
grades,
|
|
1332
|
+
inScope: (operationId) => runScope.inScope(operationId),
|
|
1333
|
+
mode: runScope.mode,
|
|
1334
|
+
model,
|
|
1335
|
+
stopped: () => options.signal?.aborted === true || networkGate.exhausted,
|
|
1336
|
+
});
|
|
1337
|
+
await runContractCalls({
|
|
1338
|
+
auth: alpha.headers,
|
|
1339
|
+
checksRun,
|
|
1340
|
+
client,
|
|
1341
|
+
findings,
|
|
1342
|
+
grades,
|
|
1343
|
+
hooks,
|
|
1344
|
+
inScope: (operationId) => runScope.inScope(operationId),
|
|
1345
|
+
mode: runScope.mode,
|
|
1346
|
+
model,
|
|
1347
|
+
nonce,
|
|
1348
|
+
owned: scenarioOwned,
|
|
1349
|
+
roots: alpha.roots,
|
|
1350
|
+
seed,
|
|
1351
|
+
stopped: () => options.signal?.aborted === true || networkGate.exhausted,
|
|
1352
|
+
validator,
|
|
1353
|
+
});
|
|
1354
|
+
/* What scenarios and contract calls created since. */
|
|
1355
|
+
await unwindLedger();
|
|
1356
|
+
const unremovable = teardown?.unsupported ?? [];
|
|
1357
|
+
if (unremovable.length > 0) {
|
|
1358
|
+
const names = unremovable.join(", ");
|
|
1359
|
+
findings.gap("world.teardown", names, "records created during the run could not be removed", `no delete operation is reachable for ${names}, so this run left ` +
|
|
1277
1360
|
"fixtures behind. Declare x-cleanup to name the route that removes them.");
|
|
1278
1361
|
}
|
|
1279
1362
|
await refreshAll("run");
|
|
@@ -1314,7 +1397,7 @@ export async function run(options) {
|
|
|
1314
1397
|
checksSuppressed,
|
|
1315
1398
|
inconclusive: findings.inconclusive,
|
|
1316
1399
|
client,
|
|
1317
|
-
created: ledger.
|
|
1400
|
+
created: ledger.recorded,
|
|
1318
1401
|
entitiesTested,
|
|
1319
1402
|
findings: findings.findings,
|
|
1320
1403
|
model,
|