config.test.ts 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import assert from "node:assert/strict";
  2. import { spawnSync } from "node:child_process";
  3. import { fileURLToPath } from "node:url";
  4. import test from "node:test";
  5. import {
  6. ConfigurationError,
  7. parseEndpoint,
  8. validateInvocation,
  9. } from "../src/config.ts";
  10. const repositoryRoot = fileURLToPath(new URL("..", import.meta.url));
  11. const executable = fileURLToPath(
  12. new URL("../bin/codex-app-server-bridge.js", import.meta.url),
  13. );
  14. test("only the exact app-server invocation is accepted", () => {
  15. validateInvocation(["app-server"]);
  16. for (const arguments_ of [[], ["help"], ["app-server", "extra"], ["--version"]]) {
  17. assert.throws(() => validateInvocation(arguments_), ConfigurationError);
  18. }
  19. });
  20. test("strict loopback endpoints are accepted", () => {
  21. assert.deepEqual(parseEndpoint("ws://127.0.0.1:4500"), {
  22. url: "ws://127.0.0.1:4500",
  23. display: "ws://127.0.0.1:4500",
  24. });
  25. assert.deepEqual(parseEndpoint("ws://[::1]:4500/"), {
  26. url: "ws://[::1]:4500",
  27. display: "ws://[::1]:4500",
  28. });
  29. });
  30. test("expanded, ambiguous, and invalid endpoints are rejected", () => {
  31. const rejected = [
  32. undefined,
  33. "",
  34. "ws://localhost:4500",
  35. "ws://127.0.0.1",
  36. "ws://127.0.0.1:0",
  37. "ws://127.0.0.1:65536",
  38. "ws://127.0.0.2:4500",
  39. "wss://127.0.0.1:4500",
  40. "http://127.0.0.1:4500",
  41. "ws://user:secret@127.0.0.1:4500",
  42. "ws://127.0.0.1:4500/path",
  43. "ws://127.0.0.1:4500/?query=value",
  44. "ws://127.0.0.1:4500/#fragment",
  45. ];
  46. for (const endpoint of rejected) {
  47. assert.throws(() => parseEndpoint(endpoint), ConfigurationError, String(endpoint));
  48. }
  49. });
  50. test("the executable reports the intentional unsupported Node error", () => {
  51. const result = spawnSync(process.execPath, [executable, "app-server"], {
  52. cwd: repositoryRoot,
  53. encoding: "utf8",
  54. env: {
  55. ...process.env,
  56. CODEX_APP_SERVER_BRIDGE_TEST_NODE_VERSION: "23.9.0",
  57. CODEX_APP_SERVER_URL: "ws://127.0.0.1:4500",
  58. },
  59. });
  60. assert.equal(result.status, 1);
  61. assert.equal(result.stdout, "");
  62. assert.match(result.stderr, /requires Node\.js 24 or newer/);
  63. });
  64. test("strict type checking rejects non-erasable TypeScript syntax", () => {
  65. const compiler = fileURLToPath(new URL("../node_modules/typescript/bin/tsc", import.meta.url));
  66. const fixture = fileURLToPath(new URL("fixtures/non-erasable.ts", import.meta.url));
  67. const result = spawnSync(
  68. process.execPath,
  69. [
  70. compiler,
  71. "--ignoreConfig",
  72. "--noEmit",
  73. "--strict",
  74. "--erasableSyntaxOnly",
  75. "--module",
  76. "NodeNext",
  77. "--moduleResolution",
  78. "NodeNext",
  79. "--target",
  80. "ES2024",
  81. fixture,
  82. ],
  83. { cwd: repositoryRoot, encoding: "utf8" },
  84. );
  85. assert.notEqual(result.status, 0);
  86. assert.match(`${result.stdout}${result.stderr}`, /erasableSyntaxOnly|not supported/i);
  87. });