@easyoref/agent 1.21.1
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/__tests__/clarify.test.ts +827 -0
- package/__tests__/config.test.ts +304 -0
- package/__tests__/enrichment.integration.test.ts +871 -0
- package/__tests__/graph.test.ts +661 -0
- package/dist/auth.d.ts +11 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +54 -0
- package/dist/auth.js.map +1 -0
- package/dist/dry-run.d.ts +12 -0
- package/dist/dry-run.d.ts.map +1 -0
- package/dist/dry-run.js +236 -0
- package/dist/dry-run.js.map +1 -0
- package/dist/extract.d.ts +180 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +210 -0
- package/dist/extract.js.map +1 -0
- package/dist/graph.d.ts +4083 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +162 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +7 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +18 -0
- package/dist/models.js.map +1 -0
- package/dist/nodes/clarify-node.d.ts +132 -0
- package/dist/nodes/clarify-node.d.ts.map +1 -0
- package/dist/nodes/clarify-node.js +118 -0
- package/dist/nodes/clarify-node.js.map +1 -0
- package/dist/nodes/clarify.d.ts +6 -0
- package/dist/nodes/clarify.d.ts.map +1 -0
- package/dist/nodes/clarify.js +124 -0
- package/dist/nodes/clarify.js.map +1 -0
- package/dist/nodes/edit-node.d.ts +71 -0
- package/dist/nodes/edit-node.d.ts.map +1 -0
- package/dist/nodes/edit-node.js +496 -0
- package/dist/nodes/edit-node.js.map +1 -0
- package/dist/nodes/edit.d.ts +6 -0
- package/dist/nodes/edit.d.ts.map +1 -0
- package/dist/nodes/edit.js +22 -0
- package/dist/nodes/edit.js.map +1 -0
- package/dist/nodes/extract-node.d.ts +174 -0
- package/dist/nodes/extract-node.d.ts.map +1 -0
- package/dist/nodes/extract-node.js +233 -0
- package/dist/nodes/extract-node.js.map +1 -0
- package/dist/nodes/extract.d.ts +6 -0
- package/dist/nodes/extract.d.ts.map +1 -0
- package/dist/nodes/extract.js +49 -0
- package/dist/nodes/extract.js.map +1 -0
- package/dist/nodes/filter-agent.d.ts +11 -0
- package/dist/nodes/filter-agent.d.ts.map +1 -0
- package/dist/nodes/filter-agent.js +60 -0
- package/dist/nodes/filter-agent.js.map +1 -0
- package/dist/nodes/filter-node.d.ts +9 -0
- package/dist/nodes/filter-node.d.ts.map +1 -0
- package/dist/nodes/filter-node.js +111 -0
- package/dist/nodes/filter-node.js.map +1 -0
- package/dist/nodes/filters.d.ts +13 -0
- package/dist/nodes/filters.d.ts.map +1 -0
- package/dist/nodes/filters.js +111 -0
- package/dist/nodes/filters.js.map +1 -0
- package/dist/nodes/message-node.d.ts +71 -0
- package/dist/nodes/message-node.d.ts.map +1 -0
- package/dist/nodes/message-node.js +491 -0
- package/dist/nodes/message-node.js.map +1 -0
- package/dist/nodes/message.d.ts +71 -0
- package/dist/nodes/message.d.ts.map +1 -0
- package/dist/nodes/message.js +496 -0
- package/dist/nodes/message.js.map +1 -0
- package/dist/nodes/vote-node.d.ts +13 -0
- package/dist/nodes/vote-node.d.ts.map +1 -0
- package/dist/nodes/vote-node.js +232 -0
- package/dist/nodes/vote-node.js.map +1 -0
- package/dist/nodes/vote.d.ts +13 -0
- package/dist/nodes/vote.d.ts.map +1 -0
- package/dist/nodes/vote.js +232 -0
- package/dist/nodes/vote.js.map +1 -0
- package/dist/queue.d.ts +15 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +41 -0
- package/dist/queue.js.map +1 -0
- package/dist/redis.d.ts +8 -0
- package/dist/redis.d.ts.map +1 -0
- package/dist/redis.js +33 -0
- package/dist/redis.js.map +1 -0
- package/dist/runtime/auth.d.ts +11 -0
- package/dist/runtime/auth.d.ts.map +1 -0
- package/dist/runtime/auth.js +54 -0
- package/dist/runtime/auth.js.map +1 -0
- package/dist/runtime/dry-run.d.ts +12 -0
- package/dist/runtime/dry-run.d.ts.map +1 -0
- package/dist/runtime/dry-run.js +236 -0
- package/dist/runtime/dry-run.js.map +1 -0
- package/dist/runtime/queue.d.ts +15 -0
- package/dist/runtime/queue.d.ts.map +1 -0
- package/dist/runtime/queue.js +41 -0
- package/dist/runtime/queue.js.map +1 -0
- package/dist/runtime/redis.d.ts +8 -0
- package/dist/runtime/redis.d.ts.map +1 -0
- package/dist/runtime/redis.js +33 -0
- package/dist/runtime/redis.js.map +1 -0
- package/dist/runtime/worker.d.ts +14 -0
- package/dist/runtime/worker.d.ts.map +1 -0
- package/dist/runtime/worker.js +135 -0
- package/dist/runtime/worker.js.map +1 -0
- package/dist/tools/alert-history.d.ts +18 -0
- package/dist/tools/alert-history.d.ts.map +1 -0
- package/dist/tools/alert-history.js +98 -0
- package/dist/tools/alert-history.js.map +1 -0
- package/dist/tools/betterstack-log.d.ts +15 -0
- package/dist/tools/betterstack-log.d.ts.map +1 -0
- package/dist/tools/betterstack-log.js +80 -0
- package/dist/tools/betterstack-log.js.map +1 -0
- package/dist/tools/index.d.ts +44 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +20 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/read-sources.d.ts +15 -0
- package/dist/tools/read-sources.d.ts.map +1 -0
- package/dist/tools/read-sources.js +67 -0
- package/dist/tools/read-sources.js.map +1 -0
- package/dist/tools/resolve-area.d.ts +19 -0
- package/dist/tools/resolve-area.d.ts.map +1 -0
- package/dist/tools/resolve-area.js +147 -0
- package/dist/tools/resolve-area.js.map +1 -0
- package/dist/tools.d.ts +115 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +439 -0
- package/dist/tools.js.map +1 -0
- package/dist/worker.d.ts +14 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +135 -0
- package/dist/worker.js.map +1 -0
- package/package.json +26 -0
- package/src/graph.ts +200 -0
- package/src/index.ts +27 -0
- package/src/models.ts +20 -0
- package/src/nodes/clarify-node.ts +172 -0
- package/src/nodes/edit-node.ts +695 -0
- package/src/nodes/extract-node.ts +299 -0
- package/src/nodes/filter-node.ts +139 -0
- package/src/nodes/message.ts +695 -0
- package/src/nodes/vote-node.ts +354 -0
- package/src/nodes/vote.ts +354 -0
- package/src/runtime/auth.ts +63 -0
- package/src/runtime/dry-run.ts +303 -0
- package/src/runtime/queue.ts +53 -0
- package/src/runtime/redis.ts +38 -0
- package/src/runtime/worker.ts +167 -0
- package/src/tools/alert-history.ts +120 -0
- package/src/tools/betterstack-log.ts +102 -0
- package/src/tools/index.ts +23 -0
- package/src/tools/read-sources.ts +86 -0
- package/src/tools/resolve-area.ts +202 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import yaml from "js-yaml";
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
11
|
+
|
|
12
|
+
// ── Helpers ──────────────────────────────────────────────
|
|
13
|
+
// We can't import config.ts directly (it evaluates at import time).
|
|
14
|
+
// Instead we test the YAML parsing/validation logic in isolation.
|
|
15
|
+
|
|
16
|
+
type AlertTypeConfig = "early" | "red_alert" | "resolved";
|
|
17
|
+
type GifMode = "funny_cats" | "none";
|
|
18
|
+
|
|
19
|
+
const ALL_ALERT_TYPES: AlertTypeConfig[] = ["early", "red_alert", "resolved"];
|
|
20
|
+
const VALID_GIF_MODES: GifMode[] = ["funny_cats", "none"];
|
|
21
|
+
|
|
22
|
+
interface ConfigYaml {
|
|
23
|
+
alert_types?: AlertTypeConfig[];
|
|
24
|
+
city_ids?: number[];
|
|
25
|
+
language?: string;
|
|
26
|
+
gif_mode?: string;
|
|
27
|
+
emoji_override?: Partial<Record<AlertTypeConfig, string>>;
|
|
28
|
+
title_override?: Partial<Record<AlertTypeConfig, string>>;
|
|
29
|
+
description_override?: Partial<Record<AlertTypeConfig, string>>;
|
|
30
|
+
observability?: { betterstack_token?: string };
|
|
31
|
+
telegram?: { bot_token?: string; chat_id?: string };
|
|
32
|
+
health_port?: number;
|
|
33
|
+
poll_interval_ms?: number;
|
|
34
|
+
data_dir?: string;
|
|
35
|
+
oref_api_url?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseAlertTypes(raw?: AlertTypeConfig[]): AlertTypeConfig[] {
|
|
39
|
+
if (!raw || !Array.isArray(raw)) return ALL_ALERT_TYPES;
|
|
40
|
+
return raw.filter((t) => ALL_ALERT_TYPES.includes(t));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parseGifMode(raw: string): GifMode {
|
|
44
|
+
const lower = raw.toLowerCase() as GifMode;
|
|
45
|
+
return VALID_GIF_MODES.includes(lower) ? lower : "none";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isValidLanguage(s: string): boolean {
|
|
49
|
+
return s === "ru" || s === "en" || s === "he" || s === "ar";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Test fixtures ────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
const TMP_DIR = join(import.meta.dirname ?? ".", "__test_tmp__");
|
|
55
|
+
|
|
56
|
+
function writeTmpYaml(name: string, content: object | string): string {
|
|
57
|
+
mkdirSync(TMP_DIR, { recursive: true });
|
|
58
|
+
const p = join(TMP_DIR, name);
|
|
59
|
+
const raw = typeof content === "string" ? content : yaml.dump(content);
|
|
60
|
+
writeFileSync(p, raw, "utf-8");
|
|
61
|
+
return p;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
beforeEach(() => mkdirSync(TMP_DIR, { recursive: true }));
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
if (existsSync(TMP_DIR)) rmSync(TMP_DIR, { recursive: true });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// ── YAML Parsing ─────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
describe("YAML config parsing", () => {
|
|
72
|
+
it("parses a minimal valid config", () => {
|
|
73
|
+
const path = writeTmpYaml("min.yaml", {
|
|
74
|
+
city_ids: [722],
|
|
75
|
+
telegram: { bot_token: "123:ABC", chat_id: "-100123" },
|
|
76
|
+
});
|
|
77
|
+
const raw = yaml.load(readFileSync(path, "utf-8")) as ConfigYaml;
|
|
78
|
+
|
|
79
|
+
expect(raw.city_ids).toEqual([722]);
|
|
80
|
+
expect(raw.telegram?.bot_token).toBe("123:ABC");
|
|
81
|
+
expect(raw.telegram?.chat_id).toBe("-100123");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("parses a full config with all fields", () => {
|
|
85
|
+
const full: ConfigYaml = {
|
|
86
|
+
alert_types: ["early", "red_alert"],
|
|
87
|
+
city_ids: [722, 723, 1],
|
|
88
|
+
language: "en",
|
|
89
|
+
gif_mode: "funny_cats",
|
|
90
|
+
title_override: { red_alert: "🚀 ROCKET!" },
|
|
91
|
+
description_override: { red_alert: "Run!" },
|
|
92
|
+
observability: { betterstack_token: "tok_abc" },
|
|
93
|
+
telegram: { bot_token: "123:ABC", chat_id: "-100" },
|
|
94
|
+
health_port: 8080,
|
|
95
|
+
poll_interval_ms: 5000,
|
|
96
|
+
data_dir: "/tmp/data",
|
|
97
|
+
};
|
|
98
|
+
const path = writeTmpYaml("full.yaml", full);
|
|
99
|
+
const raw = yaml.load(readFileSync(path, "utf-8")) as ConfigYaml;
|
|
100
|
+
|
|
101
|
+
expect(raw.alert_types).toEqual(["early", "red_alert"]);
|
|
102
|
+
expect(raw.city_ids).toEqual([722, 723, 1]);
|
|
103
|
+
expect(raw.language).toBe("en");
|
|
104
|
+
expect(raw.gif_mode).toBe("funny_cats");
|
|
105
|
+
expect(raw.title_override?.red_alert).toBe("🚀 ROCKET!");
|
|
106
|
+
expect(raw.description_override?.red_alert).toBe("Run!");
|
|
107
|
+
expect(raw.observability?.betterstack_token).toBe("tok_abc");
|
|
108
|
+
expect(raw.health_port).toBe(8080);
|
|
109
|
+
expect(raw.poll_interval_ms).toBe(5000);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("handles empty YAML file gracefully", () => {
|
|
113
|
+
const path = writeTmpYaml("empty.yaml", "");
|
|
114
|
+
const raw = yaml.load(readFileSync(path, "utf-8")) as
|
|
115
|
+
| ConfigYaml
|
|
116
|
+
| undefined;
|
|
117
|
+
|
|
118
|
+
// yaml.load of empty string returns undefined
|
|
119
|
+
expect(raw ?? {}).toEqual({});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("handles YAML with comments only", () => {
|
|
123
|
+
const path = writeTmpYaml("comments.yaml", "# just a comment\n# another");
|
|
124
|
+
const raw = yaml.load(readFileSync(path, "utf-8")) as
|
|
125
|
+
| ConfigYaml
|
|
126
|
+
| undefined;
|
|
127
|
+
|
|
128
|
+
expect(raw ?? {}).toEqual({});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ── Alert Types Validation ───────────────────────────────
|
|
133
|
+
|
|
134
|
+
describe("parseAlertTypes", () => {
|
|
135
|
+
it("returns all types when undefined", () => {
|
|
136
|
+
expect(parseAlertTypes(undefined)).toEqual(ALL_ALERT_TYPES);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("returns all types when empty array", () => {
|
|
140
|
+
// Empty array is technically valid but useless → still filtered
|
|
141
|
+
expect(parseAlertTypes([])).toEqual([]);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("filters invalid alert types", () => {
|
|
145
|
+
const input = ["early", "bogus", "red_alert"] as AlertTypeConfig[];
|
|
146
|
+
expect(parseAlertTypes(input)).toEqual(["early", "red_alert"]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("keeps valid subset", () => {
|
|
150
|
+
expect(parseAlertTypes(["resolved"])).toEqual(["resolved"]);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("returns all types when non-array passed", () => {
|
|
154
|
+
expect(parseAlertTypes("red_alert" as unknown as AlertTypeConfig[])).toEqual(
|
|
155
|
+
ALL_ALERT_TYPES,
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// ── GIF Mode Validation ─────────────────────────────────
|
|
161
|
+
|
|
162
|
+
describe("parseGifMode", () => {
|
|
163
|
+
it("parses valid modes", () => {
|
|
164
|
+
expect(parseGifMode("funny_cats")).toBe("funny_cats");
|
|
165
|
+
expect(parseGifMode("none")).toBe("none");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("is case-insensitive", () => {
|
|
169
|
+
expect(parseGifMode("FUNNY_CATS")).toBe("funny_cats");
|
|
170
|
+
expect(parseGifMode("None")).toBe("none");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("defaults to none for invalid input", () => {
|
|
174
|
+
expect(parseGifMode("invalid")).toBe("none");
|
|
175
|
+
expect(parseGifMode("")).toBe("none");
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// ── Language Validation ──────────────────────────────────
|
|
180
|
+
|
|
181
|
+
describe("isValidLanguage", () => {
|
|
182
|
+
it("accepts ru, en, he, ar", () => {
|
|
183
|
+
expect(isValidLanguage("ru")).toBe(true);
|
|
184
|
+
expect(isValidLanguage("en")).toBe(true);
|
|
185
|
+
expect(isValidLanguage("he")).toBe(true);
|
|
186
|
+
expect(isValidLanguage("ar")).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("rejects invalid languages", () => {
|
|
190
|
+
expect(isValidLanguage("fr")).toBe(false);
|
|
191
|
+
expect(isValidLanguage("")).toBe(false);
|
|
192
|
+
expect(isValidLanguage("RU")).toBe(false); // case-sensitive
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// ── City ID Resolution ───────────────────────────────────
|
|
197
|
+
|
|
198
|
+
describe("resolveCityIds (logic)", () => {
|
|
199
|
+
// Simulate the id→name map from cities.json
|
|
200
|
+
const idToName = new Map<number, string>([
|
|
201
|
+
[722, "תל אביב - דרום העיר ויפו"],
|
|
202
|
+
[723, "תל אביב - מזרח"],
|
|
203
|
+
[1, "אופקים"],
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
function resolveCityIds(ids: number[]): string[] {
|
|
207
|
+
return ids.filter((id) => idToName.has(id)).map((id) => idToName.get(id)!);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
it("resolves known IDs to Hebrew names", () => {
|
|
211
|
+
expect(resolveCityIds([722, 723])).toEqual([
|
|
212
|
+
"תל אביב - דרום העיר ויפו",
|
|
213
|
+
"תל אביב - מזרח",
|
|
214
|
+
]);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("skips unknown IDs", () => {
|
|
218
|
+
expect(resolveCityIds([722, 99999])).toEqual(["תל אביב - דרום העיר ויפו"]);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("returns empty array for all unknown IDs", () => {
|
|
222
|
+
expect(resolveCityIds([99999, 88888])).toEqual([]);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("handles empty input", () => {
|
|
226
|
+
expect(resolveCityIds([])).toEqual([]);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// ── Emoji/Title/Description Overrides ────────────────────
|
|
231
|
+
|
|
232
|
+
describe("config overrides", () => {
|
|
233
|
+
it("override fields can be partial", () => {
|
|
234
|
+
const yml: ConfigYaml = {
|
|
235
|
+
emoji_override: { early: "🚀" },
|
|
236
|
+
title_override: { red_alert: "CUSTOM SIREN" },
|
|
237
|
+
// description_override not set
|
|
238
|
+
};
|
|
239
|
+
expect(yml.emoji_override?.early).toBe("🚀");
|
|
240
|
+
expect(yml.emoji_override?.red_alert).toBeUndefined();
|
|
241
|
+
expect(yml.title_override?.red_alert).toBe("CUSTOM SIREN");
|
|
242
|
+
expect(yml.title_override?.early).toBeUndefined();
|
|
243
|
+
expect(yml.description_override).toBeUndefined();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("YAML round-trips override objects correctly", () => {
|
|
247
|
+
const overrides = {
|
|
248
|
+
emoji_override: {
|
|
249
|
+
early: "🚀",
|
|
250
|
+
red_alert: "🔴",
|
|
251
|
+
},
|
|
252
|
+
title_override: {
|
|
253
|
+
early: "Warning",
|
|
254
|
+
red_alert: "SIREN",
|
|
255
|
+
resolved: "Clear",
|
|
256
|
+
},
|
|
257
|
+
description_override: {
|
|
258
|
+
red_alert: "",
|
|
259
|
+
resolved: "You may leave the shelter.",
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
const dumped = yaml.dump(overrides);
|
|
263
|
+
const parsed = yaml.load(dumped) as ConfigYaml;
|
|
264
|
+
|
|
265
|
+
expect(parsed.emoji_override).toEqual(overrides.emoji_override);
|
|
266
|
+
expect(parsed.title_override).toEqual(overrides.title_override);
|
|
267
|
+
expect(parsed.description_override).toEqual(overrides.description_override);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("empty description string round-trips as empty", () => {
|
|
271
|
+
const yml = { description_override: { red_alert: "" } };
|
|
272
|
+
const dumped = yaml.dump(yml);
|
|
273
|
+
const parsed = yaml.load(dumped) as ConfigYaml;
|
|
274
|
+
expect(parsed.description_override?.red_alert).toBe("");
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// ── Docker Secret Fallback ───────────────────────────────
|
|
279
|
+
|
|
280
|
+
describe("secret fallback logic", () => {
|
|
281
|
+
function readSecret(
|
|
282
|
+
envValue: string | undefined,
|
|
283
|
+
secretPath: string | null,
|
|
284
|
+
): string {
|
|
285
|
+
// Simulate: YAML → env → Docker secret → ""
|
|
286
|
+
if (secretPath && existsSync(secretPath)) {
|
|
287
|
+
return readFileSync(secretPath, "utf-8").trim();
|
|
288
|
+
}
|
|
289
|
+
return envValue ?? "";
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
it("reads from secret file when available", () => {
|
|
293
|
+
const path = writeTmpYaml("secret", "my-bot-token\n");
|
|
294
|
+
expect(readSecret(undefined, path)).toBe("my-bot-token");
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("falls back to env when no secret file", () => {
|
|
298
|
+
expect(readSecret("env-token", "/nonexistent")).toBe("env-token");
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it("returns empty string when nothing available", () => {
|
|
302
|
+
expect(readSecret(undefined, null)).toBe("");
|
|
303
|
+
});
|
|
304
|
+
});
|