@serverkgg/bridge 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/package.json +3 -2
  2. package/rules/package.md +65 -0
  3. package/src/blog/articleFrontmatter.test.ts +10 -10
  4. package/src/blog/compile.test.ts +9 -9
  5. package/src/bot/botDeploy.test.ts +16 -16
  6. package/src/bot/botEnvironment.test.ts +8 -8
  7. package/src/bot/botIdentity.test.ts +12 -12
  8. package/src/bot/botPanel.test.ts +13 -13
  9. package/src/bot/botPanel.ts +4 -4
  10. package/src/bot/botState.test.ts +3 -3
  11. package/src/bot/botTerminal.test.ts +5 -5
  12. package/src/bot/botVariables.test.ts +8 -8
  13. package/src/bot/detect/botToken.test.ts +6 -6
  14. package/src/bot/detect/detect.test.ts +21 -21
  15. package/src/bot/detect/detectTable.test.ts +5 -5
  16. package/src/bot/detect/envExample.test.ts +2 -2
  17. package/src/bot/detect/envScan.test.ts +4 -4
  18. package/src/bot/detect/inferBotPlatform.test.ts +4 -4
  19. package/src/bot/detect/keys.test.ts +3 -3
  20. package/src/bot/detect/sensitiveKey.test.ts +5 -5
  21. package/src/bot/detect/serverkJson.test.ts +5 -5
  22. package/src/catalogs/curseforge.test.ts +35 -35
  23. package/src/catalogs/thunderstore.test.ts +9 -9
  24. package/src/cli/detectTable.test.ts +3 -3
  25. package/src/cli/link.test.ts +11 -11
  26. package/src/cli/main.ts +8 -0
  27. package/src/cli/rules.test.ts +198 -0
  28. package/src/cli/rules.ts +188 -0
  29. package/src/cli/validate.test.ts +179 -31
  30. package/src/cli/validate.ts +44 -0
  31. package/src/codecs/ini.test.ts +13 -13
  32. package/src/codecs/javaProperties.test.ts +11 -11
  33. package/src/codecs/sourceCfg.test.ts +14 -14
  34. package/src/codecs/xmlProperties.test.ts +10 -10
  35. package/src/core/bridgePort.test.ts +6 -6
  36. package/src/core/bridgeUserError.test.ts +4 -4
  37. package/src/core/bridgeValue.test.ts +5 -5
  38. package/src/core/describeDownloadFailure.test.ts +12 -12
  39. package/src/core/describeDownloadFailure.ts +10 -10
  40. package/src/guides/markdown.test.ts +31 -31
  41. package/src/install/installStamp.test.ts +9 -9
  42. package/src/manifest/extension.ts +36 -0
  43. package/src/manifest/index.ts +1 -0
  44. package/src/manifest/manifest.test.ts +208 -90
  45. package/src/manifest/manifest.ts +34 -0
  46. package/src/net/loopback.test.ts +10 -10
  47. package/src/presence/rosterSync.test.ts +7 -7
  48. package/src/probes/a2s.test.ts +5 -5
  49. package/src/probes/minecraftPing.test.ts +17 -17
  50. package/src/probes/raknetPing.test.ts +13 -13
  51. package/src/protocol/bridgePreview.ts +1 -0
  52. package/src/rcon/rconAccess.test.ts +20 -20
  53. package/src/rcon/rconAccess.ts +14 -14
  54. package/src/rcon/rconClient.test.ts +49 -49
  55. package/src/rcon/rconFrame.test.ts +14 -14
  56. package/src/rcon/rconIds.test.ts +4 -4
  57. package/src/rcon/rconWriter.test.ts +6 -6
  58. package/src/runtime/bridgeActivity.test.ts +15 -15
  59. package/src/runtime/bridgeBackupLease.test.ts +9 -9
  60. package/src/runtime/bridgeConfigReader.test.ts +5 -5
  61. package/src/runtime/bridgeConsoleHandler.test.ts +8 -8
  62. package/src/runtime/bridgeContext.test.ts +5 -5
  63. package/src/runtime/bridgeDriver.test.ts +94 -94
  64. package/src/runtime/bridgeEvents.test.ts +15 -15
  65. package/src/runtime/bridgeFiles.test.ts +54 -54
  66. package/src/runtime/bridgeInstall.test.ts +10 -10
  67. package/src/runtime/bridgeLiveConfig.test.ts +6 -6
  68. package/src/runtime/bridgePending.test.ts +6 -6
  69. package/src/runtime/bridgeRedaction.test.ts +16 -16
  70. package/src/runtime/bridgeRegistry.test.ts +84 -52
  71. package/src/runtime/bridgeRegistry.ts +2 -2
  72. package/src/runtime/bridgeServer.test.ts +31 -31
  73. package/src/runtime/bridgeSetup.test.ts +27 -27
  74. package/src/runtime/bridgeSetup.ts +1 -1
  75. package/src/runtime/bridgeSetupHandlers.test.ts +13 -13
  76. package/src/runtime/bridgeSignal.test.ts +7 -7
  77. package/src/runtime/bridgeSupervisor.test.ts +42 -42
  78. package/src/runtime/serve.ts +4 -4
  79. package/src/steam/avatars.test.ts +13 -13
  80. package/src/steam/steamApp.test.ts +8 -8
  81. package/src/steam/steamcmd.test.ts +17 -17
  82. package/src/steam/steamcmd.ts +4 -4
  83. package/src/steam/steamcmdNarrator.test.ts +4 -4
  84. package/src/steam/workshop.test.ts +5 -5
  85. package/src/utils/changedSettingKeys.test.ts +5 -5
  86. package/src/utils/describeExecFailure.test.ts +4 -4
  87. package/src/utils/describeExecFailure.ts +1 -1
  88. package/src/utils/sameSettingValue.test.ts +7 -7
  89. package/src/utils/token.test.ts +7 -7
  90. package/src/wine/protonCommand.test.ts +5 -5
  91. package/src/wine/wineCommand.test.ts +6 -6
  92. package/src/wine/winePrefix.test.ts +6 -6
  93. package/src/wire/activity.test.ts +8 -8
  94. package/src/wire/panel.test.ts +28 -28
@@ -1,4 +1,4 @@
1
- import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
1
+ import { afterEach, beforeEach, describe, expect, it, spyOn } from "bun:test";
2
2
  import { tmpdir } from "node:os";
3
3
  import { BridgeRuntime } from "../core";
4
4
  import { BridgeLifecycleSubject, BridgeStopSignal } from "../modules";
@@ -230,7 +230,7 @@ const wrapperArgv = () => {
230
230
  };
231
231
 
232
232
  describe("createSupervisor", () => {
233
- test("runs the game outside the bridge's own process group", async () => {
233
+ it("runs the game outside the bridge's own process group", async () => {
234
234
  const { supervisor, pid } = await startWrapper(WRAPPER, 1);
235
235
 
236
236
  try {
@@ -247,7 +247,7 @@ describe("createSupervisor", () => {
247
247
  expect(await waitGone(pid, 5000)).toBe(true);
248
248
  }, 20_000);
249
249
 
250
- test("takes the wrapper's child down with it when the stop times out", async () => {
250
+ it("takes the wrapper's child down with it when the stop times out", async () => {
251
251
  const { supervisor, pid } = await startWrapper(WRAPPER, 1);
252
252
 
253
253
  await supervisor.stop();
@@ -256,7 +256,7 @@ describe("createSupervisor", () => {
256
256
  expect(await waitGone(pid, 5000), "the game was orphaned when the wrapper was signalled").toBe(true);
257
257
  }, 20_000);
258
258
 
259
- test("kills the wrapper's child through the escalation when the wrapper ignores SIGTERM", async () => {
259
+ it("kills the wrapper's child through the escalation when the wrapper ignores SIGTERM", async () => {
260
260
  const { supervisor, pid } = await startWrapper(STUBBORN_WRAPPER, 1);
261
261
 
262
262
  await supervisor.stop();
@@ -265,7 +265,7 @@ describe("createSupervisor", () => {
265
265
  expect(await waitGone(pid, 5000), "the game outlived the SIGKILL escalation").toBe(true);
266
266
  }, 30_000);
267
267
 
268
- test("does not restart a game that exited before it ever reached its ready line", async () => {
268
+ it("does not restart a game that exited before it ever reached its ready line", async () => {
269
269
  const { supervisor, records } = crashSupervisor(() => BOOT_CRASH, NEVER_READY);
270
270
 
271
271
  await supervisor.start();
@@ -289,7 +289,7 @@ describe("createSupervisor", () => {
289
289
  expect(supervisor.restarts).toBe(0);
290
290
  }, 30_000);
291
291
 
292
- test("restarts a game that crashed after it reached its ready line", async () => {
292
+ it("restarts a game that crashed after it reached its ready line", async () => {
293
293
  const { supervisor, records } = crashSupervisor(() => RUNNING_CRASH, READY);
294
294
 
295
295
  await supervisor.start();
@@ -307,7 +307,7 @@ describe("createSupervisor", () => {
307
307
  await supervisor.stop();
308
308
  }, 30_000);
309
309
 
310
- test("flags a game killed with SIGKILL as out of memory", async () => {
310
+ it("flags a game killed with SIGKILL as out of memory", async () => {
311
311
  const { supervisor, records } = crashSupervisor(() => OOM_CRASH, NEVER_READY);
312
312
 
313
313
  await supervisor.start();
@@ -323,7 +323,7 @@ describe("createSupervisor", () => {
323
323
  await supervisor.stop();
324
324
  }, 30_000);
325
325
 
326
- test("trips the breaker on the third running crash inside the window", async () => {
326
+ it("trips the breaker on the third running crash inside the window", async () => {
327
327
  const { supervisor, records } = crashSupervisor(() => RUNNING_CRASH, READY);
328
328
 
329
329
  await supervisor.start();
@@ -346,7 +346,7 @@ describe("createSupervisor", () => {
346
346
  await supervisor.stop();
347
347
  }, 40_000);
348
348
 
349
- test("launches again when start is asked after a booting crash", async () => {
349
+ it("launches again when start is asked after a booting crash", async () => {
350
350
  let script = BOOT_CRASH;
351
351
 
352
352
  const { supervisor, records } = crashSupervisor(() => script, NEVER_READY);
@@ -367,7 +367,7 @@ describe("createSupervisor", () => {
367
367
  expect(supervisor.runtime).toBe(BridgeRuntime.Stopped);
368
368
  }, 30_000);
369
369
 
370
- test("carries the last line the game printed into the crash record", async () => {
370
+ it("carries the last line the game printed into the crash record", async () => {
371
371
  const { supervisor, records } = crashSupervisor(() => LAST_LINE_CRASH, NEVER_READY);
372
372
 
373
373
  await supervisor.start();
@@ -376,7 +376,7 @@ describe("createSupervisor", () => {
376
376
  expect(records.at(0)?.lastLine).toBe(LAST_LINE);
377
377
  }, 30_000);
378
378
 
379
- test("carries the last line even when the game exits with no pause after printing it", async () => {
379
+ it("carries the last line even when the game exits with no pause after printing it", async () => {
380
380
  const { supervisor, records } = crashSupervisor(() => TIGHT_LAST_LINE_CRASH, NEVER_READY);
381
381
 
382
382
  await supervisor.start();
@@ -386,7 +386,7 @@ describe("createSupervisor", () => {
386
386
  expect(records.at(0)?.exitCode).toBe(3);
387
387
  }, 30_000);
388
388
 
389
- test("leaves no dead child behind when the game exits the instant it launches", async () => {
389
+ it("leaves no dead child behind when the game exits the instant it launches", async () => {
390
390
  let adopted = 0;
391
391
 
392
392
  for (let attempt = 0; attempt < 40 && adopted < 20; attempt += 1) {
@@ -421,7 +421,7 @@ describe("createSupervisor", () => {
421
421
  expect(adopted, "the game never survived long enough to be adopted as the child").toBeGreaterThan(0);
422
422
  }, 30_000);
423
423
 
424
- test("treats a run that outlived the stable window as running even when the ready line never came", async () => {
424
+ it("treats a run that outlived the stable window as running even when the ready line never came", async () => {
425
425
  const { supervisor, records, stables } = timedSupervisor(() => LATE_CRASH, NEVER_READY, 300);
426
426
 
427
427
  await supervisor.start();
@@ -438,7 +438,7 @@ describe("createSupervisor", () => {
438
438
  await supervisor.stop();
439
439
  }, 30_000);
440
440
 
441
- test("keeps a slow boot starting past the stable window when the driver declares a ready timeout", async () => {
441
+ it("keeps a slow boot starting past the stable window when the driver declares a ready timeout", async () => {
442
442
  const { supervisor, records, stables } = timedSupervisor(() => LATE_CRASH, NEVER_READY, 300, 30);
443
443
 
444
444
  await supervisor.start();
@@ -457,7 +457,7 @@ describe("createSupervisor", () => {
457
457
  expect(stables()).toBe(0);
458
458
  }, 30_000);
459
459
 
460
- test("fails the boot when the declared ready timeout passes, never flipping it to running", async () => {
460
+ it("fails the boot when the declared ready timeout passes, never flipping it to running", async () => {
461
461
  const { supervisor, records, stables } = timedSupervisor(() => "exec sleep 300", NEVER_READY, 60_000, 1);
462
462
 
463
463
  await supervisor.start();
@@ -476,7 +476,7 @@ describe("createSupervisor", () => {
476
476
  expect(stables()).toBe(0);
477
477
  }, 30_000);
478
478
 
479
- test("still treats a game with no declared ready timeout as running once the default wait passes", async () => {
479
+ it("still treats a game with no declared ready timeout as running once the default wait passes", async () => {
480
480
  const { supervisor, stables } = timedSupervisor(() => "exec sleep 300", NEVER_READY, 60_000);
481
481
 
482
482
  await supervisor.start();
@@ -488,7 +488,7 @@ describe("createSupervisor", () => {
488
488
  await supervisor.stop();
489
489
  }, 30_000);
490
490
 
491
- test("opens a new episode when start is asked after the breaker tripped", async () => {
491
+ it("opens a new episode when start is asked after the breaker tripped", async () => {
492
492
  let script = RUNNING_CRASH;
493
493
 
494
494
  const { supervisor, records } = crashSupervisor(() => script, READY);
@@ -508,7 +508,7 @@ describe("createSupervisor", () => {
508
508
  await supervisor.stop();
509
509
  }, 40_000);
510
510
 
511
- test("spawns one game when two starts race inside the launch window", async () => {
511
+ it("spawns one game when two starts race inside the launch window", async () => {
512
512
  const { supervisor, pids, launches, overlapped } = racedSupervisor(200, wrapperArgv);
513
513
 
514
514
  await Promise.all([
@@ -529,7 +529,7 @@ describe("createSupervisor", () => {
529
529
  expect(await waitGone(pids.at(0) ?? 0, 5000)).toBe(true);
530
530
  }, 30_000);
531
531
 
532
- test("never spawns beside the game a stop is taking down", async () => {
532
+ it("never spawns beside the game a stop is taking down", async () => {
533
533
  const { supervisor, pids, launches, overlapped } = racedSupervisor(0, wrapperArgv);
534
534
 
535
535
  await supervisor.start();
@@ -555,7 +555,7 @@ describe("createSupervisor", () => {
555
555
  expect(await waitGone(pids.at(1) ?? 0, 5000)).toBe(true);
556
556
  }, 30_000);
557
557
 
558
- test("starts again after a launch that threw before the game was spawned", async () => {
558
+ it("starts again after a launch that threw before the game was spawned", async () => {
559
559
  let resolvable = false;
560
560
 
561
561
  const { supervisor, pids } = racedSupervisor(0, () => {
@@ -581,7 +581,7 @@ describe("createSupervisor", () => {
581
581
  expect(await waitGone(pids.at(0) ?? 0, 5000)).toBe(true);
582
582
  }, 30_000);
583
583
 
584
- test("stops a game that reports a failed boot instead of waiting for a ready line that never comes", async () => {
584
+ it("stops a game that reports a failed boot instead of waiting for a ready line that never comes", async () => {
585
585
  const records: BridgeCrashRecord[] = [];
586
586
  const pids: number[] = [];
587
587
 
@@ -628,7 +628,7 @@ describe("createSupervisor", () => {
628
628
  expect(await waitGone(pids.at(0) ?? 0, 5000)).toBe(true);
629
629
  }, 30_000);
630
630
 
631
- test("ignores the failed pattern once the game has been running long enough to count as stable", async () => {
631
+ it("ignores the failed pattern once the game has been running long enough to count as stable", async () => {
632
632
  const records: BridgeCrashRecord[] = [];
633
633
 
634
634
  const supervisor = createSupervisor({
@@ -713,7 +713,7 @@ const restartSupervisor = (script: () => string, resolveEnvironment?: () => Prom
713
713
  };
714
714
 
715
715
  describe("the resolved environment", () => {
716
- test("reaches the game beside what the bridge inherited", async () => {
716
+ it("reaches the game beside what the bridge inherited", async () => {
717
717
  const { supervisor } = restartSupervisor(
718
718
  () => 'echo "probe=$SERVERK_PROBE"; echo "path=$PATH"; echo ready; exec sleep 300',
719
719
  async () => ({
@@ -731,7 +731,7 @@ describe("the resolved environment", () => {
731
731
  await supervisor.stop();
732
732
  }, 20_000);
733
733
 
734
- test("leaves the inherited environment alone when the driver resolves none", async () => {
734
+ it("leaves the inherited environment alone when the driver resolves none", async () => {
735
735
  const { supervisor } = restartSupervisor(() => 'echo "path=$PATH"; echo ready; exec sleep 300');
736
736
  const path = supervisor.watch(/^path=(?<value>.*)$/, 10_000);
737
737
 
@@ -745,7 +745,7 @@ describe("the resolved environment", () => {
745
745
  await supervisor.stop();
746
746
  }, 20_000);
747
747
 
748
- test("refuses a key that is not a variable name as a start failure", async () => {
748
+ it("refuses a key that is not a variable name as a start failure", async () => {
749
749
  const { supervisor } = restartSupervisor(
750
750
  () => READY_THEN_WAIT,
751
751
  async () => ({
@@ -757,7 +757,7 @@ describe("the resolved environment", () => {
757
757
  expect(supervisor.running).toBe(false);
758
758
  });
759
759
 
760
- test("refuses a value carrying a NUL byte without echoing it", async () => {
760
+ it("refuses a value carrying a NUL byte without echoing it", async () => {
761
761
  const { supervisor } = restartSupervisor(
762
762
  () => READY_THEN_WAIT,
763
763
  async () => ({
@@ -776,7 +776,7 @@ describe("the resolved environment", () => {
776
776
  });
777
777
 
778
778
  describe("restart", () => {
779
- test("replaces the process without ever reporting it stopped", async () => {
779
+ it("replaces the process without ever reporting it stopped", async () => {
780
780
  const { supervisor, records, pids, stopped } = restartSupervisor(() => READY_THEN_WAIT);
781
781
 
782
782
  await supervisor.start();
@@ -819,7 +819,7 @@ describe("restart", () => {
819
819
  expect(stopped()).toBe(1);
820
820
  }, 30_000);
821
821
 
822
- test("counts a quick exit after a restart as a boot crash, not a running one", async () => {
822
+ it("counts a quick exit after a restart as a boot crash, not a running one", async () => {
823
823
  let script = READY_THEN_WAIT;
824
824
 
825
825
  const { supervisor, records, pids } = restartSupervisor(() => script);
@@ -848,7 +848,7 @@ describe("restart", () => {
848
848
  expect(supervisor.restarts).toBe(0);
849
849
  }, 30_000);
850
850
 
851
- test("refuses to restart a game that is meant to be stopped", async () => {
851
+ it("refuses to restart a game that is meant to be stopped", async () => {
852
852
  const { supervisor } = restartSupervisor(() => READY_THEN_WAIT);
853
853
 
854
854
  await expect(supervisor.restart()).rejects.toThrow("the game is not meant to be running");
@@ -907,7 +907,7 @@ const runningWith = async (supervisor: BridgeSupervisor, pids: number[], count:
907
907
  };
908
908
 
909
909
  describe("the stop signal", () => {
910
- test("stops a game that exits on SIGTERM well inside the timeout", async () => {
910
+ it("stops a game that exits on SIGTERM well inside the timeout", async () => {
911
911
  const { supervisor, pids } = signalSupervisor(SIGNAL_EXITS, 10, BridgeStopSignal.Term);
912
912
 
913
913
  await supervisor.start();
@@ -921,7 +921,7 @@ describe("the stop signal", () => {
921
921
  expect(await waitGone(pids[0] ?? 0, 5000)).toBe(true);
922
922
  }, 30_000);
923
923
 
924
- test("stops a game that exits on SIGINT when the driver asks for it", async () => {
924
+ it("stops a game that exits on SIGINT when the driver asks for it", async () => {
925
925
  const { supervisor, pids } = signalSupervisor(SIGNAL_EXITS, 10, BridgeStopSignal.Interrupt);
926
926
 
927
927
  await supervisor.start();
@@ -934,7 +934,7 @@ describe("the stop signal", () => {
934
934
  expect(await waitGone(pids[0] ?? 0, 5000)).toBe(true);
935
935
  }, 30_000);
936
936
 
937
- test("still kills a game that ignores the signal once the timeout passes", async () => {
937
+ it("still kills a game that ignores the signal once the timeout passes", async () => {
938
938
  const { supervisor, pids } = signalSupervisor(SIGNAL_IGNORED, 1, BridgeStopSignal.Term);
939
939
 
940
940
  await supervisor.start();
@@ -949,7 +949,7 @@ describe("the stop signal", () => {
949
949
  expect(await waitGone(pids[0] ?? 0, 5000), "the ignoring child outlived the kill").toBe(true);
950
950
  }, 30_000);
951
951
 
952
- test("keeps waiting the timeout before SIGTERM when the driver declares no signal", async () => {
952
+ it("keeps waiting the timeout before SIGTERM when the driver declares no signal", async () => {
953
953
  const { supervisor, pids } = signalSupervisor(SIGNAL_EXITS, 2);
954
954
 
955
955
  await supervisor.start();
@@ -963,7 +963,7 @@ describe("the stop signal", () => {
963
963
  expect(await waitGone(pids[0] ?? 0, 5000)).toBe(true);
964
964
  }, 30_000);
965
965
 
966
- test("restarts through the same signal-first path", async () => {
966
+ it("restarts through the same signal-first path", async () => {
967
967
  const { supervisor, pids } = signalSupervisor(SIGNAL_EXITS, 10, BridgeStopSignal.Term);
968
968
 
969
969
  await supervisor.start();
@@ -1056,7 +1056,7 @@ describe("secret redaction", () => {
1056
1056
  restore();
1057
1057
  });
1058
1058
 
1059
- test("hides a secret value from stdout, stderr, the stored lines and the crash last line", async () => {
1059
+ it("hides a secret value from stdout, stderr, the stored lines and the crash last line", async () => {
1060
1060
  const redact = createBridgeRedaction(
1061
1061
  [
1062
1062
  "BOT_TOKEN",
@@ -1081,7 +1081,7 @@ describe("secret redaction", () => {
1081
1081
  expect(written.join("")).toContain(REDACTED);
1082
1082
  }, 30_000);
1083
1083
 
1084
- test("hides a platform secret from the manifest that the game prints", async () => {
1084
+ it("hides a platform secret from the manifest that the game prints", async () => {
1085
1085
  const redact = createBridgeRedaction(
1086
1086
  [],
1087
1087
  variablesSource(
@@ -1105,7 +1105,7 @@ describe("secret redaction", () => {
1105
1105
  expect(records.at(0)?.lastLine).toBe(`fatal: bad token ${REDACTED}`);
1106
1106
  }, 30_000);
1107
1107
 
1108
- test("leaves a value shorter than eight characters in the lines", async () => {
1108
+ it("leaves a value shorter than eight characters in the lines", async () => {
1109
1109
  const redact = createBridgeRedaction(
1110
1110
  [
1111
1111
  "RCON_PASSWORD",
@@ -1122,7 +1122,7 @@ describe("secret redaction", () => {
1122
1122
  expect(records.at(0)?.lastLine).toBe(`fatal: bad token ${LEAKED}`);
1123
1123
  }, 30_000);
1124
1124
 
1125
- test("hides a value saved after the start from the lines that follow", async () => {
1125
+ it("hides a value saved after the start from the lines that follow", async () => {
1126
1126
  const { source, save } = variablesSource({});
1127
1127
  const redact = createBridgeRedaction(
1128
1128
  [
@@ -1152,7 +1152,7 @@ describe("secret redaction", () => {
1152
1152
  await supervisor.stop();
1153
1153
  }, 30_000);
1154
1154
 
1155
- test("runs a driver with no secret fields and no secrets without touching a line", async () => {
1155
+ it("runs a driver with no secret fields and no secrets without touching a line", async () => {
1156
1156
  const redact = createBridgeRedaction([], variablesSource({}).source);
1157
1157
 
1158
1158
  const { supervisor, records } = redactingSupervisor(LEAK_AND_CRASH, redact);
@@ -1220,7 +1220,7 @@ const subjectSupervisor = (script: string, subject?: BridgeLifecycleSubject) =>
1220
1220
  };
1221
1221
 
1222
1222
  describe("the lifecycle subject", () => {
1223
- test("names the game in the lifecycle lines when the driver declares no subject", async () => {
1223
+ it("names the game in the lifecycle lines when the driver declares no subject", async () => {
1224
1224
  const { supervisor, messages } = subjectSupervisor(READY_THEN_WAIT);
1225
1225
 
1226
1226
  await supervisor.start();
@@ -1236,7 +1236,7 @@ describe("the lifecycle subject", () => {
1236
1236
  ]);
1237
1237
  }, 20_000);
1238
1238
 
1239
- test("names the bot in every lifecycle line of a bot, the crash loop included", async () => {
1239
+ it("names the bot in every lifecycle line of a bot, the crash loop included", async () => {
1240
1240
  const { supervisor, messages, records } = subjectSupervisor(RUNNING_CRASH, BridgeLifecycleSubject.Bot);
1241
1241
 
1242
1242
  await supervisor.start();
@@ -1252,7 +1252,7 @@ describe("the lifecycle subject", () => {
1252
1252
  expect(messages.filter((message) => message.includes("game"))).toEqual([]);
1253
1253
  }, 40_000);
1254
1254
 
1255
- test("names the bot when it refuses an environment it cannot pass on", async () => {
1255
+ it("names the bot when it refuses an environment it cannot pass on", async () => {
1256
1256
  const { logger } = recordingLogger();
1257
1257
 
1258
1258
  const supervisor = createSupervisor({
@@ -41,12 +41,12 @@ const DEFAULT_SAMPLE_REFRESH_SECONDS = 30;
41
41
 
42
42
  const INSTALL_FAILED_TEXT: Record<BridgeLifecycleSubject, LocalizedText> = {
43
43
  [BridgeLifecycleSubject.Game]: {
44
- ar: "ما قدرنا نثبّت اللعبة. جرّب مرة ثانية.",
45
- en: "We could not install the game. Try again.",
44
+ ar: "تعذّر تثبيت اللعبة، جرب مرة ثانية.",
45
+ en: "We couldn't install the game. Try again.",
46
46
  },
47
47
  [BridgeLifecycleSubject.Bot]: {
48
- ar: "ما قدرنا نثبّت كود بوتك. جرّب مرة ثانية.",
49
- en: "We could not install your bot's code. Try again.",
48
+ ar: "تعذّر تثبيت كود بوتك، جرب مرة ثانية.",
49
+ en: "We couldn't install your bot's code. Try again.",
50
50
  },
51
51
  };
52
52
 
@@ -1,4 +1,4 @@
1
- import { afterEach, describe, expect, setSystemTime, test } from "bun:test";
1
+ import { afterEach, describe, expect, it, setSystemTime } from "bun:test";
2
2
  import type { BridgeContext } from "../context";
3
3
  import type { BridgeValues } from "../core";
4
4
  import { createSteamAvatars, STEAM_AVATAR_CACHE_TTL_MS, steamAvatarHashOf } from "./avatars";
@@ -79,22 +79,22 @@ afterEach(() => {
79
79
  });
80
80
 
81
81
  describe("reading the avatar hash out of a steam profile picture", () => {
82
- test("takes the hash out of the full-size url steam answers with", () => {
82
+ it("takes the hash out of the full-size url steam answers with", () => {
83
83
  expect(steamAvatarHashOf(`https://avatars.steamstatic.com/${ALICE_HASH}_full.jpg`)).toBe(ALICE_HASH);
84
84
  });
85
85
 
86
- test("takes the hash out of a url that carries no size suffix", () => {
86
+ it("takes the hash out of a url that carries no size suffix", () => {
87
87
  expect(steamAvatarHashOf(`https://avatars.steamstatic.com/${BOB_HASH}.jpg`)).toBe(BOB_HASH);
88
88
  });
89
89
 
90
- test("reports no hash for a url that is not a steam avatar", () => {
90
+ it("reports no hash for a url that is not a steam avatar", () => {
91
91
  expect(steamAvatarHashOf("https://example.com/avatar.png")).toBeNull();
92
92
  expect(steamAvatarHashOf("")).toBeNull();
93
93
  });
94
94
  });
95
95
 
96
96
  describe("keeping the steam avatars a roster shows", () => {
97
- test("asks steam for a hash it has never seen, and answers with it", async () => {
97
+ it("asks steam for a hash it has never seen, and answers with it", async () => {
98
98
  const steam = fakeSteam({
99
99
  answer: () => [
100
100
  summaryOf(ALICE, ALICE_HASH),
@@ -123,7 +123,7 @@ describe("keeping the steam avatars a roster shows", () => {
123
123
  ]);
124
124
  });
125
125
 
126
- test("answers the second time without asking steam again", async () => {
126
+ it("answers the second time without asking steam again", async () => {
127
127
  const steam = fakeSteam({
128
128
  answer: () => [
129
129
  summaryOf(ALICE, ALICE_HASH),
@@ -147,7 +147,7 @@ describe("keeping the steam avatars a roster shows", () => {
147
147
  expect(steam.calls.length).toBe(1);
148
148
  });
149
149
 
150
- test("asks only for the ids it is missing, and still answers for the cached one", async () => {
150
+ it("asks only for the ids it is missing, and still answers for the cached one", async () => {
151
151
  const steam = fakeSteam({
152
152
  answer: (steamIds) => {
153
153
  return steamIds.map((steamId) => {
@@ -175,7 +175,7 @@ describe("keeping the steam avatars a roster shows", () => {
175
175
  expect(hashes.get(BOB)).toBe(BOB_HASH);
176
176
  });
177
177
 
178
- test("asks steam again once the cached hash is six hours old", async () => {
178
+ it("asks steam again once the cached hash is six hours old", async () => {
179
179
  setSystemTime(new Date(0));
180
180
 
181
181
  const steam = fakeSteam({
@@ -211,7 +211,7 @@ describe("keeping the steam avatars a roster shows", () => {
211
211
  expect(steam.calls.length).toBe(2);
212
212
  });
213
213
 
214
- test("never asks steam about a player the game does not map to a steam id", async () => {
214
+ it("never asks steam about a player the game does not map to a steam id", async () => {
215
215
  const steam = fakeSteam({});
216
216
  const avatars = createSteamAvatars({
217
217
  steamIdOf: identity,
@@ -225,7 +225,7 @@ describe("keeping the steam avatars a roster shows", () => {
225
225
  expect(steam.calls).toEqual([]);
226
226
  });
227
227
 
228
- test("answers with nothing while the platform has no steam key", async () => {
228
+ it("answers with nothing while the platform has no steam key", async () => {
229
229
  const steam = fakeSteam({
230
230
  key: null,
231
231
  });
@@ -242,7 +242,7 @@ describe("keeping the steam avatars a roster shows", () => {
242
242
  expect(steam.warnings).toEqual([]);
243
243
  });
244
244
 
245
- test("answers with nothing and warns once when steam refuses", async () => {
245
+ it("answers with nothing and warns once when steam refuses", async () => {
246
246
  const steam = fakeSteam({
247
247
  answer: () => {
248
248
  throw new Error("429 too many requests");
@@ -266,7 +266,7 @@ describe("keeping the steam avatars a roster shows", () => {
266
266
  expect(steam.warnings.length).toBe(1);
267
267
  });
268
268
 
269
- test("never writes the api key into the warning it logs", async () => {
269
+ it("never writes the api key into the warning it logs", async () => {
270
270
  const steam = fakeSteam({
271
271
  key: "top-secret-key",
272
272
  answer: () => {
@@ -284,7 +284,7 @@ describe("keeping the steam avatars a roster shows", () => {
284
284
  expect(JSON.stringify(steam.warnings)).not.toContain("top-secret-key");
285
285
  });
286
286
 
287
- test("keeps the roster moving when steam sends a profile with no usable picture", async () => {
287
+ it("keeps the roster moving when steam sends a profile with no usable picture", async () => {
288
288
  const steam = fakeSteam({
289
289
  answer: () => [
290
290
  {
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { describe, expect, it } from "bun:test";
2
2
  import type { BridgeContext } from "../context";
3
3
  import { appManifestPath, buildIdOf, installedBuildId, missingGameRoots } from "./steamApp";
4
4
 
@@ -24,32 +24,32 @@ const testContext = (files: Record<string, string>) => {
24
24
  };
25
25
 
26
26
  describe("appManifestPath", () => {
27
- test("points at the steamapps manifest", () => {
27
+ it("points at the steamapps manifest", () => {
28
28
  expect(appManifestPath("2394010")).toBe("steamapps/appmanifest_2394010.acf");
29
29
  });
30
30
 
31
- test("accepts another steamapps directory", () => {
31
+ it("accepts another steamapps directory", () => {
32
32
  expect(appManifestPath("90", ".steam/steamapps")).toBe(".steam/steamapps/appmanifest_90.acf");
33
33
  });
34
34
  });
35
35
 
36
36
  describe("buildIdOf", () => {
37
- test("reads the build id out of an acf manifest", () => {
37
+ it("reads the build id out of an acf manifest", () => {
38
38
  expect(buildIdOf(MANIFEST)).toBe("19582821");
39
39
  });
40
40
 
41
- test("returns null when there is no build id", () => {
41
+ it("returns null when there is no build id", () => {
42
42
  expect(buildIdOf('"AppState"\n{\n}\n')).toBeNull();
43
43
  expect(buildIdOf("")).toBeNull();
44
44
  });
45
45
  });
46
46
 
47
47
  describe("installedBuildId", () => {
48
- test("returns null when the app was never installed", async () => {
48
+ it("returns null when the app was never installed", async () => {
49
49
  expect(await installedBuildId(testContext({}), "2394010")).toBeNull();
50
50
  });
51
51
 
52
- test("reads the installed build id", async () => {
52
+ it("reads the installed build id", async () => {
53
53
  const context = testContext({
54
54
  "steamapps/appmanifest_2394010.acf": MANIFEST,
55
55
  });
@@ -59,7 +59,7 @@ describe("installedBuildId", () => {
59
59
  });
60
60
 
61
61
  describe("missingGameRoots", () => {
62
- test("names only the paths that are absent", async () => {
62
+ it("names only the paths that are absent", async () => {
63
63
  const context = testContext({
64
64
  "PalServer.sh": "",
65
65
  Engine: "",
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { describe, expect, it } from "bun:test";
2
2
  import { steamcmdArgv, steamcmdFailureText, steamcmdScript, steamcmdUpdateArgs } from "./steamcmd";
3
3
  import { SteamcmdLoginKind, steamcmdLoginArgs } from "./steamcmdLogin";
4
4
  import { SteamcmdPlatform } from "./steamcmdPlatform";
@@ -9,13 +9,13 @@ const OPTIONS = {
9
9
  };
10
10
 
11
11
  describe("steamcmdLoginArgs", () => {
12
- test("logs in anonymously by default", () => {
12
+ it("logs in anonymously by default", () => {
13
13
  expect(steamcmdLoginArgs()).toEqual([
14
14
  "anonymous",
15
15
  ]);
16
16
  });
17
17
 
18
- test("passes the account credentials through", () => {
18
+ it("passes the account credentials through", () => {
19
19
  expect(
20
20
  steamcmdLoginArgs({
21
21
  kind: SteamcmdLoginKind.Account,
@@ -30,14 +30,14 @@ describe("steamcmdLoginArgs", () => {
30
30
  });
31
31
 
32
32
  describe("steamcmdScript", () => {
33
- test("lives inside the copied steamcmd directory", () => {
33
+ it("lives inside the copied steamcmd directory", () => {
34
34
  expect(steamcmdScript()).toBe(".steamcmd/steamcmd.sh");
35
35
  expect(steamcmdScript(".sc")).toBe(".sc/steamcmd.sh");
36
36
  });
37
37
  });
38
38
 
39
39
  describe("steamcmdArgv", () => {
40
- test("forces the install directory and logs in before the command", () => {
40
+ it("forces the install directory and logs in before the command", () => {
41
41
  expect(
42
42
  steamcmdArgv(OPTIONS, "/home/container", [
43
43
  "+app_update",
@@ -55,7 +55,7 @@ describe("steamcmdArgv", () => {
55
55
  ]);
56
56
  });
57
57
 
58
- test("forces the windows platform before the login", () => {
58
+ it("forces the windows platform before the login", () => {
59
59
  const argv = steamcmdArgv(
60
60
  {
61
61
  ...OPTIONS,
@@ -70,7 +70,7 @@ describe("steamcmdArgv", () => {
70
70
  expect(argv.indexOf("+@sSteamCmdForcePlatformType")).toBeLessThan(argv.indexOf("+login"));
71
71
  });
72
72
 
73
- test("leaves the platform alone on linux", () => {
73
+ it("leaves the platform alone on linux", () => {
74
74
  expect(
75
75
  steamcmdArgv(
76
76
  {
@@ -85,7 +85,7 @@ describe("steamcmdArgv", () => {
85
85
  });
86
86
 
87
87
  describe("steamcmdUpdateArgs", () => {
88
- test("updates without validating by default", () => {
88
+ it("updates without validating by default", () => {
89
89
  expect(
90
90
  steamcmdUpdateArgs("258550", {
91
91
  validate: false,
@@ -96,7 +96,7 @@ describe("steamcmdUpdateArgs", () => {
96
96
  ]);
97
97
  });
98
98
 
99
- test("adds validate last of the update flags", () => {
99
+ it("adds validate last of the update flags", () => {
100
100
  expect(
101
101
  steamcmdUpdateArgs("258550", {
102
102
  validate: true,
@@ -108,7 +108,7 @@ describe("steamcmdUpdateArgs", () => {
108
108
  ]);
109
109
  });
110
110
 
111
- test("passes a branch and its password", () => {
111
+ it("passes a branch and its password", () => {
112
112
  expect(
113
113
  steamcmdUpdateArgs("90", {
114
114
  validate: true,
@@ -126,7 +126,7 @@ describe("steamcmdUpdateArgs", () => {
126
126
  ]);
127
127
  });
128
128
 
129
- test("ignores a branch password without a branch", () => {
129
+ it("ignores a branch password without a branch", () => {
130
130
  expect(
131
131
  steamcmdUpdateArgs("90", {
132
132
  validate: false,
@@ -138,7 +138,7 @@ describe("steamcmdUpdateArgs", () => {
138
138
  ]);
139
139
  });
140
140
 
141
- test("appends the extra arguments", () => {
141
+ it("appends the extra arguments", () => {
142
142
  expect(
143
143
  steamcmdUpdateArgs("376030", {
144
144
  validate: false,
@@ -157,7 +157,7 @@ describe("steamcmdUpdateArgs", () => {
157
157
  });
158
158
 
159
159
  describe("steamcmdFailureText", () => {
160
- test("says the download account does not own the app when steam answers no subscription", () => {
160
+ it("says the download account does not own the app when steam answers no subscription", () => {
161
161
  expect(
162
162
  steamcmdFailureText(
163
163
  {
@@ -167,10 +167,10 @@ describe("steamcmdFailureText", () => {
167
167
  },
168
168
  "assetto",
169
169
  ).en,
170
- ).toBe("Steam refused to download assetto because the download account does not own it. Contact support.");
170
+ ).toBe("Steam refused to download assetto because the download account doesn't own it. Contact support.");
171
171
  });
172
172
 
173
- test("says the download failed for any other exit", () => {
173
+ it("says the download failed for any other exit", () => {
174
174
  expect(
175
175
  steamcmdFailureText(
176
176
  {
@@ -181,8 +181,8 @@ describe("steamcmdFailureText", () => {
181
181
  "palworld",
182
182
  ),
183
183
  ).toEqual({
184
- ar: "ما قدرنا ننزّل palworld من Steam. جرّب مرة ثانية.",
185
- en: "We could not download palworld from Steam. Try again.",
184
+ ar: "تعذّر تنزيل palworld من Steam، جرب مرة ثانية.",
185
+ en: "We couldn't download palworld from Steam. Try again.",
186
186
  });
187
187
  });
188
188
  });