@jango-blockchained/hoox-cli 0.5.2 → 0.5.6
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/bin/hoox.js +3 -4
- package/dist/index.js +32545 -0
- package/package.json +10 -15
- package/src/commands/check/check-command.test.ts +0 -476
- package/src/commands/check/check-command.ts +0 -1087
- package/src/commands/check/index.ts +0 -10
- package/src/commands/check/prerequisites-command.test.ts +0 -19
- package/src/commands/check/prerequisites-command.ts +0 -97
- package/src/commands/check/types.ts +0 -103
- package/src/commands/clone/clone-command.test.ts +0 -440
- package/src/commands/clone/clone-command.ts +0 -431
- package/src/commands/clone/index.ts +0 -1
- package/src/commands/config/config-command.test.ts +0 -583
- package/src/commands/config/config-command.ts +0 -896
- package/src/commands/config/env-command.test.ts +0 -43
- package/src/commands/config/env-command.ts +0 -292
- package/src/commands/config/index.ts +0 -3
- package/src/commands/config/kv-command.test.ts +0 -14
- package/src/commands/config/kv-command.ts +0 -290
- package/src/commands/dashboard/dashboard-command.test.ts +0 -47
- package/src/commands/dashboard/dashboard-command.ts +0 -130
- package/src/commands/dashboard/index.ts +0 -1
- package/src/commands/db/db-command.test.ts +0 -21
- package/src/commands/db/db-command.ts +0 -311
- package/src/commands/db/index.ts +0 -1
- package/src/commands/deploy/deploy-command.test.ts +0 -324
- package/src/commands/deploy/deploy-command.ts +0 -1054
- package/src/commands/deploy/index.ts +0 -2
- package/src/commands/deploy/telegram-service.ts +0 -76
- package/src/commands/deploy/types.ts +0 -34
- package/src/commands/dev/dev-command.test.ts +0 -405
- package/src/commands/dev/dev-command.ts +0 -398
- package/src/commands/dev/index.ts +0 -1
- package/src/commands/disclaimer/disclaimer-command.ts +0 -21
- package/src/commands/disclaimer/index.ts +0 -1
- package/src/commands/infra/index.ts +0 -5
- package/src/commands/infra/infra-command.test.ts +0 -719
- package/src/commands/infra/infra-command.ts +0 -974
- package/src/commands/infra/types.ts +0 -23
- package/src/commands/init/__tests__/cli-provisioner.test.ts +0 -17
- package/src/commands/init/cli-provisioner.ts +0 -71
- package/src/commands/init/index.ts +0 -1
- package/src/commands/init/init-command.test.ts +0 -854
- package/src/commands/init/init-command.ts +0 -597
- package/src/commands/init/types.ts +0 -23
- package/src/commands/logs/index.ts +0 -1
- package/src/commands/logs/logs-command.test.ts +0 -293
- package/src/commands/logs/logs-command.ts +0 -452
- package/src/commands/monitor/index.ts +0 -2
- package/src/commands/monitor/monitor-command.test.ts +0 -271
- package/src/commands/monitor/monitor-command.ts +0 -269
- package/src/commands/monitor/monitor-service.ts +0 -65
- package/src/commands/monitor/types.ts +0 -13
- package/src/commands/repair/index.ts +0 -2
- package/src/commands/repair/repair-command.test.ts +0 -243
- package/src/commands/repair/repair-command.ts +0 -220
- package/src/commands/repair/repair-service.ts +0 -164
- package/src/commands/repair/types.ts +0 -13
- package/src/commands/schema/index.ts +0 -1
- package/src/commands/schema/schema-command.ts +0 -137
- package/src/commands/test/index.ts +0 -2
- package/src/commands/test/test-command.test.ts +0 -318
- package/src/commands/test/test-command.ts +0 -406
- package/src/commands/tui/index.ts +0 -1
- package/src/commands/tui/tui-command.ts +0 -87
- package/src/commands/update/index.ts +0 -1
- package/src/commands/update/update-command.ts +0 -109
- package/src/commands/waf/index.ts +0 -2
- package/src/commands/waf/types.ts +0 -48
- package/src/commands/waf/waf-command.test.ts +0 -506
- package/src/commands/waf/waf-command.ts +0 -545
- package/src/index.ts +0 -261
- package/src/services/cloudflare/cloudflare-service.test.ts +0 -658
- package/src/services/cloudflare/cloudflare-service.ts +0 -433
- package/src/services/cloudflare/index.ts +0 -2
- package/src/services/cloudflare/types.ts +0 -30
- package/src/services/config/config-service.test.ts +0 -395
- package/src/services/config/config-service.ts +0 -202
- package/src/services/config/index.ts +0 -9
- package/src/services/config/types.ts +0 -66
- package/src/services/db/db-service.test.ts +0 -63
- package/src/services/db/db-service.ts +0 -131
- package/src/services/db/index.ts +0 -1
- package/src/services/docker/docker-service.ts +0 -155
- package/src/services/docker/index.ts +0 -1
- package/src/services/env/env-service.test.ts +0 -259
- package/src/services/env/env-service.ts +0 -372
- package/src/services/env/index.ts +0 -1
- package/src/services/kv/index.ts +0 -1
- package/src/services/kv/kv-sync-service.test.ts +0 -38
- package/src/services/kv/kv-sync-service.ts +0 -243
- package/src/services/prerequisites/index.ts +0 -1
- package/src/services/prerequisites/prerequisites-service.test.ts +0 -89
- package/src/services/prerequisites/prerequisites-service.ts +0 -377
- package/src/services/prerequisites/types.ts +0 -48
- package/src/services/schema/index.ts +0 -1
- package/src/services/schema/schema-service.ts +0 -99
- package/src/services/secrets/index.ts +0 -11
- package/src/services/secrets/secrets-service.test.ts +0 -502
- package/src/services/secrets/secrets-service.ts +0 -287
- package/src/services/secrets/types.ts +0 -50
- package/src/services/update/index.ts +0 -2
- package/src/services/update/update-service.test.ts +0 -76
- package/src/services/update/update-service.ts +0 -193
- package/src/ui/banner.ts +0 -141
- package/src/ui/index.ts +0 -8
- package/src/ui/menu.ts +0 -490
- package/src/utils/error-handler.ts +0 -62
- package/src/utils/errors.test.ts +0 -69
- package/src/utils/errors.ts +0 -24
- package/src/utils/formatters.test.ts +0 -180
- package/src/utils/formatters.ts +0 -269
- package/src/utils/git.ts +0 -134
- package/src/utils/theme.ts +0 -92
|
@@ -1,854 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for `hoox init` command.
|
|
3
|
-
*
|
|
4
|
-
* Mocks:
|
|
5
|
-
* - @clack/prompts → controllable prompt responses
|
|
6
|
-
* - CloudflareService → simulated wrangler output
|
|
7
|
-
* - Bun.write → captured file writes
|
|
8
|
-
* - process.exit → captured exit codes
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
afterEach,
|
|
13
|
-
beforeEach,
|
|
14
|
-
describe,
|
|
15
|
-
expect,
|
|
16
|
-
it,
|
|
17
|
-
mock,
|
|
18
|
-
spyOn,
|
|
19
|
-
} from "bun:test";
|
|
20
|
-
import { runInitCommand } from "./init-command.js";
|
|
21
|
-
import type { InitOptions } from "./types.js";
|
|
22
|
-
import { ExitCode } from "../../utils/errors.js";
|
|
23
|
-
import { CloudflareService } from "../../services/cloudflare/cloudflare-service.js";
|
|
24
|
-
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
// Mock types / helpers
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
|
|
29
|
-
/** Store captured calls so tests can assert on behaviour. */
|
|
30
|
-
interface CapturedCalls {
|
|
31
|
-
intro: string[];
|
|
32
|
-
outro: string[];
|
|
33
|
-
note: { title: string; content: string }[];
|
|
34
|
-
passwordMessages: string[];
|
|
35
|
-
textMessages: string[];
|
|
36
|
-
multiselectMessages: string[];
|
|
37
|
-
selectMessages: string[];
|
|
38
|
-
confirmMessages: string[];
|
|
39
|
-
cancelMessages: string[];
|
|
40
|
-
logInfo: string[];
|
|
41
|
-
logStep: string[];
|
|
42
|
-
logSuccess: string[];
|
|
43
|
-
logWarn: string[];
|
|
44
|
-
logError: string[];
|
|
45
|
-
/** Written files: path → content */
|
|
46
|
-
writes: Record<string, string>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function makeCapture(): CapturedCalls {
|
|
50
|
-
return {
|
|
51
|
-
intro: [],
|
|
52
|
-
outro: [],
|
|
53
|
-
note: [],
|
|
54
|
-
passwordMessages: [],
|
|
55
|
-
textMessages: [],
|
|
56
|
-
multiselectMessages: [],
|
|
57
|
-
selectMessages: [],
|
|
58
|
-
confirmMessages: [],
|
|
59
|
-
cancelMessages: [],
|
|
60
|
-
logInfo: [],
|
|
61
|
-
logStep: [],
|
|
62
|
-
logSuccess: [],
|
|
63
|
-
logWarn: [],
|
|
64
|
-
logError: [],
|
|
65
|
-
writes: {},
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let captured: CapturedCalls;
|
|
70
|
-
|
|
71
|
-
/** Configurable responses for each prompt type. */
|
|
72
|
-
interface PromptResponses {
|
|
73
|
-
password?: string | symbol;
|
|
74
|
-
text?: string | symbol;
|
|
75
|
-
multiselect?: string[] | symbol;
|
|
76
|
-
select?: string | symbol;
|
|
77
|
-
/** Confirm responses returned in order (call-by-call). Defaults are `[true, false, false]`. */
|
|
78
|
-
confirmSequence?: boolean[];
|
|
79
|
-
/** Group responses keyed by field name */
|
|
80
|
-
group?: Record<string, string>;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const defaultResponses: Required<PromptResponses> = {
|
|
84
|
-
password: "test-token",
|
|
85
|
-
text: "test-account",
|
|
86
|
-
multiselect: [],
|
|
87
|
-
select: "minimal",
|
|
88
|
-
confirmSequence: [true, false, false], // risk → provisioning → deploy
|
|
89
|
-
group: {},
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
let responses: PromptResponses = { ...defaultResponses };
|
|
93
|
-
|
|
94
|
-
/** Control how many times each prompt type has been called. */
|
|
95
|
-
interface CallCounters {
|
|
96
|
-
password: number;
|
|
97
|
-
text: number;
|
|
98
|
-
multiselect: number;
|
|
99
|
-
select: number;
|
|
100
|
-
confirm: number;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let counters: CallCounters = {
|
|
104
|
-
password: 0,
|
|
105
|
-
text: 0,
|
|
106
|
-
multiselect: 0,
|
|
107
|
-
select: 0,
|
|
108
|
-
confirm: 0,
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
function resetCounters(): void {
|
|
112
|
-
counters = { password: 0, text: 0, multiselect: 0, select: 0, confirm: 0 };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Whether to simulate cancellation on next check. */
|
|
116
|
-
let simulateCancel = false;
|
|
117
|
-
|
|
118
|
-
/** Custom password responder — set per-test for sequenced token responses. */
|
|
119
|
-
let customPasswordResponder: ((msg: string) => string | symbol) | null = null;
|
|
120
|
-
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
// Mock: CloudflareService (prototype based — no mock.module, no leakage)
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
|
|
125
|
-
const mockWhoami = mock(
|
|
126
|
-
async (): Promise<
|
|
127
|
-
{ ok: true; value: string } | { ok: false; error: string }
|
|
128
|
-
> => ({
|
|
129
|
-
ok: true,
|
|
130
|
-
value: "user@example.com",
|
|
131
|
-
})
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
// Mock: @clack/prompts (import namespace + spyOn, not mock.module)
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
|
|
138
|
-
import * as clack from "@clack/prompts";
|
|
139
|
-
|
|
140
|
-
function installClackSpies(): void {
|
|
141
|
-
spyOn(clack, "intro").mockImplementation((msg?: string) => {
|
|
142
|
-
captured.intro.push(msg ?? "");
|
|
143
|
-
});
|
|
144
|
-
spyOn(clack, "outro").mockImplementation((msg?: string) => {
|
|
145
|
-
captured.outro.push(msg ?? "");
|
|
146
|
-
});
|
|
147
|
-
spyOn(clack, "note").mockImplementation(
|
|
148
|
-
(content?: string, title?: string) => {
|
|
149
|
-
captured.note.push({ title: title ?? "", content: content ?? "" });
|
|
150
|
-
}
|
|
151
|
-
);
|
|
152
|
-
spyOn(clack, "password").mockImplementation(
|
|
153
|
-
// @ts-expect-error — mock validate fn signature simplified vs clack's PasswordOptions
|
|
154
|
-
async (opts: {
|
|
155
|
-
message: string;
|
|
156
|
-
validate?: (v: string) => string | void;
|
|
157
|
-
}) => {
|
|
158
|
-
captured.passwordMessages.push(opts.message);
|
|
159
|
-
counters.password++;
|
|
160
|
-
if (simulateCancel) return Symbol.for("clack.cancel");
|
|
161
|
-
if (customPasswordResponder) return customPasswordResponder(opts.message);
|
|
162
|
-
return responses.password ?? defaultResponses.password;
|
|
163
|
-
}
|
|
164
|
-
);
|
|
165
|
-
spyOn(clack, "text").mockImplementation(
|
|
166
|
-
// @ts-expect-error — mock validate fn signature simplified vs clack's TextOptions
|
|
167
|
-
async (opts: {
|
|
168
|
-
message: string;
|
|
169
|
-
placeholder?: string;
|
|
170
|
-
defaultValue?: string;
|
|
171
|
-
validate?: (v: string) => string | void;
|
|
172
|
-
}) => {
|
|
173
|
-
captured.textMessages.push(opts.message);
|
|
174
|
-
counters.text++;
|
|
175
|
-
return simulateCancel
|
|
176
|
-
? Symbol.for("clack.cancel")
|
|
177
|
-
: (responses.text ?? defaultResponses.text);
|
|
178
|
-
}
|
|
179
|
-
);
|
|
180
|
-
spyOn(clack, "multiselect").mockImplementation(
|
|
181
|
-
// @ts-expect-error — clack's multiselect is generic, mock uses concrete types
|
|
182
|
-
async (opts: {
|
|
183
|
-
message: string;
|
|
184
|
-
options: { value: string; label: string; hint?: string }[];
|
|
185
|
-
required?: boolean;
|
|
186
|
-
}) => {
|
|
187
|
-
captured.multiselectMessages.push(opts.message);
|
|
188
|
-
counters.multiselect++;
|
|
189
|
-
return simulateCancel
|
|
190
|
-
? Symbol.for("clack.cancel")
|
|
191
|
-
: (responses.multiselect ?? defaultResponses.multiselect);
|
|
192
|
-
}
|
|
193
|
-
);
|
|
194
|
-
spyOn(clack, "select").mockImplementation(
|
|
195
|
-
// @ts-expect-error — clack's select is generic, mock uses concrete types
|
|
196
|
-
async (opts: {
|
|
197
|
-
message: string;
|
|
198
|
-
options: { value: string; label: string; hint?: string }[];
|
|
199
|
-
}) => {
|
|
200
|
-
captured.selectMessages.push(opts.message);
|
|
201
|
-
counters.select++;
|
|
202
|
-
return simulateCancel
|
|
203
|
-
? Symbol.for("clack.cancel")
|
|
204
|
-
: (responses.select ?? defaultResponses.select);
|
|
205
|
-
}
|
|
206
|
-
);
|
|
207
|
-
spyOn(clack, "confirm").mockImplementation(
|
|
208
|
-
async (opts: { message: string; initialValue?: boolean }) => {
|
|
209
|
-
captured.confirmMessages.push(opts.message);
|
|
210
|
-
const idx = counters.confirm++;
|
|
211
|
-
const seq = responses.confirmSequence ?? defaultResponses.confirmSequence;
|
|
212
|
-
const val = idx < seq.length ? seq[idx] : seq[seq.length - 1];
|
|
213
|
-
if (simulateCancel) return Symbol.for("clack.cancel");
|
|
214
|
-
return val;
|
|
215
|
-
}
|
|
216
|
-
);
|
|
217
|
-
spyOn(clack, "group").mockImplementation(
|
|
218
|
-
// @ts-expect-error — clack's group uses PromptGroup<T>, not Record<string, () => Promise<...>>
|
|
219
|
-
async (
|
|
220
|
-
fields: Record<string, () => Promise<string | symbol>>,
|
|
221
|
-
groupOpts?: { onCancel?: () => void }
|
|
222
|
-
) => {
|
|
223
|
-
const results: Record<string, string> = {};
|
|
224
|
-
for (const [key, fn] of Object.entries(fields)) {
|
|
225
|
-
if (simulateCancel) {
|
|
226
|
-
if (groupOpts?.onCancel) groupOpts.onCancel();
|
|
227
|
-
return Symbol.for("clack.cancel");
|
|
228
|
-
}
|
|
229
|
-
const val = await fn();
|
|
230
|
-
results[key] =
|
|
231
|
-
responses.group?.[key] !== undefined
|
|
232
|
-
? responses.group[key]
|
|
233
|
-
: typeof val === "string"
|
|
234
|
-
? val
|
|
235
|
-
: "";
|
|
236
|
-
}
|
|
237
|
-
return results;
|
|
238
|
-
}
|
|
239
|
-
);
|
|
240
|
-
spyOn(clack, "isCancel").mockImplementation(
|
|
241
|
-
(value: unknown): value is symbol => {
|
|
242
|
-
return (
|
|
243
|
-
simulateCancel ||
|
|
244
|
-
(typeof value === "symbol" &&
|
|
245
|
-
Symbol.keyFor(value as symbol) === "clack.cancel")
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
);
|
|
249
|
-
spyOn(clack, "cancel").mockImplementation((msg?: string) => {
|
|
250
|
-
captured.cancelMessages.push(msg ?? "");
|
|
251
|
-
});
|
|
252
|
-
spyOn(clack.log, "info").mockImplementation((msg?: string) => {
|
|
253
|
-
captured.logInfo.push(msg ?? "");
|
|
254
|
-
});
|
|
255
|
-
spyOn(clack.log, "step").mockImplementation((msg?: string) => {
|
|
256
|
-
captured.logStep.push(msg ?? "");
|
|
257
|
-
});
|
|
258
|
-
spyOn(clack.log, "success").mockImplementation((msg?: string) => {
|
|
259
|
-
captured.logSuccess.push(msg ?? "");
|
|
260
|
-
});
|
|
261
|
-
spyOn(clack.log, "warn").mockImplementation((msg?: string) => {
|
|
262
|
-
captured.logWarn.push(msg ?? "");
|
|
263
|
-
});
|
|
264
|
-
spyOn(clack.log, "error").mockImplementation((msg?: string) => {
|
|
265
|
-
captured.logError.push(msg ?? "");
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// ---------------------------------------------------------------------------
|
|
270
|
-
// Mock: Bun.write & Bun.file
|
|
271
|
-
// ---------------------------------------------------------------------------
|
|
272
|
-
|
|
273
|
-
const origWhoamiPrototype = CloudflareService.prototype.whoami;
|
|
274
|
-
|
|
275
|
-
beforeEach(() => {
|
|
276
|
-
captured = makeCapture();
|
|
277
|
-
responses = { ...defaultResponses };
|
|
278
|
-
resetCounters();
|
|
279
|
-
simulateCancel = false;
|
|
280
|
-
customPasswordResponder = null;
|
|
281
|
-
mockWhoami.mockImplementation(async () => ({
|
|
282
|
-
ok: true,
|
|
283
|
-
value: "user@example.com",
|
|
284
|
-
}));
|
|
285
|
-
|
|
286
|
-
// Mock: CloudflareService prototype
|
|
287
|
-
CloudflareService.prototype.whoami = mockWhoami;
|
|
288
|
-
|
|
289
|
-
// Mock: @clack/prompts via spyOn (scoped, restorable)
|
|
290
|
-
installClackSpies();
|
|
291
|
-
|
|
292
|
-
// Mock Bun.write to capture all file writes
|
|
293
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
294
|
-
spyOn(Bun, "write" as any).mockImplementation(
|
|
295
|
-
async (path: string | URL, content: string | Uint8Array) => {
|
|
296
|
-
captured.writes[String(path)] =
|
|
297
|
-
typeof content === "string"
|
|
298
|
-
? content
|
|
299
|
-
: new TextDecoder().decode(content);
|
|
300
|
-
return typeof content === "string" ? content.length : content.byteLength;
|
|
301
|
-
}
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
// Mock Bun.file to simulate filesystem (no existing wrangler.jsonc by default)
|
|
305
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
306
|
-
spyOn(Bun, "file" as any).mockImplementation(
|
|
307
|
-
(_path: string | URL) =>
|
|
308
|
-
({
|
|
309
|
-
exists: async () => false,
|
|
310
|
-
text: async () => "",
|
|
311
|
-
arrayBuffer: async () => new ArrayBuffer(0),
|
|
312
|
-
json: async () => ({}),
|
|
313
|
-
size: 0,
|
|
314
|
-
name: String(_path),
|
|
315
|
-
lastModified: 0,
|
|
316
|
-
slice: () => new Blob(),
|
|
317
|
-
stream: () => new ReadableStream(),
|
|
318
|
-
type: "",
|
|
319
|
-
}) as unknown as Bun.BunFile
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
// Mock process.exit — no-op to prevent actual test termination
|
|
323
|
-
spyOn(process, "exit").mockImplementation(((_code?: number) => {
|
|
324
|
-
// no-op: prevent actual exit
|
|
325
|
-
}) as never);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
afterEach(() => {
|
|
329
|
-
mock.restore();
|
|
330
|
-
CloudflareService.prototype.whoami = origWhoamiPrototype;
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// ---------------------------------------------------------------------------
|
|
334
|
-
// Tests
|
|
335
|
-
// ---------------------------------------------------------------------------
|
|
336
|
-
|
|
337
|
-
describe("init command", () => {
|
|
338
|
-
// ------------------------------------------------------------------
|
|
339
|
-
// Interactive flow
|
|
340
|
-
// ------------------------------------------------------------------
|
|
341
|
-
|
|
342
|
-
describe("interactive flow", () => {
|
|
343
|
-
it("shows intro and outro messages", async () => {
|
|
344
|
-
responses = {
|
|
345
|
-
password: "valid-token",
|
|
346
|
-
text: "test-account-id",
|
|
347
|
-
multiselect: [],
|
|
348
|
-
select: "minimal",
|
|
349
|
-
confirmSequence: [true, false, false],
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
353
|
-
|
|
354
|
-
expect(captured.intro.length).toBeGreaterThan(0);
|
|
355
|
-
expect(captured.intro.some((m) => m.includes("Hoox Setup Wizard"))).toBe(
|
|
356
|
-
true
|
|
357
|
-
);
|
|
358
|
-
expect(captured.outro.some((m) => m.includes("Setup complete"))).toBe(
|
|
359
|
-
true
|
|
360
|
-
);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it("collects Cloudflare API token with validation", async () => {
|
|
364
|
-
responses = {
|
|
365
|
-
password: "valid-token",
|
|
366
|
-
text: "test-account-id",
|
|
367
|
-
multiselect: [],
|
|
368
|
-
select: "minimal",
|
|
369
|
-
confirmSequence: [true, false, false],
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
373
|
-
|
|
374
|
-
expect(captured.passwordMessages).toContain("Cloudflare API token:");
|
|
375
|
-
expect(mockWhoami).toHaveBeenCalled();
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
it("retries on invalid token (validation error)", async () => {
|
|
379
|
-
let callCount = 0;
|
|
380
|
-
mockWhoami.mockImplementation(async () => {
|
|
381
|
-
callCount++;
|
|
382
|
-
if (callCount === 1) {
|
|
383
|
-
return { ok: false as const, error: "Invalid credentials" };
|
|
384
|
-
}
|
|
385
|
-
return { ok: true as const, value: "user@example.com" };
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
// First password call returns bad token, second returns good token
|
|
389
|
-
let pwdCount = 0;
|
|
390
|
-
customPasswordResponder = (_msg: string) => {
|
|
391
|
-
pwdCount++;
|
|
392
|
-
return pwdCount === 1 ? "bad-token" : "good-token";
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
responses = {
|
|
396
|
-
password: "",
|
|
397
|
-
text: "test-account-id",
|
|
398
|
-
multiselect: [],
|
|
399
|
-
select: "minimal",
|
|
400
|
-
confirmSequence: [true, false, false],
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
404
|
-
|
|
405
|
-
// Should have called password at least twice (first failed, retry)
|
|
406
|
-
expect(pwdCount).toBeGreaterThanOrEqual(2);
|
|
407
|
-
expect(captured.logError.length).toBeGreaterThan(0);
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it("collects account ID with default from existing wrangler.jsonc", async () => {
|
|
411
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
412
|
-
const fileSpy = spyOn(Bun, "file" as any).mockImplementation(
|
|
413
|
-
(path: string) => {
|
|
414
|
-
if (path === "wrangler.jsonc") {
|
|
415
|
-
return {
|
|
416
|
-
exists: async () => true,
|
|
417
|
-
text: async () =>
|
|
418
|
-
'{"global":{"cloudflare_account_id":"existing-account-id-123"}}',
|
|
419
|
-
arrayBuffer: async () => new ArrayBuffer(0),
|
|
420
|
-
json: async () => ({}),
|
|
421
|
-
size: 0,
|
|
422
|
-
name: "",
|
|
423
|
-
lastModified: 0,
|
|
424
|
-
slice: () => new Blob(),
|
|
425
|
-
stream: () => new ReadableStream(),
|
|
426
|
-
type: "",
|
|
427
|
-
} as unknown as Bun.BunFile;
|
|
428
|
-
}
|
|
429
|
-
return {
|
|
430
|
-
exists: async () => false,
|
|
431
|
-
text: async () => "",
|
|
432
|
-
arrayBuffer: async () => new ArrayBuffer(0),
|
|
433
|
-
json: async () => ({}),
|
|
434
|
-
size: 0,
|
|
435
|
-
name: "",
|
|
436
|
-
lastModified: 0,
|
|
437
|
-
slice: () => new Blob(),
|
|
438
|
-
stream: () => new ReadableStream(),
|
|
439
|
-
type: "",
|
|
440
|
-
} as unknown as Bun.BunFile;
|
|
441
|
-
}
|
|
442
|
-
);
|
|
443
|
-
|
|
444
|
-
responses = {
|
|
445
|
-
password: "valid-token",
|
|
446
|
-
text: "existing-account-id-123",
|
|
447
|
-
multiselect: [],
|
|
448
|
-
select: "minimal",
|
|
449
|
-
confirmSequence: [true, false, false],
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
453
|
-
|
|
454
|
-
expect(captured.textMessages.some((m) => m.includes("Account ID"))).toBe(
|
|
455
|
-
true
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
fileSpy.mockRestore();
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
it("collects integrations via multiselect", async () => {
|
|
462
|
-
responses = {
|
|
463
|
-
password: "valid-token",
|
|
464
|
-
text: "test-account-id",
|
|
465
|
-
multiselect: ["binance", "telegram"],
|
|
466
|
-
select: "full", // full preset includes integrations
|
|
467
|
-
confirmSequence: [true, false, false],
|
|
468
|
-
group: {
|
|
469
|
-
BINANCE_KEY_BINDING: "binance-key-123",
|
|
470
|
-
BINANCE_SECRET_BINDING: "binance-secret-123",
|
|
471
|
-
TG_BOT_TOKEN_BINDING: "tg-bot-token-123",
|
|
472
|
-
},
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
476
|
-
|
|
477
|
-
expect(
|
|
478
|
-
captured.selectMessages.some((m) =>
|
|
479
|
-
m.includes("Select a worker preset")
|
|
480
|
-
)
|
|
481
|
-
).toBe(true);
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
it("collects per-integration secrets for selected integrations", async () => {
|
|
485
|
-
responses = {
|
|
486
|
-
password: "valid-token",
|
|
487
|
-
text: "test-account-id",
|
|
488
|
-
multiselect: ["telegram"],
|
|
489
|
-
select: "full",
|
|
490
|
-
confirmSequence: [true, false, false],
|
|
491
|
-
group: {
|
|
492
|
-
TG_BOT_TOKEN_BINDING: "tg-token-abc",
|
|
493
|
-
},
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
497
|
-
|
|
498
|
-
// Password prompts should include Telegram Bot Token
|
|
499
|
-
expect(
|
|
500
|
-
captured.passwordMessages.some((m) => m.includes("Telegram Bot Token"))
|
|
501
|
-
).toBe(true);
|
|
502
|
-
});
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
// ------------------------------------------------------------------
|
|
506
|
-
// Non-interactive flow
|
|
507
|
-
// ------------------------------------------------------------------
|
|
508
|
-
|
|
509
|
-
describe("non-interactive flow", () => {
|
|
510
|
-
it("skips prompts when --token and --account are provided", async () => {
|
|
511
|
-
const options: InitOptions = {
|
|
512
|
-
token: "cf-token-non-interactive",
|
|
513
|
-
account: "cf-account-non-interactive",
|
|
514
|
-
secretStore: "ss-id-123",
|
|
515
|
-
prefix: "myprefix",
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
await runInitCommand(options, { json: false, quiet: true }, true);
|
|
519
|
-
|
|
520
|
-
// No interactive prompts should have been shown
|
|
521
|
-
expect(captured.passwordMessages.length).toBe(0);
|
|
522
|
-
expect(captured.textMessages.length).toBe(0);
|
|
523
|
-
expect(captured.multiselectMessages.length).toBe(0);
|
|
524
|
-
|
|
525
|
-
// Token should have been validated
|
|
526
|
-
expect(mockWhoami).toHaveBeenCalled();
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
it("validates token in non-interactive mode", async () => {
|
|
530
|
-
mockWhoami.mockImplementation(async () => ({
|
|
531
|
-
ok: false as const,
|
|
532
|
-
error: "Bad token",
|
|
533
|
-
}));
|
|
534
|
-
|
|
535
|
-
const options: InitOptions = {
|
|
536
|
-
token: "bad-token",
|
|
537
|
-
account: "abc123def456abc123def456abc123de",
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
await runInitCommand(options, { json: false, quiet: true }, true);
|
|
541
|
-
|
|
542
|
-
expect(process.exitCode).toBe(ExitCode.ERROR);
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
it("writes wrangler.jsonc in non-interactive mode", async () => {
|
|
546
|
-
const options: InitOptions = {
|
|
547
|
-
token: "cf-token-ni",
|
|
548
|
-
account: "cf-account-ni",
|
|
549
|
-
secretStore: "ss-ni",
|
|
550
|
-
prefix: "ni-prefix",
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
await runInitCommand(options, { json: false, quiet: true }, true);
|
|
554
|
-
|
|
555
|
-
// Verify wrangler.jsonc was written
|
|
556
|
-
const workersJsonc = captured.writes["wrangler.jsonc"];
|
|
557
|
-
expect(workersJsonc).toBeDefined();
|
|
558
|
-
expect(workersJsonc).toContain("cf-token-ni");
|
|
559
|
-
expect(workersJsonc).toContain("cf-account-ni");
|
|
560
|
-
expect(workersJsonc).toContain("ss-ni");
|
|
561
|
-
expect(workersJsonc).toContain("ni-prefix");
|
|
562
|
-
expect(workersJsonc).toContain("d1-worker");
|
|
563
|
-
expect(workersJsonc).toContain("hoox");
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
it("uses default prefix 'cryptolinx' when --prefix not provided", async () => {
|
|
567
|
-
const options: InitOptions = {
|
|
568
|
-
token: "cf-token-def",
|
|
569
|
-
account: "cf-account-def",
|
|
570
|
-
};
|
|
571
|
-
|
|
572
|
-
await runInitCommand(options, { json: false, quiet: true }, true);
|
|
573
|
-
|
|
574
|
-
const workersJsonc = captured.writes["wrangler.jsonc"];
|
|
575
|
-
expect(workersJsonc).toContain("cryptolinx");
|
|
576
|
-
});
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
// ------------------------------------------------------------------
|
|
580
|
-
// wrangler.jsonc content verification
|
|
581
|
-
// ------------------------------------------------------------------
|
|
582
|
-
|
|
583
|
-
describe("wrangler.jsonc output", () => {
|
|
584
|
-
it("includes base workers (d1-worker, hoox, agent-worker, analytics-worker)", async () => {
|
|
585
|
-
responses = {
|
|
586
|
-
password: "valid-token",
|
|
587
|
-
text: "test-account",
|
|
588
|
-
multiselect: [],
|
|
589
|
-
select: "minimal",
|
|
590
|
-
confirmSequence: [true, false, false],
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
594
|
-
|
|
595
|
-
const workersJsonc = captured.writes["wrangler.jsonc"];
|
|
596
|
-
expect(workersJsonc).toBeDefined();
|
|
597
|
-
expect(workersJsonc).toContain('"d1-worker"');
|
|
598
|
-
expect(workersJsonc).toContain('"hoox"');
|
|
599
|
-
expect(workersJsonc).toContain('"agent-worker"');
|
|
600
|
-
expect(workersJsonc).toContain('"analytics-worker"');
|
|
601
|
-
});
|
|
602
|
-
|
|
603
|
-
it("includes integration workers when selected", async () => {
|
|
604
|
-
responses = {
|
|
605
|
-
password: "valid-token",
|
|
606
|
-
text: "test-account",
|
|
607
|
-
multiselect: [],
|
|
608
|
-
select: "full",
|
|
609
|
-
confirmSequence: [true, false, false],
|
|
610
|
-
group: {
|
|
611
|
-
BINANCE_KEY_BINDING: "bk",
|
|
612
|
-
BINANCE_SECRET_BINDING: "bs",
|
|
613
|
-
TG_BOT_TOKEN_BINDING: "tgt",
|
|
614
|
-
},
|
|
615
|
-
};
|
|
616
|
-
|
|
617
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
618
|
-
|
|
619
|
-
const workersJsonc = captured.writes["wrangler.jsonc"];
|
|
620
|
-
expect(workersJsonc).toBeDefined();
|
|
621
|
-
expect(workersJsonc).toContain('"trade-worker"');
|
|
622
|
-
expect(workersJsonc).toContain('"BINANCE_KEY_BINDING"');
|
|
623
|
-
expect(workersJsonc).toContain('"BINANCE_SECRET_BINDING"');
|
|
624
|
-
expect(workersJsonc).toContain('"telegram-worker"');
|
|
625
|
-
expect(workersJsonc).toContain('"TG_BOT_TOKEN_BINDING"');
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
it("includes AI provider (OpenAI) integration from full preset", async () => {
|
|
629
|
-
responses = {
|
|
630
|
-
password: "valid-token",
|
|
631
|
-
text: "test-account",
|
|
632
|
-
multiselect: [],
|
|
633
|
-
select: "full",
|
|
634
|
-
confirmSequence: [true, false, false],
|
|
635
|
-
group: {
|
|
636
|
-
AGENT_INTERNAL_KEY: "sk-openai-123",
|
|
637
|
-
},
|
|
638
|
-
};
|
|
639
|
-
|
|
640
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
641
|
-
|
|
642
|
-
const workersJsonc = captured.writes["wrangler.jsonc"];
|
|
643
|
-
expect(workersJsonc).toBeDefined();
|
|
644
|
-
expect(workersJsonc).toContain('"agent-worker"');
|
|
645
|
-
expect(workersJsonc).toContain('"AGENT_INTERNAL_KEY"');
|
|
646
|
-
});
|
|
647
|
-
|
|
648
|
-
it("includes wallet integration from full preset", async () => {
|
|
649
|
-
responses = {
|
|
650
|
-
password: "valid-token",
|
|
651
|
-
text: "test-account",
|
|
652
|
-
multiselect: [],
|
|
653
|
-
select: "full",
|
|
654
|
-
confirmSequence: [true, false, false],
|
|
655
|
-
group: {
|
|
656
|
-
WALLET_MNEMONIC_SECRET: "seed-phrase-789",
|
|
657
|
-
},
|
|
658
|
-
};
|
|
659
|
-
|
|
660
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
661
|
-
|
|
662
|
-
const workersJsonc = captured.writes["wrangler.jsonc"];
|
|
663
|
-
expect(workersJsonc).toBeDefined();
|
|
664
|
-
expect(workersJsonc).toContain('"web3-wallet-worker"');
|
|
665
|
-
expect(workersJsonc).toContain('"WALLET_MNEMONIC_SECRET"');
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
it("merges exchange secrets into single trade-worker", async () => {
|
|
669
|
-
responses = {
|
|
670
|
-
password: "valid-token",
|
|
671
|
-
text: "test-account",
|
|
672
|
-
multiselect: [],
|
|
673
|
-
select: "full",
|
|
674
|
-
confirmSequence: [true, false, false],
|
|
675
|
-
group: {
|
|
676
|
-
BINANCE_KEY_BINDING: "bk",
|
|
677
|
-
BINANCE_SECRET_BINDING: "bs",
|
|
678
|
-
MEXC_KEY_BINDING: "mk",
|
|
679
|
-
MEXC_SECRET_BINDING: "ms",
|
|
680
|
-
},
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
684
|
-
|
|
685
|
-
const workersJsonc = captured.writes["wrangler.jsonc"];
|
|
686
|
-
expect(workersJsonc).toBeDefined();
|
|
687
|
-
expect(workersJsonc).toContain('"trade-worker"');
|
|
688
|
-
expect(workersJsonc).toContain('"BINANCE_KEY_BINDING"');
|
|
689
|
-
expect(workersJsonc).toContain('"MEXC_KEY_BINDING"');
|
|
690
|
-
// Should only appear once
|
|
691
|
-
const tradeWorkerCount =
|
|
692
|
-
workersJsonc.match(/"trade-worker"/g)?.length ?? 0;
|
|
693
|
-
expect(tradeWorkerCount).toBe(1);
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
it("includes global config section", async () => {
|
|
697
|
-
responses = {
|
|
698
|
-
password: "my-cf-token",
|
|
699
|
-
text: "my-account-id",
|
|
700
|
-
multiselect: [],
|
|
701
|
-
select: "minimal",
|
|
702
|
-
confirmSequence: [true, false, false],
|
|
703
|
-
};
|
|
704
|
-
|
|
705
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
706
|
-
|
|
707
|
-
const workersJsonc = captured.writes["wrangler.jsonc"];
|
|
708
|
-
expect(workersJsonc).toBeDefined();
|
|
709
|
-
expect(workersJsonc).toContain('"global"');
|
|
710
|
-
expect(workersJsonc).toContain('"cloudflare_api_token"');
|
|
711
|
-
expect(workersJsonc).toContain('"cloudflare_account_id"');
|
|
712
|
-
expect(workersJsonc).toContain('"my-account-id"');
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
it("has JSONC header comment", async () => {
|
|
716
|
-
responses = {
|
|
717
|
-
password: "valid-token",
|
|
718
|
-
text: "test-account",
|
|
719
|
-
multiselect: [],
|
|
720
|
-
select: "minimal",
|
|
721
|
-
confirmSequence: [true, false, false],
|
|
722
|
-
};
|
|
723
|
-
|
|
724
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
725
|
-
|
|
726
|
-
const workersJsonc = captured.writes["wrangler.jsonc"];
|
|
727
|
-
expect(workersJsonc).toBeDefined();
|
|
728
|
-
expect(workersJsonc).toContain("// Hoox Workspace Configuration");
|
|
729
|
-
expect(workersJsonc).toContain("// Generated by `hoox init`");
|
|
730
|
-
});
|
|
731
|
-
});
|
|
732
|
-
|
|
733
|
-
// ------------------------------------------------------------------
|
|
734
|
-
// .dev.vars creation
|
|
735
|
-
// ------------------------------------------------------------------
|
|
736
|
-
|
|
737
|
-
describe(".dev.vars creation", () => {
|
|
738
|
-
it("creates .dev.vars for integration workers", async () => {
|
|
739
|
-
responses = {
|
|
740
|
-
password: "valid-token",
|
|
741
|
-
text: "test-account",
|
|
742
|
-
multiselect: [],
|
|
743
|
-
select: "full",
|
|
744
|
-
confirmSequence: [true, false, false],
|
|
745
|
-
group: {
|
|
746
|
-
TG_BOT_TOKEN_BINDING: "tg-secret-value",
|
|
747
|
-
},
|
|
748
|
-
};
|
|
749
|
-
|
|
750
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
751
|
-
|
|
752
|
-
const devVarsPath = "workers/telegram-worker/.dev.vars";
|
|
753
|
-
const devVars = captured.writes[devVarsPath];
|
|
754
|
-
expect(devVars).toBeDefined();
|
|
755
|
-
expect(devVars).toContain("TG_BOT_TOKEN_BINDING=tg-secret-value");
|
|
756
|
-
expect(devVars).toContain("NEVER commit this file");
|
|
757
|
-
});
|
|
758
|
-
|
|
759
|
-
it("does not create .dev.vars for workers without secrets", async () => {
|
|
760
|
-
responses = {
|
|
761
|
-
password: "valid-token",
|
|
762
|
-
text: "test-account",
|
|
763
|
-
multiselect: [],
|
|
764
|
-
select: "minimal",
|
|
765
|
-
confirmSequence: [true, false, false],
|
|
766
|
-
};
|
|
767
|
-
|
|
768
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
769
|
-
|
|
770
|
-
// d1-worker has no user-collected secrets
|
|
771
|
-
const d1DevVars = captured.writes["workers/d1-worker/.dev.vars"];
|
|
772
|
-
expect(d1DevVars).toBeUndefined();
|
|
773
|
-
});
|
|
774
|
-
|
|
775
|
-
it("creates .dev.vars for agent-worker when an AI provider is selected", async () => {
|
|
776
|
-
responses = {
|
|
777
|
-
password: "valid-token",
|
|
778
|
-
text: "test-account",
|
|
779
|
-
multiselect: [],
|
|
780
|
-
select: "full",
|
|
781
|
-
confirmSequence: [true, false, false],
|
|
782
|
-
group: {
|
|
783
|
-
AGENT_INTERNAL_KEY: "sk-openai-real-value",
|
|
784
|
-
},
|
|
785
|
-
};
|
|
786
|
-
|
|
787
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
788
|
-
|
|
789
|
-
const devVarsPath = "workers/agent-worker/.dev.vars";
|
|
790
|
-
const devVars = captured.writes[devVarsPath];
|
|
791
|
-
expect(devVars).toBeDefined();
|
|
792
|
-
expect(devVars).toContain("AGENT_INTERNAL_KEY=sk-openai-real-value");
|
|
793
|
-
expect(devVars).toContain("NEVER commit this file");
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
it("creates .dev.vars for web3-wallet-worker when wallet integration selected", async () => {
|
|
797
|
-
responses = {
|
|
798
|
-
password: "valid-token",
|
|
799
|
-
text: "test-account",
|
|
800
|
-
multiselect: [],
|
|
801
|
-
select: "full",
|
|
802
|
-
confirmSequence: [true, false, false],
|
|
803
|
-
group: {
|
|
804
|
-
WALLET_MNEMONIC_SECRET: "mnemonic-real-value",
|
|
805
|
-
},
|
|
806
|
-
};
|
|
807
|
-
|
|
808
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
809
|
-
|
|
810
|
-
const devVarsPath = "workers/web3-wallet-worker/.dev.vars";
|
|
811
|
-
const devVars = captured.writes[devVarsPath];
|
|
812
|
-
expect(devVars).toBeDefined();
|
|
813
|
-
expect(devVars).toContain("WALLET_MNEMONIC_SECRET=mnemonic-real-value");
|
|
814
|
-
expect(devVars).toContain("NEVER commit this file");
|
|
815
|
-
});
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
// ------------------------------------------------------------------
|
|
819
|
-
// Cancellation handling
|
|
820
|
-
// ------------------------------------------------------------------
|
|
821
|
-
|
|
822
|
-
describe("cancellation handling", () => {
|
|
823
|
-
it("exits on cancel during risk acknowledgment", async () => {
|
|
824
|
-
// Make the first confirm return false (user declines the risk terms)
|
|
825
|
-
responses.confirmSequence = [false];
|
|
826
|
-
|
|
827
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
828
|
-
|
|
829
|
-
expect(process.exitCode).toBe(0);
|
|
830
|
-
});
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
// ------------------------------------------------------------------
|
|
834
|
-
// Global options (--json, --quiet)
|
|
835
|
-
// ------------------------------------------------------------------
|
|
836
|
-
|
|
837
|
-
describe("global options", () => {
|
|
838
|
-
it("suppresses output in quiet mode", async () => {
|
|
839
|
-
responses = {
|
|
840
|
-
password: "valid-token",
|
|
841
|
-
text: "test-account",
|
|
842
|
-
multiselect: [],
|
|
843
|
-
select: "minimal",
|
|
844
|
-
confirmSequence: [true, false, false],
|
|
845
|
-
};
|
|
846
|
-
|
|
847
|
-
await runInitCommand({}, { json: false, quiet: true }, false);
|
|
848
|
-
|
|
849
|
-
// In quiet mode, formatSuccess/formatter functions respect quiet mode.
|
|
850
|
-
// The flow should complete without throwing.
|
|
851
|
-
expect(captured.writes["wrangler.jsonc"]).toBeDefined();
|
|
852
|
-
});
|
|
853
|
-
});
|
|
854
|
-
});
|