import assert from "node:assert/strict"; import { test } from "node:test"; import { formatRetryBranchName, isGitPushBranchCollision, isMissingRemoteBranchFailure, isRetryableGitPushFailure, redactGitSecrets, } from "./github-app.js"; test("redactGitSecrets scrubs GitHub installation/app tokens", () => { const token = ["ghs", "AbCdEf0123456789AbCdEf0123456789"].join("_"); const out = redactGitSecrets(`fatal: Authentication failed for ${token}`); assert.ok(!out.includes(token), "raw token must not survive"); assert.match(out, /ghs_\*\*\*/); }); test("redactGitSecrets scrubs gho/ghp/ghu tokens and fine-grained PATs", () => { assert.match( redactGitSecrets(["ghp", "AbCdEf0123456789AbCdEf0123456789"].join("_")), /ghp_\*\*\*/, ); assert.match( redactGitSecrets(["gho", "AbCdEf0123456789AbCdEf0123456789"].join("_")), /gho_\*\*\*/, ); const pat = ["github", "pat", "11ABCDEFG0abcdefghij", "KLMNOPqrstuvwxyz0123456789"].join("_"); const out = redactGitSecrets(pat); assert.ok(!out.includes(pat)); assert.match(out, /github_pat_\*\*\*/); }); test("redactGitSecrets scrubs x-access-token and basic auth headers", () => { const token = ["ghs", "AbCdEf0123456789AbCdEf0123456789"].join("_"); const out = redactGitSecrets( `https://x-access-token:${token}@github.com/o/r.git`, ); assert.ok(!out.includes(token)); assert.match(redactGitSecrets("AUTHORIZATION: Basic eC1hY2Nlc3M6c2VjcmV0"), /Basic \*\*\*/); }); test("redactGitSecrets leaves benign git rejection messages intact", () => { const msg = "remote: error: GH006: Protected branch update failed for refs/heads/superlog/fix.\nremote: error: Changes must be made through a pull request."; assert.equal(redactGitSecrets(msg), msg); }); test("isRetryableGitPushFailure retries any push failure output", () => { assert.equal( isRetryableGitPushFailure("error: RPC failed; curl 56 Recv failure: Connection reset by peer"), true, ); assert.equal(isRetryableGitPushFailure("fatal: the remote end hung up unexpectedly"), true); assert.equal(isRetryableGitPushFailure("fatal: unable to access repo: HTTP 503"), true); assert.equal( isRetryableGitPushFailure("remote: error: GH006: Protected branch update failed"), true, ); assert.equal(isRetryableGitPushFailure("fatal: Authentication failed"), true); assert.equal(isRetryableGitPushFailure("! [rejected] HEAD -> branch (non-fast-forward)"), true); assert.equal(isRetryableGitPushFailure("remote: error: GH013: Repository rule violations"), true); }); test("isGitPushBranchCollision detects occupied remote branch failures", () => { assert.equal( isGitPushBranchCollision( "! [rejected] HEAD -> superlog/fix-refresh-token (fetch first)\nerror: failed to push some refs", ), true, ); assert.equal(isGitPushBranchCollision("! [rejected] HEAD -> branch (non-fast-forward)"), true); assert.equal( isGitPushBranchCollision( "hint: Updates were rejected because the remote contains work that you do not have locally.", ), true, ); assert.equal( isGitPushBranchCollision("remote: error: GH006: Protected branch update failed"), false, ); assert.equal(isGitPushBranchCollision("fatal: Authentication failed"), false); }); test("formatRetryBranchName appends a bounded retry suffix", () => { assert.equal( formatRetryBranchName("superlog/fix-refresh-token", "abcdef12" + "-3456"), "superlog/fix-refresh-token-retry-abcdef12", ); assert.equal(formatRetryBranchName("superlog/fix", "bad chars!"), "superlog/fix-retry-bad-char"); }); test("isMissingRemoteBranchFailure detects clone failures for deleted base branches", () => { assert.equal( isMissingRemoteBranchFailure("warning: Could not find remote branch main to clone."), true, ); assert.equal( isMissingRemoteBranchFailure("fatal: Remote branch main not found in upstream origin"), true, ); assert.equal(isMissingRemoteBranchFailure("fatal: repository not found"), false); });