mandrel-platform 0.17.2 → 0.18.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/README.md +220 -14
- package/config/commitlint.base.mjs +36 -0
- package/config/repo-settings.schema.json +78 -0
- package/package.json +2 -1
- package/scripts/apply-uptime-monitors.mjs +378 -0
- package/scripts/apply-uptime-monitors.test.mjs +372 -0
- package/scripts/check-repo-settings.mjs +363 -0
- package/scripts/check-repo-settings.test.mjs +320 -0
- package/scripts/check-required-contexts.mjs +247 -129
- package/scripts/check-required-contexts.test.mjs +137 -0
- package/scripts/check-ruleset.mjs +435 -0
- package/scripts/check-ruleset.test.mjs +439 -0
- package/scripts/check-wrangler-baseline.mjs +514 -0
- package/scripts/check-wrangler-baseline.test.mjs +454 -0
- package/scripts/platform-sync.mjs +533 -5
- package/scripts/platform-sync.test.mjs +477 -0
- package/templates/workflows/deploy-staging.yml +86 -0
- package/templates/workflows/uptime-apply.yml +54 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* check-wrangler-baseline.test.mjs — node:test suite for the Story #177
|
|
4
|
+
* wrangler configuration baseline gate.
|
|
5
|
+
*
|
|
6
|
+
* Exercises the pure rule/parser functions directly and the full
|
|
7
|
+
* `runCli` pipeline against real temp files (wrangler.toml AND
|
|
8
|
+
* wrangler.jsonc) so both config formats are covered end to end.
|
|
9
|
+
*
|
|
10
|
+
* Run: node scripts/check-wrangler-baseline.test.mjs (or `node --test scripts/`)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import assert from 'node:assert/strict';
|
|
14
|
+
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
|
|
15
|
+
import { tmpdir } from 'node:os';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { afterEach, beforeEach, test } from 'node:test';
|
|
18
|
+
import {
|
|
19
|
+
parseArgv,
|
|
20
|
+
resolveConfigPath,
|
|
21
|
+
parseJsonc,
|
|
22
|
+
parseWranglerToml,
|
|
23
|
+
parseWranglerConfig,
|
|
24
|
+
readExceptions,
|
|
25
|
+
checkEnvSplit,
|
|
26
|
+
checkLogpush,
|
|
27
|
+
checkAnalyticsEngine,
|
|
28
|
+
checkCompatibilityDate,
|
|
29
|
+
evaluateBaseline,
|
|
30
|
+
renderReport,
|
|
31
|
+
runCli,
|
|
32
|
+
} from './check-wrangler-baseline.mjs';
|
|
33
|
+
|
|
34
|
+
let tmpDir;
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
tmpDir = mkdtempSync(join(tmpdir(), 'wrangler-baseline-test-'));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// parseArgv
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
test('parseArgv defaults maxAgeDays to 90 and flags to false', () => {
|
|
49
|
+
const parsed = parseArgv([]);
|
|
50
|
+
assert.deepEqual(parsed, { file: null, maxAgeDays: 90, warnOnly: false, json: false, help: false });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('parseArgv reads --file, --max-age-days, --warn-only, --json, --help', () => {
|
|
54
|
+
const parsed = parseArgv(['--file', 'custom.toml', '--max-age-days', '30', '--warn-only', '--json', '--help']);
|
|
55
|
+
assert.deepEqual(parsed, { file: 'custom.toml', maxAgeDays: 30, warnOnly: true, json: true, help: true });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('parseArgv falls back to 90 on a non-numeric --max-age-days', () => {
|
|
59
|
+
const parsed = parseArgv(['--max-age-days', 'nope']);
|
|
60
|
+
assert.equal(parsed.maxAgeDays, 90);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// resolveConfigPath
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
test('resolveConfigPath returns null when no config exists', () => {
|
|
68
|
+
assert.equal(resolveConfigPath(null, tmpDir), null);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('resolveConfigPath auto-detects wrangler.jsonc over wrangler.toml', () => {
|
|
72
|
+
writeFileSync(join(tmpDir, 'wrangler.toml'), 'name = "x"\n');
|
|
73
|
+
writeFileSync(join(tmpDir, 'wrangler.jsonc'), '{"name": "x"}\n');
|
|
74
|
+
assert.equal(resolveConfigPath(null, tmpDir), join(tmpDir, 'wrangler.jsonc'));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('resolveConfigPath falls back to wrangler.toml when no jsonc/json present', () => {
|
|
78
|
+
writeFileSync(join(tmpDir, 'wrangler.toml'), 'name = "x"\n');
|
|
79
|
+
assert.equal(resolveConfigPath(null, tmpDir), join(tmpDir, 'wrangler.toml'));
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('resolveConfigPath honors an explicit --file and returns null if missing', () => {
|
|
83
|
+
const explicit = join(tmpDir, 'custom.jsonc');
|
|
84
|
+
assert.equal(resolveConfigPath(explicit, tmpDir), null);
|
|
85
|
+
writeFileSync(explicit, '{}');
|
|
86
|
+
assert.equal(resolveConfigPath(explicit, tmpDir), explicit);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// parseJsonc / parseWranglerToml / parseWranglerConfig
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
test('parseJsonc strips // and block comments', () => {
|
|
94
|
+
const text = `{
|
|
95
|
+
// top comment
|
|
96
|
+
"name": "x", /* inline */
|
|
97
|
+
"logpush": true
|
|
98
|
+
}`;
|
|
99
|
+
assert.deepEqual(parseJsonc(text), { name: 'x', logpush: true });
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('parseWranglerToml parses top-level keys, booleans, and strings', () => {
|
|
103
|
+
const text = `
|
|
104
|
+
name = "my-worker"
|
|
105
|
+
logpush = true
|
|
106
|
+
compatibility_date = "2026-01-01"
|
|
107
|
+
`;
|
|
108
|
+
const parsed = parseWranglerToml(text);
|
|
109
|
+
assert.equal(parsed.name, 'my-worker');
|
|
110
|
+
assert.equal(parsed.logpush, true);
|
|
111
|
+
assert.equal(parsed.compatibility_date, '2026-01-01');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('parseWranglerToml parses [env.<name>] tables as nested objects', () => {
|
|
115
|
+
const text = `
|
|
116
|
+
name = "my-worker"
|
|
117
|
+
|
|
118
|
+
[env.staging]
|
|
119
|
+
logpush = true
|
|
120
|
+
|
|
121
|
+
[env.production]
|
|
122
|
+
logpush = false
|
|
123
|
+
`;
|
|
124
|
+
const parsed = parseWranglerToml(text);
|
|
125
|
+
assert.deepEqual(Object.keys(parsed.env), ['staging', 'production']);
|
|
126
|
+
assert.equal(parsed.env.staging.logpush, true);
|
|
127
|
+
assert.equal(parsed.env.production.logpush, false);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('parseWranglerToml parses [[analytics_engine_datasets]] as an array of tables', () => {
|
|
131
|
+
const text = `
|
|
132
|
+
name = "my-worker"
|
|
133
|
+
|
|
134
|
+
[[analytics_engine_datasets]]
|
|
135
|
+
binding = "AE"
|
|
136
|
+
dataset = "events"
|
|
137
|
+
`;
|
|
138
|
+
const parsed = parseWranglerToml(text);
|
|
139
|
+
assert.ok(Array.isArray(parsed.analytics_engine_datasets));
|
|
140
|
+
assert.equal(parsed.analytics_engine_datasets.length, 1);
|
|
141
|
+
assert.equal(parsed.analytics_engine_datasets[0].binding, 'AE');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('parseWranglerToml parses [[env.production.analytics_engine_datasets]] nested under an env table', () => {
|
|
145
|
+
const text = `
|
|
146
|
+
[[env.production.analytics_engine_datasets]]
|
|
147
|
+
binding = "AE"
|
|
148
|
+
dataset = "events"
|
|
149
|
+
`;
|
|
150
|
+
const parsed = parseWranglerToml(text);
|
|
151
|
+
assert.ok(Array.isArray(parsed.env.production.analytics_engine_datasets));
|
|
152
|
+
assert.equal(parsed.env.production.analytics_engine_datasets[0].dataset, 'events');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('parseWranglerToml strips trailing # comments outside quotes', () => {
|
|
156
|
+
const text = `compatibility_date = "2026-01-01" # bumped by renovate\n`;
|
|
157
|
+
assert.equal(parseWranglerToml(text).compatibility_date, '2026-01-01');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('parseWranglerToml parses a [mandrel.wranglerBaselineExceptions] table', () => {
|
|
161
|
+
const text = `
|
|
162
|
+
[mandrel.wranglerBaselineExceptions]
|
|
163
|
+
analyticsEngine = "no telemetry sink for this static-asset Worker"
|
|
164
|
+
`;
|
|
165
|
+
const parsed = parseWranglerToml(text);
|
|
166
|
+
assert.equal(
|
|
167
|
+
parsed.mandrel.wranglerBaselineExceptions.analyticsEngine,
|
|
168
|
+
'no telemetry sink for this static-asset Worker',
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('parseWranglerConfig dispatches by extension', () => {
|
|
173
|
+
assert.deepEqual(parseWranglerConfig('wrangler.jsonc', '{"a":1}'), { a: 1 });
|
|
174
|
+
assert.deepEqual(parseWranglerConfig('wrangler.toml', 'a = 1\n'), { a: 1 });
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// readExceptions
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
test('readExceptions returns {} when no mandrel block is present', () => {
|
|
182
|
+
assert.deepEqual(readExceptions({}), {});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('readExceptions reads string-valued exceptions and ignores non-strings', () => {
|
|
186
|
+
const config = {
|
|
187
|
+
mandrel: {
|
|
188
|
+
wranglerBaselineExceptions: {
|
|
189
|
+
logpush: 'no log sink budget for this Worker',
|
|
190
|
+
analyticsEngine: 42,
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
assert.deepEqual(readExceptions(config), { logpush: 'no log sink budget for this Worker' });
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
// Individual rules
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
test('checkEnvSplit fails with no env block', () => {
|
|
202
|
+
assert.equal(checkEnvSplit({}).pass, false);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('checkEnvSplit passes with at least one named environment', () => {
|
|
206
|
+
assert.equal(checkEnvSplit({ env: { staging: {} } }).pass, true);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('checkLogpush passes on a top-level logpush = true', () => {
|
|
210
|
+
assert.equal(checkLogpush({ logpush: true }).pass, true);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('checkLogpush passes when every named env sets logpush = true', () => {
|
|
214
|
+
const config = { env: { staging: { logpush: true }, production: { logpush: true } } };
|
|
215
|
+
assert.equal(checkLogpush(config).pass, true);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test('checkLogpush fails when only some named envs set logpush', () => {
|
|
219
|
+
const config = { env: { staging: { logpush: true }, production: {} } };
|
|
220
|
+
assert.equal(checkLogpush(config).pass, false);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('checkLogpush fails with no logpush anywhere', () => {
|
|
224
|
+
assert.equal(checkLogpush({}).pass, false);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('checkAnalyticsEngine passes with a top-level binding', () => {
|
|
228
|
+
const config = { analytics_engine_datasets: [{ binding: 'AE' }] };
|
|
229
|
+
assert.equal(checkAnalyticsEngine(config).pass, true);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('checkAnalyticsEngine passes with a binding on a named environment', () => {
|
|
233
|
+
const config = { env: { production: { analytics_engine_datasets: [{ binding: 'AE' }] } } };
|
|
234
|
+
assert.equal(checkAnalyticsEngine(config).pass, true);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('checkAnalyticsEngine fails with no binding anywhere', () => {
|
|
238
|
+
assert.equal(checkAnalyticsEngine({}).pass, false);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('checkCompatibilityDate passes within the policy window', () => {
|
|
242
|
+
const now = new Date('2026-07-01T00:00:00Z');
|
|
243
|
+
const result = checkCompatibilityDate({ compatibility_date: '2026-06-01' }, 90, now);
|
|
244
|
+
assert.equal(result.pass, true);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test('checkCompatibilityDate fails beyond the policy window', () => {
|
|
248
|
+
const now = new Date('2026-07-01T00:00:00Z');
|
|
249
|
+
const result = checkCompatibilityDate({ compatibility_date: '2025-01-01' }, 90, now);
|
|
250
|
+
assert.equal(result.pass, false);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('checkCompatibilityDate fails when the field is missing', () => {
|
|
254
|
+
assert.equal(checkCompatibilityDate({}, 90).pass, false);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test('checkCompatibilityDate fails on a malformed date string', () => {
|
|
258
|
+
assert.equal(checkCompatibilityDate({ compatibility_date: 'not-a-date' }, 90).pass, false);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
// evaluateBaseline (exception reconciliation)
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
test('evaluateBaseline reports zero violations for a fully-compliant config', () => {
|
|
266
|
+
const now = new Date('2026-07-01T00:00:00Z');
|
|
267
|
+
const config = {
|
|
268
|
+
env: { production: {} },
|
|
269
|
+
logpush: true,
|
|
270
|
+
analytics_engine_datasets: [{ binding: 'AE' }],
|
|
271
|
+
compatibility_date: '2026-06-01',
|
|
272
|
+
};
|
|
273
|
+
const report = evaluateBaseline(config, 90, now);
|
|
274
|
+
assert.deepEqual(report.violations, []);
|
|
275
|
+
assert.equal(report.findings.every((f) => f.pass), true);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('evaluateBaseline reports every failing rule as a violation with no exceptions declared', () => {
|
|
279
|
+
const report = evaluateBaseline({}, 90, new Date('2026-07-01T00:00:00Z'));
|
|
280
|
+
const ids = report.violations.map((v) => v.id).sort();
|
|
281
|
+
assert.deepEqual(ids, ['analytics-engine', 'compat-date-stale', 'env-split', 'logpush']);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test('evaluateBaseline suppresses a violation with a declared exception, but still reports it', () => {
|
|
285
|
+
const config = {
|
|
286
|
+
env: { production: {} },
|
|
287
|
+
logpush: true,
|
|
288
|
+
compatibility_date: '2026-06-01',
|
|
289
|
+
mandrel: {
|
|
290
|
+
wranglerBaselineExceptions: {
|
|
291
|
+
'analytics-engine': 'no telemetry sink for this static-asset Worker',
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
const report = evaluateBaseline(config, 90, new Date('2026-07-01T00:00:00Z'));
|
|
296
|
+
assert.deepEqual(report.violations, []);
|
|
297
|
+
assert.deepEqual(report.exceptions, [
|
|
298
|
+
{ id: 'analytics-engine', reason: 'no telemetry sink for this static-asset Worker' },
|
|
299
|
+
]);
|
|
300
|
+
const aeFinding = report.findings.find((f) => f.id === 'analytics-engine');
|
|
301
|
+
assert.equal(aeFinding.excepted, true);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// ---------------------------------------------------------------------------
|
|
305
|
+
// renderReport
|
|
306
|
+
// ---------------------------------------------------------------------------
|
|
307
|
+
|
|
308
|
+
test('renderReport prints a pass line for a clean config', () => {
|
|
309
|
+
const report = evaluateBaseline(
|
|
310
|
+
{
|
|
311
|
+
env: { production: {} },
|
|
312
|
+
logpush: true,
|
|
313
|
+
analytics_engine_datasets: [{ binding: 'AE' }],
|
|
314
|
+
compatibility_date: '2026-06-01',
|
|
315
|
+
},
|
|
316
|
+
90,
|
|
317
|
+
new Date('2026-07-01T00:00:00Z'),
|
|
318
|
+
);
|
|
319
|
+
const text = renderReport(report, 'wrangler.toml');
|
|
320
|
+
assert.match(text, /All baseline rules satisfied/);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test('renderReport prints a failure summary and points at the exception mechanism', () => {
|
|
324
|
+
const report = evaluateBaseline({}, 90, new Date('2026-07-01T00:00:00Z'));
|
|
325
|
+
const text = renderReport(report, 'wrangler.toml');
|
|
326
|
+
assert.match(text, /violation\(s\)/);
|
|
327
|
+
assert.match(text, /wranglerBaselineExceptions/);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// ---------------------------------------------------------------------------
|
|
331
|
+
// runCli — end to end against real temp files, both formats
|
|
332
|
+
// ---------------------------------------------------------------------------
|
|
333
|
+
|
|
334
|
+
function noopStreams() {
|
|
335
|
+
let out = '';
|
|
336
|
+
let err = '';
|
|
337
|
+
return { stdout: { write: (s) => (out += s) }, stderr: { write: (s) => (err += s) }, get out() { return out; }, get err() { return err; } };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
test('runCli exits 0 with a no-op message when no wrangler config exists', () => {
|
|
341
|
+
const streams = noopStreams();
|
|
342
|
+
const exit = runCli({ argv: [], cwd: tmpDir, stdout: streams.stdout, stderr: streams.stderr });
|
|
343
|
+
assert.equal(exit, 0);
|
|
344
|
+
assert.match(streams.out, /No wrangler\.toml/);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test('runCli exits 1 on a violating wrangler.toml (strict/default mode)', () => {
|
|
348
|
+
writeFileSync(join(tmpDir, 'wrangler.toml'), 'name = "x"\n');
|
|
349
|
+
const streams = noopStreams();
|
|
350
|
+
const exit = runCli({ argv: [], cwd: tmpDir, stdout: streams.stdout, stderr: streams.stderr, now: new Date('2026-07-01T00:00:00Z') });
|
|
351
|
+
assert.equal(exit, 1);
|
|
352
|
+
assert.match(streams.out, /❌/);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test('runCli exits 0 on a violating config with --warn-only (advisory rollout)', () => {
|
|
356
|
+
writeFileSync(join(tmpDir, 'wrangler.toml'), 'name = "x"\n');
|
|
357
|
+
const streams = noopStreams();
|
|
358
|
+
const exit = runCli({
|
|
359
|
+
argv: ['--warn-only'],
|
|
360
|
+
cwd: tmpDir,
|
|
361
|
+
stdout: streams.stdout,
|
|
362
|
+
stderr: streams.stderr,
|
|
363
|
+
now: new Date('2026-07-01T00:00:00Z'),
|
|
364
|
+
});
|
|
365
|
+
assert.equal(exit, 0);
|
|
366
|
+
assert.match(streams.out, /❌/);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test('runCli exits 0 on a fully-compliant wrangler.jsonc', () => {
|
|
370
|
+
const jsonc = `{
|
|
371
|
+
// named environment split
|
|
372
|
+
"env": { "production": {} },
|
|
373
|
+
"logpush": true,
|
|
374
|
+
"analytics_engine_datasets": [{ "binding": "AE", "dataset": "events" }],
|
|
375
|
+
"compatibility_date": "2026-06-15"
|
|
376
|
+
}`;
|
|
377
|
+
writeFileSync(join(tmpDir, 'wrangler.jsonc'), jsonc);
|
|
378
|
+
const streams = noopStreams();
|
|
379
|
+
const exit = runCli({ argv: [], cwd: tmpDir, stdout: streams.stdout, stderr: streams.stderr, now: new Date('2026-07-01T00:00:00Z') });
|
|
380
|
+
assert.equal(exit, 0);
|
|
381
|
+
assert.match(streams.out, /All baseline rules satisfied/);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('runCli exits 0 on a fully-compliant wrangler.toml', () => {
|
|
385
|
+
const toml = `
|
|
386
|
+
name = "my-worker"
|
|
387
|
+
logpush = true
|
|
388
|
+
compatibility_date = "2026-06-15"
|
|
389
|
+
|
|
390
|
+
[env.production]
|
|
391
|
+
|
|
392
|
+
[[analytics_engine_datasets]]
|
|
393
|
+
binding = "AE"
|
|
394
|
+
dataset = "events"
|
|
395
|
+
`;
|
|
396
|
+
writeFileSync(join(tmpDir, 'wrangler.toml'), toml);
|
|
397
|
+
const streams = noopStreams();
|
|
398
|
+
const exit = runCli({ argv: [], cwd: tmpDir, stdout: streams.stdout, stderr: streams.stderr, now: new Date('2026-07-01T00:00:00Z') });
|
|
399
|
+
assert.equal(exit, 0);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test('runCli --json emits a machine-readable envelope', () => {
|
|
403
|
+
writeFileSync(join(tmpDir, 'wrangler.toml'), 'name = "x"\n');
|
|
404
|
+
const streams = noopStreams();
|
|
405
|
+
const exit = runCli({
|
|
406
|
+
argv: ['--json', '--warn-only'],
|
|
407
|
+
cwd: tmpDir,
|
|
408
|
+
stdout: streams.stdout,
|
|
409
|
+
stderr: streams.stderr,
|
|
410
|
+
now: new Date('2026-07-01T00:00:00Z'),
|
|
411
|
+
});
|
|
412
|
+
assert.equal(exit, 0);
|
|
413
|
+
const parsed = JSON.parse(streams.out);
|
|
414
|
+
assert.equal(parsed.kind, 'wrangler-baseline-report');
|
|
415
|
+
assert.equal(parsed.found, true);
|
|
416
|
+
assert.ok(parsed.violations.length > 0);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test('runCli --json reports found:false when no config exists', () => {
|
|
420
|
+
const streams = noopStreams();
|
|
421
|
+
const exit = runCli({ argv: ['--json'], cwd: tmpDir, stdout: streams.stdout, stderr: streams.stderr });
|
|
422
|
+
assert.equal(exit, 0);
|
|
423
|
+
const parsed = JSON.parse(streams.out);
|
|
424
|
+
assert.equal(parsed.found, false);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test('runCli --file honors an explicit path outside the default candidates', () => {
|
|
428
|
+
writeFileSync(join(tmpDir, 'custom-wrangler.jsonc'), '{"logpush": true}');
|
|
429
|
+
const streams = noopStreams();
|
|
430
|
+
const exit = runCli({
|
|
431
|
+
argv: ['--file', 'custom-wrangler.jsonc', '--warn-only'],
|
|
432
|
+
cwd: tmpDir,
|
|
433
|
+
stdout: streams.stdout,
|
|
434
|
+
stderr: streams.stderr,
|
|
435
|
+
now: new Date('2026-07-01T00:00:00Z'),
|
|
436
|
+
});
|
|
437
|
+
assert.equal(exit, 0);
|
|
438
|
+
assert.match(streams.out, /custom-wrangler\.jsonc/);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test('runCli exits 1 with a parse error on malformed JSON', () => {
|
|
442
|
+
writeFileSync(join(tmpDir, 'wrangler.jsonc'), '{ not valid json');
|
|
443
|
+
const streams = noopStreams();
|
|
444
|
+
const exit = runCli({ argv: [], cwd: tmpDir, stdout: streams.stdout, stderr: streams.stderr });
|
|
445
|
+
assert.equal(exit, 1);
|
|
446
|
+
assert.match(streams.err, /failed to parse/);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test('runCli --help prints usage and exits 0 without touching the filesystem', () => {
|
|
450
|
+
const streams = noopStreams();
|
|
451
|
+
const exit = runCli({ argv: ['--help'], cwd: tmpDir, stdout: streams.stdout, stderr: streams.stderr });
|
|
452
|
+
assert.equal(exit, 0);
|
|
453
|
+
assert.match(streams.out, /check-wrangler-baseline\.mjs/);
|
|
454
|
+
});
|