feat: truncate --fields body uniformly (task 0021)
All checks were successful
CI / test (pull_request) Successful in 49s
CI / test (push) Successful in 49s

Rule that the `body` extra field truncates at 500 chars like `issue view`,
resolving the spec's Principle 3 / Command Surface contradiction. Route the
`body` extractor through a new `truncatedBody()` FieldDef so `issue list`,
`issue create`, `pr list`, and `search` all present it exactly as the detail
views do, and add a `--full` flag to each to suppress truncation, keeping the
inline hint's "use --full" promise honest.
This commit was merged in pull request #21.
This commit is contained in:
2026-07-14 12:23:24 -04:00
parent ed87f023cb
commit b5955cd7b8
13 changed files with 277 additions and 23 deletions

View File

@@ -309,6 +309,48 @@ describe("issue create", () => {
expect(stdout).toContain("number: 7");
});
it("applies the body-truncation ruling to a created issue, and --full suppresses it", async () => {
// Truncated inline at 500 chars with the hint.
server = await startFixtureServer([
{
method: "POST",
path: ISSUES_PATH,
status: 201,
body: createdIssue({ body: "x".repeat(650) }),
},
]);
const truncated = await runCliTest(
["issue", "create", "--title", "T", "--fields", "body"],
{ env: testModeEnv(server.url) },
);
expect(truncated.exitCode).toBe(0);
expect(truncated.stdout).toContain("x".repeat(500));
expect(truncated.stdout).toContain(
"... (truncated, 650 chars total - use --full to see complete body)",
);
expect(truncated.stdout).not.toContain("x".repeat(650));
// --full returns the body raw, with no truncation hint.
await server.close();
server = await startFixtureServer([
{
method: "POST",
path: ISSUES_PATH,
status: 201,
body: createdIssue({ body: "x".repeat(650) }),
},
]);
const full = await runCliTest(
["issue", "create", "--title", "T", "--fields", "body", "--full"],
{ env: testModeEnv(server.url) },
);
expect(full.exitCode).toBe(0);
expect(full.stdout).toContain("x".repeat(650));
expect(full.stdout).not.toContain("truncated");
});
it("rejects an unknown --fields name with VALIDATION_ERROR", async () => {
server = await startFixtureServer([]);
const { stdout, exitCode } = await runCliTest(

View File

@@ -469,6 +469,49 @@ describe("issue list --fields", () => {
expect(row).toMatch(/\d+(mo|[smhdy]) ago/);
});
it("truncates an over-limit body inline, appending the same hint as issue view", async () => {
const fullBody = "x".repeat(650);
server = await startFixtureServer([
{
method: "GET",
path: ISSUES_PATH,
headers: { "X-Total-Count": "1" },
body: [{ ...issueOf(50), body: fullBody }],
},
]);
const { stdout, exitCode } = await runCliTest(
["issue", "list", "--fields", "body"],
{ env: testModeEnv(server.url) },
);
expect(exitCode).toBe(0);
expect(stdout).toContain("x".repeat(500));
expect(stdout).toContain(
"... (truncated, 650 chars total - use --full to see complete body)",
);
expect(stdout).not.toContain("x".repeat(650));
});
it("returns the complete body untruncated under --full, with no truncation hint", async () => {
const fullBody = "x".repeat(650);
server = await startFixtureServer([
{
method: "GET",
path: ISSUES_PATH,
headers: { "X-Total-Count": "1" },
body: [{ ...issueOf(50), body: fullBody }],
},
]);
const { stdout, exitCode } = await runCliTest(
["issue", "list", "--fields", "body", "--full"],
{ env: testModeEnv(server.url) },
);
expect(exitCode).toBe(0);
expect(stdout).toContain("x".repeat(650));
expect(stdout).not.toContain("truncated");
});
it("rejects an unknown --fields name with exit code 2", async () => {
server = await startFixtureServer([]);
const { stdout, exitCode } = await runCliTest(

View File

@@ -463,6 +463,48 @@ describe("pr list --fields", () => {
expect(row).toMatch(/\d+(mo|[smhdy]) ago/);
});
it("applies the body-truncation ruling to a PR body, and --full suppresses it", async () => {
// Truncated inline at 500 chars with the hint.
server = await startFixtureServer([
{
method: "GET",
path: PULLS_PATH,
headers: { "X-Total-Count": "1" },
body: [pullOf(7, { body: "x".repeat(650) })],
},
reviewsRoute(7, []),
]);
const truncated = await runCliTest(["pr", "list", "--fields", "body"], {
env: testModeEnv(server.url),
});
expect(truncated.exitCode).toBe(0);
expect(truncated.stdout).toContain("x".repeat(500));
expect(truncated.stdout).toContain(
"... (truncated, 650 chars total - use --full to see complete body)",
);
expect(truncated.stdout).not.toContain("x".repeat(650));
// --full returns the body raw, with no truncation hint.
await server.close();
server = await startFixtureServer([
{
method: "GET",
path: PULLS_PATH,
headers: { "X-Total-Count": "1" },
body: [pullOf(7, { body: "x".repeat(650) })],
},
reviewsRoute(7, []),
]);
const full = await runCliTest(["pr", "list", "--fields", "body", "--full"], {
env: testModeEnv(server.url),
});
expect(full.exitCode).toBe(0);
expect(full.stdout).toContain("x".repeat(650));
expect(full.stdout).not.toContain("truncated");
});
it("rejects an unknown --fields name with exit code 2", async () => {
server = await startFixtureServer([]);
const { stdout, exitCode } = await runCliTest(["pr", "list", "--fields", "bogus"], {

View File

@@ -323,6 +323,46 @@ describe("search issues --fields", () => {
expect(row).toContain("http://gitea.example/testowner/testrepo/issues/42");
});
it("applies the body-truncation ruling to a match body, and --full suppresses it", async () => {
// Truncated inline at 500 chars with the hint.
server = await startFixtureServer([
{
method: "GET",
path: SEARCH_PATH,
body: [searchIssueOf(42, { body: "x".repeat(650) })],
},
]);
const truncated = await runCliTest(
["search", "issues", "login", "--fields", "body"],
{ env: testModeEnv(server.url) },
);
expect(truncated.exitCode).toBe(0);
expect(truncated.stdout).toContain("x".repeat(500));
expect(truncated.stdout).toContain(
"... (truncated, 650 chars total - use --full to see complete body)",
);
expect(truncated.stdout).not.toContain("x".repeat(650));
// --full returns the body raw, with no truncation hint.
await server.close();
server = await startFixtureServer([
{
method: "GET",
path: SEARCH_PATH,
body: [searchIssueOf(42, { body: "x".repeat(650) })],
},
]);
const full = await runCliTest(
["search", "issues", "login", "--fields", "body", "--full"],
{ env: testModeEnv(server.url) },
);
expect(full.exitCode).toBe(0);
expect(full.stdout).toContain("x".repeat(650));
expect(full.stdout).not.toContain("truncated");
});
it("rejects an unknown --fields name with VALIDATION_ERROR", async () => {
server = await startFixtureServer([]);