Skip to content

Commit 130b782

Browse files
committed
chore: add retries to setup in case the environment is unstable
1 parent f78fb4c commit 130b782

File tree

4 files changed

+73
-22
lines changed

4 files changed

+73
-22
lines changed

tests/integration/tools/atlas/accessLists.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { describeWithAtlas, withProject } from "./atlasHelpers.js";
1+
import { afterAllWithRetry, beforeAllWithRetry, describeWithAtlas, withProject } from "./atlasHelpers.js";
22
import { expectDefined, getResponseElements } from "../../helpers.js";
3-
import { afterAll, beforeAll, describe, expect, it } from "vitest";
3+
import { describe, expect, it } from "vitest";
44
import { ensureCurrentIpInAccessList } from "../../../../src/common/atlas/accessListUtils.js";
55

66
function generateRandomIp(): string {
@@ -17,13 +17,13 @@ describeWithAtlas("ip access lists", (integration) => {
1717
const cidrBlocks = [generateRandomIp() + "/16", generateRandomIp() + "/24"];
1818
const values = [...ips, ...cidrBlocks];
1919

20-
beforeAll(async () => {
20+
beforeAllWithRetry(async () => {
2121
const apiClient = integration.mcpServer().session.apiClient;
2222
const ipInfo = await apiClient.getIpInfo();
2323
values.push(ipInfo.currentIpv4Address);
2424
});
2525

26-
afterAll(async () => {
26+
afterAllWithRetry(async () => {
2727
const apiClient = integration.mcpServer().session.apiClient;
2828

2929
const projectId = getProjectId();

tests/integration/tools/atlas/atlasHelpers.ts

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { ApiClient } from "../../../../src/common/atlas/apiClient.js";
44
import type { IntegrationTest } from "../../helpers.js";
55
import { setupIntegrationTest, defaultTestConfig, defaultDriverOptions } from "../../helpers.js";
66
import type { SuiteCollector } from "vitest";
7-
import { afterAll, beforeAll, describe } from "vitest";
7+
import { afterAll, beforeEach, describe } from "vitest";
88

99
export type IntegrationTestFunction = (integration: IntegrationTest) => void;
1010

@@ -36,19 +36,13 @@ export function withProject(integration: IntegrationTest, fn: ProjectTestFunctio
3636
return describe("with project", () => {
3737
let projectId: string = "";
3838

39-
beforeAll(async () => {
39+
beforeAllWithRetry(async () => {
4040
const apiClient = integration.mcpServer().session.apiClient;
41-
42-
try {
43-
const group = await createProject(apiClient);
44-
projectId = group.id;
45-
} catch (error) {
46-
console.error("Failed to create project:", error);
47-
throw error;
48-
}
41+
const group = await createProject(apiClient);
42+
projectId = group.id;
4943
});
5044

51-
afterAll(async () => {
45+
afterAllWithRetry(async () => {
5246
const apiClient = integration.mcpServer().session.apiClient;
5347
if (projectId) {
5448
// projectId may be empty if beforeAll failed.
@@ -70,6 +64,63 @@ export function withProject(integration: IntegrationTest, fn: ProjectTestFunctio
7064
});
7165
}
7266

67+
export function beforeAllWithRetry(fixture: () => Promise<void>): void {
68+
beforeEach(async () => {
69+
const MAX_SETUP_ATTEMPTS = 10;
70+
const SETUP_BACKOFF_MS = 10;
71+
let lastError: Error | undefined = undefined;
72+
73+
for (let attempt = 0; attempt < MAX_SETUP_ATTEMPTS; attempt++) {
74+
try {
75+
await fixture();
76+
lastError = undefined;
77+
break;
78+
} catch (error: unknown) {
79+
if (error instanceof Error) {
80+
lastError = error;
81+
} else {
82+
lastError = new Error(String(error));
83+
}
84+
85+
console.error("beforeAll(attempt:", attempt, "):", error);
86+
await new Promise((resolve) => setTimeout(resolve, SETUP_BACKOFF_MS * attempt));
87+
}
88+
}
89+
90+
if (lastError) {
91+
throw lastError;
92+
}
93+
});
94+
}
95+
96+
export function afterAllWithRetry(fixture: () => Promise<void>): void {
97+
afterAll(async () => {
98+
const MAX_SETUP_ATTEMPTS = 10;
99+
const SETUP_BACKOFF_MS = 10;
100+
let lastError: Error | undefined = undefined;
101+
102+
for (let attempt = 0; attempt < MAX_SETUP_ATTEMPTS; attempt++) {
103+
try {
104+
await fixture();
105+
lastError = undefined;
106+
break;
107+
} catch (error) {
108+
if (error instanceof Error) {
109+
lastError = error;
110+
} else {
111+
lastError = new Error(String(error));
112+
}
113+
console.error("afterAll(attempt:", attempt, "):", error);
114+
await new Promise((resolve) => setTimeout(resolve, SETUP_BACKOFF_MS * attempt));
115+
}
116+
}
117+
118+
if (lastError) {
119+
throw lastError;
120+
}
121+
});
122+
}
123+
73124
export function parseTable(text: string): Record<string, string>[] {
74125
const data = text
75126
.split("\n")

tests/integration/tools/atlas/clusters.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { Session } from "../../../../src/common/session.js";
22
import { expectDefined, getResponseElements } from "../../helpers.js";
3-
import { describeWithAtlas, withProject, randomId } from "./atlasHelpers.js";
3+
import { describeWithAtlas, withProject, randomId, afterAllWithRetry, beforeAllWithRetry } from "./atlasHelpers.js";
44
import type { ClusterDescription20240805 } from "../../../../src/common/atlas/openapi.js";
5-
import { afterAll, beforeAll, describe, expect, it } from "vitest";
5+
import { describe, expect, it } from "vitest";
66

77
function sleep(ms: number): Promise<void> {
88
return new Promise((resolve) => setTimeout(resolve, ms));
@@ -60,7 +60,7 @@ describeWithAtlas("clusters", (integration) => {
6060
withProject(integration, ({ getProjectId }) => {
6161
const clusterName = "ClusterTest-" + randomId;
6262

63-
afterAll(async () => {
63+
afterAllWithRetry(async () => {
6464
const projectId = getProjectId();
6565
if (projectId) {
6666
const session: Session = integration.mcpServer().session;
@@ -160,7 +160,7 @@ describeWithAtlas("clusters", (integration) => {
160160
});
161161

162162
describe("atlas-connect-cluster", () => {
163-
beforeAll(async () => {
163+
beforeAllWithRetry(async () => {
164164
const projectId = getProjectId();
165165
await waitCluster(integration.mcpServer().session, projectId, clusterName, (cluster) => {
166166
return (

tests/integration/tools/atlas/projects.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { ObjectId } from "mongodb";
2-
import { parseTable, describeWithAtlas } from "./atlasHelpers.js";
2+
import { parseTable, describeWithAtlas, afterAllWithRetry } from "./atlasHelpers.js";
33
import { expectDefined, getDataFromUntrustedContent, getResponseElements } from "../../helpers.js";
4-
import { afterAll, describe, expect, it } from "vitest";
4+
import { describe, expect, it } from "vitest";
55

66
const randomId = new ObjectId().toString();
77

88
describeWithAtlas("projects", (integration) => {
99
const projName = "testProj-" + randomId;
1010

11-
afterAll(async () => {
11+
afterAllWithRetry(async () => {
1212
const session = integration.mcpServer().session;
1313

1414
const projects = await session.apiClient.listProjects();

0 commit comments

Comments
 (0)