Files
pikasTech-unidesk/scripts/src/gh-token-repository-override.test.ts
T

185 lines
7.4 KiB
TypeScript

import { afterEach, describe, expect, test } from "bun:test";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { resolveToken, type GitHubYamlAuthConfig } from "./gh/auth-and-safety";
import { authRequired } from "./gh/client";
import { resolvePositionalIssueReference, resolvePositionalPrReference } from "./gh/refs";
import type { GitHubOptions, GitHubResolvedNumberReference } from "./gh/types";
const originalGhToken = process.env.GH_TOKEN;
const originalGithubToken = process.env.GITHUB_TOKEN;
const projectedEnv = "UNIDESK_TEST_GH_REPOSITORY_OVERRIDE";
const originalProjectedToken = process.env[projectedEnv];
const temporaryRoots: string[] = [];
afterEach(() => {
if (originalGhToken === undefined) delete process.env.GH_TOKEN;
else process.env.GH_TOKEN = originalGhToken;
if (originalGithubToken === undefined) delete process.env.GITHUB_TOKEN;
else process.env.GITHUB_TOKEN = originalGithubToken;
if (originalProjectedToken === undefined) delete process.env[projectedEnv];
else process.env[projectedEnv] = originalProjectedToken;
while (temporaryRoots.length > 0) rmSync(temporaryRoots.pop()!, { recursive: true, force: true });
});
function testAuthConfig(): GitHubYamlAuthConfig {
const root = mkdtempSync("/tmp/unidesk-gh-token-override-");
temporaryRoots.push(root);
writeFileSync(join(root, "global-token.txt"), "global-token\n");
writeFileSync(join(root, "selfmedia-token.txt"), "selfmedia-token\n");
return {
tokenSource: {
enabled: true,
priority: "before-env",
root,
sourceRef: "global-token.txt",
sourceKey: "GH_TOKEN",
format: "raw-token",
scope: "global",
configRef: "test#github.auth.tokenSource",
},
repositoryOverrides: [{
repository: "pikainc/selfmedia",
priority: "before-env",
root,
sourceRef: "selfmedia-token.txt",
sourceKey: "GH_TOKEN",
format: "raw-token",
scope: "repository-override",
configRef: "test#github.auth.repositoryOverrides[0]",
}],
};
}
function options(repo: string): GitHubOptions {
return { repo } as GitHubOptions;
}
function resolvedRepo(value: GitHubResolvedNumberReference | { ok: boolean }): string {
if (!("number" in value)) throw new Error("expected a resolved positional reference");
return value.repo;
}
describe("GitHub repository token overrides", () => {
test("selects the exact repository override without affecting global repositories", () => {
delete process.env.GH_TOKEN;
delete process.env.GITHUB_TOKEN;
const authConfig = testAuthConfig();
const selfmedia = resolveToken("pikainc/selfmedia", false, authConfig);
const unidesk = resolveToken("pikasTech/unidesk", false, authConfig);
const other = resolveToken("owner/other", false, authConfig);
expect(selfmedia.token).toBe("selfmedia-token");
expect(selfmedia.probe).toMatchObject({ scope: "repository-override", sourceRef: "selfmedia-token.txt", sourceKey: "GH_TOKEN", valuesPrinted: false });
expect(unidesk.token).toBe("global-token");
expect(other.token).toBe("global-token");
expect(unidesk.probe).toMatchObject({ scope: "global", sourceRef: "global-token.txt", valuesPrinted: false });
expect(other.probe).toMatchObject({ scope: "global", sourceRef: "global-token.txt", valuesPrinted: false });
expect(selfmedia.probe.tokenFingerprint).not.toBe(unidesk.probe.tokenFingerprint);
});
test("keeps before-env repository overrides ahead of global environment tokens", () => {
process.env.GH_TOKEN = "environment-token";
process.env.GITHUB_TOKEN = "secondary-environment-token";
const selected = resolveToken("pikainc/selfmedia", false, testAuthConfig());
expect(selected.token).toBe("selfmedia-token");
expect(selected.probe).toMatchObject({ source: "yaml-token-source", scope: "repository-override", yamlSourcePriority: "before-env" });
});
test("uses the YAML-declared projected environment for repository overrides", () => {
process.env.GH_TOKEN = "global-environment-token";
process.env[projectedEnv] = "projected-repository-token";
const authConfig = testAuthConfig();
authConfig.repositoryOverrides[0] = {
...authConfig.repositoryOverrides[0],
sourceRef: "missing-token.txt",
projectedEnv,
};
const selected = resolveToken("pikainc/selfmedia", false, authConfig);
expect(selected.token).toBe("projected-repository-token");
expect(selected.probe).toMatchObject({
source: "yaml-token-projection",
scope: "repository-override",
sourceExists: false,
sourceKeyPresent: false,
projectedEnv,
projectedEnvPresent: true,
valuesPrinted: false,
});
});
test("fails closed when a declared repository override is missing", () => {
process.env.GH_TOKEN = "global-environment-token";
delete process.env[projectedEnv];
const authConfig = testAuthConfig();
authConfig.repositoryOverrides[0] = {
...authConfig.repositoryOverrides[0],
sourceRef: "missing-token.txt",
projectedEnv,
};
const selected = resolveToken("pikainc/selfmedia", true, authConfig);
const failure = authRequired("pikainc/selfmedia", "pr view", selected.probe);
expect(selected.token).toBeNull();
expect(selected.probe).toMatchObject({
present: false,
scope: "repository-override",
sourceExists: false,
sourceKeyPresent: false,
projectedEnv,
projectedEnvPresent: false,
ghFallbackAttempted: false,
valuesPrinted: false,
});
expect(failure).toMatchObject({
ok: false,
degradedReason: "missing-token",
runnerDisposition: "infra-blocked",
token: {
scope: "repository-override",
projectedEnvPresent: false,
valuesPrinted: false,
},
});
expect(JSON.stringify(failure)).not.toContain("global-environment-token");
});
test("matches repository overrides without case sensitivity", () => {
delete process.env.GH_TOKEN;
delete process.env.GITHUB_TOKEN;
const authConfig = testAuthConfig();
for (const repo of ["PikaInc/selfmedia", "pikainc/SELFMEDIA", "PIKAINC/SELFMEDIA"]) {
const selected = resolveToken(repo, false, authConfig);
expect(selected.token).toBe("selfmedia-token");
expect(selected.probe).toMatchObject({ scope: "repository-override", sourceRef: "selfmedia-token.txt", valuesPrinted: false });
}
const global = resolveToken("PikasTech/UniDesk", false, authConfig);
expect(global.token).toBe("global-token");
expect(global.probe).toMatchObject({ scope: "global", sourceRef: "global-token.txt", valuesPrinted: false });
});
test("uses the final positional repository for issue and PR token selection", () => {
delete process.env.GH_TOKEN;
delete process.env.GITHUB_TOKEN;
const authConfig = testAuthConfig();
const issueRef = resolvePositionalIssueReference(["issue", "edit", "pikainc/selfmedia#12"], 2, "issue edit", options("pikasTech/unidesk"));
const prRef = resolvePositionalPrReference(["pr", "comment", "pikainc/selfmedia#34"], 2, "pr comment", options("pikasTech/unidesk"));
const issueToken = resolveToken(resolvedRepo(issueRef), false, authConfig);
const prToken = resolveToken(resolvedRepo(prRef), false, authConfig);
const defaultToken = resolveToken("pikasTech/unidesk", false, authConfig);
expect(issueToken.token).toBe("selfmedia-token");
expect(prToken.token).toBe("selfmedia-token");
expect(defaultToken.token).toBe("global-token");
});
});