@jango-blockchained/hoox-cli 0.5.1 → 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.
Files changed (106) hide show
  1. package/bin/hoox.js +3 -4
  2. package/dist/index.js +32545 -0
  3. package/package.json +10 -15
  4. package/src/commands/check/check-command.test.ts +0 -476
  5. package/src/commands/check/check-command.ts +0 -1144
  6. package/src/commands/check/index.ts +0 -10
  7. package/src/commands/check/prerequisites-command.test.ts +0 -19
  8. package/src/commands/check/prerequisites-command.ts +0 -96
  9. package/src/commands/check/types.ts +0 -103
  10. package/src/commands/clone/clone-command.test.ts +0 -442
  11. package/src/commands/clone/clone-command.ts +0 -431
  12. package/src/commands/clone/index.ts +0 -1
  13. package/src/commands/config/config-command.test.ts +0 -583
  14. package/src/commands/config/config-command.ts +0 -897
  15. package/src/commands/config/env-command.test.ts +0 -43
  16. package/src/commands/config/env-command.ts +0 -295
  17. package/src/commands/config/index.ts +0 -3
  18. package/src/commands/config/kv-command.test.ts +0 -14
  19. package/src/commands/config/kv-command.ts +0 -290
  20. package/src/commands/dashboard/dashboard-command.test.ts +0 -47
  21. package/src/commands/dashboard/dashboard-command.ts +0 -128
  22. package/src/commands/dashboard/index.ts +0 -1
  23. package/src/commands/db/db-command.test.ts +0 -21
  24. package/src/commands/db/db-command.ts +0 -312
  25. package/src/commands/db/index.ts +0 -1
  26. package/src/commands/deploy/deploy-command.test.ts +0 -304
  27. package/src/commands/deploy/deploy-command.ts +0 -1135
  28. package/src/commands/deploy/index.ts +0 -2
  29. package/src/commands/deploy/telegram-service.ts +0 -76
  30. package/src/commands/deploy/types.ts +0 -34
  31. package/src/commands/dev/dev-command.test.ts +0 -383
  32. package/src/commands/dev/dev-command.ts +0 -398
  33. package/src/commands/dev/index.ts +0 -1
  34. package/src/commands/infra/index.ts +0 -5
  35. package/src/commands/infra/infra-command.test.ts +0 -719
  36. package/src/commands/infra/infra-command.ts +0 -974
  37. package/src/commands/infra/types.ts +0 -23
  38. package/src/commands/init/__tests__/cli-provisioner.test.ts +0 -17
  39. package/src/commands/init/cli-provisioner.ts +0 -71
  40. package/src/commands/init/index.ts +0 -1
  41. package/src/commands/init/init-command.test.ts +0 -876
  42. package/src/commands/init/init-command.ts +0 -593
  43. package/src/commands/init/types.ts +0 -23
  44. package/src/commands/logs/index.ts +0 -1
  45. package/src/commands/logs/logs-command.test.ts +0 -294
  46. package/src/commands/logs/logs-command.ts +0 -452
  47. package/src/commands/monitor/index.ts +0 -2
  48. package/src/commands/monitor/monitor-command.test.ts +0 -263
  49. package/src/commands/monitor/monitor-command.ts +0 -269
  50. package/src/commands/monitor/monitor-service.ts +0 -65
  51. package/src/commands/monitor/types.ts +0 -13
  52. package/src/commands/repair/index.ts +0 -2
  53. package/src/commands/repair/repair-command.test.ts +0 -224
  54. package/src/commands/repair/repair-command.ts +0 -220
  55. package/src/commands/repair/repair-service.ts +0 -138
  56. package/src/commands/repair/types.ts +0 -13
  57. package/src/commands/test/index.ts +0 -2
  58. package/src/commands/test/test-command.test.ts +0 -319
  59. package/src/commands/test/test-command.ts +0 -406
  60. package/src/commands/tui/index.ts +0 -1
  61. package/src/commands/tui/tui-command.ts +0 -87
  62. package/src/commands/update/index.ts +0 -1
  63. package/src/commands/update/update-command.ts +0 -168
  64. package/src/commands/waf/index.ts +0 -2
  65. package/src/commands/waf/types.ts +0 -48
  66. package/src/commands/waf/waf-command.test.ts +0 -506
  67. package/src/commands/waf/waf-command.ts +0 -548
  68. package/src/index.ts +0 -269
  69. package/src/services/cloudflare/cloudflare-service.test.ts +0 -658
  70. package/src/services/cloudflare/cloudflare-service.ts +0 -433
  71. package/src/services/cloudflare/index.ts +0 -2
  72. package/src/services/cloudflare/types.ts +0 -30
  73. package/src/services/config/config-service.test.ts +0 -395
  74. package/src/services/config/config-service.ts +0 -207
  75. package/src/services/config/index.ts +0 -9
  76. package/src/services/config/types.ts +0 -66
  77. package/src/services/db/db-service.test.ts +0 -51
  78. package/src/services/db/db-service.ts +0 -131
  79. package/src/services/db/index.ts +0 -1
  80. package/src/services/docker/docker-service.ts +0 -155
  81. package/src/services/docker/index.ts +0 -1
  82. package/src/services/env/env-service.test.ts +0 -236
  83. package/src/services/env/env-service.ts +0 -386
  84. package/src/services/env/index.ts +0 -1
  85. package/src/services/kv/index.ts +0 -1
  86. package/src/services/kv/kv-sync-service.test.ts +0 -38
  87. package/src/services/kv/kv-sync-service.ts +0 -234
  88. package/src/services/prerequisites/index.ts +0 -1
  89. package/src/services/prerequisites/prerequisites-service.test.ts +0 -89
  90. package/src/services/prerequisites/prerequisites-service.ts +0 -377
  91. package/src/services/prerequisites/types.ts +0 -48
  92. package/src/services/secrets/index.ts +0 -11
  93. package/src/services/secrets/secrets-service.test.ts +0 -495
  94. package/src/services/secrets/secrets-service.ts +0 -287
  95. package/src/services/secrets/types.ts +0 -50
  96. package/src/services/update/index.ts +0 -2
  97. package/src/services/update/update-service.test.ts +0 -76
  98. package/src/services/update/update-service.ts +0 -193
  99. package/src/ui/banner.ts +0 -141
  100. package/src/ui/index.ts +0 -8
  101. package/src/ui/menu.ts +0 -490
  102. package/src/utils/errors.test.ts +0 -69
  103. package/src/utils/errors.ts +0 -23
  104. package/src/utils/formatters.test.ts +0 -180
  105. package/src/utils/formatters.ts +0 -269
  106. package/src/utils/theme.ts +0 -94
@@ -1,658 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
2
- import { CloudflareService } from "./cloudflare-service.js";
3
-
4
- // ---------------------------------------------------------------------------
5
- // Helpers
6
- // ---------------------------------------------------------------------------
7
-
8
- /** Save a reference to the real Bun.spawn so we can restore it after tests. */
9
- const realSpawn = Bun.spawn;
10
-
11
- type MockSpawnResult = {
12
- stdout: Blob;
13
- stderr: Blob;
14
- exited: Promise<number>;
15
- stdin?: {
16
- write: ReturnType<typeof mock>;
17
- end: ReturnType<typeof mock>;
18
- };
19
- kill: ReturnType<typeof mock>;
20
- };
21
-
22
- /**
23
- * Creates a mock spawn result with the given stdout, stderr, and exit code.
24
- * Used to replace Bun.spawn in tests.
25
- */
26
- function makeSpawnResult(
27
- stdoutText: string,
28
- stderrText: string,
29
- exitCode: number
30
- ): MockSpawnResult {
31
- return {
32
- stdout: new Blob([stdoutText]),
33
- stderr: new Blob([stderrText]),
34
- exited: Promise.resolve(exitCode),
35
- stdin: {
36
- write: mock(() => {}),
37
- end: mock(() => {}),
38
- },
39
- kill: mock(() => {}),
40
- };
41
- }
42
-
43
- /**
44
- * Convenience: a successful spawn with the given stdout.
45
- */
46
- function successSpawn(stdout: string): MockSpawnResult {
47
- return makeSpawnResult(stdout, "", 0);
48
- }
49
-
50
- /**
51
- * Convenience: a failed spawn with the given stderr and exit code.
52
- */
53
- function errorSpawn(stderr: string, exitCode = 1): MockSpawnResult {
54
- return makeSpawnResult("", stderr, exitCode);
55
- }
56
-
57
- /** Install a mock Bun.spawn that returns the given result. */
58
- function mockSpawnWith(result: MockSpawnResult): void {
59
- const spawnMock = mock(() => result);
60
- (Bun as unknown as Record<string, unknown>).spawn = spawnMock;
61
- }
62
-
63
- /** Track spawn calls so we can assert on arguments. */
64
- let lastSpawnCmd: string[] = [];
65
- let lastSpawnCwd: string | undefined;
66
-
67
- function mockSpawnWithCapture(result: MockSpawnResult): void {
68
- const spawnMock = mock((cmd: string[], options?: { cwd?: string }) => {
69
- lastSpawnCmd = cmd;
70
- lastSpawnCwd = options?.cwd;
71
- return result;
72
- });
73
- (Bun as unknown as Record<string, unknown>).spawn = spawnMock;
74
- }
75
-
76
- // ---------------------------------------------------------------------------
77
- // Setup / Teardown
78
- // ---------------------------------------------------------------------------
79
-
80
- beforeEach(() => {
81
- lastSpawnCmd = [];
82
- lastSpawnCwd = undefined;
83
- });
84
-
85
- afterEach(() => {
86
- (Bun as unknown as Record<string, unknown>).spawn = realSpawn;
87
- mock.restore();
88
- });
89
-
90
- // ---------------------------------------------------------------------------
91
- // Tests
92
- // ---------------------------------------------------------------------------
93
-
94
- describe("CloudflareService", () => {
95
- // -- Constructor ----------------------------------------------------------
96
-
97
- it("defaults cwd to process.cwd()", () => {
98
- const service = new CloudflareService();
99
- // Cannot easily inspect private cwd, but we verify via spawn cwd later
100
- expect(service).toBeDefined();
101
- });
102
-
103
- it("accepts a custom cwd", () => {
104
- const service = new CloudflareService("/custom/path");
105
- expect(service).toBeDefined();
106
- });
107
-
108
- // -- whoami ---------------------------------------------------------------
109
-
110
- describe("whoami", () => {
111
- it("returns ok with stdout on success", async () => {
112
- mockSpawnWithCapture(successSpawn("user@example.com"));
113
-
114
- const service = new CloudflareService();
115
- const result = await service.whoami();
116
-
117
- expect(result.ok).toBe(true);
118
- if (result.ok) {
119
- expect(result.value).toBe("user@example.com");
120
- }
121
- expect(lastSpawnCmd).toEqual(["wrangler", "whoami"]);
122
- });
123
-
124
- it("returns error on non-zero exit", async () => {
125
- mockSpawnWithCapture(errorSpawn("Not authenticated"));
126
-
127
- const service = new CloudflareService();
128
- const result = await service.whoami();
129
-
130
- expect(result.ok).toBe(false);
131
- if (!result.ok) {
132
- expect(result.error).toContain("Not authenticated");
133
- }
134
- });
135
- });
136
-
137
- // -- deploy ---------------------------------------------------------------
138
-
139
- describe("deploy", () => {
140
- it("deploys without --env by default", async () => {
141
- const stdout =
142
- "Published test-worker (0.5 sec)\n https://test-worker.cryptolinx.workers.dev";
143
- mockSpawnWithCapture(successSpawn(stdout));
144
-
145
- const service = new CloudflareService();
146
- const result = await service.deploy("workers/test-worker");
147
-
148
- expect(result.ok).toBe(true);
149
- if (result.ok) {
150
- expect(result.value.url).toBe(
151
- "https://test-worker.cryptolinx.workers.dev"
152
- );
153
- }
154
- expect(lastSpawnCmd).toEqual(["wrangler", "deploy"]);
155
- });
156
-
157
- it("includes --env when specified", async () => {
158
- const stdout =
159
- "Published test-worker (production)\n https://test-worker.cryptolinx.workers.dev";
160
- mockSpawnWithCapture(successSpawn(stdout));
161
-
162
- const service = new CloudflareService();
163
- const result = await service.deploy("workers/test-worker", "production");
164
-
165
- expect(result.ok).toBe(true);
166
- if (result.ok) {
167
- expect(result.value.url).toBe(
168
- "https://test-worker.cryptolinx.workers.dev"
169
- );
170
- }
171
- expect(lastSpawnCmd).toEqual([
172
- "wrangler",
173
- "deploy",
174
- "--env",
175
- "production",
176
- ]);
177
- });
178
-
179
- it("returns url undefined when stdout has no URL", async () => {
180
- mockSpawnWithCapture(successSpawn("Deployed successfully. No URL here."));
181
-
182
- const service = new CloudflareService();
183
- const result = await service.deploy("workers/test-worker");
184
-
185
- expect(result.ok).toBe(true);
186
- if (result.ok) {
187
- expect(result.value.url).toBeUndefined();
188
- }
189
- });
190
-
191
- it("returns error on deploy failure", async () => {
192
- mockSpawnWithCapture(errorSpawn("Authentication error"));
193
-
194
- const service = new CloudflareService();
195
- const result = await service.deploy("workers/broken");
196
-
197
- expect(result.ok).toBe(false);
198
- if (!result.ok) {
199
- expect(result.error).toContain("Authentication error");
200
- }
201
- });
202
- });
203
-
204
- // -- dev ------------------------------------------------------------------
205
-
206
- describe("dev", () => {
207
- it("returns default port 8787", async () => {
208
- mockSpawnWithCapture(successSpawn(""));
209
-
210
- const service = new CloudflareService();
211
- const result = await service.dev("workers/test-worker");
212
-
213
- expect(result.ok).toBe(true);
214
- if (result.ok) {
215
- expect(result.value.port).toBe(8787);
216
- }
217
- expect(lastSpawnCmd).toEqual(["wrangler", "dev", "--port", "8787"]);
218
- });
219
-
220
- it("returns custom port when specified", async () => {
221
- mockSpawnWithCapture(successSpawn(""));
222
-
223
- const service = new CloudflareService();
224
- const result = await service.dev("workers/test-worker", 3000);
225
-
226
- expect(result.ok).toBe(true);
227
- if (result.ok) {
228
- expect(result.value.port).toBe(3000);
229
- }
230
- expect(lastSpawnCmd).toEqual(["wrangler", "dev", "--port", "3000"]);
231
- });
232
- });
233
-
234
- // -- D1 -------------------------------------------------------------------
235
-
236
- describe("d1 operations", () => {
237
- it("d1List calls wrangler d1 list --json", async () => {
238
- mockSpawnWithCapture(successSpawn('[{"name":"test-db"}]'));
239
-
240
- const service = new CloudflareService();
241
- const result = await service.d1List();
242
-
243
- expect(result.ok).toBe(true);
244
- expect(lastSpawnCmd).toEqual(["wrangler", "d1", "list", "--json"]);
245
- });
246
-
247
- it("d1Create calls wrangler d1 create with name", async () => {
248
- mockSpawnWithCapture(successSpawn("Created database my-db"));
249
-
250
- const service = new CloudflareService();
251
- const result = await service.d1Create("my-db");
252
-
253
- expect(result.ok).toBe(true);
254
- expect(lastSpawnCmd).toEqual(["wrangler", "d1", "create", "my-db"]);
255
- });
256
-
257
- it("d1Delete calls wrangler d1 delete with name", async () => {
258
- mockSpawnWithCapture(successSpawn("Deleted database old-db"));
259
-
260
- const service = new CloudflareService();
261
- const result = await service.d1Delete("old-db");
262
-
263
- expect(result.ok).toBe(true);
264
- expect(lastSpawnCmd).toEqual(["wrangler", "d1", "delete", "old-db"]);
265
- });
266
- });
267
-
268
- // -- KV -------------------------------------------------------------------
269
-
270
- describe("kv operations", () => {
271
- it("kvList calls wrangler kv namespace list", async () => {
272
- mockSpawnWithCapture(successSpawn("[]"));
273
-
274
- const service = new CloudflareService();
275
- const result = await service.kvList();
276
-
277
- expect(result.ok).toBe(true);
278
- expect(lastSpawnCmd).toEqual(["wrangler", "kv", "namespace", "list"]);
279
- });
280
-
281
- it("kvCreate calls wrangler kv namespace create", async () => {
282
- mockSpawnWithCapture(successSpawn("Created namespace my-kv"));
283
-
284
- const service = new CloudflareService();
285
- const result = await service.kvCreate("my-kv");
286
-
287
- expect(result.ok).toBe(true);
288
- expect(lastSpawnCmd).toEqual([
289
- "wrangler",
290
- "kv",
291
- "namespace",
292
- "create",
293
- "my-kv",
294
- ]);
295
- });
296
-
297
- it("kvDelete calls wrangler kv namespace delete", async () => {
298
- mockSpawnWithCapture(successSpawn("Deleted namespace"));
299
-
300
- const service = new CloudflareService();
301
- const result = await service.kvDelete("abc123");
302
-
303
- expect(result.ok).toBe(true);
304
- expect(lastSpawnCmd).toEqual([
305
- "wrangler",
306
- "kv",
307
- "namespace",
308
- "delete",
309
- "--namespace-id",
310
- "abc123",
311
- ]);
312
- });
313
- });
314
-
315
- // -- R2 -------------------------------------------------------------------
316
-
317
- describe("r2 operations", () => {
318
- it("r2List calls wrangler r2 bucket list", async () => {
319
- mockSpawnWithCapture(successSpawn("[]"));
320
-
321
- const service = new CloudflareService();
322
- const result = await service.r2List();
323
-
324
- expect(result.ok).toBe(true);
325
- expect(lastSpawnCmd).toEqual(["wrangler", "r2", "bucket", "list"]);
326
- });
327
-
328
- it("r2Create calls wrangler r2 bucket create", async () => {
329
- mockSpawnWithCapture(successSpawn("Created bucket my-bucket"));
330
-
331
- const service = new CloudflareService();
332
- const result = await service.r2Create("my-bucket");
333
-
334
- expect(result.ok).toBe(true);
335
- expect(lastSpawnCmd).toEqual([
336
- "wrangler",
337
- "r2",
338
- "bucket",
339
- "create",
340
- "my-bucket",
341
- ]);
342
- });
343
-
344
- it("r2Delete calls wrangler r2 bucket delete", async () => {
345
- mockSpawnWithCapture(successSpawn("Deleted bucket"));
346
-
347
- const service = new CloudflareService();
348
- const result = await service.r2Delete("old-bucket");
349
-
350
- expect(result.ok).toBe(true);
351
- expect(lastSpawnCmd).toEqual([
352
- "wrangler",
353
- "r2",
354
- "bucket",
355
- "delete",
356
- "old-bucket",
357
- ]);
358
- });
359
- });
360
-
361
- // -- Queues ---------------------------------------------------------------
362
-
363
- describe("queue operations", () => {
364
- it("queueList calls wrangler queues list", async () => {
365
- mockSpawnWithCapture(successSpawn("[]"));
366
-
367
- const service = new CloudflareService();
368
- const result = await service.queueList();
369
-
370
- expect(result.ok).toBe(true);
371
- expect(lastSpawnCmd).toEqual(["wrangler", "queues", "list"]);
372
- });
373
-
374
- it("queueCreate calls wrangler queues create", async () => {
375
- mockSpawnWithCapture(successSpawn("Created queue my-queue"));
376
-
377
- const service = new CloudflareService();
378
- const result = await service.queueCreate("my-queue");
379
-
380
- expect(result.ok).toBe(true);
381
- expect(lastSpawnCmd).toEqual([
382
- "wrangler",
383
- "queues",
384
- "create",
385
- "my-queue",
386
- ]);
387
- });
388
-
389
- it("queueDelete calls wrangler queues delete", async () => {
390
- mockSpawnWithCapture(successSpawn("Deleted queue"));
391
-
392
- const service = new CloudflareService();
393
- const result = await service.queueDelete("old-queue");
394
-
395
- expect(result.ok).toBe(true);
396
- expect(lastSpawnCmd).toEqual([
397
- "wrangler",
398
- "queues",
399
- "delete",
400
- "old-queue",
401
- ]);
402
- });
403
- });
404
-
405
- // -- Vectorize ------------------------------------------------------------
406
-
407
- describe("vectorize operations", () => {
408
- it("vectorizeList calls wrangler vectorize list --json", async () => {
409
- mockSpawnWithCapture(successSpawn("[]"));
410
-
411
- const service = new CloudflareService();
412
- const result = await service.vectorizeList();
413
-
414
- expect(result.ok).toBe(true);
415
- expect(lastSpawnCmd).toEqual(["wrangler", "vectorize", "list", "--json"]);
416
- });
417
-
418
- it("vectorizeCreate calls wrangler vectorize create with defaults", async () => {
419
- mockSpawnWithCapture(successSpawn("Created index my-index"));
420
-
421
- const service = new CloudflareService();
422
- const result = await service.vectorizeCreate("my-index");
423
-
424
- expect(result.ok).toBe(true);
425
- expect(lastSpawnCmd).toEqual([
426
- "wrangler",
427
- "vectorize",
428
- "create",
429
- "my-index",
430
- "--dimensions",
431
- "768",
432
- "--metric",
433
- "cosine",
434
- ]);
435
- });
436
-
437
- it("vectorizeCreate passes custom dimensions and metric", async () => {
438
- mockSpawnWithCapture(successSpawn("Created index my-index"));
439
-
440
- const service = new CloudflareService();
441
- const result = await service.vectorizeCreate(
442
- "my-index",
443
- 384,
444
- "euclidean"
445
- );
446
-
447
- expect(result.ok).toBe(true);
448
- expect(lastSpawnCmd).toEqual([
449
- "wrangler",
450
- "vectorize",
451
- "create",
452
- "my-index",
453
- "--dimensions",
454
- "384",
455
- "--metric",
456
- "euclidean",
457
- ]);
458
- });
459
-
460
- it("vectorizeDelete calls wrangler vectorize delete", async () => {
461
- mockSpawnWithCapture(successSpawn("Deleted index"));
462
-
463
- const service = new CloudflareService();
464
- const result = await service.vectorizeDelete("old-index");
465
-
466
- expect(result.ok).toBe(true);
467
- expect(lastSpawnCmd).toEqual([
468
- "wrangler",
469
- "vectorize",
470
- "delete",
471
- "old-index",
472
- ]);
473
- });
474
- });
475
-
476
- // -- Analytics ------------------------------------------------------------
477
-
478
- describe("analytics operations", () => {
479
- it("analyticsList calls wrangler analytics dataset list", async () => {
480
- mockSpawnWithCapture(successSpawn("[]"));
481
-
482
- const service = new CloudflareService();
483
- const result = await service.analyticsList();
484
-
485
- expect(result.ok).toBe(true);
486
- expect(lastSpawnCmd).toEqual([
487
- "wrangler",
488
- "analytics",
489
- "dataset",
490
- "list",
491
- ]);
492
- });
493
-
494
- it("analyticsCreate returns error with Dashboard URL instructions", async () => {
495
- const service = new CloudflareService();
496
- const result = await service.analyticsCreate("my-dataset");
497
-
498
- expect(result.ok).toBe(false);
499
- if (!result.ok) {
500
- expect(result.error).toContain("cannot be created via wrangler");
501
- expect(result.error).toContain("dash.cloudflare.com");
502
- }
503
- });
504
- });
505
-
506
- // -- Secrets --------------------------------------------------------------
507
-
508
- describe("secret operations", () => {
509
- it("secretList calls wrangler secret list --name", async () => {
510
- mockSpawnWithCapture(successSpawn("[]"));
511
-
512
- const service = new CloudflareService();
513
- const result = await service.secretList("my-worker");
514
-
515
- expect(result.ok).toBe(true);
516
- expect(lastSpawnCmd).toEqual([
517
- "wrangler",
518
- "secret",
519
- "list",
520
- "--name",
521
- "my-worker",
522
- ]);
523
- });
524
-
525
- it("secretPut pipes value through stdin (never via CLI args)", async () => {
526
- let stdinWritten = "";
527
- let stdinEnded = false;
528
-
529
- const spawnResult = {
530
- stdout: new Blob(["Secret set successfully"]),
531
- stderr: new Blob([""]),
532
- exited: Promise.resolve(0),
533
- stdin: {
534
- write: mock((data: string) => {
535
- stdinWritten = data;
536
- }),
537
- end: mock(() => {
538
- stdinEnded = true;
539
- }),
540
- },
541
- kill: mock(() => {}),
542
- };
543
-
544
- const spawnMock = mock((cmd: string[]) => {
545
- lastSpawnCmd = cmd;
546
- return spawnResult;
547
- });
548
- (Bun as unknown as Record<string, unknown>).spawn = spawnMock;
549
-
550
- const service = new CloudflareService();
551
- const result = await service.secretPut(
552
- "my-worker",
553
- "API_KEY",
554
- "super-secret-value"
555
- );
556
-
557
- expect(result.ok).toBe(true);
558
- // The secret value should NEVER appear in CLI args
559
- const argsJoined = lastSpawnCmd.join(" ");
560
- expect(argsJoined).not.toContain("super-secret-value");
561
- // stdin should receive the secret value
562
- expect(stdinWritten).toBe("super-secret-value\n");
563
- expect(stdinEnded).toBe(true);
564
- });
565
-
566
- it("secretDelete calls wrangler secret delete", async () => {
567
- mockSpawnWithCapture(successSpawn("Deleted secret"));
568
-
569
- const service = new CloudflareService();
570
- const result = await service.secretDelete("my-worker", "OLD_KEY");
571
-
572
- expect(result.ok).toBe(true);
573
- expect(lastSpawnCmd).toEqual([
574
- "wrangler",
575
- "secret",
576
- "delete",
577
- "OLD_KEY",
578
- "--name",
579
- "my-worker",
580
- ]);
581
- });
582
- });
583
-
584
- // -- zonesList ------------------------------------------------------------
585
-
586
- describe("zonesList", () => {
587
- it("calls wrangler zones list", async () => {
588
- mockSpawnWithCapture(successSpawn("[]"));
589
-
590
- const service = new CloudflareService();
591
- const result = await service.zonesList();
592
-
593
- expect(result.ok).toBe(true);
594
- expect(lastSpawnCmd).toEqual(["wrangler", "zones", "list"]);
595
- });
596
- });
597
-
598
- // -- Error handling -------------------------------------------------------
599
-
600
- describe("error handling", () => {
601
- it("returns ok:false when wrangler exits non-zero", async () => {
602
- mockSpawnWithCapture(errorSpawn("fatal error: something broke", 2));
603
-
604
- const service = new CloudflareService();
605
- const result = await service.whoami();
606
-
607
- expect(result.ok).toBe(false);
608
- if (!result.ok) {
609
- expect(result.error).toBe("fatal error: something broke");
610
- }
611
- });
612
-
613
- it("falls back to exit code message when stderr is empty", async () => {
614
- mockSpawnWithCapture(makeSpawnResult("", "", 1));
615
-
616
- const service = new CloudflareService();
617
- const result = await service.whoami();
618
-
619
- expect(result.ok).toBe(false);
620
- if (!result.ok) {
621
- expect(result.error).toBe("wrangler exited with code 1");
622
- }
623
- });
624
-
625
- it("returns error when Bun.spawn itself throws", async () => {
626
- // Simulate spawn throwing (e.g. wrangler not installed)
627
- const spawnMock = mock(() => {
628
- throw new Error("ENOENT: wrangler not found");
629
- });
630
- (Bun as unknown as Record<string, unknown>).spawn = spawnMock;
631
-
632
- const service = new CloudflareService();
633
- const result = await service.whoami();
634
-
635
- expect(result.ok).toBe(false);
636
- if (!result.ok) {
637
- expect(result.error).toContain("Failed to spawn wrangler");
638
- expect(result.error).toContain("ENOENT");
639
- }
640
- });
641
- });
642
-
643
- // -- Stdout parsing -------------------------------------------------------
644
-
645
- describe("stdout parsing", () => {
646
- it("trims trailing whitespace from stdout", async () => {
647
- mockSpawnWithCapture(successSpawn(" hello world \n"));
648
-
649
- const service = new CloudflareService();
650
- const result = await service.whoami();
651
-
652
- expect(result.ok).toBe(true);
653
- if (result.ok) {
654
- expect(result.value).toBe("hello world");
655
- }
656
- });
657
- });
658
- });