* feat(projects): add keyboard-driven project setup * test(app): update add project loading assertion * fix(projects): harden GitHub project setup
135 lines
3.5 KiB
TypeScript
135 lines
3.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
createGitHubService,
|
|
type GitHubCommandRunner,
|
|
type GitHubCommandRunnerOptions,
|
|
} from "./github-service.js";
|
|
|
|
interface RunnerCall {
|
|
args: string[];
|
|
options: GitHubCommandRunnerOptions;
|
|
}
|
|
|
|
function createRunner(outputs: string[]): { calls: RunnerCall[]; runner: GitHubCommandRunner } {
|
|
const calls: RunnerCall[] = [];
|
|
return {
|
|
calls,
|
|
runner: async (args, options) => {
|
|
calls.push({ args, options });
|
|
return { stdout: outputs.shift() ?? "", stderr: "" };
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("GitHub repository search", () => {
|
|
it("lists recent owned repositories for an empty query and normalizes clone identity", async () => {
|
|
const runner = createRunner([
|
|
JSON.stringify([
|
|
{
|
|
id: " R_recent ",
|
|
name: " paseo ",
|
|
nameWithOwner: " getpaseo/paseo ",
|
|
description: null,
|
|
visibility: "PUBLIC",
|
|
updatedAt: "2026-07-15T12:00:00Z",
|
|
sshUrl: " git@github.com:getpaseo/paseo.git ",
|
|
url: "https://github.com/getpaseo/paseo",
|
|
},
|
|
]),
|
|
"ssh\n",
|
|
]);
|
|
const service = createGitHubService({
|
|
runner: runner.runner,
|
|
resolveGhPath: async () => "/usr/bin/gh",
|
|
});
|
|
|
|
await expect(
|
|
service.searchRepositories({ cwd: "/tmp", query: " ", limit: 8 }),
|
|
).resolves.toEqual([
|
|
{
|
|
id: "R_recent",
|
|
name: "paseo",
|
|
nameWithOwner: "getpaseo/paseo",
|
|
description: null,
|
|
visibility: "public",
|
|
updatedAt: "2026-07-15T12:00:00Z",
|
|
cloneUrl: "git@github.com:getpaseo/paseo.git",
|
|
},
|
|
]);
|
|
expect(runner.calls).toEqual([
|
|
{
|
|
args: [
|
|
"repo",
|
|
"list",
|
|
"--json",
|
|
"id,name,nameWithOwner,description,visibility,updatedAt,sshUrl,url",
|
|
"--limit",
|
|
"8",
|
|
],
|
|
options: { cwd: "/tmp" },
|
|
},
|
|
{
|
|
args: ["config", "get", "git_protocol", "--host", "github.com"],
|
|
options: { cwd: "/tmp" },
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("searches accessible repositories for a typed query", async () => {
|
|
const runner = createRunner([
|
|
JSON.stringify([
|
|
{
|
|
id: 42,
|
|
name: "private-repo",
|
|
fullName: "octo/private-repo",
|
|
description: "Private project",
|
|
visibility: "PRIVATE",
|
|
updatedAt: "2026-07-14T08:00:00Z",
|
|
url: "https://github.com/octo/private-repo",
|
|
},
|
|
]),
|
|
"https",
|
|
]);
|
|
const service = createGitHubService({
|
|
runner: runner.runner,
|
|
resolveGhPath: async () => "/usr/bin/gh",
|
|
});
|
|
|
|
await expect(
|
|
service.searchRepositories({ cwd: "/tmp", query: " private project ", limit: 5 }),
|
|
).resolves.toEqual([
|
|
{
|
|
id: "42",
|
|
name: "private-repo",
|
|
nameWithOwner: "octo/private-repo",
|
|
description: "Private project",
|
|
visibility: "private",
|
|
updatedAt: "2026-07-14T08:00:00Z",
|
|
cloneUrl: "https://github.com/octo/private-repo",
|
|
},
|
|
]);
|
|
expect(runner.calls).toEqual([
|
|
{
|
|
args: [
|
|
"search",
|
|
"repos",
|
|
"private project",
|
|
"--json",
|
|
"id,name,fullName,description,visibility,updatedAt,url",
|
|
"--sort",
|
|
"updated",
|
|
"--order",
|
|
"desc",
|
|
"--limit",
|
|
"5",
|
|
],
|
|
options: { cwd: "/tmp" },
|
|
},
|
|
{
|
|
args: ["config", "get", "git_protocol", "--host", "github.com"],
|
|
options: { cwd: "/tmp" },
|
|
},
|
|
]);
|
|
});
|
|
});
|