blueprint-runtime 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +224 -3
- package/dist/src/blueprint/load.js +83 -0
- package/dist/src/blueprint/load.js.map +1 -0
- package/dist/src/blueprint/plan.js +202 -0
- package/dist/src/blueprint/plan.js.map +1 -0
- package/dist/src/cli.js +182 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/expr/dates.js +152 -0
- package/dist/src/expr/dates.js.map +1 -0
- package/dist/src/expr/decimal.js +66 -0
- package/dist/src/expr/decimal.js.map +1 -0
- package/dist/src/expr/errors.js +31 -0
- package/dist/src/expr/errors.js.map +1 -0
- package/dist/src/expr/evaluate.js +250 -0
- package/dist/src/expr/evaluate.js.map +1 -0
- package/dist/src/expr/functions.js +460 -0
- package/dist/src/expr/functions.js.map +1 -0
- package/dist/src/expr/parser.js +228 -0
- package/dist/src/expr/parser.js.map +1 -0
- package/dist/src/expr/scan.js +101 -0
- package/dist/src/expr/scan.js.map +1 -0
- package/dist/src/expr/values.js +118 -0
- package/dist/src/expr/values.js.map +1 -0
- package/dist/src/findings.js +10 -0
- package/dist/src/findings.js.map +1 -0
- package/dist/src/inspect.js +313 -0
- package/dist/src/inspect.js.map +1 -0
- package/dist/src/json/parse.js +225 -0
- package/dist/src/json/parse.js.map +1 -0
- package/dist/src/registry.js +39 -0
- package/dist/src/registry.js.map +1 -0
- package/dist/src/run/case.js +122 -0
- package/dist/src/run/case.js.map +1 -0
- package/dist/src/run/compare.js +94 -0
- package/dist/src/run/compare.js.map +1 -0
- package/dist/src/run/fixtures.js +122 -0
- package/dist/src/run/fixtures.js.map +1 -0
- package/dist/src/run/modules.js +380 -0
- package/dist/src/run/modules.js.map +1 -0
- package/dist/src/run/operators.js +151 -0
- package/dist/src/run/operators.js.map +1 -0
- package/dist/src/run/scheduler.js +306 -0
- package/dist/src/run/scheduler.js.map +1 -0
- package/dist/src/run/trace.js +2 -0
- package/dist/src/run/trace.js.map +1 -0
- package/package.json +60 -6
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { loadBlueprint } from '../blueprint/load.js';
|
|
4
|
+
import { UnsupportedError } from '../expr/errors.js';
|
|
5
|
+
import { inspectBytes } from '../inspect.js';
|
|
6
|
+
import { isObject, JsonParseError, parseJson } from '../json/parse.js';
|
|
7
|
+
import { compareExecutions } from './compare.js';
|
|
8
|
+
import { FixtureError, FixtureSet } from './fixtures.js';
|
|
9
|
+
import { InvalidInputError, LimitExceededError, runScenario } from './scheduler.js';
|
|
10
|
+
class CaseError extends Error {
|
|
11
|
+
}
|
|
12
|
+
function readJson(file, what) {
|
|
13
|
+
let text;
|
|
14
|
+
try {
|
|
15
|
+
text = readFileSync(file, 'utf8');
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
throw new CaseError(`cannot read the ${what} ${file}`);
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
return parseJson(text, 64).value;
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
if (e instanceof JsonParseError)
|
|
25
|
+
throw new CaseError(`${what} ${file} line ${e.loc.line}: ${e.message}`);
|
|
26
|
+
throw e;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function clockOf(env, expected) {
|
|
30
|
+
const now = isObject(env) ? env['now'] : undefined;
|
|
31
|
+
if (now === undefined)
|
|
32
|
+
return {};
|
|
33
|
+
if (now === 'recorded') {
|
|
34
|
+
const started = new Map();
|
|
35
|
+
for (const s of expected.steps)
|
|
36
|
+
if (s.kind === 'module' && s.startedAt)
|
|
37
|
+
started.set(`${s.id}#${s.operation}`, Date.parse(s.startedAt));
|
|
38
|
+
return {
|
|
39
|
+
clock: (id, operation) => {
|
|
40
|
+
const t = started.get(`${id}#${operation}`);
|
|
41
|
+
if (t === undefined)
|
|
42
|
+
throw new CaseError(`environment.now is "recorded", but the expected run has no start time for module ${id}, operation ${operation}`);
|
|
43
|
+
return t;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (typeof now !== 'string' || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z$/.test(now))
|
|
48
|
+
throw new CaseError('environment.now must be an ISO instant like "2026-10-10T12:00:00.000Z", or "recorded"');
|
|
49
|
+
const t = Date.parse(now);
|
|
50
|
+
return { clock: () => t };
|
|
51
|
+
}
|
|
52
|
+
export function runCase(file) {
|
|
53
|
+
const base = { name: file, file, verdict: 'invalid' };
|
|
54
|
+
try {
|
|
55
|
+
const spec = readJson(file, 'case');
|
|
56
|
+
if (!isObject(spec) || spec['schemaVersion'] !== 1)
|
|
57
|
+
throw new CaseError('a case needs "schemaVersion": 1');
|
|
58
|
+
const str = (k) => {
|
|
59
|
+
const v = spec[k];
|
|
60
|
+
if (typeof v !== 'string' || v === '')
|
|
61
|
+
throw new CaseError(`a case needs a "${k}" path`);
|
|
62
|
+
return resolve(dirname(file), v);
|
|
63
|
+
};
|
|
64
|
+
if (typeof spec['name'] === 'string')
|
|
65
|
+
base.name = spec['name'];
|
|
66
|
+
const env = spec['environment'];
|
|
67
|
+
const timezone = isObject(env) && typeof env['timezone'] === 'string' ? env['timezone'] : null;
|
|
68
|
+
const blueprintFile = str('blueprint');
|
|
69
|
+
const bytes = (() => {
|
|
70
|
+
try {
|
|
71
|
+
return readFileSync(blueprintFile);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
throw new CaseError(`cannot read the blueprint ${blueprintFile}`);
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
77
|
+
const report = inspectBytes(blueprintFile, bytes);
|
|
78
|
+
if (report.verdict === 'invalid')
|
|
79
|
+
return { ...base, verdict: 'invalid', message: report.findings.find((f) => f.severity === 'error')?.message ?? 'invalid blueprint' };
|
|
80
|
+
if (report.verdict === 'unsupported')
|
|
81
|
+
return { ...base, verdict: 'unsupported', message: report.findings.find((f) => f.severity === 'unsupported')?.message ?? 'unsupported' };
|
|
82
|
+
const plan = loadBlueprint(bytes).plan;
|
|
83
|
+
const input = readJson(str('input'), 'input');
|
|
84
|
+
const expected = readJson(str('expected'), 'expected run');
|
|
85
|
+
if (!isObject(expected) || expected.schemaVersion !== 2 || !Array.isArray(expected.steps))
|
|
86
|
+
throw new CaseError('the expected run is not a reference (schemaVersion 2 with steps)');
|
|
87
|
+
let fixtures;
|
|
88
|
+
try {
|
|
89
|
+
fixtures = typeof spec['fixtures'] === 'string' ? FixtureSet.fromJson(readJson(str('fixtures'), 'fixtures')) : new FixtureSet();
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
if (e instanceof FixtureError)
|
|
93
|
+
return { ...base, verdict: 'invalid', message: e.message };
|
|
94
|
+
throw e;
|
|
95
|
+
}
|
|
96
|
+
let trace;
|
|
97
|
+
try {
|
|
98
|
+
trace = runScenario(plan, input, { timezone, fixtures, ...clockOf(env, expected) });
|
|
99
|
+
fixtures.assertAllUsed();
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
if (e instanceof FixtureError)
|
|
103
|
+
return { ...base, verdict: 'fixture', message: e.message };
|
|
104
|
+
if (e instanceof InvalidInputError)
|
|
105
|
+
return { ...base, verdict: 'invalid', message: e.message };
|
|
106
|
+
if (e instanceof UnsupportedError)
|
|
107
|
+
return { ...base, verdict: 'unsupported', message: e.message };
|
|
108
|
+
if (e instanceof LimitExceededError)
|
|
109
|
+
return { ...base, verdict: 'limit', message: e.message };
|
|
110
|
+
throw e;
|
|
111
|
+
}
|
|
112
|
+
const divergence = compareExecutions(expected, trace);
|
|
113
|
+
base.against = expected.source === 'blueprint-runtime' ? 'snapshot' : 'make';
|
|
114
|
+
return divergence ? { ...base, verdict: 'fail', divergence, message: divergence.message, trace } : { ...base, verdict: 'pass', trace };
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
if (e instanceof CaseError)
|
|
118
|
+
return { ...base, verdict: 'invalid', message: e.message };
|
|
119
|
+
throw e;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=case.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case.js","sourceRoot":"","sources":["../../../src/run/case.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAkB,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,iBAAiB,EAAmB,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA+BpF,MAAM,SAAU,SAAQ,KAAK;CAAG;AAEhC,SAAS,QAAQ,CAAC,IAAY,EAAE,IAAY;IAC1C,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,SAAS,CAAC,mBAAmB,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,CAAC;QACH,OAAO,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC;IACnC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,cAAc;YAAE,MAAM,IAAI,SAAS,CAAC,GAAG,IAAI,IAAI,IAAI,SAAS,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACzG,MAAM,CAAC,CAAC;IACV,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,GAA0B,EAAE,QAAmB;IAC9D,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC1C,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK;YAAE,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS;gBAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QACvI,OAAO;YACL,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE;gBACvB,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,SAAS,EAAE,CAAC,CAAC;gBAC5C,IAAI,CAAC,KAAK,SAAS;oBAAE,MAAM,IAAI,SAAS,CAAC,oFAAoF,EAAE,eAAe,SAAS,EAAE,CAAC,CAAC;gBAC3J,OAAO,CAAC,CAAC;YACX,CAAC;SACF,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,oDAAoD,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,uFAAuF,CAAC,CAAC;IAC7M,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,MAAM,IAAI,GAAe,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAClE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,iCAAiC,CAAC,CAAC;QAC3G,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE;YACxB,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE;gBAAE,MAAM,IAAI,SAAS,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;YACzF,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACnC,CAAC,CAAC;QACF,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,QAAQ;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;QAChC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAE/F,MAAM,aAAa,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE;YAClB,IAAI,CAAC;gBACH,OAAO,YAAY,CAAC,aAAa,CAAC,CAAC;YACrC,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,SAAS,CAAC,6BAA6B,aAAa,EAAE,CAAC,CAAC;YACpE,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,MAAM,MAAM,GAAG,YAAY,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QAClD,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS;YAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,EAAE,OAAO,IAAI,mBAAmB,EAAE,CAAC;QACvK,IAAI,MAAM,CAAC,OAAO,KAAK,aAAa;YAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,aAAa,CAAC,EAAE,OAAO,IAAI,aAAa,EAAE,CAAC;QAC/K,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,IAAK,CAAC;QAExC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,cAAc,CAAyB,CAAC;QACnF,IAAI,CAAC,QAAQ,CAAC,QAAgC,CAAC,IAAI,QAAQ,CAAC,aAAa,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,kEAAkE,CAAC,CAAC;QAE3M,IAAI,QAAoB,CAAC;QACzB,IAAI,CAAC;YACH,QAAQ,GAAG,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,EAAE,CAAC;QAClI,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,YAAY;gBAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;YAC1F,MAAM,CAAC,CAAC;QACV,CAAC;QAED,IAAI,KAAY,CAAC;QACjB,IAAI,CAAC;YACH,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YACpF,QAAQ,CAAC,aAAa,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,YAAY;gBAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;YAC1F,IAAI,CAAC,YAAY,iBAAiB;gBAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;YAC/F,IAAI,CAAC,YAAY,gBAAgB;gBAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;YAClG,IAAI,CAAC,YAAY,kBAAkB;gBAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;YAC9F,MAAM,CAAC,CAAC;QACV,CAAC;QACD,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACtD,IAAI,CAAC,OAAO,GAAI,QAA2C,CAAC,MAAM,KAAK,mBAAmB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;QACjH,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACzI,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,SAAS;YAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QACvF,MAAM,CAAC,CAAC;IACV,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { escapePointerToken, isObject } from '../json/parse.js';
|
|
2
|
+
const show = (v) => (v === undefined ? '(absent)' : JSON.stringify(v));
|
|
3
|
+
const describeStep = (s) => !s ? 'nothing' : s.kind === 'module' ? `module ${s.id}${s.operation > 1 ? ` (operation ${s.operation})` : ''}` : `the filter before module ${s.moduleId}`;
|
|
4
|
+
/** First difference between two JSON values, or null. Object key order does not matter. */
|
|
5
|
+
export function firstDifference(expected, actual, pointer = '') {
|
|
6
|
+
if (Array.isArray(expected) && Array.isArray(actual)) {
|
|
7
|
+
for (let i = 0; i < Math.max(expected.length, actual.length); i++) {
|
|
8
|
+
const d = firstDifference(expected[i], actual[i], `${pointer}/${i}`);
|
|
9
|
+
if (d)
|
|
10
|
+
return d;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (isObject(expected) && isObject(actual)) {
|
|
15
|
+
const keys = [...new Set([...Object.keys(expected), ...Object.keys(actual)])];
|
|
16
|
+
for (const k of keys) {
|
|
17
|
+
const d = firstDifference(expected[k], actual[k], `${pointer}/${escapePointerToken(k)}`);
|
|
18
|
+
if (d)
|
|
19
|
+
return d;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return Object.is(expected, actual) ? null : { pointer, expected, actual };
|
|
24
|
+
}
|
|
25
|
+
export function compareExecutions(expected, actual) {
|
|
26
|
+
// A snapshot made with `bpr run` is not Make evidence; say whose run it was.
|
|
27
|
+
const who = expected.source === 'blueprint-runtime' ? 'the snapshot' : 'Make';
|
|
28
|
+
const n = Math.max(expected.steps.length, actual.steps.length);
|
|
29
|
+
for (let i = 0; i < n; i++) {
|
|
30
|
+
const e = expected.steps[i];
|
|
31
|
+
const a = actual.steps[i];
|
|
32
|
+
const same = e && a && e.kind === a.kind && (e.kind === 'module' ? a.kind === 'module' && e.id === a.id && e.operation === a.operation : a.kind === 'filter' && e.moduleId === a.moduleId);
|
|
33
|
+
if (!same) {
|
|
34
|
+
return {
|
|
35
|
+
where: 'step',
|
|
36
|
+
step: i + 1,
|
|
37
|
+
expected: e ? (e.kind === 'module' ? e.id : `filter ${e.moduleId}`) : null,
|
|
38
|
+
actual: a ? (a.kind === 'module' ? a.id : `filter ${a.moduleId}`) : null,
|
|
39
|
+
message: !a ? `step ${i + 1}: ${who} did ${describeStep(e)}, the local run had stopped` : !e ? `step ${i + 1}: the local run did ${describeStep(a)}, which ${who} did not` : `step ${i + 1}: ${who} did ${describeStep(e)}, the local run did ${describeStep(a)}`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (e.kind === 'filter' && a.kind === 'filter') {
|
|
43
|
+
if (e.passed !== a.passed || firstDifference(e.report, a.report)) {
|
|
44
|
+
return {
|
|
45
|
+
where: 'filter',
|
|
46
|
+
step: i + 1,
|
|
47
|
+
moduleId: e.moduleId,
|
|
48
|
+
expected: { passed: e.passed, report: e.report },
|
|
49
|
+
actual: { passed: a.passed, report: a.report },
|
|
50
|
+
message: `step ${i + 1}, filter before module ${e.moduleId}: ${who} ${e.passed ? 'passed' : 'stopped'} the bundle ${show(e.report)}, the local run ${a.passed ? 'passed' : 'stopped'} it ${show(a.report)}`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (e.kind !== 'module' || a.kind !== 'module')
|
|
56
|
+
continue;
|
|
57
|
+
for (const side of ['input', 'output']) {
|
|
58
|
+
const eb = e[side];
|
|
59
|
+
const ab = a[side];
|
|
60
|
+
if (eb.length !== ab.length) {
|
|
61
|
+
return { where: side, step: i + 1, moduleId: e.id, operation: e.operation, expected: eb.length, actual: ab.length, message: `${describeStep(e)} ${side}: ${who} had ${eb.length} bundle(s), the local run ${ab.length}` };
|
|
62
|
+
}
|
|
63
|
+
for (let b = 0; b < eb.length; b++) {
|
|
64
|
+
const d = firstDifference(eb[b], ab[b]);
|
|
65
|
+
if (d) {
|
|
66
|
+
return {
|
|
67
|
+
where: side,
|
|
68
|
+
step: i + 1,
|
|
69
|
+
moduleId: e.id,
|
|
70
|
+
operation: e.operation,
|
|
71
|
+
bundle: b + 1,
|
|
72
|
+
pointer: d.pointer,
|
|
73
|
+
expected: d.expected,
|
|
74
|
+
actual: d.actual,
|
|
75
|
+
message: `${describeStep(e)} ${side} bundle ${b + 1} at ${d.pointer || '(whole bundle)'}: ${who} had ${show(d.expected)}, the local run ${show(d.actual)}`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const outcome = (x) => (x.status === 'error' ? { status: x.status, ...x.error } : { status: x.status });
|
|
82
|
+
const od = firstDifference(outcome(expected), outcome(actual));
|
|
83
|
+
if (od) {
|
|
84
|
+
return { where: 'outcome', pointer: od.pointer, expected: od.expected, actual: od.actual, message: `run outcome ${od.pointer}: ${who} had ${show(od.expected)}, the local run ${show(od.actual)}` };
|
|
85
|
+
}
|
|
86
|
+
if (expected.response) {
|
|
87
|
+
const rd = firstDifference(expected.response, (actual.response ?? null));
|
|
88
|
+
if (rd) {
|
|
89
|
+
return { where: 'response', pointer: rd.pointer, expected: rd.expected, actual: rd.actual, message: `webhook response ${rd.pointer}: ${who} sent ${show(rd.expected)}, the local run ${show(rd.actual)}` };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../../src/run/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { isObject } from '../json/parse.js';
|
|
2
|
+
import { firstDifference } from './compare.js';
|
|
3
|
+
/** A fixture is missing, ambiguous, unused or does not match: exit 5. */
|
|
4
|
+
export class FixtureError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'FixtureError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const key = (module, occurrence) => `${module}#${occurrence}`;
|
|
11
|
+
export class FixtureSet {
|
|
12
|
+
byKey = new Map();
|
|
13
|
+
used = new Set();
|
|
14
|
+
uses = [];
|
|
15
|
+
constructor(fixtures = []) {
|
|
16
|
+
for (const f of fixtures) {
|
|
17
|
+
const k = key(f.module, f.occurrence);
|
|
18
|
+
if (this.byKey.has(k))
|
|
19
|
+
throw new FixtureError(`two fixtures for module ${f.module}, invocation ${f.occurrence}`);
|
|
20
|
+
this.byKey.set(k, f);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Validates a parsed fixtures file. Throws FixtureError with the offending entry. */
|
|
24
|
+
static fromJson(doc) {
|
|
25
|
+
if (!isObject(doc) || doc['schemaVersion'] !== 1 || !Array.isArray(doc['fixtures']))
|
|
26
|
+
throw new FixtureError('a fixtures file needs "schemaVersion": 1 and a "fixtures" array');
|
|
27
|
+
return new FixtureSet(doc['fixtures'].map((f, i) => parseFixture(f, i)));
|
|
28
|
+
}
|
|
29
|
+
take(module, occurrence, mode) {
|
|
30
|
+
const k = key(module, occurrence);
|
|
31
|
+
const f = this.byKey.get(k);
|
|
32
|
+
if (!f)
|
|
33
|
+
throw new FixtureError(`module ${module}, invocation ${occurrence}: no fixture (offline runs never reach the network)`);
|
|
34
|
+
if (f.mode !== mode)
|
|
35
|
+
throw new FixtureError(`module ${module}, invocation ${occurrence}: the fixture is "${f.mode}", this module needs "${mode}"`);
|
|
36
|
+
if (this.used.has(k))
|
|
37
|
+
throw new FixtureError(`module ${module}, invocation ${occurrence}: fixture used twice`);
|
|
38
|
+
this.used.add(k);
|
|
39
|
+
this.uses.push({ module, occurrence, mode });
|
|
40
|
+
return f;
|
|
41
|
+
}
|
|
42
|
+
/** The HTTP fixture for this call, after checking the request the scenario built. */
|
|
43
|
+
http(module, occurrence, request) {
|
|
44
|
+
const f = this.take(module, occurrence, 'http');
|
|
45
|
+
const d = firstDifference(f.request, request);
|
|
46
|
+
if (d)
|
|
47
|
+
throw new FixtureError(`module ${module}, invocation ${occurrence}: request ${d.pointer || '/'} expected ${JSON.stringify(d.expected ?? null)}, the scenario built ${JSON.stringify(d.actual ?? null)}. Fixture was not consumed.`);
|
|
48
|
+
return f;
|
|
49
|
+
}
|
|
50
|
+
/** The module fixture for this call, after checking the mapped input. */
|
|
51
|
+
module(module, occurrence, input) {
|
|
52
|
+
const f = this.take(module, occurrence, 'module');
|
|
53
|
+
const d = firstDifference(f.input, input);
|
|
54
|
+
if (d)
|
|
55
|
+
throw new FixtureError(`module ${module}, invocation ${occurrence}: input ${d.pointer || '/'} expected ${JSON.stringify(d.expected ?? null)}, the scenario mapped ${JSON.stringify(d.actual ?? null)}. Fixture was not consumed.`);
|
|
56
|
+
return f;
|
|
57
|
+
}
|
|
58
|
+
/** Fails if any fixture was never used (spec 7.1: required fixture unused). */
|
|
59
|
+
assertAllUsed() {
|
|
60
|
+
const unused = [...this.byKey.keys()].filter((k) => !this.used.has(k));
|
|
61
|
+
if (unused.length)
|
|
62
|
+
throw new FixtureError(`fixture${unused.length > 1 ? 's' : ''} never used: ${unused.map((k) => `module ${k.replace('#', ', invocation ')}`).join('; ')}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function parseFixture(f, i) {
|
|
66
|
+
const bad = (why) => new FixtureError(`fixtures[${i}]: ${why}`);
|
|
67
|
+
if (!isObject(f))
|
|
68
|
+
throw bad('expected an object');
|
|
69
|
+
const { module, occurrence, mode } = f;
|
|
70
|
+
if (typeof module !== 'number' || !Number.isSafeInteger(module) || module < 1)
|
|
71
|
+
throw bad('"module" must be a module id');
|
|
72
|
+
if (typeof occurrence !== 'number' || !Number.isSafeInteger(occurrence) || occurrence < 1)
|
|
73
|
+
throw bad('"occurrence" must be a 1-based invocation number');
|
|
74
|
+
const allowed = (keys) => {
|
|
75
|
+
const extra = Object.keys(f).filter((k) => !keys.includes(k));
|
|
76
|
+
if (extra.length)
|
|
77
|
+
throw bad(`unknown field${extra.length > 1 ? 's' : ''} ${extra.join(', ')}`);
|
|
78
|
+
};
|
|
79
|
+
if (mode === 'http') {
|
|
80
|
+
allowed(['module', 'occurrence', 'mode', 'request', 'response']);
|
|
81
|
+
const req = f['request'];
|
|
82
|
+
const res = f['response'];
|
|
83
|
+
if (!isObject(req) || typeof req['method'] !== 'string' || typeof req['url'] !== 'string')
|
|
84
|
+
throw bad('"request" needs "method" and "url"');
|
|
85
|
+
for (const k of ['contentType', 'body'])
|
|
86
|
+
if (!(k in req) || (req[k] !== null && typeof req[k] !== 'string'))
|
|
87
|
+
throw bad(`"request.${k}" must be text or null`);
|
|
88
|
+
if (Object.keys(req).length !== 4)
|
|
89
|
+
throw bad('"request" takes exactly method, url, contentType and body');
|
|
90
|
+
if (!isObject(res) || typeof res['status'] !== 'number' || typeof res['body'] !== 'string')
|
|
91
|
+
throw bad('"response" needs a numeric "status" and a text "body"');
|
|
92
|
+
const headers = res['headers'] ?? [];
|
|
93
|
+
if (!Array.isArray(headers) || !headers.every((h) => Array.isArray(h) && h.length === 2 && typeof h[0] === 'string' && typeof h[1] === 'string'))
|
|
94
|
+
throw bad('"response.headers" must be a list of [name, value] pairs');
|
|
95
|
+
return {
|
|
96
|
+
module, occurrence, mode,
|
|
97
|
+
request: { method: req['method'], url: req['url'], contentType: req['contentType'], body: req['body'] },
|
|
98
|
+
response: { status: res['status'], headers: headers, body: res['body'] },
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (mode === 'module') {
|
|
102
|
+
allowed(['module', 'occurrence', 'mode', 'input', 'output', 'error']);
|
|
103
|
+
if (!('input' in f))
|
|
104
|
+
throw bad('"input" (the expected mapped input bundle) is required');
|
|
105
|
+
const output = f['output'];
|
|
106
|
+
const error = f['error'];
|
|
107
|
+
if ((output === undefined) === (error === undefined))
|
|
108
|
+
throw bad('give exactly one of "output" or "error"');
|
|
109
|
+
if (output !== undefined && !Array.isArray(output))
|
|
110
|
+
throw bad('"output" must be a list of bundles');
|
|
111
|
+
if (error !== undefined && (!isObject(error) || typeof error['name'] !== 'string' || typeof error['message'] !== 'string'))
|
|
112
|
+
throw bad('"error" needs "name" and "message"');
|
|
113
|
+
const fx = { module, occurrence, mode, input: f['input'] };
|
|
114
|
+
if (output !== undefined)
|
|
115
|
+
fx.output = output;
|
|
116
|
+
if (error !== undefined)
|
|
117
|
+
fx.error = { name: error['name'], message: error['message'] };
|
|
118
|
+
return fx;
|
|
119
|
+
}
|
|
120
|
+
throw bad('"mode" must be "http" or "module"');
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../../../src/run/fixtures.ts"],"names":[],"mappings":"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"}
|