Skip to content

Commit 04bd8ea

Browse files
committed
fix(tests): isolate mock.module tests to prevent cross-file pollution
Tests in resolver.test.ts and errors.test.ts used mock.module() to mock api-client.js, which persists across test files in Bun's single-threaded runner. When these files ran before utils.test.ts, the module mock intercepted listOrganizations/listProjects calls, returning stale mock data instead of going through the test's globalThis.fetch mock. Move these files to test/isolated/dsn/ so they run in the separate test:isolated script, matching the pattern used by resolve-target.test.ts. Also removes debug logging added in previous commits.
1 parent e9b10f5 commit 04bd8ea

File tree

5 files changed

+11
-105
lines changed

5 files changed

+11
-105
lines changed

src/commands/issue/utils.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -133,34 +133,12 @@ async function resolveProjectSearch(
133133
cwd
134134
);
135135
if (aliasResult) {
136-
// DEBUG
137-
console.error(
138-
"DEBUG resolveProjectSearch: alias hit for",
139-
projectSlug,
140-
"→",
141-
JSON.stringify(aliasResult)
142-
);
143136
return aliasResult;
144137
}
145138

146-
// DEBUG
147-
console.error(
148-
"DEBUG resolveProjectSearch: no alias for",
149-
projectSlug,
150-
", calling findProjectsBySlug"
151-
);
152-
153139
// 2. Search for project across all accessible orgs
154140
const projects = await findProjectsBySlug(projectSlug.toLowerCase());
155141

156-
// DEBUG
157-
console.error(
158-
"DEBUG resolveProjectSearch: findProjectsBySlug returned",
159-
projects.length,
160-
"projects:",
161-
JSON.stringify(projects.map((p) => ({ slug: p.slug, org: p.orgSlug })))
162-
);
163-
164142
if (projects.length === 0) {
165143
throw new ContextError(`Project '${projectSlug}' not found`, commandHint, [
166144
"No project with this slug found in any accessible organization",

src/lib/api-client.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -614,42 +614,19 @@ export function listRepositories(orgSlug: string): Promise<SentryRepository[]> {
614614
export async function findProjectsBySlug(
615615
projectSlug: string
616616
): Promise<ProjectWithOrg[]> {
617-
// DEBUG
618-
console.error("DEBUG findProjectsBySlug: searching for", projectSlug);
619617
const orgs = await listOrganizations();
620-
// DEBUG
621-
console.error(
622-
"DEBUG findProjectsBySlug: listOrganizations returned",
623-
orgs.length,
624-
"orgs:",
625-
JSON.stringify(orgs.map((o) => o.slug))
626-
);
627618

628619
// Search in parallel for performance
629620
const searchResults = await Promise.all(
630621
orgs.map(async (org) => {
631622
try {
632623
const projects = await listProjects(org.slug);
633-
// DEBUG
634-
console.error(
635-
`DEBUG findProjectsBySlug: listProjects(${org.slug}) returned`,
636-
projects.length,
637-
"projects:",
638-
JSON.stringify(projects.map((p) => p.slug))
639-
);
640624
const match = projects.find((p) => p.slug === projectSlug);
641625
if (match) {
642626
return { ...match, orgSlug: org.slug };
643627
}
644628
return null;
645629
} catch (error) {
646-
// DEBUG
647-
console.error(
648-
`DEBUG findProjectsBySlug: listProjects(${org.slug}) threw:`,
649-
error instanceof Error
650-
? `${error.constructor.name}: ${error.message}`
651-
: String(error)
652-
);
653630
// Re-throw auth errors - user needs to login
654631
if (error instanceof AuthError) {
655632
throw error;

test/commands/issue/utils.test.ts

Lines changed: 11 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -306,17 +306,13 @@ describe("resolveOrgAndIssueId", () => {
306306
);
307307
await clearProjectAliases();
308308

309-
const fetchLog: string[] = [];
310-
311309
// @ts-expect-error - partial mock
312310
globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
313311
const req = new Request(input, init);
314312
const url = req.url;
315-
fetchLog.push(`FETCH: ${req.method} ${url}`);
316313

317314
// getUserRegions - return empty regions to use fallback path
318315
if (url.includes("/users/me/regions/")) {
319-
fetchLog.push(" → matched: /users/me/regions/");
320316
return new Response(JSON.stringify({ regions: [] }), {
321317
status: 200,
322318
headers: { "Content-Type": "application/json" },
@@ -329,7 +325,6 @@ describe("resolveOrgAndIssueId", () => {
329325
!url.includes("/projects/") &&
330326
!url.includes("/issues/")
331327
) {
332-
fetchLog.push(" → matched: listOrganizations");
333328
return new Response(
334329
JSON.stringify([{ id: "1", slug: "my-org", name: "My Org" }]),
335330
{
@@ -341,7 +336,6 @@ describe("resolveOrgAndIssueId", () => {
341336

342337
// listProjects for my-org
343338
if (url.includes("organizations/my-org/projects/")) {
344-
fetchLog.push(" → matched: listProjects for my-org");
345339
return new Response(
346340
JSON.stringify([
347341
{ id: "123", slug: "craft", name: "Craft", platform: "javascript" },
@@ -360,7 +354,6 @@ describe("resolveOrgAndIssueId", () => {
360354
}
361355

362356
if (url.includes("organizations/my-org/issues/CRAFT-G")) {
363-
fetchLog.push(" → matched: issue CRAFT-G");
364357
return new Response(
365358
JSON.stringify({
366359
id: "777888999",
@@ -379,43 +372,19 @@ describe("resolveOrgAndIssueId", () => {
379372
);
380373
}
381374

382-
fetchLog.push(" → NO MATCH (returning 404)");
383375
return new Response(JSON.stringify({ detail: "Not found" }), {
384376
status: 404,
385377
});
386378
};
387379

388-
// Debug: Check DB state before calling
389-
const { getDatabase: getDb } = await import("../../../src/lib/db/index.js");
390-
const testDb = getDb();
391-
const authRow = testDb
392-
.query("SELECT * FROM auth WHERE id = 1")
393-
.get() as Record<string, unknown> | null;
394-
const aliasRows = testDb
395-
.query("SELECT * FROM project_aliases")
396-
.all() as Record<string, unknown>[];
397-
const regionRows = testDb
398-
.query("SELECT * FROM org_regions")
399-
.all() as Record<string, unknown>[];
400-
console.error("DEBUG: auth =", JSON.stringify(authRow));
401-
console.error("DEBUG: aliases =", JSON.stringify(aliasRows));
402-
console.error("DEBUG: regions =", JSON.stringify(regionRows));
403-
console.error("DEBUG: SENTRY_CONFIG_DIR =", process.env.SENTRY_CONFIG_DIR);
404-
console.error("DEBUG: cwd =", getConfigDir());
405-
406-
try {
407-
const result = await resolveOrgAndIssueId({
408-
issueArg: "craft-g",
409-
cwd: getConfigDir(),
410-
command: "explain",
411-
});
380+
const result = await resolveOrgAndIssueId({
381+
issueArg: "craft-g",
382+
cwd: getConfigDir(),
383+
command: "explain",
384+
});
412385

413-
expect(result.org).toBe("my-org");
414-
expect(result.issueId).toBe("777888999");
415-
} catch (error) {
416-
console.error("FETCH LOG:", fetchLog.join("\n"));
417-
throw error;
418-
}
386+
expect(result.org).toBe("my-org");
387+
expect(result.issueId).toBe("777888999");
419388
});
420389

421390
test("throws when project not found in any org", async () => {
@@ -492,17 +461,13 @@ describe("resolveOrgAndIssueId", () => {
492461

493462
await setOrgRegion("org2", DEFAULT_SENTRY_URL);
494463

495-
const fetchLog: string[] = [];
496-
497464
// @ts-expect-error - partial mock
498465
globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
499466
const req = new Request(input, init);
500467
const url = req.url;
501-
fetchLog.push(`FETCH: ${req.method} ${url}`);
502468

503469
// getUserRegions - return empty regions to use fallback path
504470
if (url.includes("/users/me/regions/")) {
505-
fetchLog.push(" → matched: /users/me/regions/");
506471
return new Response(JSON.stringify({ regions: [] }), {
507472
status: 200,
508473
headers: { "Content-Type": "application/json" },
@@ -515,7 +480,6 @@ describe("resolveOrgAndIssueId", () => {
515480
!url.includes("/projects/") &&
516481
!url.includes("/issues/")
517482
) {
518-
fetchLog.push(" → matched: listOrganizations");
519483
return new Response(
520484
JSON.stringify([
521485
{ id: "1", slug: "org1", name: "Org 1" },
@@ -530,7 +494,6 @@ describe("resolveOrgAndIssueId", () => {
530494

531495
// listProjects for org1 - has "common" project
532496
if (url.includes("organizations/org1/projects/")) {
533-
fetchLog.push(" → matched: listProjects for org1");
534497
return new Response(
535498
JSON.stringify([
536499
{
@@ -549,7 +512,6 @@ describe("resolveOrgAndIssueId", () => {
549512

550513
// listProjects for org2 - also has "common" project
551514
if (url.includes("organizations/org2/projects/")) {
552-
fetchLog.push(" → matched: listProjects for org2");
553515
return new Response(
554516
JSON.stringify([
555517
{ id: "456", slug: "common", name: "Common", platform: "python" },
@@ -561,29 +523,18 @@ describe("resolveOrgAndIssueId", () => {
561523
);
562524
}
563525

564-
fetchLog.push(" → NO MATCH (returning 404)");
565526
return new Response(JSON.stringify({ detail: "Not found" }), {
566527
status: 404,
567528
});
568529
};
569530

570-
try {
571-
await resolveOrgAndIssueId({
531+
await expect(
532+
resolveOrgAndIssueId({
572533
issueArg: "common-g",
573534
cwd: getConfigDir(),
574535
command: "explain",
575-
});
576-
// If we get here, the function didn't throw — log and fail
577-
console.error("FETCH LOG (no throw):", fetchLog.join("\n"));
578-
throw new Error("Expected resolveOrgAndIssueId to throw but it resolved");
579-
} catch (error: unknown) {
580-
const msg = error instanceof Error ? error.message : String(error);
581-
if (!msg.includes("multiple organizations")) {
582-
console.error("FETCH LOG (wrong error):", fetchLog.join("\n"));
583-
console.error("Actual error:", msg);
584-
}
585-
expect(msg).toContain("multiple organizations");
586-
}
536+
})
537+
).rejects.toThrow("multiple organizations");
587538
});
588539

589540
test("short suffix auth error (401) propagates", async () => {

0 commit comments

Comments
 (0)