@jango-blockchained/hoox-cli 0.5.2 → 0.5.7
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 +32989 -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,502 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, mock } from "bun:test";
|
|
2
|
-
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import type { Result } from "./types.js";
|
|
6
|
-
import { SecretsService } from "./secrets-service.js";
|
|
7
|
-
|
|
8
|
-
// ---------------------------------------------------------------------------
|
|
9
|
-
// Helpers
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Narrow a discriminated union Result<T> after checking ok.
|
|
14
|
-
* Calling expect(result.ok).toBe(true) does not narrow the type,
|
|
15
|
-
* so we use this to unwrap the value after asserting ok.
|
|
16
|
-
*/
|
|
17
|
-
function expectOk<T>(result: Result<T>): T {
|
|
18
|
-
expect(result.ok).toBe(true);
|
|
19
|
-
return (result as { ok: true; value: T }).value;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Assert a Result<T> is an error and return the error message.
|
|
24
|
-
*/
|
|
25
|
-
function expectErr<T>(result: Result<T>): string {
|
|
26
|
-
expect(result.ok).toBe(false);
|
|
27
|
-
return (result as { ok: false; error: string }).error;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const WORKERS_JSONC = JSON.stringify({
|
|
31
|
-
global: {},
|
|
32
|
-
workers: {
|
|
33
|
-
"telegram-worker": {
|
|
34
|
-
enabled: true,
|
|
35
|
-
path: "workers/telegram-worker",
|
|
36
|
-
secrets: ["TG_BOT_TOKEN_BINDING"],
|
|
37
|
-
},
|
|
38
|
-
"trade-worker": {
|
|
39
|
-
enabled: true,
|
|
40
|
-
path: "workers/trade-worker",
|
|
41
|
-
secrets: [
|
|
42
|
-
"API_SERVICE_KEY_BINDING",
|
|
43
|
-
"BINANCE_KEY_BINDING",
|
|
44
|
-
"BINANCE_SECRET_BINDING",
|
|
45
|
-
],
|
|
46
|
-
},
|
|
47
|
-
"d1-worker": {
|
|
48
|
-
enabled: true,
|
|
49
|
-
path: "workers/d1-worker",
|
|
50
|
-
},
|
|
51
|
-
"no-secrets-worker": {
|
|
52
|
-
enabled: true,
|
|
53
|
-
path: "workers/no-secrets",
|
|
54
|
-
secrets: [],
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Creates a SecretsService that reads from a synthetic wrangler.jsonc string
|
|
61
|
-
* instead of hitting the real file-system. We override `Bun.file` for the
|
|
62
|
-
* config path only and call the private constructor via a test-only subclass.
|
|
63
|
-
*/
|
|
64
|
-
async function createService(workersJsonc?: string): Promise<SecretsService> {
|
|
65
|
-
const jsonc = workersJsonc ?? WORKERS_JSONC;
|
|
66
|
-
|
|
67
|
-
// Spy on Bun.file so we can intercept the config read while letting
|
|
68
|
-
// real .dev.vars file-system calls through for the integration-style tests.
|
|
69
|
-
const originalBunFile = Bun.file;
|
|
70
|
-
|
|
71
|
-
// Use direct property assignment for Bun.file mock.
|
|
72
|
-
// Object.defineProperty with configurable:true fails because Bun.file
|
|
73
|
-
// is not configurable, but direct assignment (which requires only writable)
|
|
74
|
-
// works in Bun 1.3.x for individual Bun properties.
|
|
75
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
-
(Bun as any).file = mock((path: string) => {
|
|
77
|
-
if (typeof path === "string" && path.endsWith("wrangler.jsonc")) {
|
|
78
|
-
return {
|
|
79
|
-
exists: mock(async () => true),
|
|
80
|
-
text: mock(async () => jsonc),
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
// Fall through to the real Bun.file for everything else
|
|
84
|
-
return originalBunFile(path);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const svc = await SecretsService.create("wrangler.jsonc");
|
|
88
|
-
|
|
89
|
-
// Restore original so subsequent tests aren't polluted
|
|
90
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
-
(Bun as any).file = originalBunFile;
|
|
92
|
-
|
|
93
|
-
return svc;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Returns a fresh temporary directory that is cleaned up after the test. */
|
|
97
|
-
function tmpDir(): string {
|
|
98
|
-
const dir = mkdtempSync(join(tmpdir(), "hoox-secrets-test-"));
|
|
99
|
-
return dir;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// ---------------------------------------------------------------------------
|
|
103
|
-
// Tests
|
|
104
|
-
// ---------------------------------------------------------------------------
|
|
105
|
-
|
|
106
|
-
describe("SecretsService", () => {
|
|
107
|
-
// -- listSecrets ----------------------------------------------------------
|
|
108
|
-
|
|
109
|
-
describe("listSecrets", () => {
|
|
110
|
-
it("returns correct secret names for a worker that has secrets", async () => {
|
|
111
|
-
const svc = await createService();
|
|
112
|
-
expect(svc.listSecrets("trade-worker")).toEqual([
|
|
113
|
-
"API_SERVICE_KEY_BINDING",
|
|
114
|
-
"BINANCE_KEY_BINDING",
|
|
115
|
-
"BINANCE_SECRET_BINDING",
|
|
116
|
-
]);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("returns single secret for telegram-worker", async () => {
|
|
120
|
-
const svc = await createService();
|
|
121
|
-
expect(svc.listSecrets("telegram-worker")).toEqual([
|
|
122
|
-
"TG_BOT_TOKEN_BINDING",
|
|
123
|
-
]);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("returns empty array for worker not in config", async () => {
|
|
127
|
-
const svc = await createService();
|
|
128
|
-
expect(svc.listSecrets("nonexistent")).toEqual([]);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("returns empty array for worker with no secrets declared", async () => {
|
|
132
|
-
const svc = await createService();
|
|
133
|
-
expect(svc.listSecrets("d1-worker")).toEqual([]);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("returns empty array for worker with explicitly empty secrets", async () => {
|
|
137
|
-
const svc = await createService();
|
|
138
|
-
expect(svc.listSecrets("no-secrets-worker")).toEqual([]);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// -- listAllSecrets -------------------------------------------------------
|
|
143
|
-
|
|
144
|
-
describe("listAllSecrets", () => {
|
|
145
|
-
it("returns map of workers that have secrets", async () => {
|
|
146
|
-
const svc = await createService();
|
|
147
|
-
const all = svc.listAllSecrets();
|
|
148
|
-
|
|
149
|
-
expect(Object.keys(all)).toHaveLength(2);
|
|
150
|
-
expect(all["telegram-worker"]).toEqual(["TG_BOT_TOKEN_BINDING"]);
|
|
151
|
-
expect(all["trade-worker"]).toEqual([
|
|
152
|
-
"API_SERVICE_KEY_BINDING",
|
|
153
|
-
"BINANCE_KEY_BINDING",
|
|
154
|
-
"BINANCE_SECRET_BINDING",
|
|
155
|
-
]);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("omits workers without secrets", async () => {
|
|
159
|
-
const svc = await createService();
|
|
160
|
-
const all = svc.listAllSecrets();
|
|
161
|
-
expect(all["d1-worker"]).toBeUndefined();
|
|
162
|
-
expect(all["no-secrets-worker"]).toBeUndefined();
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// -- checkLocalSecrets ----------------------------------------------------
|
|
167
|
-
|
|
168
|
-
describe("checkLocalSecrets", () => {
|
|
169
|
-
it("reports all secrets missing when .dev.vars does not exist", async () => {
|
|
170
|
-
const svc = await createService();
|
|
171
|
-
// Use a temp dir so no .dev.vars file exists
|
|
172
|
-
const dir = tmpDir();
|
|
173
|
-
try {
|
|
174
|
-
// Override the worker path to point at the temp dir
|
|
175
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
176
|
-
(svc as any).config.workers["trade-worker"].path = dir;
|
|
177
|
-
|
|
178
|
-
const result = await svc.checkLocalSecrets("trade-worker");
|
|
179
|
-
expect(result.worker).toBe("trade-worker");
|
|
180
|
-
expect(result.allSet).toBe(false);
|
|
181
|
-
expect(result.missing).toEqual([
|
|
182
|
-
"API_SERVICE_KEY_BINDING",
|
|
183
|
-
"BINANCE_KEY_BINDING",
|
|
184
|
-
"BINANCE_SECRET_BINDING",
|
|
185
|
-
]);
|
|
186
|
-
expect(result.secrets).toHaveLength(3);
|
|
187
|
-
for (const s of result.secrets) {
|
|
188
|
-
expect(s.set).toBe(false);
|
|
189
|
-
}
|
|
190
|
-
} finally {
|
|
191
|
-
rmSync(dir, { recursive: true, force: true });
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("reports secrets as set when .dev.vars has real values", async () => {
|
|
196
|
-
const dir = tmpDir();
|
|
197
|
-
try {
|
|
198
|
-
writeFileSync(
|
|
199
|
-
join(dir, ".dev.vars"),
|
|
200
|
-
"API_SERVICE_KEY_BINDING=abc123\nBINANCE_KEY_BINDING=xyz789\nBINANCE_SECRET_BINDING=sec456\n"
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
const svc = await createService();
|
|
204
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
205
|
-
(svc as any).config.workers["trade-worker"].path = dir;
|
|
206
|
-
|
|
207
|
-
const result = await svc.checkLocalSecrets("trade-worker");
|
|
208
|
-
expect(result.allSet).toBe(true);
|
|
209
|
-
expect(result.missing).toEqual([]);
|
|
210
|
-
for (const s of result.secrets) {
|
|
211
|
-
expect(s.set).toBe(true);
|
|
212
|
-
expect(s.source).toBe(join(dir, ".dev.vars"));
|
|
213
|
-
}
|
|
214
|
-
} finally {
|
|
215
|
-
rmSync(dir, { recursive: true, force: true });
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it("flags placeholder values as missing", async () => {
|
|
220
|
-
const dir = tmpDir();
|
|
221
|
-
try {
|
|
222
|
-
writeFileSync(
|
|
223
|
-
join(dir, ".dev.vars"),
|
|
224
|
-
"API_SERVICE_KEY_BINDING=placeholder_api_service_key\nBINANCE_KEY_BINDING=binance-real-key\nBINANCE_SECRET_BINDING=your_secret\n"
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
const svc = await createService();
|
|
228
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
229
|
-
(svc as any).config.workers["trade-worker"].path = dir;
|
|
230
|
-
|
|
231
|
-
const result = await svc.checkLocalSecrets("trade-worker");
|
|
232
|
-
expect(result.allSet).toBe(false);
|
|
233
|
-
// Only BINANCE_KEY_BINDING should be set (not placeholder)
|
|
234
|
-
expect(result.missing).toContain("API_SERVICE_KEY_BINDING");
|
|
235
|
-
expect(result.missing).toContain("BINANCE_SECRET_BINDING");
|
|
236
|
-
expect(result.missing).not.toContain("BINANCE_KEY_BINDING");
|
|
237
|
-
} finally {
|
|
238
|
-
rmSync(dir, { recursive: true, force: true });
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("handles comments and blank lines in .dev.vars", async () => {
|
|
243
|
-
const dir = tmpDir();
|
|
244
|
-
try {
|
|
245
|
-
writeFileSync(
|
|
246
|
-
join(dir, ".dev.vars"),
|
|
247
|
-
"# This is a comment\nAPI_SERVICE_KEY_BINDING=real-key\n\n# Another comment\nBINANCE_KEY_BINDING=another-key\nBINANCE_SECRET_BINDING=third-key\n"
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
const svc = await createService();
|
|
251
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
252
|
-
(svc as any).config.workers["trade-worker"].path = dir;
|
|
253
|
-
|
|
254
|
-
const result = await svc.checkLocalSecrets("trade-worker");
|
|
255
|
-
expect(result.allSet).toBe(true);
|
|
256
|
-
expect(result.missing).toEqual([]);
|
|
257
|
-
} finally {
|
|
258
|
-
rmSync(dir, { recursive: true, force: true });
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it("returns empty result for unknown worker", async () => {
|
|
263
|
-
const svc = await createService();
|
|
264
|
-
const result = await svc.checkLocalSecrets("unknown-worker");
|
|
265
|
-
expect(result.worker).toBe("unknown-worker");
|
|
266
|
-
expect(result.secrets).toEqual([]);
|
|
267
|
-
expect(result.allSet).toBe(false);
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// -- generateDevVars ------------------------------------------------------
|
|
272
|
-
|
|
273
|
-
describe("generateDevVars", () => {
|
|
274
|
-
it("creates .dev.vars with placeholder entries", async () => {
|
|
275
|
-
const dir = tmpDir();
|
|
276
|
-
try {
|
|
277
|
-
const svc = await createService();
|
|
278
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
279
|
-
(svc as any).config.workers["telegram-worker"].path = dir;
|
|
280
|
-
|
|
281
|
-
const result = await svc.generateDevVars("telegram-worker");
|
|
282
|
-
expect(expectOk(result)).toBe(join(dir, ".dev.vars"));
|
|
283
|
-
|
|
284
|
-
// Verify file content
|
|
285
|
-
const content = await Bun.file(join(dir, ".dev.vars")).text();
|
|
286
|
-
expect(content).toContain(
|
|
287
|
-
"TG_BOT_TOKEN_BINDING=placeholder_tg_bot_token_binding"
|
|
288
|
-
);
|
|
289
|
-
} finally {
|
|
290
|
-
rmSync(dir, { recursive: true, force: true });
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it("writes empty file for worker with no secrets", async () => {
|
|
295
|
-
const dir = tmpDir();
|
|
296
|
-
try {
|
|
297
|
-
const svc = await createService();
|
|
298
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
299
|
-
(svc as any).config.workers["no-secrets-worker"].path = dir;
|
|
300
|
-
|
|
301
|
-
const result = await svc.generateDevVars("no-secrets-worker");
|
|
302
|
-
expect(expectOk(result)).toBe(join(dir, ".dev.vars"));
|
|
303
|
-
|
|
304
|
-
const content = await Bun.file(join(dir, ".dev.vars")).text();
|
|
305
|
-
expect(content).toBe("");
|
|
306
|
-
} finally {
|
|
307
|
-
rmSync(dir, { recursive: true, force: true });
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
it("returns error for unknown worker", async () => {
|
|
312
|
-
const svc = await createService();
|
|
313
|
-
const result = await svc.generateDevVars("unknown-worker");
|
|
314
|
-
expect(expectErr(result)).toContain("not found in config");
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it("overwrites existing .dev.vars", async () => {
|
|
318
|
-
const dir = tmpDir();
|
|
319
|
-
try {
|
|
320
|
-
writeFileSync(join(dir, ".dev.vars"), "OLD_KEY=old_value\n");
|
|
321
|
-
|
|
322
|
-
const svc = await createService();
|
|
323
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
324
|
-
(svc as any).config.workers["telegram-worker"].path = dir;
|
|
325
|
-
|
|
326
|
-
await svc.generateDevVars("telegram-worker");
|
|
327
|
-
|
|
328
|
-
const content = await Bun.file(join(dir, ".dev.vars")).text();
|
|
329
|
-
expect(content).toContain(
|
|
330
|
-
"TG_BOT_TOKEN_BINDING=placeholder_tg_bot_token_binding"
|
|
331
|
-
);
|
|
332
|
-
expect(content).not.toContain("OLD_KEY");
|
|
333
|
-
} finally {
|
|
334
|
-
rmSync(dir, { recursive: true, force: true });
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// -- syncToCloudflare -----------------------------------------------------
|
|
340
|
-
|
|
341
|
-
describe("syncToCloudflare", () => {
|
|
342
|
-
it("pushes secrets via wrangler when valid values are in .dev.vars", async () => {
|
|
343
|
-
const dir = tmpDir();
|
|
344
|
-
try {
|
|
345
|
-
writeFileSync(
|
|
346
|
-
join(dir, ".dev.vars"),
|
|
347
|
-
"TG_BOT_TOKEN_BINDING=my-real-token\n"
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
const svc = await createService();
|
|
351
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
352
|
-
(svc as any).config.workers["telegram-worker"].path = dir;
|
|
353
|
-
|
|
354
|
-
// Stub execWranglerSecretPut to avoid real wrangler calls.
|
|
355
|
-
const called: Array<[string, string]> = [];
|
|
356
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
357
|
-
(svc as any).execWranglerSecretPut = mock(
|
|
358
|
-
async (_path: string, name: string, value: string) => {
|
|
359
|
-
called.push([name, value]);
|
|
360
|
-
}
|
|
361
|
-
);
|
|
362
|
-
|
|
363
|
-
const result = await svc.syncToCloudflare("telegram-worker");
|
|
364
|
-
expect(expectOk(result)).toEqual(["TG_BOT_TOKEN_BINDING"]);
|
|
365
|
-
expect(called).toEqual([["TG_BOT_TOKEN_BINDING", "my-real-token"]]);
|
|
366
|
-
} finally {
|
|
367
|
-
rmSync(dir, { recursive: true, force: true });
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it("skips secrets that are placeholder values", async () => {
|
|
372
|
-
const dir = tmpDir();
|
|
373
|
-
try {
|
|
374
|
-
writeFileSync(
|
|
375
|
-
join(dir, ".dev.vars"),
|
|
376
|
-
"API_SERVICE_KEY_BINDING=placeholder_api_service_key\nBINANCE_KEY_BINDING=real-binance-key\nBINANCE_SECRET_BINDING=generate_something\n"
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
const svc = await createService();
|
|
380
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
381
|
-
(svc as any).config.workers["trade-worker"].path = dir;
|
|
382
|
-
|
|
383
|
-
const called: Array<[string, string]> = [];
|
|
384
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
385
|
-
(svc as any).execWranglerSecretPut = mock(
|
|
386
|
-
async (_path: string, name: string, value: string) => {
|
|
387
|
-
called.push([name, value]);
|
|
388
|
-
}
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
const result = await svc.syncToCloudflare("trade-worker");
|
|
392
|
-
const errMsg = expectErr(result);
|
|
393
|
-
// Only BINANCE_KEY_BINDING was synced — but errors exist, so overall result is error
|
|
394
|
-
expect(errMsg).toContain("API_SERVICE_KEY_BINDING");
|
|
395
|
-
expect(errMsg).toContain("BINANCE_SECRET_BINDING");
|
|
396
|
-
expect(called).toEqual([["BINANCE_KEY_BINDING", "real-binance-key"]]);
|
|
397
|
-
} finally {
|
|
398
|
-
rmSync(dir, { recursive: true, force: true });
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
it("returns error when no .dev.vars exists and all secrets are missing", async () => {
|
|
403
|
-
const dir = tmpDir();
|
|
404
|
-
try {
|
|
405
|
-
const svc = await createService();
|
|
406
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
407
|
-
(svc as any).config.workers["trade-worker"].path = dir;
|
|
408
|
-
|
|
409
|
-
const called: Array<[string, string]> = [];
|
|
410
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
411
|
-
(svc as any).execWranglerSecretPut = mock(
|
|
412
|
-
async (_path: string, name: string, value: string) => {
|
|
413
|
-
called.push([name, value]);
|
|
414
|
-
}
|
|
415
|
-
);
|
|
416
|
-
|
|
417
|
-
const result = await svc.syncToCloudflare("trade-worker");
|
|
418
|
-
const errMsg = expectErr(result);
|
|
419
|
-
expect(errMsg).toContain("API_SERVICE_KEY_BINDING");
|
|
420
|
-
expect(errMsg).toContain("BINANCE_KEY_BINDING");
|
|
421
|
-
expect(errMsg).toContain("BINANCE_SECRET_BINDING");
|
|
422
|
-
expect(called).toHaveLength(0);
|
|
423
|
-
} finally {
|
|
424
|
-
rmSync(dir, { recursive: true, force: true });
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it("returns error for unknown worker", async () => {
|
|
429
|
-
const svc = await createService();
|
|
430
|
-
const result = await svc.syncToCloudflare("unknown-worker");
|
|
431
|
-
expect(expectErr(result)).toContain("not found in config");
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
it("handles wrangler failures gracefully", async () => {
|
|
435
|
-
const dir = tmpDir();
|
|
436
|
-
try {
|
|
437
|
-
writeFileSync(
|
|
438
|
-
join(dir, ".dev.vars"),
|
|
439
|
-
"TG_BOT_TOKEN_BINDING=my-token\n"
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
const svc = await createService();
|
|
443
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
444
|
-
(svc as any).config.workers["telegram-worker"].path = dir;
|
|
445
|
-
|
|
446
|
-
// Stub execWranglerSecretPut to throw
|
|
447
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
448
|
-
(svc as any).execWranglerSecretPut = mock(async () => {
|
|
449
|
-
throw new Error("wrangler command failed");
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
const result = await svc.syncToCloudflare("telegram-worker");
|
|
453
|
-
expect(expectErr(result)).toContain("wrangler command failed");
|
|
454
|
-
} finally {
|
|
455
|
-
rmSync(dir, { recursive: true, force: true });
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
it("works for worker with no secrets", async () => {
|
|
460
|
-
const dir = tmpDir();
|
|
461
|
-
try {
|
|
462
|
-
const svc = await createService();
|
|
463
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
464
|
-
(svc as any).config.workers["no-secrets-worker"].path = dir;
|
|
465
|
-
|
|
466
|
-
const result = await svc.syncToCloudflare("no-secrets-worker");
|
|
467
|
-
expect(expectOk(result)).toEqual([]);
|
|
468
|
-
} finally {
|
|
469
|
-
rmSync(dir, { recursive: true, force: true });
|
|
470
|
-
}
|
|
471
|
-
});
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// -- Edge cases -----------------------------------------------------------
|
|
475
|
-
|
|
476
|
-
describe("edge cases", () => {
|
|
477
|
-
it("throws when config file does not exist", async () => {
|
|
478
|
-
await expect(
|
|
479
|
-
SecretsService.create("/nonexistent/wrangler.jsonc")
|
|
480
|
-
).rejects.toThrow("Config file not found");
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
it("handles worker at path with trailing/leading whitespace in .dev.vars", async () => {
|
|
484
|
-
const dir = tmpDir();
|
|
485
|
-
try {
|
|
486
|
-
writeFileSync(
|
|
487
|
-
join(dir, ".dev.vars"),
|
|
488
|
-
" TG_BOT_TOKEN_BINDING = my-token \n"
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
const svc = await createService();
|
|
492
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
493
|
-
(svc as any).config.workers["telegram-worker"].path = dir;
|
|
494
|
-
|
|
495
|
-
const result = await svc.checkLocalSecrets("telegram-worker");
|
|
496
|
-
expect(result.allSet).toBe(true);
|
|
497
|
-
} finally {
|
|
498
|
-
rmSync(dir, { recursive: true, force: true });
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
});
|