flow run: a declined confirmation stops the flow; --apply --yes confirms write steps

confirm() prints 'declined: <reason>' and exits 3 whenever the answer is no or nobody can answer (not a
terminal without --yes, serve without the owner's dialog), so no command reports success after declining.
flow run stops at such a step with 'step N declined: <reason>' (status declined), never ok. flow run
--apply --yes passes --yes to write steps only; --yes without --apply is refused. Pitfall 24.
This commit is contained in:
CRTkafa 2026-10-12 08:44:56 +08:00
parent 5fbb9e63e3
commit 366362d62b
12 changed files with 116 additions and 19 deletions

View file

@ -35,6 +35,7 @@ beforeEach(() => {
Object.defineProperty(process.stdin, 'isTTY', { value: false, configurable: true });
});
afterEach(() => {
process.exitCode = 0; // a declined confirmation sets it (DECLINED_EXIT); the tests below check it
globalThis.fetch = realFetch;
for (const [k, v] of Object.entries(saved)) { if (v === undefined) delete process.env[k]; else process.env[k] = v; }
rmSync(root, { recursive: true, force: true });
@ -236,12 +237,14 @@ describe('command (mocked fetch)', () => {
expect(out).toContain('100.00 -> 150.00 USD (+50%)');
expect(out).toContain('not a terminal');
expect(calls.some(c => c.method === 'PUT')).toBe(false);
expect(process.exitCode).toBe(3);
});
test('pause under storebound serve is refused even with --yes', async () => {
process.env.STOREBOUND_SERVE = '1';
const calls = mockFetch(api);
await quiet(() => run(ctx(['pause', '--campaign', '444555666', '--yes'])));
expect(calls.some(c => c.method === 'PUT')).toBe(false);
expect(process.exitCode).toBe(3);
});
test('budget with --yes in a terminal-less run sends the documented body', async () => {
const calls = mockFetch(api);