@serverkgg/bridge 0.1.2 → 0.1.3
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/package.json +3 -2
- package/rules/package.md +60 -0
- package/src/blog/articleFrontmatter.test.ts +10 -10
- package/src/blog/compile.test.ts +9 -9
- package/src/bot/botDeploy.test.ts +16 -16
- package/src/bot/botEnvironment.test.ts +8 -8
- package/src/bot/botIdentity.test.ts +12 -12
- package/src/bot/botPanel.test.ts +13 -13
- package/src/bot/botPanel.ts +4 -4
- package/src/bot/botState.test.ts +3 -3
- package/src/bot/botTerminal.test.ts +5 -5
- package/src/bot/botVariables.test.ts +8 -8
- package/src/bot/detect/botToken.test.ts +6 -6
- package/src/bot/detect/detect.test.ts +21 -21
- package/src/bot/detect/detectTable.test.ts +5 -5
- package/src/bot/detect/envExample.test.ts +2 -2
- package/src/bot/detect/envScan.test.ts +4 -4
- package/src/bot/detect/inferBotPlatform.test.ts +4 -4
- package/src/bot/detect/keys.test.ts +3 -3
- package/src/bot/detect/sensitiveKey.test.ts +5 -5
- package/src/bot/detect/serverkJson.test.ts +5 -5
- package/src/catalogs/curseforge.test.ts +35 -35
- package/src/catalogs/thunderstore.test.ts +9 -9
- package/src/cli/detectTable.test.ts +3 -3
- package/src/cli/link.test.ts +11 -11
- package/src/cli/main.ts +8 -0
- package/src/cli/rules.test.ts +198 -0
- package/src/cli/rules.ts +188 -0
- package/src/cli/validate.test.ts +52 -30
- package/src/cli/validate.ts +2 -0
- package/src/codecs/ini.test.ts +13 -13
- package/src/codecs/javaProperties.test.ts +11 -11
- package/src/codecs/sourceCfg.test.ts +14 -14
- package/src/codecs/xmlProperties.test.ts +10 -10
- package/src/core/bridgePort.test.ts +6 -6
- package/src/core/bridgeUserError.test.ts +4 -4
- package/src/core/bridgeValue.test.ts +5 -5
- package/src/core/describeDownloadFailure.test.ts +12 -12
- package/src/core/describeDownloadFailure.ts +10 -10
- package/src/guides/markdown.test.ts +31 -31
- package/src/install/installStamp.test.ts +9 -9
- package/src/manifest/extension.ts +36 -0
- package/src/manifest/index.ts +1 -0
- package/src/manifest/manifest.test.ts +208 -90
- package/src/manifest/manifest.ts +34 -0
- package/src/net/loopback.test.ts +10 -10
- package/src/presence/rosterSync.test.ts +7 -7
- package/src/probes/a2s.test.ts +5 -5
- package/src/probes/minecraftPing.test.ts +17 -17
- package/src/probes/raknetPing.test.ts +13 -13
- package/src/rcon/rconAccess.test.ts +20 -20
- package/src/rcon/rconAccess.ts +13 -13
- package/src/rcon/rconClient.test.ts +49 -49
- package/src/rcon/rconFrame.test.ts +14 -14
- package/src/rcon/rconIds.test.ts +4 -4
- package/src/rcon/rconWriter.test.ts +6 -6
- package/src/runtime/bridgeActivity.test.ts +15 -15
- package/src/runtime/bridgeBackupLease.test.ts +9 -9
- package/src/runtime/bridgeConfigReader.test.ts +5 -5
- package/src/runtime/bridgeConsoleHandler.test.ts +8 -8
- package/src/runtime/bridgeContext.test.ts +5 -5
- package/src/runtime/bridgeDriver.test.ts +94 -94
- package/src/runtime/bridgeEvents.test.ts +15 -15
- package/src/runtime/bridgeFiles.test.ts +54 -54
- package/src/runtime/bridgeInstall.test.ts +10 -10
- package/src/runtime/bridgeLiveConfig.test.ts +6 -6
- package/src/runtime/bridgePending.test.ts +6 -6
- package/src/runtime/bridgeRedaction.test.ts +16 -16
- package/src/runtime/bridgeRegistry.test.ts +52 -52
- package/src/runtime/bridgeRegistry.ts +2 -2
- package/src/runtime/bridgeServer.test.ts +31 -31
- package/src/runtime/bridgeSetup.test.ts +27 -27
- package/src/runtime/bridgeSetup.ts +1 -1
- package/src/runtime/bridgeSetupHandlers.test.ts +13 -13
- package/src/runtime/bridgeSignal.test.ts +7 -7
- package/src/runtime/bridgeSupervisor.test.ts +42 -42
- package/src/runtime/serve.ts +4 -4
- package/src/steam/avatars.test.ts +13 -13
- package/src/steam/steamApp.test.ts +8 -8
- package/src/steam/steamcmd.test.ts +17 -17
- package/src/steam/steamcmd.ts +4 -4
- package/src/steam/steamcmdNarrator.test.ts +4 -4
- package/src/steam/workshop.test.ts +5 -5
- package/src/utils/changedSettingKeys.test.ts +5 -5
- package/src/utils/describeExecFailure.test.ts +4 -4
- package/src/utils/describeExecFailure.ts +1 -1
- package/src/utils/sameSettingValue.test.ts +7 -7
- package/src/utils/token.test.ts +7 -7
- package/src/wine/protonCommand.test.ts +5 -5
- package/src/wine/wineCommand.test.ts +6 -6
- package/src/wine/winePrefix.test.ts +6 -6
- package/src/wire/activity.test.ts +8 -8
- package/src/wire/panel.test.ts +28 -28
package/src/bot/botPanel.ts
CHANGED
|
@@ -23,12 +23,12 @@ export enum BotDeployAction {
|
|
|
23
23
|
export const BOT_IDENTITY_REFRESH_SECONDS = 60;
|
|
24
24
|
|
|
25
25
|
const DEPLOY_PENDING: LocalizedText = {
|
|
26
|
-
ar: "
|
|
26
|
+
ar: "يوجد نشر قيد التنفيذ، عطه لحظة حتى يكتمل وجرب مرة ثانية.",
|
|
27
27
|
en: "A deploy is already running. Wait for it to finish, then try again.",
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
const INVALID_DEPLOY: LocalizedText = {
|
|
31
|
-
ar: "طلب النشر هذا ناقص أو
|
|
31
|
+
ar: "طلب النشر هذا ناقص أو غير صحيح. جرب النشر من صفحة البوت.",
|
|
32
32
|
en: "This deploy request is incomplete or malformed. Try deploying from the bot page.",
|
|
33
33
|
};
|
|
34
34
|
|
|
@@ -51,8 +51,8 @@ export const createBotPanel = (): BridgeTab => {
|
|
|
51
51
|
en: "Your bot",
|
|
52
52
|
},
|
|
53
53
|
empty: {
|
|
54
|
-
ar: "
|
|
55
|
-
en: "We learn who your bot is once it
|
|
54
|
+
ar: "نتعرف على بوتك بمجرد تشغيله وتسجيل دخوله.",
|
|
55
|
+
en: "We learn who your bot is once it starts and signs in.",
|
|
56
56
|
},
|
|
57
57
|
},
|
|
58
58
|
],
|
package/src/bot/botState.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { BotDeploymentOutcome, BotDeploymentSource, DEPLOYMENT_STATE_FILE, readDeploymentState } from "./botState";
|
|
3
3
|
|
|
4
4
|
const filesWith = (content: string | null) => {
|
|
@@ -13,7 +13,7 @@ const filesWith = (content: string | null) => {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
describe("readDeploymentState", () => {
|
|
16
|
-
|
|
16
|
+
it("reads what the launcher wrote", async () => {
|
|
17
17
|
const state = {
|
|
18
18
|
deployment: "dep_a1b2c3d4",
|
|
19
19
|
source: BotDeploymentSource.Snapshot,
|
|
@@ -29,7 +29,7 @@ describe("readDeploymentState", () => {
|
|
|
29
29
|
expect(await readDeploymentState(filesWith(JSON.stringify(state)))).toEqual(state);
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
it("answers null for a missing, broken or foreign file", async () => {
|
|
33
33
|
expect(await readDeploymentState(filesWith(null))).toBeNull();
|
|
34
34
|
expect(await readDeploymentState(filesWith("{"))).toBeNull();
|
|
35
35
|
expect(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { BridgeTerminalLevel, type BridgeTerminalRule } from "../terminal";
|
|
3
3
|
import { bridgeTerminalRuleSchema } from "../wire";
|
|
4
4
|
import { LAUNCHER_RULES, NODE_RULES, PYTHON_RULES } from "./botTerminal";
|
|
@@ -8,7 +8,7 @@ const levelOf = (rules: BridgeTerminalRule[], line: string) => {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
describe("the bot terminal rules", () => {
|
|
11
|
-
|
|
11
|
+
it("colour the launcher's own lines, failures as errors", () => {
|
|
12
12
|
expect(levelOf(LAUNCHER_RULES, "serverk: install failed: npm ci exited 1")).toBe(BridgeTerminalLevel.Error);
|
|
13
13
|
expect(levelOf(LAUNCHER_RULES, "serverk: start failed: no bot token yet — open Setup")).toBe(
|
|
14
14
|
BridgeTerminalLevel.Error,
|
|
@@ -17,7 +17,7 @@ describe("the bot terminal rules", () => {
|
|
|
17
17
|
expect(levelOf(LAUNCHER_RULES, "Logged in as helper#0001")).toBeNull();
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
it("catch node errors, stack frames and warnings", () => {
|
|
21
21
|
expect(levelOf(NODE_RULES, "TypeError: Cannot read properties of undefined (reading 'id')")).toBe(
|
|
22
22
|
BridgeTerminalLevel.Error,
|
|
23
23
|
);
|
|
@@ -31,7 +31,7 @@ describe("the bot terminal rules", () => {
|
|
|
31
31
|
expect(levelOf(NODE_RULES, "Ready! Logged in as helper")).toBeNull();
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
it("catch python tracebacks, exceptions, log levels and warnings", () => {
|
|
35
35
|
expect(levelOf(PYTHON_RULES, "Traceback (most recent call last):")).toBe(BridgeTerminalLevel.Error);
|
|
36
36
|
expect(levelOf(PYTHON_RULES, ' File "/home/container/bot.py", line 3, in <module>')).toBe(
|
|
37
37
|
BridgeTerminalLevel.Error,
|
|
@@ -61,7 +61,7 @@ describe("the bot terminal rules", () => {
|
|
|
61
61
|
expect(levelOf(PYTHON_RULES, "[2026-09-27 10:00:00] [INFO ] discord.client: logging in")).toBeNull();
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
it("cross the wire the way the panel reads them", () => {
|
|
65
65
|
for (const rule of [
|
|
66
66
|
...LAUNCHER_RULES,
|
|
67
67
|
...NODE_RULES,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_REPO_BRANCH,
|
|
4
4
|
DEPLOYMENT_ID_PATTERN,
|
|
@@ -34,7 +34,7 @@ const contextWith = (variables: Record<string, string>, secrets: Record<string,
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
describe("readBotConfig", () => {
|
|
37
|
-
|
|
37
|
+
it("reads the platform-owned keys, trims them and takes the git token from the secrets", () => {
|
|
38
38
|
expect(
|
|
39
39
|
readBotConfig(
|
|
40
40
|
contextWith(
|
|
@@ -69,7 +69,7 @@ describe("readBotConfig", () => {
|
|
|
69
69
|
});
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
it("answers nothing for empty values, main for the branch and null for an unknown platform", () => {
|
|
73
73
|
expect(
|
|
74
74
|
readBotConfig(
|
|
75
75
|
contextWith({
|
|
@@ -93,7 +93,7 @@ describe("readBotConfig", () => {
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
describe("the patterns", () => {
|
|
96
|
-
|
|
96
|
+
it("pin a ref only to a full sha-1 or sha-256", () => {
|
|
97
97
|
expect(REPO_REF_PATTERN.test(SHA)).toBe(true);
|
|
98
98
|
expect(REPO_REF_PATTERN.test("a".repeat(64))).toBe(true);
|
|
99
99
|
expect(REPO_REF_PATTERN.test(SHA.slice(0, 12))).toBe(false);
|
|
@@ -101,7 +101,7 @@ describe("the patterns", () => {
|
|
|
101
101
|
expect(REPO_REF_PATTERN.test("main")).toBe(false);
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
it("keep deployment and snapshot ids to url-safe words", () => {
|
|
105
105
|
for (const pattern of [
|
|
106
106
|
DEPLOYMENT_ID_PATTERN,
|
|
107
107
|
SNAPSHOT_ID_PATTERN,
|
|
@@ -114,7 +114,7 @@ describe("the patterns", () => {
|
|
|
114
114
|
}
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
it("take a root directory only as a plain relative folder path, the shape the api enforces", () => {
|
|
118
118
|
for (const root of [
|
|
119
119
|
"bot",
|
|
120
120
|
"apps/bot",
|
|
@@ -145,7 +145,7 @@ describe("the patterns", () => {
|
|
|
145
145
|
}
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
it("accept https repositories and git branch names", () => {
|
|
149
149
|
expect(REPO_URL_PATTERN.test("https://github.com/you/bot.git")).toBe(true);
|
|
150
150
|
expect(REPO_URL_PATTERN.test("https://user:pass@github.com/you/bot")).toBe(false);
|
|
151
151
|
expect(REPO_URL_PATTERN.test("git@github.com:you/bot.git")).toBe(false);
|
|
@@ -153,7 +153,7 @@ describe("the patterns", () => {
|
|
|
153
153
|
expect(REPO_BRANCH_PATTERN.test("-rf")).toBe(false);
|
|
154
154
|
});
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
it("accept only branch names git check-ref-format accepts", () => {
|
|
157
157
|
for (const branch of [
|
|
158
158
|
"main",
|
|
159
159
|
"release-1.2",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { BotPlatform } from "./botPlatform";
|
|
3
3
|
import { discordApplicationId, findBotToken, platformOfKey, platformOfToken, telegramBotId } from "./botToken";
|
|
4
4
|
|
|
@@ -9,14 +9,14 @@ const DISCORD_TOKEN = `${btoa(DISCORD_ID).replaceAll("=", "")}.GxYz12.${"a".repe
|
|
|
9
9
|
const TELEGRAM_TOKEN = "7123456789:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw";
|
|
10
10
|
|
|
11
11
|
describe("token shapes", () => {
|
|
12
|
-
|
|
12
|
+
it("read the ids hidden in each platform's token", () => {
|
|
13
13
|
expect(telegramBotId(TELEGRAM_TOKEN)).toBe("7123456789");
|
|
14
14
|
expect(discordApplicationId(` ${DISCORD_TOKEN} `)).toBe(DISCORD_ID);
|
|
15
15
|
expect(platformOfToken(TELEGRAM_TOKEN)).toBe(BotPlatform.Telegram);
|
|
16
16
|
expect(platformOfToken(DISCORD_TOKEN)).toBe(BotPlatform.Discord);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
it("do not mistake a JSON web token or a random key for a bot token", () => {
|
|
20
20
|
const jwt = `${btoa('{"alg":"HS256"}').replaceAll("=", "")}.eyJzdWIiOiIxIn0.${"b".repeat(43)}`;
|
|
21
21
|
|
|
22
22
|
expect(platformOfToken(jwt)).toBeNull();
|
|
@@ -24,7 +24,7 @@ describe("token shapes", () => {
|
|
|
24
24
|
expect(platformOfToken("")).toBeNull();
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
it("read the platform a key name points at", () => {
|
|
28
28
|
expect(platformOfKey("DISCORD_TOKEN")).toBe(BotPlatform.Discord);
|
|
29
29
|
expect(platformOfKey("my_telegram_chat")).toBe(BotPlatform.Telegram);
|
|
30
30
|
expect(platformOfKey("TOKEN")).toBeNull();
|
|
@@ -32,7 +32,7 @@ describe("token shapes", () => {
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
describe("findBotToken", () => {
|
|
35
|
-
|
|
35
|
+
it("prefers the tutorial names, then any value shaped like the platform's token", () => {
|
|
36
36
|
expect(
|
|
37
37
|
findBotToken(BotPlatform.Discord, {
|
|
38
38
|
variables: {},
|
|
@@ -58,7 +58,7 @@ describe("findBotToken", () => {
|
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
it("ignores a named key whose value is not the platform's token", () => {
|
|
62
62
|
expect(
|
|
63
63
|
findBotToken(BotPlatform.Telegram, {
|
|
64
64
|
variables: {},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { BotFramework } from "./botFramework";
|
|
3
3
|
import { BotInspectWarning } from "./botInspectWarning";
|
|
4
4
|
import { BotPackageManager } from "./botPackageManager";
|
|
@@ -52,7 +52,7 @@ const TELEGRAM_PYTHON = {
|
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
describe("detect on the templates", () => {
|
|
55
|
-
|
|
55
|
+
it("reads the discord.js template end to end", () => {
|
|
56
56
|
expect(
|
|
57
57
|
project(DISCORD_NODE, [
|
|
58
58
|
"package-lock.json",
|
|
@@ -85,7 +85,7 @@ describe("detect on the templates", () => {
|
|
|
85
85
|
} satisfies DetectResult);
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
it("reads the python-telegram-bot template end to end", () => {
|
|
89
89
|
expect(project(TELEGRAM_PYTHON)).toEqual({
|
|
90
90
|
runtime: BotRuntime.Python,
|
|
91
91
|
package: "bot-python",
|
|
@@ -113,7 +113,7 @@ describe("detect on the templates", () => {
|
|
|
113
113
|
} satisfies DetectResult);
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
it("reads the lockfile a pnpm or yarn project ships", () => {
|
|
117
117
|
expect(
|
|
118
118
|
project(DISCORD_NODE, [
|
|
119
119
|
"pnpm-lock.yaml",
|
|
@@ -129,7 +129,7 @@ describe("detect on the templates", () => {
|
|
|
129
129
|
).toBe(BotPackageManager.Yarn);
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
it("installs a pyproject project with pip", () => {
|
|
133
133
|
const result = project({
|
|
134
134
|
"pyproject.toml": '[project]\nname = "bot"\ndependencies = [\n "discord.py>=2.4",\n]\n',
|
|
135
135
|
"bot.py": 'import discord\nimport os\nos.getenv("DISCORD_TOKEN")\n',
|
|
@@ -148,7 +148,7 @@ describe("detect on the templates", () => {
|
|
|
148
148
|
});
|
|
149
149
|
|
|
150
150
|
describe("detect start commands", () => {
|
|
151
|
-
|
|
151
|
+
it("follows the launcher's order: start script, main, known files, then the Procfile", () => {
|
|
152
152
|
expect(
|
|
153
153
|
project({
|
|
154
154
|
"package.json": JSON.stringify({
|
|
@@ -181,7 +181,7 @@ describe("detect start commands", () => {
|
|
|
181
181
|
});
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
it("warns when nothing starts and when node would run TypeScript directly", () => {
|
|
185
185
|
expect(
|
|
186
186
|
project({
|
|
187
187
|
"package.json": "{}",
|
|
@@ -206,7 +206,7 @@ describe("detect start commands", () => {
|
|
|
206
206
|
});
|
|
207
207
|
|
|
208
208
|
describe("detect warnings", () => {
|
|
209
|
-
|
|
209
|
+
it("flags a missing lockfile, loose requirements and a missing manifest", () => {
|
|
210
210
|
expect(
|
|
211
211
|
project({
|
|
212
212
|
"package.json": "{}",
|
|
@@ -240,7 +240,7 @@ describe("detect warnings", () => {
|
|
|
240
240
|
]);
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
-
|
|
243
|
+
it("flags committed secrets and dependency folders but not the templates for them", () => {
|
|
244
244
|
expect(
|
|
245
245
|
project(DISCORD_NODE, [
|
|
246
246
|
"package-lock.json",
|
|
@@ -258,7 +258,7 @@ describe("detect warnings", () => {
|
|
|
258
258
|
]);
|
|
259
259
|
});
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
it("flags two runtimes and picks the one whose framework it found", () => {
|
|
262
262
|
const result = project({
|
|
263
263
|
...DISCORD_NODE,
|
|
264
264
|
"requirements.txt": "requests==2.32.0\n",
|
|
@@ -268,7 +268,7 @@ describe("detect warnings", () => {
|
|
|
268
268
|
expect(result.warnings).toContain(BotInspectWarning.TwoRuntimes);
|
|
269
269
|
});
|
|
270
270
|
|
|
271
|
-
|
|
271
|
+
it("flags two platforms and leaves the choice to the customer", () => {
|
|
272
272
|
const result = project({
|
|
273
273
|
"package.json": JSON.stringify({
|
|
274
274
|
dependencies: {
|
|
@@ -284,7 +284,7 @@ describe("detect warnings", () => {
|
|
|
284
284
|
expect(result.warnings).toContain(BotInspectWarning.TwoPlatforms);
|
|
285
285
|
});
|
|
286
286
|
|
|
287
|
-
|
|
287
|
+
it("drops keys the platform owns and warns, but keeps quiet about keys the runtime gives anyway", () => {
|
|
288
288
|
const result = project({
|
|
289
289
|
...DISCORD_NODE,
|
|
290
290
|
"index.js": "process.env.REPO_URL; process.env.HOME; process.env.SERVERK_BOT; process.env.GUILD_ID;",
|
|
@@ -306,7 +306,7 @@ describe("detect warnings", () => {
|
|
|
306
306
|
});
|
|
307
307
|
|
|
308
308
|
describe("detect with serverk.json", () => {
|
|
309
|
-
|
|
309
|
+
it("lets the repository's own file win and says when the code disagrees", () => {
|
|
310
310
|
const result = project({
|
|
311
311
|
...DISCORD_NODE,
|
|
312
312
|
"serverk.json": JSON.stringify({
|
|
@@ -336,7 +336,7 @@ describe("detect with serverk.json", () => {
|
|
|
336
336
|
expect(result.warnings).toContain(BotInspectWarning.ManifestDisagrees);
|
|
337
337
|
});
|
|
338
338
|
|
|
339
|
-
|
|
339
|
+
it("installs with the file's own install command over the lockfile's", () => {
|
|
340
340
|
const result = project({
|
|
341
341
|
...DISCORD_NODE,
|
|
342
342
|
"package-lock.json": "{}",
|
|
@@ -352,7 +352,7 @@ describe("detect with serverk.json", () => {
|
|
|
352
352
|
expect(result.packageManager).toBe(BotPackageManager.Npm);
|
|
353
353
|
});
|
|
354
354
|
|
|
355
|
-
|
|
355
|
+
it("reports a broken file and falls back to the code", () => {
|
|
356
356
|
const result = project({
|
|
357
357
|
...DISCORD_NODE,
|
|
358
358
|
"package-lock.json": "{}",
|
|
@@ -367,7 +367,7 @@ describe("detect with serverk.json", () => {
|
|
|
367
367
|
});
|
|
368
368
|
|
|
369
369
|
describe("compareCodePoints", () => {
|
|
370
|
-
|
|
370
|
+
it("orders by code point, the way Python sorts strings, whatever the locale", () => {
|
|
371
371
|
expect(
|
|
372
372
|
[
|
|
373
373
|
"b.js",
|
|
@@ -387,7 +387,7 @@ describe("compareCodePoints", () => {
|
|
|
387
387
|
});
|
|
388
388
|
|
|
389
389
|
describe("detect env suggestions", () => {
|
|
390
|
-
|
|
390
|
+
it("reads the example file first, then every way code reads the environment", () => {
|
|
391
391
|
const result = project({
|
|
392
392
|
"package.json": "{}",
|
|
393
393
|
"index.js": [
|
|
@@ -438,7 +438,7 @@ describe("detect env suggestions", () => {
|
|
|
438
438
|
]);
|
|
439
439
|
});
|
|
440
440
|
|
|
441
|
-
|
|
441
|
+
it("names the token key the code reads for its platform", () => {
|
|
442
442
|
const result = project({
|
|
443
443
|
"requirements.txt": "discord.py==2.7.1\n",
|
|
444
444
|
"main.py": 'import os\nimport discord\nos.environ.get("MY_BOT_TOKEN")\nos.getenv("LOG_CHANNEL")\n',
|
|
@@ -450,7 +450,7 @@ describe("detect env suggestions", () => {
|
|
|
450
450
|
});
|
|
451
451
|
|
|
452
452
|
describe("detectWantedFiles", () => {
|
|
453
|
-
|
|
453
|
+
it("asks for the small files the kit reads and at most forty small sources", () => {
|
|
454
454
|
const files = [
|
|
455
455
|
{
|
|
456
456
|
path: "package.json",
|
|
@@ -507,7 +507,7 @@ describe("detectWantedFiles", () => {
|
|
|
507
507
|
});
|
|
508
508
|
|
|
509
509
|
describe("the dependency readers", () => {
|
|
510
|
-
|
|
510
|
+
it("read requirement names and whether each one names a version", () => {
|
|
511
511
|
expect(parseRequirements("Discord.py[voice]==2.7.1\n-r base.txt\n# note\naiohttp ; python_version>'3'\n")).toEqual([
|
|
512
512
|
{
|
|
513
513
|
name: "discord-py",
|
|
@@ -520,7 +520,7 @@ describe("the dependency readers", () => {
|
|
|
520
520
|
]);
|
|
521
521
|
});
|
|
522
522
|
|
|
523
|
-
|
|
523
|
+
it("read project and poetry dependencies from pyproject.toml", () => {
|
|
524
524
|
expect(
|
|
525
525
|
parsePyprojectDependencies(
|
|
526
526
|
'[project]\ndependencies = ["hikari>=2", \'aiogram\']\n[tool.poetry.dependencies]\npython = "^3.12"\ntelethon = "*"\n',
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { BotFramework } from "./botFramework";
|
|
3
3
|
import { DETECT_TABLE, renderDetectTable } from "./detectTable";
|
|
4
4
|
import { isEnvKey } from "./envKey";
|
|
5
5
|
import { parseServerkJson } from "./serverkJson";
|
|
6
6
|
|
|
7
7
|
describe("renderDetectTable", () => {
|
|
8
|
-
|
|
8
|
+
it("renders the table the launchers read as stable, tab-indented JSON", () => {
|
|
9
9
|
const rendered = renderDetectTable();
|
|
10
10
|
|
|
11
11
|
expect(rendered.endsWith("}\n")).toBe(true);
|
|
@@ -13,7 +13,7 @@ describe("renderDetectTable", () => {
|
|
|
13
13
|
expect(JSON.parse(rendered)).toEqual(DETECT_TABLE);
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
it("names an install command for every manager a lockfile or manifest points at", () => {
|
|
17
17
|
for (const lockfile of DETECT_TABLE.node.lockfiles) {
|
|
18
18
|
expect(DETECT_TABLE.node.installCommands[lockfile.manager]).toBeDefined();
|
|
19
19
|
}
|
|
@@ -25,7 +25,7 @@ describe("renderDetectTable", () => {
|
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
it("carries the serverk.json rules the launchers need to agree with the parser", () => {
|
|
29
29
|
const rules = DETECT_TABLE.serverkJson;
|
|
30
30
|
const envKey = new RegExp(rules.envKey);
|
|
31
31
|
|
|
@@ -61,7 +61,7 @@ describe("renderDetectTable", () => {
|
|
|
61
61
|
).toBeNull();
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
it("compiles the Procfile line in both JavaScript and Python syntax", () => {
|
|
65
65
|
const line = new RegExp(DETECT_TABLE.procfile.line);
|
|
66
66
|
|
|
67
67
|
expect(line.exec("worker: node bot.js")?.slice(1)).toEqual([
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { parseEnvExample } from "./envExample";
|
|
3
3
|
|
|
4
4
|
describe("parseEnvExample", () => {
|
|
5
|
-
|
|
5
|
+
it("reads keys and example values the way dotenv files write them", () => {
|
|
6
6
|
expect(
|
|
7
7
|
parseEnvExample(
|
|
8
8
|
"# Discord\r\nDISCORD_TOKEN=\r\nexport PREFIX=\"!\" # command prefix\nOWNER_ID = 1234 # yours\nQUOTED='a # b'\nnot a line\n1BAD=x\n",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { scanEnvKeys } from "./envScan";
|
|
3
3
|
|
|
4
4
|
describe("scanEnvKeys", () => {
|
|
5
|
-
|
|
5
|
+
it("finds every way Node code reads a variable", () => {
|
|
6
6
|
expect(
|
|
7
7
|
scanEnvKeys(
|
|
8
8
|
"src/index.ts",
|
|
@@ -18,7 +18,7 @@ describe("scanEnvKeys", () => {
|
|
|
18
18
|
]);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
it("finds every way Python code reads a variable", () => {
|
|
22
22
|
expect(
|
|
23
23
|
scanEnvKeys(
|
|
24
24
|
"bot/main.py",
|
|
@@ -33,7 +33,7 @@ describe("scanEnvKeys", () => {
|
|
|
33
33
|
]);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
it("reads nothing from files that are not code", () => {
|
|
37
37
|
expect(scanEnvKeys("README.md", "process.env.A")).toEqual([]);
|
|
38
38
|
});
|
|
39
39
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { BotPlatform } from "./botPlatform";
|
|
3
3
|
import { inferBotPlatform } from "./inferBotPlatform";
|
|
4
4
|
|
|
5
5
|
const TELEGRAM_TOKEN = "7123456789:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw";
|
|
6
6
|
|
|
7
7
|
describe("inferBotPlatform", () => {
|
|
8
|
-
|
|
8
|
+
it("takes the explicit platform first", () => {
|
|
9
9
|
expect(
|
|
10
10
|
inferBotPlatform({
|
|
11
11
|
variables: {
|
|
@@ -17,7 +17,7 @@ describe("inferBotPlatform", () => {
|
|
|
17
17
|
).toBe(BotPlatform.Telegram);
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
it("then the key names, then the token shape", () => {
|
|
21
21
|
expect(
|
|
22
22
|
inferBotPlatform({
|
|
23
23
|
variables: {},
|
|
@@ -36,7 +36,7 @@ describe("inferBotPlatform", () => {
|
|
|
36
36
|
).toBe(BotPlatform.Telegram);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
it("answers null when the hints disagree or do not exist", () => {
|
|
40
40
|
expect(
|
|
41
41
|
inferBotPlatform({
|
|
42
42
|
variables: {},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { BOT_PLATFORM_KEYS, BOT_PLATFORM_SECRET_KEYS, BOT_PROTECTED_KEYS, isReservedBotKey } from "./keys";
|
|
3
3
|
|
|
4
4
|
describe("isReservedBotKey", () => {
|
|
5
|
-
|
|
5
|
+
it("reserves every platform-owned key, the protected runtime keys and the SERVERK_ prefix", () => {
|
|
6
6
|
for (const key of [
|
|
7
7
|
...BOT_PLATFORM_KEYS,
|
|
8
8
|
...BOT_PLATFORM_SECRET_KEYS,
|
|
@@ -14,7 +14,7 @@ describe("isReservedBotKey", () => {
|
|
|
14
14
|
}
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
it("leaves the customer's own keys alone, case included", () => {
|
|
18
18
|
for (const key of [
|
|
19
19
|
"DISCORD_TOKEN",
|
|
20
20
|
"NODE_OPTIONS",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { isSensitiveKey, keySegments } from "./sensitiveKey";
|
|
3
3
|
|
|
4
4
|
describe("isSensitiveKey", () => {
|
|
5
|
-
|
|
5
|
+
it("reads a key as whole segments, split on underscores, case changes and digits", () => {
|
|
6
6
|
expect(keySegments("DISCORD_BOT_TOKEN")).toEqual([
|
|
7
7
|
"DISCORD",
|
|
8
8
|
"BOT",
|
|
@@ -19,7 +19,7 @@ describe("isSensitiveKey", () => {
|
|
|
19
19
|
]);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
it("hides every key that names a secret in one of its segments", () => {
|
|
23
23
|
for (const key of [
|
|
24
24
|
"DISCORD_TOKEN",
|
|
25
25
|
"TELEGRAM_BOT_TOKEN",
|
|
@@ -46,7 +46,7 @@ describe("isSensitiveKey", () => {
|
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
it("hides connection strings, which carry their password inside the link", () => {
|
|
50
50
|
for (const key of [
|
|
51
51
|
"DATABASE_URL",
|
|
52
52
|
"DB_URI",
|
|
@@ -65,7 +65,7 @@ describe("isSensitiveKey", () => {
|
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
it("leaves plain settings visible, including words that only contain a secret word", () => {
|
|
69
69
|
for (const key of [
|
|
70
70
|
"AUTHOR",
|
|
71
71
|
"AUTHOR_NAME",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { BotFramework } from "./botFramework";
|
|
3
3
|
import { BotPlatform } from "./botPlatform";
|
|
4
4
|
import { BotRuntime } from "./botRuntime";
|
|
5
5
|
import { parseServerkJson } from "./serverkJson";
|
|
6
6
|
|
|
7
7
|
describe("parseServerkJson", () => {
|
|
8
|
-
|
|
8
|
+
it("reads every field and ignores unknown ones", () => {
|
|
9
9
|
expect(
|
|
10
10
|
parseServerkJson(
|
|
11
11
|
JSON.stringify({
|
|
@@ -45,7 +45,7 @@ describe("parseServerkJson", () => {
|
|
|
45
45
|
});
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
it("accepts an empty object", () => {
|
|
49
49
|
expect(parseServerkJson("{}")).toEqual({
|
|
50
50
|
runtime: null,
|
|
51
51
|
platform: null,
|
|
@@ -56,7 +56,7 @@ describe("parseServerkJson", () => {
|
|
|
56
56
|
});
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
it("refuses a file with any field it cannot trust", () => {
|
|
60
60
|
for (const text of [
|
|
61
61
|
"[]",
|
|
62
62
|
"not json",
|
|
@@ -74,7 +74,7 @@ describe("parseServerkJson", () => {
|
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
it("counts a command's length in characters, not UTF-16 units", () => {
|
|
78
78
|
const command = `echo ${"🤖".repeat(1019)}`;
|
|
79
79
|
|
|
80
80
|
expect(
|