git-package.test.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import assert from "node:assert/strict";
  2. import { execFileSync, spawnSync } from "node:child_process";
  3. import { chmod, mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
  4. import { tmpdir } from "node:os";
  5. import { join } from "node:path";
  6. import { pathToFileURL } from "node:url";
  7. import test from "node:test";
  8. const verifier = new URL("../scripts/verify-git-package.mjs", import.meta.url);
  9. function isolatedGitEnvironment() {
  10. const environment = { ...process.env };
  11. delete environment.GIT_INDEX_FILE;
  12. return environment;
  13. }
  14. function runVerifier(args: string[], cache?: string) {
  15. const environment = isolatedGitEnvironment();
  16. if (cache !== undefined) environment.CODEX_APP_SERVER_BRIDGE_NPM_CACHE = cache;
  17. return spawnSync(process.execPath, [verifier.pathname, ...args], {
  18. encoding: "utf8",
  19. env: environment,
  20. timeout: 60_000,
  21. });
  22. }
  23. test("Git-package verifier requires exactly one immutable package spec without leaking credentials", () => {
  24. const secret = "credential-must-not-appear";
  25. const invalid = [
  26. [],
  27. ["git+https://git.example.invalid/team/codex-app-server-bridge.git#main"],
  28. [`git+https://user:${secret}@git.example.invalid/team/codex-app-server-bridge.git#main`],
  29. ["git+https://git.example.invalid/team/codex-app-server-bridge.git#abc123"],
  30. [
  31. "git+https://git.example.invalid/team/codex-app-server-bridge.git#v0.1.1",
  32. "git+https://git.example.invalid/team/codex-app-server-bridge.git#v0.1.0",
  33. ],
  34. ];
  35. for (const args of invalid) {
  36. const result = runVerifier(args);
  37. assert.notEqual(result.status, 0);
  38. assert.equal(result.stderr.includes(secret), false);
  39. assert.match(result.stderr, /Git-package verification failed:/);
  40. }
  41. });
  42. test("Git-package verifier installs and launches a disposable prebuilt Git revision", async () => {
  43. const root = await mkdtemp(join(tmpdir(), "codex-app-server-bridge-git-test-"));
  44. const repository = join(root, "codex-app-server-bridge.git");
  45. const cache = join(root, "npm-cache");
  46. try {
  47. await Promise.all([
  48. mkdir(join(repository, "bin"), { recursive: true }),
  49. mkdir(join(repository, "dist"), { recursive: true }),
  50. ]);
  51. await writeFile(join(repository, "package.json"), `${JSON.stringify({
  52. name: "codex-app-server-bridge",
  53. version: "9.9.9",
  54. private: true,
  55. type: "module",
  56. bin: { "codex-app-server-bridge": "bin/codex-app-server-bridge.js" },
  57. files: ["bin/", "dist/cli.js", "dist/config.js"],
  58. engines: { node: ">=24" },
  59. }, null, 2)}\n`);
  60. const launcher = `#!/usr/bin/env node
  61. if (process.argv.slice(2).join(" ") !== "app-server") {
  62. process.stderr.write("codex-app-server-bridge: Usage: codex-app-server-bridge app-server\\n");
  63. process.exitCode = 1;
  64. }
  65. `;
  66. const executable = join(repository, "bin", "codex-app-server-bridge.js");
  67. await writeFile(executable, launcher);
  68. await chmod(executable, 0o755);
  69. await writeFile(join(repository, "dist", "cli.js"), "export async function runBridge() { return 1; }\n");
  70. await writeFile(join(repository, "dist", "config.js"), "export const USAGE = 'fixture';\n");
  71. const gitEnvironment = isolatedGitEnvironment();
  72. execFileSync("git", ["init", "--quiet"], { cwd: repository, env: gitEnvironment });
  73. execFileSync("git", ["config", "user.name", "Codex Test"], { cwd: repository, env: gitEnvironment });
  74. execFileSync("git", ["config", "user.email", "codex-test@example.invalid"], { cwd: repository, env: gitEnvironment });
  75. execFileSync("git", ["add", "."], { cwd: repository, env: gitEnvironment });
  76. execFileSync("git", ["commit", "--quiet", "-m", "prebuilt fixture"], { cwd: repository, env: gitEnvironment });
  77. const revision = execFileSync("git", ["rev-parse", "HEAD"], {
  78. cwd: repository,
  79. encoding: "utf8",
  80. env: gitEnvironment,
  81. }).trim();
  82. const packageSpec = `git+${pathToFileURL(repository).href}#${revision}`;
  83. const result = runVerifier([packageSpec], cache);
  84. assert.equal(result.status, 0, result.stderr);
  85. const report = JSON.parse(result.stdout) as {
  86. ok: boolean;
  87. revision: string;
  88. storage: string;
  89. invocation: string;
  90. };
  91. assert.equal(report.ok, true);
  92. assert.equal(report.revision, revision);
  93. assert.equal(report.storage, "isolated-prefix");
  94. assert.equal(report.invocation, "verified");
  95. } finally {
  96. await rm(root, { recursive: true, force: true });
  97. }
  98. });