@alternative-path/qa-path-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/QUICK_INSTALL.md +133 -0
- package/README.md +226 -0
- package/TOOLS_DOCUMENTATION.md +675 -0
- package/dist/__tests__/tools/module-tools.test.d.ts +2 -0
- package/dist/__tests__/tools/module-tools.test.d.ts.map +1 -0
- package/dist/__tests__/tools/module-tools.test.js +145 -0
- package/dist/__tests__/tools/module-tools.test.js.map +1 -0
- package/dist/__tests__/tools/project-tools.test.d.ts +2 -0
- package/dist/__tests__/tools/project-tools.test.d.ts.map +1 -0
- package/dist/__tests__/tools/project-tools.test.js +674 -0
- package/dist/__tests__/tools/project-tools.test.js.map +1 -0
- package/dist/__tests__/tools/query-tools.test.d.ts +2 -0
- package/dist/__tests__/tools/query-tools.test.d.ts.map +1 -0
- package/dist/__tests__/tools/query-tools.test.js +225 -0
- package/dist/__tests__/tools/query-tools.test.js.map +1 -0
- package/dist/__tests__/tools/testgroup-launch-tools.test.d.ts +2 -0
- package/dist/__tests__/tools/testgroup-launch-tools.test.d.ts.map +1 -0
- package/dist/__tests__/tools/testgroup-launch-tools.test.js +553 -0
- package/dist/__tests__/tools/testgroup-launch-tools.test.js.map +1 -0
- package/dist/__tests__/utils/mcp-error-mapper.test.d.ts +2 -0
- package/dist/__tests__/utils/mcp-error-mapper.test.d.ts.map +1 -0
- package/dist/__tests__/utils/mcp-error-mapper.test.js +240 -0
- package/dist/__tests__/utils/mcp-error-mapper.test.js.map +1 -0
- package/dist/__tests__/utils/mcp-response.test.d.ts +2 -0
- package/dist/__tests__/utils/mcp-response.test.d.ts.map +1 -0
- package/dist/__tests__/utils/mcp-response.test.js +72 -0
- package/dist/__tests__/utils/mcp-response.test.js.map +1 -0
- package/dist/agents/test-planner-context.d.ts +7 -0
- package/dist/agents/test-planner-context.d.ts.map +1 -0
- package/dist/agents/test-planner-context.js +283 -0
- package/dist/agents/test-planner-context.js.map +1 -0
- package/dist/agents/test-planner-prompt.d.ts +34 -0
- package/dist/agents/test-planner-prompt.d.ts.map +1 -0
- package/dist/agents/test-planner-prompt.js +82 -0
- package/dist/agents/test-planner-prompt.js.map +1 -0
- package/dist/api-client.d.ts +52 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +285 -0
- package/dist/api-client.js.map +1 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +7 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +175 -0
- package/dist/index.js.map +1 -0
- package/dist/services/project-context-service.d.ts +15 -0
- package/dist/services/project-context-service.d.ts.map +1 -0
- package/dist/services/project-context-service.js +36 -0
- package/dist/services/project-context-service.js.map +1 -0
- package/dist/tools/auth-tools.d.ts +16 -0
- package/dist/tools/auth-tools.d.ts.map +1 -0
- package/dist/tools/auth-tools.js +66 -0
- package/dist/tools/auth-tools.js.map +1 -0
- package/dist/tools/automation-tools.d.ts +28 -0
- package/dist/tools/automation-tools.d.ts.map +1 -0
- package/dist/tools/automation-tools.js +541 -0
- package/dist/tools/automation-tools.js.map +1 -0
- package/dist/tools/export-import-tools.d.ts +18 -0
- package/dist/tools/export-import-tools.d.ts.map +1 -0
- package/dist/tools/export-import-tools.js +61 -0
- package/dist/tools/export-import-tools.js.map +1 -0
- package/dist/tools/module-tools.d.ts +43 -0
- package/dist/tools/module-tools.d.ts.map +1 -0
- package/dist/tools/module-tools.js +289 -0
- package/dist/tools/module-tools.js.map +1 -0
- package/dist/tools/project-context-tools.d.ts +19 -0
- package/dist/tools/project-context-tools.d.ts.map +1 -0
- package/dist/tools/project-context-tools.js +133 -0
- package/dist/tools/project-context-tools.js.map +1 -0
- package/dist/tools/project-tools.d.ts +47 -0
- package/dist/tools/project-tools.d.ts.map +1 -0
- package/dist/tools/project-tools.js +362 -0
- package/dist/tools/project-tools.js.map +1 -0
- package/dist/tools/query-tools.d.ts +22 -0
- package/dist/tools/query-tools.d.ts.map +1 -0
- package/dist/tools/query-tools.js +127 -0
- package/dist/tools/query-tools.js.map +1 -0
- package/dist/tools/testcase-tools.d.ts +135 -0
- package/dist/tools/testcase-tools.d.ts.map +1 -0
- package/dist/tools/testcase-tools.js +845 -0
- package/dist/tools/testcase-tools.js.map +1 -0
- package/dist/tools/testgroup-launch-tools.d.ts +37 -0
- package/dist/tools/testgroup-launch-tools.d.ts.map +1 -0
- package/dist/tools/testgroup-launch-tools.js +727 -0
- package/dist/tools/testgroup-launch-tools.js.map +1 -0
- package/dist/utils/mcp-error-mapper.d.ts +27 -0
- package/dist/utils/mcp-error-mapper.d.ts.map +1 -0
- package/dist/utils/mcp-error-mapper.js +79 -0
- package/dist/utils/mcp-error-mapper.js.map +1 -0
- package/dist/utils/mcp-response.d.ts +26 -0
- package/dist/utils/mcp-response.d.ts.map +1 -0
- package/dist/utils/mcp-response.js +34 -0
- package/dist/utils/mcp-response.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,727 @@
|
|
|
1
|
+
import { jsonResponse } from "../utils/mcp-response.js";
|
|
2
|
+
/**
|
|
3
|
+
* MCP tools for test groups and test launches.
|
|
4
|
+
* - list_testgroups: list test groups in a project (with pagination support)
|
|
5
|
+
* - get_testgroup: get a specific test group by ID
|
|
6
|
+
* - create_testgroup: create a new test group
|
|
7
|
+
* - update_tests_in_testgroup: add or remove test cases from an existing test group
|
|
8
|
+
* - create_test_launch: create a new test launch in an existing test group
|
|
9
|
+
* - update_test_launch: update details of an existing test launch (status: completed | aborted)
|
|
10
|
+
*/
|
|
11
|
+
export class TestGroupLaunchTools {
|
|
12
|
+
apiClient;
|
|
13
|
+
projectContext;
|
|
14
|
+
constructor(apiClient, projectContext) {
|
|
15
|
+
this.apiClient = apiClient;
|
|
16
|
+
this.projectContext = projectContext;
|
|
17
|
+
}
|
|
18
|
+
getTools() {
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
name: "list_testgroups",
|
|
22
|
+
description: "List test groups in a project with filtering and sorting. Returns test groups with their details including name, description, and associated test case counts. Supports text-based queries in canonical format for efficient filtering (e.g., \"name contains 'API' AND createdByUser = 'user-id'\"). IMPORTANT: Always call get_query_schema with entity='testgroup' first to understand available fields, operators, and valid values. Use field names (not labels) in queries, and use IDs for association fields like createdByUser.",
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: "object",
|
|
25
|
+
properties: {
|
|
26
|
+
query: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "Text query in canonical format for filtering test groups (e.g., \"name = 'API Test Group'\", \"name contains 'API'\", \"createdByUser = 'user-id' AND created_at > '2024-01-01'\"). IMPORTANT: Always call get_query_schema with entity='testgroup' first to understand available fields, operators, and valid values. Use field names (not labels) in queries. Use IDs for association fields like createdByUser. Leave empty or omit only if you need ALL test groups. For finding a specific test group, use a query filter instead of fetching all test groups. Max length: 5000 characters.",
|
|
29
|
+
maxLength: 5000,
|
|
30
|
+
},
|
|
31
|
+
sortField: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Optional field to sort by. Allowed values: 'name', 'description', 'key', 'group_type', 'created_at', 'updated_at', 'next_scheduled_run', 'createdByUser'.",
|
|
34
|
+
enum: ["name", "description", "key", "group_type", "created_at", "updated_at", "next_scheduled_run", "createdByUser"],
|
|
35
|
+
},
|
|
36
|
+
sortOrder: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "Optional sort order: 'ASC' or 'DESC'. Defaults to 'DESC'.",
|
|
39
|
+
enum: ["ASC", "DESC"],
|
|
40
|
+
},
|
|
41
|
+
page: {
|
|
42
|
+
type: "number",
|
|
43
|
+
description: "Page number (required, minimum: 1).",
|
|
44
|
+
minimum: 1,
|
|
45
|
+
},
|
|
46
|
+
pageSize: {
|
|
47
|
+
type: "number",
|
|
48
|
+
description: "Number of results per page (required, minimum: 1, maximum: 10000).",
|
|
49
|
+
minimum: 1,
|
|
50
|
+
maximum: 10000,
|
|
51
|
+
},
|
|
52
|
+
projectId: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "Optional project ID (UUID). If not provided, uses the active project set via 'set_active_project'. " +
|
|
55
|
+
"If no active project is set, the tool will return an error with instructions on how to set one.",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
required: ["page", "pageSize"],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "get_testgroup",
|
|
63
|
+
description: "Get detailed information about a specific test group by ID. Returns the test group with all its details including associated test cases.",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
testGroupId: {
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "UUID of the test group to retrieve (required).",
|
|
70
|
+
},
|
|
71
|
+
projectId: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "Optional project ID (UUID). If not provided, uses the active project set via 'set_active_project'. " +
|
|
74
|
+
"If no active project is set, the tool will return an error with instructions on how to set one.",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
required: ["testGroupId"],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "create_testgroup",
|
|
82
|
+
description: "Create a new test group in a project. Test groups are used to organize test cases for execution (test launches).",
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: "object",
|
|
85
|
+
properties: {
|
|
86
|
+
name: {
|
|
87
|
+
type: "string",
|
|
88
|
+
description: "Name of the test group (required).",
|
|
89
|
+
},
|
|
90
|
+
description: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "Optional description of the test group.",
|
|
93
|
+
},
|
|
94
|
+
// next_scheduled_run: {
|
|
95
|
+
// type: "string",
|
|
96
|
+
// description:
|
|
97
|
+
// "Optional ISO 8601 datetime for next scheduled run (e.g. 2025-02-01T10:00:00.000Z).",
|
|
98
|
+
// },
|
|
99
|
+
projectId: {
|
|
100
|
+
type: "string",
|
|
101
|
+
description: "Optional project ID (UUID). If not provided, uses the active project set via 'set_active_project'. " +
|
|
102
|
+
"If no active project is set, the tool will return an error with instructions on how to set one.",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
required: ["name"],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "update_tests_in_testgroup",
|
|
110
|
+
description: "Add and/or remove test cases from an existing test group. Provide addTestCaseIds to add test cases, removeTestCaseIds to remove them. At least one of addTestCaseIds or removeTestCaseIds must be provided.",
|
|
111
|
+
inputSchema: {
|
|
112
|
+
type: "object",
|
|
113
|
+
properties: {
|
|
114
|
+
testGroupId: {
|
|
115
|
+
type: "string",
|
|
116
|
+
description: "UUID of the test group to update.",
|
|
117
|
+
},
|
|
118
|
+
addTestCaseIds: {
|
|
119
|
+
type: "array",
|
|
120
|
+
items: { type: "string" },
|
|
121
|
+
description: "Optional array of test case UUIDs to add to the test group.",
|
|
122
|
+
},
|
|
123
|
+
removeTestCaseIds: {
|
|
124
|
+
type: "array",
|
|
125
|
+
items: { type: "string" },
|
|
126
|
+
description: "Optional array of test case UUIDs to remove from the test group.",
|
|
127
|
+
},
|
|
128
|
+
projectId: {
|
|
129
|
+
type: "string",
|
|
130
|
+
description: "Optional project ID (UUID). If not provided, uses the active project set via 'set_active_project'. " +
|
|
131
|
+
"If no active project is set, the tool will return an error with instructions on how to set one.",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
required: ["testGroupId"],
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "list_environments",
|
|
139
|
+
description: "List environments for the project. Use this to get environment IDs for create_test_launch when the user has not specified environment by name. Required for create_test_launch flow.",
|
|
140
|
+
inputSchema: {
|
|
141
|
+
type: "object",
|
|
142
|
+
properties: {
|
|
143
|
+
projectId: {
|
|
144
|
+
type: "string",
|
|
145
|
+
description: "Optional project ID (UUID). If not provided, uses the active project set via 'set_active_project'.",
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
required: [],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "list_llm_models",
|
|
153
|
+
description: "List LLM (automation) models available for test launches. Use this to get llmModelId for create_test_launch when the user has not specified model by name. Required for create_test_launch flow.",
|
|
154
|
+
inputSchema: {
|
|
155
|
+
type: "object",
|
|
156
|
+
properties: {
|
|
157
|
+
projectId: {
|
|
158
|
+
type: "string",
|
|
159
|
+
description: "Optional project ID (UUID). If not provided, uses the active project. Backend uses org context.",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
required: [],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "create_test_launch",
|
|
167
|
+
description: "Create a new test launch in an existing test group. A test launch runs the test cases associated with the group. Requires environmentId and llmModelId (UUIDs). If the user provided environment or LLM by name, call list_environments and list_llm_models first, map the user's choice to the id from the response, then call this tool with those ids.",
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
testGroupId: {
|
|
172
|
+
type: "string",
|
|
173
|
+
description: "UUID of the test group to launch.",
|
|
174
|
+
},
|
|
175
|
+
name: {
|
|
176
|
+
type: "string",
|
|
177
|
+
description: "Optional name for the launch (max 100 characters). If not provided, a default may be used.",
|
|
178
|
+
},
|
|
179
|
+
environmentId: {
|
|
180
|
+
type: "string",
|
|
181
|
+
description: "Environment UUID for the launch (required). Get ids from list_environments. If the user gave an environment name, call list_environments and map the name to the id, then pass it here.",
|
|
182
|
+
},
|
|
183
|
+
llmModelId: {
|
|
184
|
+
type: "string",
|
|
185
|
+
description: "LLM model UUID for the launch (required). Get ids from list_llm_models. If the user gave a model name, call list_llm_models and map the name to the id, then pass it here.",
|
|
186
|
+
},
|
|
187
|
+
projectId: {
|
|
188
|
+
type: "string",
|
|
189
|
+
description: "Optional project ID (UUID). If not provided, uses the active project set via 'set_active_project'. " +
|
|
190
|
+
"If no active project is set, the tool will return an error with instructions on how to set one.",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
required: ["testGroupId"],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: "list_test_launches",
|
|
198
|
+
description: "List test launches for a specific test group. Supports searching by name/key and filtering by status. Use this to find a launch by name (e.g., 'Regression launch') before updating its status.",
|
|
199
|
+
inputSchema: {
|
|
200
|
+
type: "object",
|
|
201
|
+
properties: {
|
|
202
|
+
testGroupId: {
|
|
203
|
+
type: "string",
|
|
204
|
+
description: "UUID of the test group to list launches for (required).",
|
|
205
|
+
},
|
|
206
|
+
search: {
|
|
207
|
+
type: "string",
|
|
208
|
+
description: "Optional search term to filter launches by name or key (case-insensitive). Use this to find launches by name, e.g., search for 'Regression launch' to find 'Regression launch'.",
|
|
209
|
+
maxLength: 255,
|
|
210
|
+
},
|
|
211
|
+
status: {
|
|
212
|
+
type: "string",
|
|
213
|
+
enum: ["all", "new", "in_progress", "completed", "aborted", "paused"],
|
|
214
|
+
description: "Optional status filter. Allowed values: 'all', 'new', 'in_progress', 'completed', 'aborted', 'paused'. Defaults to 'all'.",
|
|
215
|
+
},
|
|
216
|
+
page: {
|
|
217
|
+
type: "number",
|
|
218
|
+
description: "Page number (default: 1, minimum: 1).",
|
|
219
|
+
minimum: 1,
|
|
220
|
+
default: 1,
|
|
221
|
+
},
|
|
222
|
+
pageSize: {
|
|
223
|
+
type: "number",
|
|
224
|
+
description: "Number of results per page (default: 25, minimum: 1, maximum: 100).",
|
|
225
|
+
minimum: 1,
|
|
226
|
+
maximum: 100,
|
|
227
|
+
default: 25,
|
|
228
|
+
},
|
|
229
|
+
projectId: {
|
|
230
|
+
type: "string",
|
|
231
|
+
description: "Optional project ID (UUID). If not provided, uses the active project set via 'set_active_project'. " +
|
|
232
|
+
"If no active project is set, the tool will return an error with instructions on how to set one.",
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
required: ["testGroupId"],
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "get_test_launch",
|
|
240
|
+
description: "Get detailed information about a specific test launch by ID. Returns launch details including status, execution statistics, and associated test group information.",
|
|
241
|
+
inputSchema: {
|
|
242
|
+
type: "object",
|
|
243
|
+
properties: {
|
|
244
|
+
launchId: {
|
|
245
|
+
type: "string",
|
|
246
|
+
description: "UUID of the test launch to retrieve (required).",
|
|
247
|
+
},
|
|
248
|
+
projectId: {
|
|
249
|
+
type: "string",
|
|
250
|
+
description: "Optional project ID (UUID). If not provided, uses the active project set via 'set_active_project'. " +
|
|
251
|
+
"If no active project is set, the tool will return an error with instructions on how to set one.",
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
required: ["launchId"],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: "update_test_launch",
|
|
259
|
+
description: "Update an existing test launch. Currently supports updating status to 'completed' or 'aborted' (e.g. to mark a launch as completed or abort a running launch). Use list_test_launches to find the launchId if you only know the launch name.",
|
|
260
|
+
inputSchema: {
|
|
261
|
+
type: "object",
|
|
262
|
+
properties: {
|
|
263
|
+
launchId: {
|
|
264
|
+
type: "string",
|
|
265
|
+
description: "UUID of the test launch to update. Use list_test_launches to find the launchId if you only know the launch name.",
|
|
266
|
+
},
|
|
267
|
+
status: {
|
|
268
|
+
type: "string",
|
|
269
|
+
enum: ["completed", "aborted"],
|
|
270
|
+
description: "New status for the launch: 'completed' or 'aborted'.",
|
|
271
|
+
},
|
|
272
|
+
projectId: {
|
|
273
|
+
type: "string",
|
|
274
|
+
description: "Optional project ID (UUID). If not provided, uses the active project set via 'set_active_project'. " +
|
|
275
|
+
"If no active project is set, the tool will return an error with instructions on how to set one.",
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
required: ["launchId", "status"],
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
handles(name) {
|
|
284
|
+
return [
|
|
285
|
+
"list_testgroups",
|
|
286
|
+
"get_testgroup",
|
|
287
|
+
"create_testgroup",
|
|
288
|
+
"update_tests_in_testgroup",
|
|
289
|
+
"list_environments",
|
|
290
|
+
"list_llm_models",
|
|
291
|
+
"create_test_launch",
|
|
292
|
+
"list_test_launches",
|
|
293
|
+
"get_test_launch",
|
|
294
|
+
"update_test_launch",
|
|
295
|
+
].includes(name);
|
|
296
|
+
}
|
|
297
|
+
async handle(name, args) {
|
|
298
|
+
switch (name) {
|
|
299
|
+
case "list_testgroups":
|
|
300
|
+
return await this.listTestGroups(args);
|
|
301
|
+
case "get_testgroup":
|
|
302
|
+
return await this.getTestGroup(args);
|
|
303
|
+
case "create_testgroup":
|
|
304
|
+
return await this.createTestGroup(args);
|
|
305
|
+
case "update_tests_in_testgroup":
|
|
306
|
+
return await this.updateTestsInTestGroup(args);
|
|
307
|
+
case "list_environments":
|
|
308
|
+
return await this.listEnvironments(args);
|
|
309
|
+
case "list_llm_models":
|
|
310
|
+
return await this.listLlmModels(args);
|
|
311
|
+
case "create_test_launch":
|
|
312
|
+
return await this.createTestLaunch(args);
|
|
313
|
+
case "list_test_launches":
|
|
314
|
+
return await this.listTestLaunches(args);
|
|
315
|
+
case "get_test_launch":
|
|
316
|
+
return await this.getTestLaunch(args);
|
|
317
|
+
case "update_test_launch":
|
|
318
|
+
return await this.updateTestLaunch(args);
|
|
319
|
+
default:
|
|
320
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
getProjectId(args, toolName) {
|
|
324
|
+
return this.projectContext.resolveProjectId(args, toolName);
|
|
325
|
+
}
|
|
326
|
+
async listTestGroups(args) {
|
|
327
|
+
const projectId = this.getProjectId(args, "list_testgroups");
|
|
328
|
+
const { sortField, sortOrder, query, page, pageSize } = args;
|
|
329
|
+
// Validate required pagination parameters
|
|
330
|
+
if (!page || typeof page !== "number" || page < 1) {
|
|
331
|
+
return jsonResponse({
|
|
332
|
+
success: false,
|
|
333
|
+
error_code: "INVALID_PAGE",
|
|
334
|
+
message: "page (number) is required and must be >= 1.",
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
if (!pageSize || typeof pageSize !== "number" || pageSize < 1 || pageSize > 10000) {
|
|
338
|
+
return jsonResponse({
|
|
339
|
+
success: false,
|
|
340
|
+
error_code: "INVALID_PAGE_SIZE",
|
|
341
|
+
message: "pageSize (number) is required and must be between 1 and 10000.",
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
const ALLOWED_SORT_FIELDS = ["name", "description", "key", "group_type", "created_at", "updated_at", "next_scheduled_run", "createdByUser"];
|
|
345
|
+
if (sortField && !ALLOWED_SORT_FIELDS.includes(sortField)) {
|
|
346
|
+
return jsonResponse({
|
|
347
|
+
success: false,
|
|
348
|
+
error_code: "INVALID_SORT_FIELD",
|
|
349
|
+
message: `sortField must be one of: ${ALLOWED_SORT_FIELDS.join(", ")}.`,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
if (query && typeof query === "string" && query.length > 5000) {
|
|
353
|
+
return jsonResponse({
|
|
354
|
+
success: false,
|
|
355
|
+
error_code: "INVALID_QUERY",
|
|
356
|
+
message: "query string must not exceed 5000 characters.",
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
const body = {
|
|
360
|
+
page,
|
|
361
|
+
pageSize,
|
|
362
|
+
};
|
|
363
|
+
if (sortField)
|
|
364
|
+
body.sortField = sortField;
|
|
365
|
+
if (sortOrder)
|
|
366
|
+
body.sortOrder = sortOrder;
|
|
367
|
+
if (query && query.trim().length > 0)
|
|
368
|
+
body.query = query.trim();
|
|
369
|
+
try {
|
|
370
|
+
const response = await this.apiClient.post(`${projectId}/testgroups/get`, body);
|
|
371
|
+
if (response.success !== true) {
|
|
372
|
+
return jsonResponse({
|
|
373
|
+
success: false,
|
|
374
|
+
error_code: "LIST_FAILED",
|
|
375
|
+
message: response.message ?? "Failed to retrieve test groups.",
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const data = response?.data ?? response;
|
|
379
|
+
return jsonResponse({
|
|
380
|
+
success: true,
|
|
381
|
+
message: "Test groups fetched successfully.",
|
|
382
|
+
data,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
catch (err) {
|
|
386
|
+
return jsonResponse({
|
|
387
|
+
success: false,
|
|
388
|
+
error_code: "LIST_FAILED",
|
|
389
|
+
message: err?.message ?? "Failed to fetch test groups.",
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async getTestGroup(args) {
|
|
394
|
+
const projectId = this.getProjectId(args, "get_testgroup");
|
|
395
|
+
const { testGroupId } = args;
|
|
396
|
+
if (!testGroupId || typeof testGroupId !== "string") {
|
|
397
|
+
return jsonResponse({
|
|
398
|
+
success: false,
|
|
399
|
+
message: "testGroupId (string) is required.",
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
const response = await this.apiClient.get(`${projectId}/testgroups/get/${testGroupId}`);
|
|
404
|
+
const data = response?.data ?? response;
|
|
405
|
+
return jsonResponse({
|
|
406
|
+
success: true,
|
|
407
|
+
message: "Test group fetched successfully.",
|
|
408
|
+
data,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
catch (err) {
|
|
412
|
+
return jsonResponse({
|
|
413
|
+
success: false,
|
|
414
|
+
message: err?.message ?? "Failed to fetch test group.",
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
async createTestGroup(args) {
|
|
419
|
+
const projectId = this.getProjectId(args, "create_testgroup");
|
|
420
|
+
const { name, description } = args;
|
|
421
|
+
if (!name || typeof name !== "string") {
|
|
422
|
+
return jsonResponse({
|
|
423
|
+
success: false,
|
|
424
|
+
message: "name (string) is required.",
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
const body = { name };
|
|
429
|
+
if (description != null)
|
|
430
|
+
body.description = description;
|
|
431
|
+
// if (next_scheduled_run != null) body.next_scheduled_run = next_scheduled_run;
|
|
432
|
+
const response = await this.apiClient.post(`${projectId}/testgroups/create`, body);
|
|
433
|
+
const data = response?.data ?? response;
|
|
434
|
+
return jsonResponse({
|
|
435
|
+
success: true,
|
|
436
|
+
message: "Test group created successfully.",
|
|
437
|
+
data,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
catch (err) {
|
|
441
|
+
return jsonResponse({
|
|
442
|
+
success: false,
|
|
443
|
+
message: err?.message ?? "Failed to create test group.",
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async updateTestsInTestGroup(args) {
|
|
448
|
+
const projectId = this.getProjectId(args, "update_tests_in_testgroup");
|
|
449
|
+
const { testGroupId, addTestCaseIds, removeTestCaseIds } = args;
|
|
450
|
+
if (!testGroupId) {
|
|
451
|
+
return jsonResponse({
|
|
452
|
+
success: false,
|
|
453
|
+
message: "testGroupId is required.",
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
const addIds = Array.isArray(addTestCaseIds) ? addTestCaseIds.filter((id) => id) : [];
|
|
457
|
+
const removeIds = Array.isArray(removeTestCaseIds) ? removeTestCaseIds.filter((id) => id) : [];
|
|
458
|
+
if (addIds.length === 0 && removeIds.length === 0) {
|
|
459
|
+
return jsonResponse({
|
|
460
|
+
success: false,
|
|
461
|
+
message: "Provide at least one of addTestCaseIds or removeTestCaseIds (non-empty arrays).",
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
const results = {};
|
|
465
|
+
const errors = [];
|
|
466
|
+
if (addIds.length > 0) {
|
|
467
|
+
try {
|
|
468
|
+
await this.apiClient.post(`${projectId}/testgroups/associate-testcases`, {
|
|
469
|
+
testGroupId,
|
|
470
|
+
testCaseIds: addIds,
|
|
471
|
+
});
|
|
472
|
+
results.added = addIds.length;
|
|
473
|
+
}
|
|
474
|
+
catch (err) {
|
|
475
|
+
errors.push(`Add: ${err?.message ?? String(err)}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (removeIds.length > 0) {
|
|
479
|
+
try {
|
|
480
|
+
await this.apiClient.delete(`${projectId}/testgroups/remove-testcases/${testGroupId}`, { data: { testCaseIds: removeIds } });
|
|
481
|
+
results.removed = removeIds.length;
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
errors.push(`Remove: ${err?.message ?? String(err)}`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (errors.length > 0) {
|
|
488
|
+
results.errors = errors;
|
|
489
|
+
return jsonResponse({
|
|
490
|
+
success: errors.length < 2,
|
|
491
|
+
message: errors.length === 2
|
|
492
|
+
? "Both add and remove failed."
|
|
493
|
+
: "One operation failed.",
|
|
494
|
+
...results,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
return jsonResponse({
|
|
498
|
+
success: true,
|
|
499
|
+
message: "Test group test cases updated successfully.",
|
|
500
|
+
...results,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
async listEnvironments(args) {
|
|
504
|
+
const projectId = this.getProjectId(args, "list_environments");
|
|
505
|
+
try {
|
|
506
|
+
const response = await this.apiClient.get(`${projectId}/environment/fetch-environment-list`);
|
|
507
|
+
const data = response?.data ?? response;
|
|
508
|
+
const list = Array.isArray(data) ? data : [];
|
|
509
|
+
return jsonResponse({
|
|
510
|
+
success: true,
|
|
511
|
+
message: "Environments fetched successfully.",
|
|
512
|
+
data: list,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
catch (err) {
|
|
516
|
+
return jsonResponse({
|
|
517
|
+
success: false,
|
|
518
|
+
error_code: "LIST_FAILED",
|
|
519
|
+
message: err?.message ?? "Failed to fetch environments.",
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
async listLlmModels(args) {
|
|
524
|
+
this.getProjectId(args, "list_llm_models"); // ensure project context resolved
|
|
525
|
+
try {
|
|
526
|
+
const response = await this.apiClient.get("aimodel/fetch-automation-models");
|
|
527
|
+
const data = response?.data ?? response;
|
|
528
|
+
const list = Array.isArray(data) ? data : [];
|
|
529
|
+
return jsonResponse({
|
|
530
|
+
success: true,
|
|
531
|
+
message: "LLM models fetched successfully.",
|
|
532
|
+
data: list,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
catch (err) {
|
|
536
|
+
return jsonResponse({
|
|
537
|
+
success: false,
|
|
538
|
+
error_code: "LIST_FAILED",
|
|
539
|
+
message: err?.message ?? "Failed to fetch LLM models.",
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
async createTestLaunch(args) {
|
|
544
|
+
const projectId = this.getProjectId(args, "create_test_launch");
|
|
545
|
+
const { testGroupId, name, environmentId, llmModelId } = args;
|
|
546
|
+
if (!testGroupId) {
|
|
547
|
+
return jsonResponse({
|
|
548
|
+
success: false,
|
|
549
|
+
message: "testGroupId is required.",
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
const ENVIRONMENT_REQUIRED_RESPONSE = {
|
|
553
|
+
success: false,
|
|
554
|
+
error_code: "ENVIRONMENT_REQUIRED",
|
|
555
|
+
message: "environmentId is required.",
|
|
556
|
+
ACTION_REQUIRED: [
|
|
557
|
+
"Call list_environments to fetch the available environments for this project.",
|
|
558
|
+
"If the user provided an environment by name, map it to the id from the list (e.g. match by name).",
|
|
559
|
+
"Ask the user to choose one if needed, then retry create_test_launch with environmentId set to the chosen id.",
|
|
560
|
+
],
|
|
561
|
+
};
|
|
562
|
+
const LLM_MODEL_REQUIRED_RESPONSE = {
|
|
563
|
+
success: false,
|
|
564
|
+
error_code: "LLM_MODEL_REQUIRED",
|
|
565
|
+
message: "llmModelId is required.",
|
|
566
|
+
ACTION_REQUIRED: [
|
|
567
|
+
"Call list_llm_models to fetch the available LLM models.",
|
|
568
|
+
"If the user provided a model by name, map it to the id from the list (e.g. match by name).",
|
|
569
|
+
"Ask the user to choose one if needed, then retry create_test_launch with llmModelId set to the chosen id.",
|
|
570
|
+
],
|
|
571
|
+
};
|
|
572
|
+
if (!environmentId) {
|
|
573
|
+
return jsonResponse(ENVIRONMENT_REQUIRED_RESPONSE);
|
|
574
|
+
}
|
|
575
|
+
if (!llmModelId) {
|
|
576
|
+
return jsonResponse(LLM_MODEL_REQUIRED_RESPONSE);
|
|
577
|
+
}
|
|
578
|
+
try {
|
|
579
|
+
const body = {
|
|
580
|
+
testgroupId: testGroupId,
|
|
581
|
+
isSequential: true,
|
|
582
|
+
config: {
|
|
583
|
+
environmentId,
|
|
584
|
+
llmModelId,
|
|
585
|
+
},
|
|
586
|
+
};
|
|
587
|
+
if (name != null)
|
|
588
|
+
body.name = name;
|
|
589
|
+
const response = await this.apiClient.post(`${projectId}/test-launch/createTestLaunch`, body);
|
|
590
|
+
const data = response?.data ?? response;
|
|
591
|
+
return jsonResponse({
|
|
592
|
+
success: true,
|
|
593
|
+
message: "Test launch created successfully.",
|
|
594
|
+
data,
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
catch (err) {
|
|
598
|
+
return jsonResponse({
|
|
599
|
+
success: false,
|
|
600
|
+
error_code: "LAUNCH_CREATE_FAILED",
|
|
601
|
+
message: err?.message ?? "Failed to create test launch.",
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
async listTestLaunches(args) {
|
|
606
|
+
const projectId = this.getProjectId(args, "list_test_launches");
|
|
607
|
+
const { testGroupId, search, status, page = 1, pageSize = 25 } = args;
|
|
608
|
+
if (!testGroupId) {
|
|
609
|
+
return jsonResponse({
|
|
610
|
+
success: false,
|
|
611
|
+
message: "testGroupId is required.",
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
// Validate pagination
|
|
615
|
+
if (typeof page !== "number" || page < 1) {
|
|
616
|
+
return jsonResponse({
|
|
617
|
+
success: false,
|
|
618
|
+
message: "page must be a number >= 1.",
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
if (typeof pageSize !== "number" || pageSize < 1 || pageSize > 100) {
|
|
622
|
+
return jsonResponse({
|
|
623
|
+
success: false,
|
|
624
|
+
message: "pageSize must be a number between 1 and 100.",
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
// Validate status
|
|
628
|
+
const allowedStatuses = ["all", "new", "in_progress", "completed", "aborted", "paused"];
|
|
629
|
+
if (status && !allowedStatuses.includes(status)) {
|
|
630
|
+
return jsonResponse({
|
|
631
|
+
success: false,
|
|
632
|
+
message: `status must be one of: ${allowedStatuses.join(", ")}.`,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
// Validate search length
|
|
636
|
+
if (search && typeof search === "string" && search.length > 255) {
|
|
637
|
+
return jsonResponse({
|
|
638
|
+
success: false,
|
|
639
|
+
message: "search string must not exceed 255 characters.",
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
try {
|
|
643
|
+
const params = {
|
|
644
|
+
page,
|
|
645
|
+
pageSize,
|
|
646
|
+
};
|
|
647
|
+
if (search && search.trim().length > 0) {
|
|
648
|
+
params.search = search.trim();
|
|
649
|
+
}
|
|
650
|
+
if (status) {
|
|
651
|
+
params.status = status;
|
|
652
|
+
}
|
|
653
|
+
const response = await this.apiClient.get(`${projectId}/test-launch/${testGroupId}/launches`, { params });
|
|
654
|
+
const data = response?.data ?? response;
|
|
655
|
+
return jsonResponse({
|
|
656
|
+
success: true,
|
|
657
|
+
message: "Test launches fetched successfully.",
|
|
658
|
+
data,
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
catch (err) {
|
|
662
|
+
return jsonResponse({
|
|
663
|
+
success: false,
|
|
664
|
+
message: err?.message ?? "Failed to fetch test launches.",
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
async getTestLaunch(args) {
|
|
669
|
+
const projectId = this.getProjectId(args, "get_test_launch");
|
|
670
|
+
const { launchId } = args;
|
|
671
|
+
if (!launchId) {
|
|
672
|
+
return jsonResponse({
|
|
673
|
+
success: false,
|
|
674
|
+
message: "launchId is required.",
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
try {
|
|
678
|
+
const response = await this.apiClient.get(`${projectId}/test-launch/getLaunchDetailsSummary/${launchId}`);
|
|
679
|
+
const data = response?.data ?? response;
|
|
680
|
+
return jsonResponse({
|
|
681
|
+
success: true,
|
|
682
|
+
message: "Test launch fetched successfully.",
|
|
683
|
+
data,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
catch (err) {
|
|
687
|
+
return jsonResponse({
|
|
688
|
+
success: false,
|
|
689
|
+
message: err?.message ?? "Failed to fetch test launch.",
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
async updateTestLaunch(args) {
|
|
694
|
+
const projectId = this.getProjectId(args, "update_test_launch");
|
|
695
|
+
const { launchId, status } = args;
|
|
696
|
+
if (!launchId) {
|
|
697
|
+
return jsonResponse({
|
|
698
|
+
success: false,
|
|
699
|
+
message: "launchId is required.",
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
const allowed = ["completed", "aborted"];
|
|
703
|
+
const normalizedStatus = typeof status === "string" ? status.toLowerCase() : status;
|
|
704
|
+
if (!allowed.includes(normalizedStatus)) {
|
|
705
|
+
return jsonResponse({
|
|
706
|
+
success: false,
|
|
707
|
+
message: `status must be one of: ${allowed.join(", ")}.`,
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
try {
|
|
711
|
+
await this.apiClient.patch(`${projectId}/test-launch/status/${launchId}`, { status: normalizedStatus });
|
|
712
|
+
return jsonResponse({
|
|
713
|
+
success: true,
|
|
714
|
+
message: `Test launch status updated to '${normalizedStatus}' successfully.`,
|
|
715
|
+
launchId,
|
|
716
|
+
status: normalizedStatus,
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
catch (err) {
|
|
720
|
+
return jsonResponse({
|
|
721
|
+
success: false,
|
|
722
|
+
message: err?.message ?? "Failed to update test launch.",
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
//# sourceMappingURL=testgroup-launch-tools.js.map
|