Skip to content

Commit 246f781

Browse files
committed
fix(project): apply --limit flag in handleProjectSearch
handleProjectSearch was accepting flags.limit but never applying it, displaying all matches regardless. Now slices results to flags.limit and shows a truncation message when matches exceed the limit. Also applies limit to JSON output for consistency with handleAutoDetect and handleOrgAll.
1 parent 82dc53a commit 246f781

File tree

2 files changed

+129
-8
lines changed

2 files changed

+129
-8
lines changed

src/commands/project/list.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -482,12 +482,11 @@ export async function handleProjectSearch(
482482
const projects: ProjectWithOrg[] = await findProjectsBySlug(projectSlug);
483483
const filtered = filterByPlatform(projects, flags.platform);
484484

485-
if (flags.json) {
486-
writeJson(stdout, filtered);
487-
return;
488-
}
489-
490485
if (filtered.length === 0) {
486+
if (flags.json) {
487+
writeJson(stdout, []);
488+
return;
489+
}
491490
if (projects.length > 0 && flags.platform) {
492491
stdout.write(
493492
`No project '${projectSlug}' found matching platform '${flags.platform}'.\n`
@@ -501,11 +500,22 @@ export async function handleProjectSearch(
501500
);
502501
}
503502

504-
displayProjectTable(stdout, filtered);
503+
const limited = filtered.slice(0, flags.limit);
505504

506-
if (filtered.length > 1) {
505+
if (flags.json) {
506+
writeJson(stdout, limited);
507+
return;
508+
}
509+
510+
displayProjectTable(stdout, limited);
511+
512+
if (filtered.length > limited.length) {
513+
stdout.write(
514+
`\nShowing ${limited.length} of ${filtered.length} matches. Use --limit to show more.\n`
515+
);
516+
} else if (limited.length > 1) {
507517
stdout.write(
508-
`\nFound '${projectSlug}' in ${filtered.length} organizations\n`
518+
`\nFound '${projectSlug}' in ${limited.length} organizations\n`
509519
);
510520
}
511521

test/commands/project/list.test.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,117 @@ describe("handleProjectSearch", () => {
853853
expect(text).toContain("matching platform 'rust'");
854854
expect(text).not.toContain("not found");
855855
});
856+
857+
test("respects --limit flag", async () => {
858+
await setOrgRegion("org-a", DEFAULT_SENTRY_URL);
859+
await setOrgRegion("org-b", DEFAULT_SENTRY_URL);
860+
861+
const project: SentryProject = {
862+
id: "1",
863+
slug: "frontend",
864+
name: "Frontend",
865+
platform: "javascript",
866+
dateCreated: "2024-01-01T00:00:00Z",
867+
status: "active",
868+
};
869+
870+
// Mock that returns 2 orgs, each with the same project slug
871+
// @ts-expect-error - partial mock
872+
globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
873+
const req = new Request(input, init);
874+
const url = req.url;
875+
876+
if (url.match(/\/projects\/[^/]+\/[^/]+\//)) {
877+
return new Response(JSON.stringify(project), {
878+
status: 200,
879+
headers: { "Content-Type": "application/json" },
880+
});
881+
}
882+
883+
if (url.includes("/organizations/") && !url.includes("/projects/")) {
884+
return new Response(
885+
JSON.stringify([
886+
{ id: "1", slug: "org-a", name: "Org A" },
887+
{ id: "2", slug: "org-b", name: "Org B" },
888+
]),
889+
{
890+
status: 200,
891+
headers: { "Content-Type": "application/json" },
892+
}
893+
);
894+
}
895+
896+
return new Response(JSON.stringify({ detail: "Not found" }), {
897+
status: 404,
898+
});
899+
};
900+
901+
const { writer, output } = createCapture();
902+
903+
await handleProjectSearch(writer, "frontend", {
904+
limit: 1,
905+
json: false,
906+
});
907+
908+
const text = output();
909+
// Should show truncation message since 2 matches but limit is 1
910+
expect(text).toContain("Showing 1 of 2 matches");
911+
expect(text).toContain("--limit");
912+
});
913+
914+
test("--limit also applies to JSON output", async () => {
915+
await setOrgRegion("org-a", DEFAULT_SENTRY_URL);
916+
await setOrgRegion("org-b", DEFAULT_SENTRY_URL);
917+
918+
const project: SentryProject = {
919+
id: "1",
920+
slug: "frontend",
921+
name: "Frontend",
922+
platform: "javascript",
923+
dateCreated: "2024-01-01T00:00:00Z",
924+
status: "active",
925+
};
926+
927+
// @ts-expect-error - partial mock
928+
globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
929+
const req = new Request(input, init);
930+
const url = req.url;
931+
932+
if (url.match(/\/projects\/[^/]+\/[^/]+\//)) {
933+
return new Response(JSON.stringify(project), {
934+
status: 200,
935+
headers: { "Content-Type": "application/json" },
936+
});
937+
}
938+
939+
if (url.includes("/organizations/") && !url.includes("/projects/")) {
940+
return new Response(
941+
JSON.stringify([
942+
{ id: "1", slug: "org-a", name: "Org A" },
943+
{ id: "2", slug: "org-b", name: "Org B" },
944+
]),
945+
{
946+
status: 200,
947+
headers: { "Content-Type": "application/json" },
948+
}
949+
);
950+
}
951+
952+
return new Response(JSON.stringify({ detail: "Not found" }), {
953+
status: 404,
954+
});
955+
};
956+
957+
const { writer, output } = createCapture();
958+
959+
await handleProjectSearch(writer, "frontend", {
960+
limit: 1,
961+
json: true,
962+
});
963+
964+
const parsed = JSON.parse(output());
965+
expect(parsed).toHaveLength(1);
966+
});
856967
});
857968

858969
// ─── displayProjectTable ────────────────────────────────────────

0 commit comments

Comments
 (0)