Ctrl K
ComunidadGuías…

utils/gitAuthServer.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { existsSync, mkdtempSync, readFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { type GitAuthServer, startGitAuthServer } from "./gitAuthServer.ts";

let server: GitAuthServer | undefined;

afterEach(async () => {
  if (server) {
    await server.close();
    server = undefined;
  }
});

function makeTmpdir(): string {
  return mkdtempSync(join(tmpdir(), "askpass-test-"));
}

describe("git auth server lifecycle", () => {
  it("starts and listens on a port", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);
    expect(server.port).toBeGreaterThan(0);
  });

  it("closes cleanly", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);
    const port = server.port;
    await server.close();
    server = undefined;

    // port should no longer accept connections
    const err = await fetch(`http://127.0.0.1:${port}/test`).catch((e) => e);
    expect(err).toBeInstanceOf(Error);
  });
});

describe("token delivery", () => {
  it("returns token on first request with valid code", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);
    const code = server.register("ghs_test_token_12345");

    const res = await fetch(`http://127.0.0.1:${server.port}/${code}`);
    expect(res.status).toBe(200);
    const body = await res.text();
    expect(body).toBe("ghs_test_token_12345");
  });

  it("returns 404 for unknown code", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);

    const res = await fetch(`http://127.0.0.1:${server.port}/nonexistent-code`);
    expect(res.status).toBe(404);
  });

  it("returns 400 for empty code", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);

    const res = await fetch(`http://127.0.0.1:${server.port}/`);
    expect(res.status).toBe(400);
  });

  it("returns 405 for non-GET methods", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);
    const code = server.register("token");

    const res = await fetch(`http://127.0.0.1:${server.port}/${code}`, { method: "POST" });
    expect(res.status).toBe(405);
  });
});

describe("code lifecycle (tamper detection)", () => {
  it("returns the token on repeated use while the code is active", async () => {
    // a single $git() call can produce multiple legitimate askpass requests:
    // git itself (username + password), git-lfs pre-push hook, custom hooks.
    // they must all succeed until $git()'s finally calls revoke().
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);
    const code = server.register("ghs_active_test");

    for (let i = 0; i < 5; i++) {
      const res = await fetch(`http://127.0.0.1:${server.port}/${code}`);
      expect(res.status).toBe(200);
      expect(await res.text()).toBe("ghs_active_test");
    }
  });

  it("returns 409 after revoke (replay-after-call trap)", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);
    const code = server.register("ghs_tamper_test");

    const first = await fetch(`http://127.0.0.1:${server.port}/${code}`);
    expect(first.status).toBe(200);

    server.revoke(code);

    const replay = await fetch(`http://127.0.0.1:${server.port}/${code}`);
    expect(replay.status).toBe(409);
    expect(await replay.text()).toBe("compromised");
  });

  it("revoke() on an unknown code is a no-op", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);
    expect(() => server!.revoke("nonexistent")).not.toThrow();
  });

  it("each register() call produces an independent code", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);
    const code1 = server.register("token-a");
    const code2 = server.register("token-b");

    expect(code1).not.toBe(code2);

    const res1 = await fetch(`http://127.0.0.1:${server.port}/${code1}`);
    expect(await res1.text()).toBe("token-a");

    const res2 = await fetch(`http://127.0.0.1:${server.port}/${code2}`);
    expect(await res2.text()).toBe("token-b");
  });
});

describe("askpass script generation", () => {
  it("writes an executable script file", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);
    const code = server.register("ghs_script_test");
    const scriptPath = server.writeAskpassScript(code);

    expect(existsSync(scriptPath)).toBe(true);
    expect(scriptPath.startsWith(tmp)).toBe(true);

    const content = readFileSync(scriptPath, "utf-8");
    expect(content).toContain("#!/usr/bin/env node");
    expect(content).toContain(String(server.port));
    expect(content).toContain(code);
    // token should NOT be in the script — only port and code
    expect(content).not.toContain("ghs_script_test");
  });

  it("script handles Username prompt locally (no server call)", async () => {
    const tmp = makeTmpdir();
    server = await startGitAuthServer(tmp);
    const code = server.register("ghs_username_test");
    const scriptPath = server.writeAskpassScript(code);
    const content = readFileSync(scriptPath, "utf-8");

    // script checks for /^Username/i and returns "x-access-token" without HTTP
    expect(content).toContain("Username");
    expect(content).toContain("x-access-token");
  });
});