The client (gitea-js) appends /api/v1 to the base URL itself, so a GITEA_AXI_API_URL that already carries it — a natural guess given the variable's name — doubled the segment and failed as a spurious REPO_NOT_FOUND. Normalize the base URL by stripping a trailing /api/v1 (and any trailing slashes) on both the env-URL and tea-login paths, so the host base and the /api/v1 endpoint both resolve.
168 lines
5.4 KiB
TypeScript
168 lines
5.4 KiB
TypeScript
import { afterEach, describe, expect, it } from "vitest";
|
|
import { resolveRepoContext } from "../src/context.js";
|
|
import type { CliDeps } from "../src/deps.js";
|
|
import { startFixtureServer, type FixtureServer } from "./fixture-server.js";
|
|
import { runCliTest, testModeEnv } from "./harness.js";
|
|
|
|
let server: FixtureServer | undefined;
|
|
|
|
afterEach(async () => {
|
|
await server?.close();
|
|
server = undefined;
|
|
});
|
|
|
|
describe("context overrides", () => {
|
|
it("prefers the -R flag over GITEA_AXI_REPO", async () => {
|
|
server = await startFixtureServer([
|
|
{ method: "GET", path: "/api/v1/repos/flagowner/flagrepo/issues", body: [] },
|
|
]);
|
|
const { exitCode } = await runCliTest(
|
|
["issue", "list", "-R", "flagowner/flagrepo"],
|
|
{ env: testModeEnv(server.url) },
|
|
);
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(server.requests[0]!.path).toBe("/api/v1/repos/flagowner/flagrepo/issues");
|
|
});
|
|
|
|
it("accepts -R before the command", async () => {
|
|
server = await startFixtureServer([
|
|
{ method: "GET", path: "/api/v1/repos/flagowner/flagrepo/issues", body: [] },
|
|
]);
|
|
const { exitCode } = await runCliTest(
|
|
["-R", "flagowner/flagrepo", "issue", "list"],
|
|
{ env: testModeEnv(server.url) },
|
|
);
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(server.requests[0]!.path).toBe("/api/v1/repos/flagowner/flagrepo/issues");
|
|
});
|
|
|
|
it("accepts the --repo=OWNER/NAME form", async () => {
|
|
server = await startFixtureServer([
|
|
{ method: "GET", path: "/api/v1/repos/flagowner/flagrepo/issues", body: [] },
|
|
]);
|
|
const { exitCode } = await runCliTest(
|
|
["issue", "list", "--repo=flagowner/flagrepo"],
|
|
{ env: testModeEnv(server.url) },
|
|
);
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(server.requests[0]!.path).toBe("/api/v1/repos/flagowner/flagrepo/issues");
|
|
});
|
|
|
|
it("includes the -R override in next-step suggestions when context came from a flag", async () => {
|
|
server = await startFixtureServer([
|
|
{ method: "GET", path: "/api/v1/repos/flagowner/flagrepo/issues", body: [] },
|
|
]);
|
|
const { stdout } = await runCliTest(
|
|
["issue", "list", "-R", "flagowner/flagrepo"],
|
|
{ env: testModeEnv(server.url) },
|
|
);
|
|
|
|
expect(stdout).toContain("-R flagowner/flagrepo");
|
|
});
|
|
|
|
it("includes the -R override in suggestions when context came from the environment", async () => {
|
|
server = await startFixtureServer([
|
|
{ method: "GET", path: "/api/v1/repos/testowner/testrepo/issues", body: [] },
|
|
]);
|
|
const { stdout } = await runCliTest(["issue", "list"], {
|
|
env: testModeEnv(server.url),
|
|
});
|
|
|
|
expect(stdout).toContain("-R testowner/testrepo");
|
|
});
|
|
|
|
it("fails with REPO_NOT_FOUND when test mode has no repository context", async () => {
|
|
server = await startFixtureServer([]);
|
|
const { stdout, exitCode } = await runCliTest(["issue", "list"], {
|
|
env: { GITEA_AXI_API_URL: server.url, GITEA_AXI_TOKEN: "test-token" },
|
|
});
|
|
|
|
expect(exitCode).toBe(1);
|
|
expect(stdout).toContain("code: REPO_NOT_FOUND");
|
|
expect(stdout).toContain("GITEA_AXI_REPO");
|
|
});
|
|
|
|
it("rejects a malformed -R value with exit code 2", async () => {
|
|
server = await startFixtureServer([]);
|
|
const { stdout, exitCode } = await runCliTest(
|
|
["issue", "list", "-R", "not-a-repo"],
|
|
{ env: testModeEnv(server.url) },
|
|
);
|
|
|
|
expect(exitCode).toBe(2);
|
|
expect(stdout).toContain("code: VALIDATION_ERROR");
|
|
expect(stdout).toContain("OWNER/NAME");
|
|
});
|
|
|
|
it("rejects -R without a value with exit code 2", async () => {
|
|
const { stdout, exitCode } = await runCliTest(["issue", "list", "-R"], {
|
|
env: { GITEA_AXI_API_URL: "http://127.0.0.1:1" },
|
|
});
|
|
|
|
expect(exitCode).toBe(2);
|
|
expect(stdout).toContain("code: VALIDATION_ERROR");
|
|
});
|
|
|
|
it("test mode never spawns git or tea", async () => {
|
|
// An empty PATH makes any subprocess spawn fail loudly; success here
|
|
// proves the git and tea subprocesses were suppressed.
|
|
server = await startFixtureServer([
|
|
{ method: "GET", path: "/api/v1/repos/testowner/testrepo/issues", body: [] },
|
|
]);
|
|
const { exitCode } = await runCliTest(["issue", "list"], {
|
|
env: { ...testModeEnv(server.url), PATH: "" },
|
|
});
|
|
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("apiUrl normalization", () => {
|
|
function depsWithApiUrl(apiUrl: string): CliDeps {
|
|
return {
|
|
env: {
|
|
GITEA_AXI_API_URL: apiUrl,
|
|
GITEA_AXI_REPO: "acme/widgets",
|
|
GITEA_AXI_TOKEN: "test-token",
|
|
},
|
|
cwd: process.cwd(),
|
|
globals: {},
|
|
};
|
|
}
|
|
|
|
it("strips a trailing /api/v1 suffix from the host base", async () => {
|
|
const context = await resolveRepoContext(
|
|
depsWithApiUrl("https://git.example.com/api/v1"),
|
|
);
|
|
|
|
expect(context.apiUrl).toBe("https://git.example.com");
|
|
});
|
|
|
|
it("strips a trailing /api/v1/ with a trailing slash", async () => {
|
|
const context = await resolveRepoContext(
|
|
depsWithApiUrl("https://git.example.com/api/v1/"),
|
|
);
|
|
|
|
expect(context.apiUrl).toBe("https://git.example.com");
|
|
});
|
|
|
|
it("leaves a host base without an /api/v1 suffix unchanged", async () => {
|
|
const context = await resolveRepoContext(
|
|
depsWithApiUrl("https://git.example.com"),
|
|
);
|
|
|
|
expect(context.apiUrl).toBe("https://git.example.com");
|
|
});
|
|
|
|
it("strips a lone trailing slash from the host base", async () => {
|
|
const context = await resolveRepoContext(
|
|
depsWithApiUrl("https://git.example.com/"),
|
|
);
|
|
|
|
expect(context.apiUrl).toBe("https://git.example.com");
|
|
});
|
|
});
|