@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/codecs/ini.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { mergeIni, parseIni } from "./ini";
|
|
3
3
|
|
|
4
4
|
const SAMPLE = [
|
|
@@ -14,38 +14,38 @@ const SAMPLE = [
|
|
|
14
14
|
].join("\n");
|
|
15
15
|
|
|
16
16
|
describe("parseIni", () => {
|
|
17
|
-
|
|
17
|
+
it("reads the keys of one section", () => {
|
|
18
18
|
expect(parseIni(SAMPLE, "Server")).toEqual({
|
|
19
19
|
name: "serverk",
|
|
20
20
|
maxPlayers: "10",
|
|
21
21
|
});
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
it("reads another section without leaking keys", () => {
|
|
25
25
|
expect(parseIni(SAMPLE, "Admin")).toEqual({
|
|
26
26
|
password: "secret",
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
it("reads the keys before the first header when no section is asked for", () => {
|
|
31
31
|
expect(parseIni("welcome=hello\n[Server]\nname=x\n")).toEqual({
|
|
32
32
|
welcome: "hello",
|
|
33
33
|
});
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
it("keeps the last of a repeated key", () => {
|
|
37
37
|
expect(parseIni("[S]\nmod=a\nmod=b\n", "S")).toEqual({
|
|
38
38
|
mod: "b",
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
it("returns nothing for an unknown section", () => {
|
|
43
43
|
expect(parseIni(SAMPLE, "Nothing")).toEqual({});
|
|
44
44
|
});
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
describe("mergeIni", () => {
|
|
48
|
-
|
|
48
|
+
it("rewrites a value in place and keeps comments and order", () => {
|
|
49
49
|
expect(
|
|
50
50
|
mergeIni(
|
|
51
51
|
SAMPLE,
|
|
@@ -69,7 +69,7 @@ describe("mergeIni", () => {
|
|
|
69
69
|
);
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
it("appends an unknown key at the end of its section", () => {
|
|
73
73
|
expect(
|
|
74
74
|
mergeIni(
|
|
75
75
|
SAMPLE,
|
|
@@ -93,7 +93,7 @@ describe("mergeIni", () => {
|
|
|
93
93
|
);
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
it("creates a section that does not exist yet", () => {
|
|
97
97
|
expect(
|
|
98
98
|
mergeIni(
|
|
99
99
|
"[Server]\nname=x\n",
|
|
@@ -105,7 +105,7 @@ describe("mergeIni", () => {
|
|
|
105
105
|
).toBe("[Server]\nname=x\n\n[Game]\nlevel=3\n");
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
it("drops repeated occurrences of a key it rewrites", () => {
|
|
109
109
|
expect(
|
|
110
110
|
mergeIni(
|
|
111
111
|
"[S]\nmod=a\nmod=b\nother=1\n",
|
|
@@ -117,7 +117,7 @@ describe("mergeIni", () => {
|
|
|
117
117
|
).toBe("[S]\nmod=c\nother=1\n");
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
it("writes into an empty file", () => {
|
|
121
121
|
expect(
|
|
122
122
|
mergeIni(
|
|
123
123
|
"",
|
|
@@ -129,7 +129,7 @@ describe("mergeIni", () => {
|
|
|
129
129
|
).toBe("[Server]\nname=x\n");
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
it("writes preamble keys when no section is asked for", () => {
|
|
133
133
|
expect(
|
|
134
134
|
mergeIni("welcome=hello\n[Server]\nname=x\n", {
|
|
135
135
|
welcome: "salam",
|
|
@@ -138,7 +138,7 @@ describe("mergeIni", () => {
|
|
|
138
138
|
).toBe("welcome=salam\nextra=1\n[Server]\nname=x\n");
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
it("leaves other sections untouched", () => {
|
|
142
142
|
expect(
|
|
143
143
|
mergeIni(
|
|
144
144
|
"[A]\nkey=1\n[B]\nkey=2\n",
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { escapeJavaProperty, mergeJavaProperties, parseJavaProperties, unescapeJavaProperty } from "./javaProperties";
|
|
3
3
|
|
|
4
4
|
const ARABIC = "سيرفرك";
|
|
5
5
|
|
|
6
6
|
describe("parseJavaProperties", () => {
|
|
7
|
-
|
|
7
|
+
it("decodes the unicode escapes the java server writes", () => {
|
|
8
8
|
expect(parseJavaProperties("motd=\\u0633\\u064A\\u0631\\u0641\\u0631\\u0643\n")).toEqual({
|
|
9
9
|
motd: ARABIC,
|
|
10
10
|
});
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
it("decodes backslash escapes in values", () => {
|
|
14
14
|
expect(parseJavaProperties("resource-pack=https\\://example.com/pack.zip\nmotd=a\\tb\\\\c\n")).toEqual({
|
|
15
15
|
"resource-pack": "https://example.com/pack.zip",
|
|
16
16
|
motd: "a\tb\\c",
|
|
17
17
|
});
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
it("repairs a value the server stored as latin-1 bytes of utf-8", () => {
|
|
21
21
|
expect(parseJavaProperties("motd=ddss \\u00D9\\u008A\\u00D8\\u00AA\n")).toEqual({
|
|
22
22
|
motd: "ddss يت",
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
it("keeps plain latin-1 text that is not utf-8", () => {
|
|
27
27
|
expect(unescapeJavaProperty("caf\\u00E9")).toBe("café");
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
it("keeps comments and plain values", () => {
|
|
31
31
|
expect(parseJavaProperties("#Minecraft server properties\nmax-players=20\n")).toEqual({
|
|
32
32
|
"max-players": "20",
|
|
33
33
|
});
|
|
@@ -35,7 +35,7 @@ describe("parseJavaProperties", () => {
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
describe("mergeJavaProperties", () => {
|
|
38
|
-
|
|
38
|
+
it("writes non ascii text as unicode escapes", () => {
|
|
39
39
|
expect(
|
|
40
40
|
mergeJavaProperties("motd=A Minecraft Server\nmax-players=20\n", {
|
|
41
41
|
motd: ARABIC,
|
|
@@ -43,21 +43,21 @@ describe("mergeJavaProperties", () => {
|
|
|
43
43
|
).toBe("motd=\\u0633\\u064A\\u0631\\u0641\\u0631\\u0643\nmax-players=20\n");
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
it("escapes the characters java treats as separators", () => {
|
|
47
47
|
expect(escapeJavaProperty("https://a.b#c=d!e")).toBe("https\\://a.b\\#c\\=d\\!e");
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
it("escapes a leading space and control characters", () => {
|
|
51
51
|
expect(escapeJavaProperty(" a\tb\\c")).toBe("\\ a\\tb\\\\c");
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
it("round trips astral characters", () => {
|
|
55
55
|
const value = "hi 🎮";
|
|
56
56
|
|
|
57
57
|
expect(unescapeJavaProperty(escapeJavaProperty(value))).toBe(value);
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
it("appends a key the file does not have yet", () => {
|
|
61
61
|
expect(
|
|
62
62
|
mergeJavaProperties("", {
|
|
63
63
|
motd: "hello",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
decodeSourceCfgValue,
|
|
4
4
|
encodeSourceCfgValue,
|
|
@@ -8,21 +8,21 @@ import {
|
|
|
8
8
|
} from "./sourceCfg";
|
|
9
9
|
|
|
10
10
|
describe("readSourceCfgEntry", () => {
|
|
11
|
-
|
|
11
|
+
it("splits a key from its value", () => {
|
|
12
12
|
expect(readSourceCfgEntry('hostname "serverk"')).toEqual({
|
|
13
13
|
key: "hostname",
|
|
14
14
|
value: '"serverk"',
|
|
15
15
|
});
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
it("accepts a key with no value", () => {
|
|
19
19
|
expect(readSourceCfgEntry("sv_lan")).toEqual({
|
|
20
20
|
key: "sv_lan",
|
|
21
21
|
value: "",
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
it("ignores comments and blank lines", () => {
|
|
26
26
|
expect(readSourceCfgEntry("// a comment")).toBeNull();
|
|
27
27
|
expect(readSourceCfgEntry("# a comment")).toBeNull();
|
|
28
28
|
expect(readSourceCfgEntry(" ")).toBeNull();
|
|
@@ -31,32 +31,32 @@ describe("readSourceCfgEntry", () => {
|
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
describe("decodeSourceCfgValue", () => {
|
|
34
|
-
|
|
34
|
+
it("unquotes a string and unescapes it", () => {
|
|
35
35
|
expect(decodeSourceCfgValue('"a \\"b\\" c"')).toBe('a "b" c');
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
it("reads booleans and numbers", () => {
|
|
39
39
|
expect(decodeSourceCfgValue("true")).toBe(true);
|
|
40
40
|
expect(decodeSourceCfgValue("false")).toBe(false);
|
|
41
41
|
expect(decodeSourceCfgValue("42")).toBe(42);
|
|
42
42
|
expect(decodeSourceCfgValue("-1.5")).toBe(-1.5);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
it("leaves a bare word alone", () => {
|
|
46
46
|
expect(decodeSourceCfgValue("de_dust2")).toBe("de_dust2");
|
|
47
47
|
});
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
describe("encodeSourceCfgValue", () => {
|
|
51
|
-
|
|
51
|
+
it("quotes and escapes text", () => {
|
|
52
52
|
expect(encodeSourceCfgValue('a "b"')).toBe('"a \\"b\\""');
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
it("flattens newlines", () => {
|
|
56
56
|
expect(encodeSourceCfgValue("a\nb")).toBe('"a b"');
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
it("writes booleans and numbers bare", () => {
|
|
60
60
|
expect(encodeSourceCfgValue(true)).toBe("true");
|
|
61
61
|
expect(encodeSourceCfgValue(7)).toBe("7");
|
|
62
62
|
expect(encodeSourceCfgValue(null)).toBe('""');
|
|
@@ -64,7 +64,7 @@ describe("encodeSourceCfgValue", () => {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
describe("parseSourceCfg", () => {
|
|
67
|
-
|
|
67
|
+
it("reads a source cfg file", () => {
|
|
68
68
|
expect(parseSourceCfg('// header\nhostname "serverk"\nsv_maxplayers 24\nsv_cheats false\n')).toEqual({
|
|
69
69
|
hostname: "serverk",
|
|
70
70
|
sv_maxplayers: 24,
|
|
@@ -74,7 +74,7 @@ describe("parseSourceCfg", () => {
|
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
describe("mergeSourceCfg", () => {
|
|
77
|
-
|
|
77
|
+
it("rewrites a value and keeps the comments", () => {
|
|
78
78
|
expect(
|
|
79
79
|
mergeSourceCfg('// header\nhostname "old"\nsv_maxplayers 24\n', {
|
|
80
80
|
hostname: "new",
|
|
@@ -82,7 +82,7 @@ describe("mergeSourceCfg", () => {
|
|
|
82
82
|
).toBe('// header\nhostname "new"\nsv_maxplayers 24\n');
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
it("appends an unknown key", () => {
|
|
86
86
|
expect(
|
|
87
87
|
mergeSourceCfg('hostname "x"\n', {
|
|
88
88
|
rcon_password: "abc",
|
|
@@ -90,7 +90,7 @@ describe("mergeSourceCfg", () => {
|
|
|
90
90
|
).toBe('hostname "x"\nrcon_password "abc"\n');
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
it("collapses a repeated key it rewrites", () => {
|
|
94
94
|
expect(
|
|
95
95
|
mergeSourceCfg("map de_dust2\nmap de_nuke\n", {
|
|
96
96
|
map: "de_inferno",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { mergeXmlProperties, parseXmlProperties } from "./xmlProperties";
|
|
3
3
|
|
|
4
4
|
const SAMPLE = [
|
|
@@ -13,7 +13,7 @@ const SAMPLE = [
|
|
|
13
13
|
].join("\n");
|
|
14
14
|
|
|
15
15
|
describe("parseXmlProperties", () => {
|
|
16
|
-
|
|
16
|
+
it("reads every property whichever way the attributes are ordered", () => {
|
|
17
17
|
expect(parseXmlProperties(SAMPLE)).toEqual({
|
|
18
18
|
ServerName: "serverk",
|
|
19
19
|
ServerPort: "26900",
|
|
@@ -21,25 +21,25 @@ describe("parseXmlProperties", () => {
|
|
|
21
21
|
});
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
it("decodes entities", () => {
|
|
25
25
|
expect(parseXmlProperties('<property name="ServerName" value="a & b <c>"/>')).toEqual({
|
|
26
26
|
ServerName: "a & b <c>",
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
it("accepts single quoted attributes and an empty value", () => {
|
|
31
31
|
expect(parseXmlProperties("<property name='ServerPassword' value=''/>")).toEqual({
|
|
32
32
|
ServerPassword: "",
|
|
33
33
|
});
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
it("ignores everything that is not a property", () => {
|
|
37
37
|
expect(parseXmlProperties('<ServerSettings>\n<other name="x" value="1"/>\n</ServerSettings>')).toEqual({});
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
describe("mergeXmlProperties", () => {
|
|
42
|
-
|
|
42
|
+
it("rewrites a value in place and keeps order, comments and the header", () => {
|
|
43
43
|
expect(
|
|
44
44
|
mergeXmlProperties(SAMPLE, {
|
|
45
45
|
ServerPort: "26950",
|
|
@@ -58,7 +58,7 @@ describe("mergeXmlProperties", () => {
|
|
|
58
58
|
);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
it("rewrites a property whose value attribute comes first", () => {
|
|
62
62
|
expect(
|
|
63
63
|
mergeXmlProperties(SAMPLE, {
|
|
64
64
|
ServerMaxPlayerCount: "16",
|
|
@@ -66,7 +66,7 @@ describe("mergeXmlProperties", () => {
|
|
|
66
66
|
).toContain('<property value="16" name="ServerMaxPlayerCount"/>');
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
it("adds an unknown property before the closing tag with the same indent", () => {
|
|
70
70
|
expect(
|
|
71
71
|
mergeXmlProperties(SAMPLE, {
|
|
72
72
|
ServerPassword: "1234",
|
|
@@ -86,7 +86,7 @@ describe("mergeXmlProperties", () => {
|
|
|
86
86
|
);
|
|
87
87
|
});
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
it("escapes a value it writes", () => {
|
|
90
90
|
expect(
|
|
91
91
|
mergeXmlProperties(SAMPLE, {
|
|
92
92
|
ServerName: 'a & b "c"',
|
|
@@ -94,7 +94,7 @@ describe("mergeXmlProperties", () => {
|
|
|
94
94
|
).toContain('<property name="ServerName" value="a & b "c""/>');
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
it("adds a value attribute to a property that has none", () => {
|
|
98
98
|
expect(
|
|
99
99
|
mergeXmlProperties('<property name="ServerName"/>', {
|
|
100
100
|
ServerName: "x",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { PortProtocol, portCountOf, SOCKET_PROTOCOLS, socketProtocolsOf } from "./bridgePort";
|
|
3
3
|
|
|
4
4
|
describe("socketProtocolsOf", () => {
|
|
5
|
-
|
|
5
|
+
it("keeps a single socket protocol as it is", () => {
|
|
6
6
|
expect(socketProtocolsOf(PortProtocol.Tcp)).toEqual([
|
|
7
7
|
PortProtocol.Tcp,
|
|
8
8
|
]);
|
|
@@ -11,14 +11,14 @@ describe("socketProtocolsOf", () => {
|
|
|
11
11
|
]);
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
it("expands a pair into both socket protocols", () => {
|
|
15
15
|
expect(socketProtocolsOf(PortProtocol.TcpUdp)).toEqual([
|
|
16
16
|
PortProtocol.Tcp,
|
|
17
17
|
PortProtocol.Udp,
|
|
18
18
|
]);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
it("never hands out the shared list itself", () => {
|
|
22
22
|
const expanded = socketProtocolsOf(PortProtocol.TcpUdp);
|
|
23
23
|
|
|
24
24
|
expanded.pop();
|
|
@@ -31,11 +31,11 @@ describe("socketProtocolsOf", () => {
|
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
describe("portCountOf", () => {
|
|
34
|
-
|
|
34
|
+
it("reads a port stored before blocks existed as a single port", () => {
|
|
35
35
|
expect(portCountOf({})).toBe(1);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
it("reads the count of a block", () => {
|
|
39
39
|
expect(
|
|
40
40
|
portCountOf({
|
|
41
41
|
count: 32,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { Bridge } from "../modules";
|
|
3
3
|
import { BridgeUserError } from "./bridgeUserError";
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ describe("BridgeUserError", () => {
|
|
|
8
8
|
en: "this is not a file we are waiting for",
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
it("reads as english in a log line and keeps both languages for the player", () => {
|
|
12
12
|
const error = new BridgeUserError(text);
|
|
13
13
|
|
|
14
14
|
expect(error).toBeInstanceOf(Error);
|
|
@@ -17,7 +17,7 @@ describe("BridgeUserError", () => {
|
|
|
17
17
|
expect(error.text).toEqual(text);
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
it("keeps the cause a driver attaches for the staff log", () => {
|
|
21
21
|
const cause = new Error("cdn.modrinth.com answered 404");
|
|
22
22
|
const error = new BridgeUserError(text, {
|
|
23
23
|
cause,
|
|
@@ -27,7 +27,7 @@ describe("BridgeUserError", () => {
|
|
|
27
27
|
expect(error.message).toBe(text.en);
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
it("is the class a driver reaches through the published namespace", () => {
|
|
31
31
|
expect(new Bridge.UserError(text)).toBeInstanceOf(BridgeUserError);
|
|
32
32
|
});
|
|
33
33
|
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import type { BridgeRow } from "./bridgeValue";
|
|
3
3
|
import { isLocalizedText } from "./bridgeValue";
|
|
4
4
|
|
|
5
5
|
describe("isLocalizedText", () => {
|
|
6
|
-
|
|
6
|
+
it("recognises the shape a driver authors copy in", () => {
|
|
7
7
|
expect(
|
|
8
8
|
isLocalizedText({
|
|
9
9
|
ar: "التركيب",
|
|
@@ -12,7 +12,7 @@ describe("isLocalizedText", () => {
|
|
|
12
12
|
).toBe(true);
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
it("leaves every other bridge value alone", () => {
|
|
16
16
|
expect(isLocalizedText("Hytale")).toBe(false);
|
|
17
17
|
expect(isLocalizedText(12)).toBe(false);
|
|
18
18
|
expect(isLocalizedText(true)).toBe(false);
|
|
@@ -20,7 +20,7 @@ describe("isLocalizedText", () => {
|
|
|
20
20
|
expect(isLocalizedText(undefined)).toBe(false);
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
it("refuses a half-written text rather than rendering an empty language", () => {
|
|
24
24
|
expect(
|
|
25
25
|
isLocalizedText({
|
|
26
26
|
en: "Setup",
|
|
@@ -30,7 +30,7 @@ describe("isLocalizedText", () => {
|
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
describe("BridgeRow", () => {
|
|
33
|
-
|
|
33
|
+
it('carries a localized cell beside its scalars so a driver never writes "English / عربي" into one string', () => {
|
|
34
34
|
const row: BridgeRow = {
|
|
35
35
|
id: "u-1",
|
|
36
36
|
kills: 12,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { BridgeDownloadError } from "./bridgeDownloadError";
|
|
3
3
|
import { BridgeNetError } from "./bridgeNetError";
|
|
4
4
|
import { describeDownloadFailure } from "./describeDownloadFailure";
|
|
@@ -6,7 +6,7 @@ import { describeDownloadFailure } from "./describeDownloadFailure";
|
|
|
6
6
|
const SUBJECT = "All the Mods 9";
|
|
7
7
|
|
|
8
8
|
describe("describeDownloadFailure", () => {
|
|
9
|
-
|
|
9
|
+
it("says the file is gone for 404 and 410", () => {
|
|
10
10
|
for (const status of [
|
|
11
11
|
404,
|
|
12
12
|
410,
|
|
@@ -17,39 +17,39 @@ describe("describeDownloadFailure", () => {
|
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
it("says the source refused for 401 and 403", () => {
|
|
21
21
|
expect(describeDownloadFailure(new BridgeNetError(403, "forbidden"), SUBJECT).en).toBe(
|
|
22
22
|
"The source refused the download of All the Mods 9.",
|
|
23
23
|
);
|
|
24
24
|
expect(describeDownloadFailure(new BridgeDownloadError(401, "unauthorized"), SUBJECT).ar).toBe(
|
|
25
|
-
"المصدر
|
|
25
|
+
"رفض المصدر تنزيل All the Mods 9.",
|
|
26
26
|
);
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
it("says the source is busy for 429", () => {
|
|
30
30
|
expect(describeDownloadFailure(new BridgeDownloadError(429, "slow down"), SUBJECT).en).toContain("busy");
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
it("says the source is down for a 5xx", () => {
|
|
34
34
|
expect(describeDownloadFailure(new BridgeNetError(503, "unavailable"), SUBJECT).en).toBe(
|
|
35
|
-
"The source of All the Mods 9 is down right now. Try again in a
|
|
35
|
+
"The source of All the Mods 9 is down right now. Try again in a moment.",
|
|
36
36
|
);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
it("says the source could not be reached when there is no status", () => {
|
|
40
40
|
expect(describeDownloadFailure(new BridgeDownloadError(null, "ECONNRESET"), SUBJECT).en).toBe(
|
|
41
|
-
"We
|
|
41
|
+
"We couldn't reach the source to download All the Mods 9. Try again.",
|
|
42
42
|
);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
const generic = "We
|
|
45
|
+
it("falls back to a plain download failure for anything else", () => {
|
|
46
|
+
const generic = "We couldn't download All the Mods 9. Try again.";
|
|
47
47
|
|
|
48
48
|
expect(describeDownloadFailure(new BridgeDownloadError(400, "bad request"), SUBJECT).en).toBe(generic);
|
|
49
49
|
expect(describeDownloadFailure(new Error("digest mismatch"), SUBJECT).en).toBe(generic);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
it("always writes both languages", () => {
|
|
53
53
|
const text = describeDownloadFailure(new BridgeDownloadError(500, "boom"), SUBJECT);
|
|
54
54
|
|
|
55
55
|
expect(text.ar.length).toBeGreaterThan(0);
|
|
@@ -29,41 +29,41 @@ export const describeDownloadFailure = (error: unknown, subject: string): Locali
|
|
|
29
29
|
|
|
30
30
|
if (status === null) {
|
|
31
31
|
return {
|
|
32
|
-
ar:
|
|
33
|
-
en: `We
|
|
32
|
+
ar: `تعذّر الوصول إلى المصدر لتنزيل ${subject}، جرب مرة ثانية.`,
|
|
33
|
+
en: `We couldn't reach the source to download ${subject}. Try again.`,
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
if (status !== undefined && REMOVED_STATUSES.includes(status)) {
|
|
38
38
|
return {
|
|
39
|
-
ar: `${subject}
|
|
39
|
+
ar: `${subject} لم يعد متوفرًا لدى المصدر.`,
|
|
40
40
|
en: `${subject} is no longer available from its source.`,
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
if (status !== undefined && REFUSED_STATUSES.includes(status)) {
|
|
45
45
|
return {
|
|
46
|
-
ar:
|
|
46
|
+
ar: `رفض المصدر تنزيل ${subject}.`,
|
|
47
47
|
en: `The source refused the download of ${subject}.`,
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
if (status === BUSY_STATUS) {
|
|
52
52
|
return {
|
|
53
|
-
ar: `المصدر مشغول
|
|
54
|
-
en: `The source is busy right now, so we
|
|
53
|
+
ar: `المصدر مشغول حاليًا وتعذّر تنزيل ${subject}، عطه لحظة وجرب مرة ثانية.`,
|
|
54
|
+
en: `The source is busy right now, so we couldn't download ${subject}. Give it a moment and try again.`,
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
if (status !== undefined && status >= SERVER_ERROR_STATUS) {
|
|
59
59
|
return {
|
|
60
|
-
ar:
|
|
61
|
-
en: `The source of ${subject} is down right now. Try again in a
|
|
60
|
+
ar: `يواجه مصدر ${subject} عطلًا حاليًا، جرب مرة ثانية بعد لحظة.`,
|
|
61
|
+
en: `The source of ${subject} is down right now. Try again in a moment.`,
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
return {
|
|
66
|
-
ar:
|
|
67
|
-
en: `We
|
|
66
|
+
ar: `تعذّر تنزيل ${subject}، جرب مرة ثانية.`,
|
|
67
|
+
en: `We couldn't download ${subject}. Try again.`,
|
|
68
68
|
};
|
|
69
69
|
};
|