- Add GitHub API integration via @octokit/rest: createGitHubClient, getRepoInfo (parses HTTPS/SSH remotes), createPullRequest, getPullRequest, listPullRequestReviews, createIssueComment - Add diff-aware context module: getRecentlyChangedFiles, getChangedFilesWithContext, rankFilesByRelevance — prioritizes recently-changed files for context window budget allocation - Add accurate token counting via tiktoken: countTokens (async), countTokensSync, initTokenCounter — falls back to chars/4 heuristic when tiktoken is unavailable - 27 new tests across 3 test files
150 lines
4.8 KiB
TypeScript
150 lines
4.8 KiB
TypeScript
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
parseRemoteUrl,
|
|
createGitHubClient,
|
|
getRepoInfo,
|
|
} from "../resources/extensions/gsd/github-client.ts";
|
|
|
|
describe("parseRemoteUrl — extracts owner/repo from git remote URLs", () => {
|
|
it("parses HTTPS URL with .git suffix", () => {
|
|
const result = parseRemoteUrl("https://github.com/octocat/hello-world.git");
|
|
assert.deepEqual(result, { owner: "octocat", repo: "hello-world" });
|
|
});
|
|
|
|
it("parses HTTPS URL without .git suffix", () => {
|
|
const result = parseRemoteUrl("https://github.com/octocat/hello-world");
|
|
assert.deepEqual(result, { owner: "octocat", repo: "hello-world" });
|
|
});
|
|
|
|
it("parses SSH URL with .git suffix", () => {
|
|
const result = parseRemoteUrl("git@github.com:octocat/hello-world.git");
|
|
assert.deepEqual(result, { owner: "octocat", repo: "hello-world" });
|
|
});
|
|
|
|
it("parses SSH URL without .git suffix", () => {
|
|
const result = parseRemoteUrl("git@github.com:octocat/hello-world");
|
|
assert.deepEqual(result, { owner: "octocat", repo: "hello-world" });
|
|
});
|
|
|
|
it("parses ssh:// protocol URL", () => {
|
|
const result = parseRemoteUrl(
|
|
"ssh://git@github.com/octocat/hello-world.git",
|
|
);
|
|
assert.deepEqual(result, { owner: "octocat", repo: "hello-world" });
|
|
});
|
|
|
|
it("handles repos with hyphens and underscores", () => {
|
|
const result = parseRemoteUrl(
|
|
"https://github.com/my-org/my_cool-repo.git",
|
|
);
|
|
assert.deepEqual(result, { owner: "my-org", repo: "my_cool-repo" });
|
|
});
|
|
|
|
it("returns null for non-GitHub URLs", () => {
|
|
const result = parseRemoteUrl("https://gitlab.com/owner/repo.git");
|
|
assert.equal(result, null);
|
|
});
|
|
|
|
it("returns null for malformed URLs", () => {
|
|
assert.equal(parseRemoteUrl("not-a-url"), null);
|
|
assert.equal(parseRemoteUrl(""), null);
|
|
});
|
|
|
|
it("returns null for bare paths", () => {
|
|
assert.equal(parseRemoteUrl("/home/user/repo.git"), null);
|
|
});
|
|
});
|
|
|
|
describe("createGitHubClient — Octokit instantiation", () => {
|
|
it("returns null when no token is provided and env vars are unset", () => {
|
|
const origGH = process.env.GITHUB_TOKEN;
|
|
const origGH2 = process.env.GH_TOKEN;
|
|
delete process.env.GITHUB_TOKEN;
|
|
delete process.env.GH_TOKEN;
|
|
|
|
try {
|
|
const client = createGitHubClient();
|
|
assert.equal(client, null);
|
|
} finally {
|
|
if (origGH !== undefined) process.env.GITHUB_TOKEN = origGH;
|
|
if (origGH2 !== undefined) process.env.GH_TOKEN = origGH2;
|
|
}
|
|
});
|
|
|
|
it("creates a client when a token is provided directly", () => {
|
|
const client = createGitHubClient("ghp_test123");
|
|
assert.notEqual(client, null);
|
|
assert.equal(typeof client!.pulls, "object");
|
|
assert.equal(typeof client!.issues, "object");
|
|
});
|
|
|
|
it("creates a client from GITHUB_TOKEN env var", () => {
|
|
const origGH = process.env.GITHUB_TOKEN;
|
|
const origGH2 = process.env.GH_TOKEN;
|
|
delete process.env.GH_TOKEN;
|
|
process.env.GITHUB_TOKEN = "ghp_env_test";
|
|
|
|
try {
|
|
const client = createGitHubClient();
|
|
assert.notEqual(client, null);
|
|
} finally {
|
|
if (origGH !== undefined) {
|
|
process.env.GITHUB_TOKEN = origGH;
|
|
} else {
|
|
delete process.env.GITHUB_TOKEN;
|
|
}
|
|
if (origGH2 !== undefined) process.env.GH_TOKEN = origGH2;
|
|
}
|
|
});
|
|
|
|
it("creates a client from GH_TOKEN env var", () => {
|
|
const origGH = process.env.GITHUB_TOKEN;
|
|
const origGH2 = process.env.GH_TOKEN;
|
|
delete process.env.GITHUB_TOKEN;
|
|
process.env.GH_TOKEN = "ghp_gh_token_test";
|
|
|
|
try {
|
|
const client = createGitHubClient();
|
|
assert.notEqual(client, null);
|
|
} finally {
|
|
if (origGH !== undefined) process.env.GITHUB_TOKEN = origGH;
|
|
if (origGH2 !== undefined) {
|
|
process.env.GH_TOKEN = origGH2;
|
|
} else {
|
|
delete process.env.GH_TOKEN;
|
|
}
|
|
}
|
|
});
|
|
|
|
it("prefers explicit token over env vars", () => {
|
|
const origGH = process.env.GITHUB_TOKEN;
|
|
process.env.GITHUB_TOKEN = "ghp_from_env";
|
|
|
|
try {
|
|
const client = createGitHubClient("ghp_explicit");
|
|
assert.notEqual(client, null);
|
|
} finally {
|
|
if (origGH !== undefined) {
|
|
process.env.GITHUB_TOKEN = origGH;
|
|
} else {
|
|
delete process.env.GITHUB_TOKEN;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("getRepoInfo — detects repo from git working directory", () => {
|
|
it("returns owner/repo for the current repository", async () => {
|
|
const info = await getRepoInfo(process.cwd());
|
|
// This test repo is gsd-build/gsd-2
|
|
assert.notEqual(info, null);
|
|
assert.equal(info!.owner, "gsd-build");
|
|
assert.equal(info!.repo, "gsd-2" /* or GSD-2 depending on remote */);
|
|
});
|
|
|
|
it("returns null for a non-git directory", async () => {
|
|
const info = await getRepoInfo("/tmp");
|
|
assert.equal(info, null);
|
|
});
|
|
});
|