-
Notifications
You must be signed in to change notification settings - Fork 411
fix: implement retry logic for creating test suites to handle concurr… #844
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1836e02
3dc09b1
3ce2d4d
681d0d8
98465a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -100,31 +100,55 @@ function configureTestPlanTools(server: McpServer, _: () => Promise<string>, con | |||||||||||||||||||||||||||||
| name: z.string().describe("Name of the child test suite"), | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| async ({ project, planId, parentSuiteId, name }) => { | ||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||
| const connection = await connectionProvider(); | ||||||||||||||||||||||||||||||
| const testPlanApi = await connection.getTestPlanApi(); | ||||||||||||||||||||||||||||||
| const maxRetries = 5; | ||||||||||||||||||||||||||||||
| const baseDelay = 500; // milliseconds | ||||||||||||||||||||||||||||||
| const jitterMax = 200; // milliseconds | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| for (let attempt = 0; attempt <= maxRetries; attempt++) { | ||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||
| const connection = await connectionProvider(); | ||||||||||||||||||||||||||||||
| const testPlanApi = await connection.getTestPlanApi(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const testSuiteToCreate = { | ||||||||||||||||||||||||||||||
| name, | ||||||||||||||||||||||||||||||
| parentSuite: { | ||||||||||||||||||||||||||||||
| id: parentSuiteId, | ||||||||||||||||||||||||||||||
| name: "", | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| suiteType: 2, | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const testSuiteToCreate = { | ||||||||||||||||||||||||||||||
| name, | ||||||||||||||||||||||||||||||
| parentSuite: { | ||||||||||||||||||||||||||||||
| id: parentSuiteId, | ||||||||||||||||||||||||||||||
| name: "", | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| suiteType: 2, | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
| const createdTestSuite = await testPlanApi.createTestSuite(testSuiteToCreate, project, planId); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const createdTestSuite = await testPlanApi.createTestSuite(testSuiteToCreate, project, planId); | ||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||
| content: [{ type: "text", text: JSON.stringify(createdTestSuite, null, 2) }], | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||
| const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||
| content: [{ type: "text", text: JSON.stringify(createdTestSuite, null, 2) }], | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||
| const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; | ||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||
| content: [{ type: "text", text: `Error creating test suite: ${errorMessage}` }], | ||||||||||||||||||||||||||||||
| isError: true, | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
| // Check if it's a concurrency conflict error | ||||||||||||||||||||||||||||||
| const isConcurrencyError = errorMessage.includes("TF26071") || errorMessage.includes("got update") || errorMessage.includes("changed by someone else"); | ||||||||||||||||||||||||||||||
|
Comment on lines
+129
to
+130
|
||||||||||||||||||||||||||||||
| // Check if it's a concurrency conflict error | |
| const isConcurrencyError = errorMessage.includes("TF26071") || errorMessage.includes("got update") || errorMessage.includes("changed by someone else"); | |
| // Prefer structured error information when available | |
| const typedError = error as { statusCode?: number; code?: string; message?: string }; | |
| const statusCode = typeof typedError?.statusCode === "number" ? typedError.statusCode : undefined; | |
| const errorCode = typeof typedError?.code === "string" ? typedError.code : undefined; | |
| // Check if it's a concurrency conflict error | |
| const isConcurrencyError = | |
| statusCode === 409 || // HTTP 409 Conflict | |
| errorCode === "ConcurrencyConflictException" || // Azure DevOps concurrency error code (if provided) | |
| errorMessage.includes("TF26071") || | |
| errorMessage.includes("got update") || | |
| errorMessage.includes("changed by someone else"); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,2 @@ | ||
| export const packageVersion = "2.4.0"; | ||
|
|
Uh oh!
There was an error while loading. Please reload this page.