feat: scaffold CLI and minimal issue list (task 0001)
Tracer bullet for gitea-axi: runnable npm package on axi-sdk-js with gitea-js as the sole HTTP layer, ESM on Node 20+. - issue list with --state/--limit, default fields, count line from X-Total-Count, type=issues guard, explicit empty state, and next-step suggestions - repo context detection from the git origin remote (SSH/scp/HTTPS), tea credential discovery with the three-way login-matching split, and -R/--repo and --login overrides (flag > env > auto) - token retrieval via tea login helper get: tea's login list JSON carries no token (ADR 0001 amended) - full AxiError classification table with path-based 404 split, TOON errors on stdout, exit codes 0/1/2 - test mode (GITEA_AXI_API_URL/TOKEN/REPO) suppressing subprocesses, fixture server, and vitest suites driving the CLI seam (50 tests)
This commit is contained in:
119
test/context.test.ts
Normal file
119
test/context.test.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user