| 1 | import { execFileSync } from "node:child_process"; |
| 2 | import { describe, expect, it } from "vitest"; |
| 3 | import { parseCredentialInput } from "../src/commands/auth.js"; |
| 4 | import { isolatedEnv, run } from "./helpers.js"; |
| 5 | |
| 6 | describe("git credential helper", () => { |
| 7 | const env = isolatedEnv({ DIFFINERY_URL: "https://forge.example.com", DIFFINERY_TOKEN: "dfy_secret" }); |
| 8 | |
| 9 | it("parses key=value lines up to the blank line", () => { |
| 10 | expect(parseCredentialInput("protocol=https\nhost=a.b\npath=x/y.git\n\nignored=1\n")).toEqual({ protocol: "https", host: "a.b", path: "x/y.git" }); |
| 11 | }); |
| 12 | |
| 13 | it("answers get for the configured host", async () => { |
| 14 | const r = await run(["auth", "git-credential", "get"], { env, stdin: "protocol=https\nhost=forge.example.com\n\n" }); |
| 15 | expect(r.code).toBe(0); |
| 16 | expect(r.stdout).toBe("protocol=https\nhost=forge.example.com\nusername=x-token\npassword=dfy_secret\n"); |
| 17 | }); |
| 18 | |
| 19 | it("stays silent for other hosts, other protocols and other operations", async () => { |
| 20 | for (const stdin of ["protocol=https\nhost=github.com\n\n", "protocol=http\nhost=forge.example.com\n\n"]) { |
| 21 | const r = await run(["auth", "git-credential", "get"], { env, stdin }); |
| 22 | expect(r.code).toBe(0); |
| 23 | expect(r.stdout).toBe(""); |
| 24 | } |
| 25 | for (const op of ["store", "erase"]) { |
| 26 | const r = await run(["auth", "git-credential", op], { env, stdin: "protocol=https\nhost=forge.example.com\nusername=x\npassword=y\n\n" }); |
| 27 | expect(r.code).toBe(0); |
| 28 | expect(r.stdout).toBe(""); |
| 29 | } |
| 30 | }); |
| 31 | |
| 32 | it("matches hosts with ports", async () => { |
| 33 | const e = { ...env, DIFFINERY_URL: "http://127.0.0.1:8787" }; |
| 34 | const r = await run(["auth", "git-credential", "get"], { env: e, stdin: "protocol=http\nhost=127.0.0.1:8787\n" }); |
| 35 | expect(r.stdout).toContain("password=dfy_secret"); |
| 36 | }); |
| 37 | |
| 38 | it("prints nothing without a token", async () => { |
| 39 | const e = { ...env }; |
| 40 | delete e.DIFFINERY_TOKEN; |
| 41 | const r = await run(["auth", "git-credential", "get"], { env: e, stdin: "protocol=https\nhost=forge.example.com\n" }); |
| 42 | expect(r.stdout).toBe(""); |
| 43 | }); |
| 44 | |
| 45 | it("auth setup-git configures a URL-scoped helper in the global git config", async () => { |
| 46 | const r = await run(["auth", "setup-git"], { env }); |
| 47 | expect(r.code).toBe(0); |
| 48 | const helpers = execFileSync("git", ["config", "--global", "--get-all", "credential.https://forge.example.com.helper"], { env }).toString().split("\n"); |
| 49 | expect(helpers.slice(0, 2)).toEqual(["", "!diffinery auth git-credential"]); |
| 50 | // Idempotent. |
| 51 | await run(["auth", "setup-git"], { env }); |
| 52 | expect(execFileSync("git", ["config", "--global", "--get-all", "credential.https://forge.example.com.helper"], { env }).toString().split("\n").slice(0, -1)).toEqual(["", "!diffinery auth git-credential"]); |
| 53 | }); |
| 54 | }); |