Skip to content

Commit 2c88c9c

Browse files
betegonclaude
andcommitted
test: verify DSN shortcut and cached-orgs optimizations
Add two targeted tests proving the performance optimizations work: 1. resolveIssue with project-search format uses DSN shortcut when the DSN-detected project matches, skipping the expensive listOrganizations() fan-out entirely. 2. findProjectsBySlug uses cached org_regions to skip getUserRegions() + listOrganizationsInRegion() when org data is already cached from a previous command. Both tests verify the optimization by tracking HTTP requests and asserting that /users/me/regions/ and /organizations/ are never called. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0516332 commit 2c88c9c

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

test/commands/issue/utils.test.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from "../../../src/commands/issue/utils.js";
1515
import { DEFAULT_SENTRY_URL } from "../../../src/lib/constants.js";
1616
import { setAuthToken } from "../../../src/lib/db/auth.js";
17+
import { setCachedProject } from "../../../src/lib/db/project-cache.js";
1718
import { setOrgRegion } from "../../../src/lib/db/regions.js";
1819
import { ResolutionError } from "../../../src/lib/errors.js";
1920
import { useTestConfigDir } from "../../helpers.js";
@@ -1657,3 +1658,95 @@ describe("resolveIssue: numeric 404 error handling", () => {
16571658
).rejects.not.toBeInstanceOf(ResolutionError);
16581659
});
16591660
});
1661+
1662+
describe("resolveIssue: project-search DSN shortcut", () => {
1663+
const getDsnTestConfigDir = useTestConfigDir("test-dsn-shortcut-", {
1664+
isolateProjectRoot: true,
1665+
});
1666+
1667+
let dsnOriginalFetch: typeof globalThis.fetch;
1668+
1669+
beforeEach(async () => {
1670+
dsnOriginalFetch = globalThis.fetch;
1671+
await setAuthToken("test-token");
1672+
await setOrgRegion("my-org", DEFAULT_SENTRY_URL);
1673+
// Seed project cache so resolveFromDsn resolves without any API call.
1674+
// orgId is "123" (DSN parser strips the "o" prefix from o123.ingest.*)
1675+
await setCachedProject("123", "456", {
1676+
orgSlug: "my-org",
1677+
orgName: "My Org",
1678+
projectSlug: "my-project",
1679+
projectName: "My Project",
1680+
projectId: "456",
1681+
});
1682+
});
1683+
1684+
afterEach(() => {
1685+
globalThis.fetch = dsnOriginalFetch;
1686+
});
1687+
1688+
test("uses DSN shortcut when project matches, skips listOrganizations", async () => {
1689+
const { writeFileSync } = await import("node:fs");
1690+
const { join } = await import("node:path");
1691+
const cwd = getDsnTestConfigDir();
1692+
1693+
// Write a DSN so detectDsn finds it
1694+
writeFileSync(
1695+
join(cwd, ".env"),
1696+
"SENTRY_DSN=https://abc@o123.ingest.us.sentry.io/456"
1697+
);
1698+
1699+
const requests: string[] = [];
1700+
1701+
// @ts-expect-error - partial mock
1702+
globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
1703+
const req = new Request(input, init);
1704+
const url = req.url;
1705+
requests.push(url);
1706+
1707+
// Short ID resolution — the only HTTP call the shortcut should make
1708+
if (url.includes("/shortids/MY-PROJECT-5BS/")) {
1709+
return new Response(
1710+
JSON.stringify({
1711+
organizationSlug: "my-org",
1712+
projectSlug: "my-project",
1713+
group: {
1714+
id: "999",
1715+
shortId: "MY-PROJECT-5BS",
1716+
title: "Test Issue",
1717+
status: "unresolved",
1718+
platform: "javascript",
1719+
type: "error",
1720+
count: "1",
1721+
userCount: 1,
1722+
},
1723+
}),
1724+
{ status: 200, headers: { "Content-Type": "application/json" } }
1725+
);
1726+
}
1727+
1728+
return new Response(JSON.stringify({ detail: "Not found" }), {
1729+
status: 404,
1730+
headers: { "Content-Type": "application/json" },
1731+
});
1732+
};
1733+
1734+
const result = await resolveIssue({
1735+
issueArg: "my-project-5BS",
1736+
cwd,
1737+
command: "view",
1738+
});
1739+
1740+
// Shortcut resolved correctly
1741+
expect(result.org).toBe("my-org");
1742+
expect(result.issue.id).toBe("999");
1743+
1744+
// The expensive listOrganizations calls were skipped
1745+
expect(requests.some((r) => r.includes("/users/me/regions/"))).toBe(false);
1746+
expect(
1747+
requests.some(
1748+
(r) => r.includes("/organizations/") && !r.includes("/shortids/")
1749+
)
1750+
).toBe(false);
1751+
});
1752+
});

test/lib/api-client.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,52 @@ describe("findProjectsBySlug", () => {
827827
const { projects } = await findProjectsBySlug("wrong-slug");
828828
expect(projects).toHaveLength(0);
829829
});
830+
831+
test("uses cached org regions to skip listOrganizations", async () => {
832+
const { findProjectsBySlug } = await import("../../src/lib/api-client.js");
833+
834+
// Seed org_regions cache — this is the optimization under test
835+
await setOrgRegion("acme", DEFAULT_SENTRY_URL);
836+
837+
const requests: string[] = [];
838+
839+
globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
840+
const req = new Request(input, init);
841+
const url = req.url;
842+
requests.push(url);
843+
844+
// getProject for acme/frontend — success
845+
if (url.includes("/projects/acme/frontend/")) {
846+
return new Response(
847+
JSON.stringify({ id: "101", slug: "frontend", name: "Frontend" }),
848+
{ status: 200, headers: { "Content-Type": "application/json" } }
849+
);
850+
}
851+
852+
return new Response(JSON.stringify({ detail: "Not found" }), {
853+
status: 404,
854+
headers: { "Content-Type": "application/json" },
855+
});
856+
};
857+
858+
const { projects, orgs } = await findProjectsBySlug("frontend");
859+
860+
// Found via cached orgs
861+
expect(projects).toHaveLength(1);
862+
expect(projects[0].slug).toBe("frontend");
863+
expect(projects[0].orgSlug).toBe("acme");
864+
865+
// orgs is empty because we used cached path (no full listing)
866+
expect(orgs).toHaveLength(0);
867+
868+
// The expensive listOrganizations calls were skipped
869+
expect(requests.some((r) => r.includes("/users/me/regions/"))).toBe(false);
870+
expect(
871+
requests.some(
872+
(r) => r.includes("/organizations/") && !r.includes("/projects/")
873+
)
874+
).toBe(false);
875+
});
830876
});
831877

832878
describe("resolveEventInOrg", () => {

0 commit comments

Comments
 (0)