@uarlouski/testrail-mcp-server 1.11.0 → 2.1.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/README.md +24 -11
- package/dist/client/testrail.js +62 -5
- package/dist/index.js +14 -55
- package/dist/tools/{add_case.js → cases/add_case.js} +2 -1
- package/dist/tools/{get_case.js → cases/get_case.js} +3 -2
- package/dist/tools/{get_case_fields.js → cases/get_case_fields.js} +5 -3
- package/dist/tools/{get_cases.js → cases/get_cases.js} +9 -2
- package/dist/tools/cases/index.js +14 -0
- package/dist/tools/cases/types.js +40 -0
- package/dist/tools/{update_case.js → cases/update_case.js} +2 -1
- package/dist/tools/{update_cases.js → cases/update_cases.js} +2 -1
- package/dist/tools/commons/delete_entity.js +24 -0
- package/dist/tools/commons/get_configurations.js +17 -0
- package/dist/tools/{get_labels.js → commons/get_labels.js} +2 -1
- package/dist/tools/{get_priorities.js → commons/get_priorities.js} +2 -1
- package/dist/tools/{get_statuses.js → commons/get_statuses.js} +2 -1
- package/dist/tools/{get_templates.js → commons/get_templates.js} +2 -1
- package/dist/tools/{get_tests.js → commons/get_tests.js} +2 -1
- package/dist/tools/{get_users.js → commons/get_users.js} +2 -1
- package/dist/tools/commons/index.js +18 -0
- package/dist/tools/commons/types.js +49 -0
- package/dist/tools/projects/index.js +4 -0
- package/dist/tools/projects/query_project.js +29 -0
- package/dist/tools/projects/types.js +14 -0
- package/dist/tools/registry.js +46 -0
- package/dist/tools/{add_results.js → results/add_results.js} +2 -1
- package/dist/tools/{add_results_for_cases.js → results/add_results_for_cases.js} +2 -1
- package/dist/tools/results/get_results.js +18 -0
- package/dist/tools/results/index.js +8 -0
- package/dist/tools/results/types.js +8 -0
- package/dist/tools/{add_attachment_to_run.js → runs/add_attachment_to_run.js} +2 -1
- package/dist/tools/runs/index.js +8 -0
- package/dist/tools/runs/mutate_run.js +40 -0
- package/dist/tools/runs/query_run.js +35 -0
- package/dist/tools/runs/types.js +43 -0
- package/dist/tools/{get_sections.js → sections/get_sections.js} +7 -3
- package/dist/tools/sections/index.js +6 -0
- package/dist/tools/sections/mutate_section.js +25 -0
- package/dist/tools/sections/types.js +21 -0
- package/dist/tools/shared_steps/add_shared_step.js +2 -1
- package/dist/tools/shared_steps/get_shared_step.js +2 -1
- package/dist/tools/shared_steps/get_shared_step_history.js +2 -1
- package/dist/tools/shared_steps/get_shared_steps.js +2 -1
- package/dist/tools/shared_steps/index.js +12 -0
- package/dist/tools/shared_steps/types.js +18 -0
- package/dist/tools/shared_steps/update_shared_step.js +2 -1
- package/dist/tools/suites/index.js +6 -0
- package/dist/tools/suites/mutate_suite.js +25 -0
- package/dist/tools/suites/query_suite.js +28 -0
- package/dist/tools/suites/types.js +28 -0
- package/dist/utils/mutate_handler.js +22 -0
- package/dist/utils/query_handler.js +20 -0
- package/dist/utils/validator.js +15 -1
- package/package.json +1 -1
- package/dist/tools/add_run.js +0 -19
- package/dist/tools/get_projects.js +0 -15
- package/dist/tools/shared_steps/delete_shared_step.js +0 -18
- package/dist/types/testrail.js +0 -130
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ProjectSchema, GetOneProjectSchema, GetManyProjectsSchema } from "./types.js";
|
|
3
|
+
import { handleQuery } from "../../utils/query_handler.js";
|
|
4
|
+
const parameters = {
|
|
5
|
+
payload: z.discriminatedUnion("action", [
|
|
6
|
+
GetOneProjectSchema,
|
|
7
|
+
GetManyProjectsSchema,
|
|
8
|
+
]).describe("The payload containing the action ('one' or 'many') and corresponding parameters"),
|
|
9
|
+
};
|
|
10
|
+
export const queryProjectTool = {
|
|
11
|
+
name: "query_project",
|
|
12
|
+
mode: "read",
|
|
13
|
+
description: "Retrieve a single project or all projects in TestRail. Set payload.action to 'one' or 'many' to specify the operation.",
|
|
14
|
+
parameters,
|
|
15
|
+
handler: async (args, client) => {
|
|
16
|
+
return handleQuery(args.payload, async (p) => {
|
|
17
|
+
const project = await client.getProject(p.project_id);
|
|
18
|
+
return {
|
|
19
|
+
project: ProjectSchema.parse(project),
|
|
20
|
+
};
|
|
21
|
+
}, async (p) => {
|
|
22
|
+
const projects = await client.getProjects();
|
|
23
|
+
const filteredProjects = projects.filter(prj => !prj.is_completed);
|
|
24
|
+
return {
|
|
25
|
+
projects: filteredProjects.map(prj => ProjectSchema.parse(prj)),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const ProjectSchema = z.object({
|
|
3
|
+
id: z.number(),
|
|
4
|
+
name: z.string(),
|
|
5
|
+
is_completed: z.boolean(),
|
|
6
|
+
suite_mode: z.number(),
|
|
7
|
+
});
|
|
8
|
+
export const GetOneProjectSchema = z.object({
|
|
9
|
+
action: z.literal("one").describe("Retrieve a single project by ID"),
|
|
10
|
+
project_id: z.number().int().describe("The ID of the project"),
|
|
11
|
+
});
|
|
12
|
+
export const GetManyProjectsSchema = z.object({
|
|
13
|
+
action: z.literal("many").describe("Retrieve all available projects"),
|
|
14
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { casesTools } from "./cases/index.js";
|
|
2
|
+
import { projectsTools } from "./projects/index.js";
|
|
3
|
+
import { suitesTools } from "./suites/index.js";
|
|
4
|
+
import { sectionsTools } from "./sections/index.js";
|
|
5
|
+
import { runsTools } from "./runs/index.js";
|
|
6
|
+
import { sharedStepsTools } from "./shared_steps/index.js";
|
|
7
|
+
import { resultsTools } from "./results/index.js";
|
|
8
|
+
import { commonsTools } from "./commons/index.js";
|
|
9
|
+
export function getToolsToRegister(config) {
|
|
10
|
+
const tools = [
|
|
11
|
+
...projectsTools,
|
|
12
|
+
...suitesTools,
|
|
13
|
+
...casesTools,
|
|
14
|
+
...sectionsTools,
|
|
15
|
+
...runsTools,
|
|
16
|
+
...resultsTools,
|
|
17
|
+
...commonsTools,
|
|
18
|
+
];
|
|
19
|
+
if (config.enableSharedSteps) {
|
|
20
|
+
tools.push(...sharedStepsTools);
|
|
21
|
+
}
|
|
22
|
+
const allowWrite = config.allowWrite !== false;
|
|
23
|
+
const allowRead = config.allowRead !== false;
|
|
24
|
+
const allowDelete = config.allowDelete === true;
|
|
25
|
+
const filteredTools = tools.filter(tool => {
|
|
26
|
+
if (tool.mode === 'write')
|
|
27
|
+
return allowWrite;
|
|
28
|
+
if (tool.mode === 'read')
|
|
29
|
+
return allowRead;
|
|
30
|
+
return allowDelete;
|
|
31
|
+
});
|
|
32
|
+
return filteredTools.map(tool => {
|
|
33
|
+
const defaultAnnotations = {
|
|
34
|
+
readOnlyHint: tool.mode === 'read',
|
|
35
|
+
destructiveHint: tool.mode === 'delete',
|
|
36
|
+
idempotentHint: tool.mode === 'read',
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
...tool,
|
|
40
|
+
annotations: {
|
|
41
|
+
...defaultAnnotations,
|
|
42
|
+
...tool.annotations,
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ResultSchema } from "
|
|
2
|
+
import { ResultSchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
run_id: z.number().describe("The ID of the test run"),
|
|
5
5
|
results: z.array(z.object({
|
|
@@ -11,6 +11,7 @@ const parameters = {
|
|
|
11
11
|
};
|
|
12
12
|
export const addResultsTool = {
|
|
13
13
|
name: "add_results",
|
|
14
|
+
mode: "write",
|
|
14
15
|
description: "Add one or more test results to a test run",
|
|
15
16
|
parameters,
|
|
16
17
|
handler: async ({ run_id, results }, client) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ResultSchema } from "
|
|
2
|
+
import { ResultSchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
run_id: z.number().describe("The ID of the test run"),
|
|
5
5
|
results: z.array(z.object({
|
|
@@ -11,6 +11,7 @@ const parameters = {
|
|
|
11
11
|
};
|
|
12
12
|
export const addResultsForCasesTool = {
|
|
13
13
|
name: "add_results_for_cases",
|
|
14
|
+
mode: "write",
|
|
14
15
|
description: "Add one or more test results to a test run using case IDs instead of test IDs",
|
|
15
16
|
parameters,
|
|
16
17
|
handler: async ({ run_id, results }, client) => {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ResultSchema } from "./types.js";
|
|
3
|
+
const parameters = {
|
|
4
|
+
test_id: z.number().int().describe("The ID of the test"),
|
|
5
|
+
};
|
|
6
|
+
export const getResultsTool = {
|
|
7
|
+
name: "get_results",
|
|
8
|
+
mode: "read",
|
|
9
|
+
description: "Get results for a specific test in TestRail.",
|
|
10
|
+
parameters,
|
|
11
|
+
handler: async (args, client) => {
|
|
12
|
+
const { test_id } = args;
|
|
13
|
+
const results = await client.getResults(test_id);
|
|
14
|
+
return {
|
|
15
|
+
results: results.map(r => ResultSchema.parse(r)),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { addResultsTool } from "./add_results.js";
|
|
2
|
+
import { addResultsForCasesTool } from "./add_results_for_cases.js";
|
|
3
|
+
import { getResultsTool } from "./get_results.js";
|
|
4
|
+
export const resultsTools = [
|
|
5
|
+
addResultsTool,
|
|
6
|
+
addResultsForCasesTool,
|
|
7
|
+
getResultsTool,
|
|
8
|
+
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { AttachmentSchema } from "
|
|
2
|
+
import { AttachmentSchema } from "./types.js";
|
|
3
3
|
import * as fs from "fs";
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
import * as os from "os";
|
|
@@ -10,6 +10,7 @@ const parameters = {
|
|
|
10
10
|
};
|
|
11
11
|
export const addAttachmentToRunTool = {
|
|
12
12
|
name: "add_attachment_to_run",
|
|
13
|
+
mode: "write",
|
|
13
14
|
description: "Add an attachment to a test run in TestRail. If the file_path points to a directory, it will be automatically zipped before uploading. Maximum upload size is 256MB.",
|
|
14
15
|
parameters,
|
|
15
16
|
handler: async ({ run_id, file_path }, client) => {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { RunSchema, CreateRunSchema, UpdateRunSchema } from "./types.js";
|
|
3
|
+
import { handleMutate } from "../../utils/mutate_handler.js";
|
|
4
|
+
const parameters = {
|
|
5
|
+
payload: z.discriminatedUnion("action", [
|
|
6
|
+
CreateRunSchema,
|
|
7
|
+
UpdateRunSchema,
|
|
8
|
+
]).describe("The mutation payload containing the action (create or update) and the corresponding run attributes"),
|
|
9
|
+
};
|
|
10
|
+
export const mutateRunTool = {
|
|
11
|
+
name: "mutate_run",
|
|
12
|
+
mode: "write",
|
|
13
|
+
description: "Create a new test run or update an existing one in TestRail. Set payload.action to 'create' or 'update' to specify the operation.",
|
|
14
|
+
parameters,
|
|
15
|
+
handler: async (args, client) => {
|
|
16
|
+
const { payload } = args;
|
|
17
|
+
return handleMutate(payload, async (createPayload) => {
|
|
18
|
+
const { project_id, action, ...data } = createPayload;
|
|
19
|
+
const params = { ...data };
|
|
20
|
+
if (!params.suite_id && params.case_ids && params.case_ids.length > 0) {
|
|
21
|
+
// Determine suite_id automatically from the first case
|
|
22
|
+
const caseData = await client.getCase(params.case_ids[0]);
|
|
23
|
+
params.suite_id = caseData.suite_id;
|
|
24
|
+
params.include_all = false;
|
|
25
|
+
}
|
|
26
|
+
else if (params.suite_id) {
|
|
27
|
+
// suite_id provided
|
|
28
|
+
params.include_all = !params.case_ids || params.case_ids.length === 0;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// Neither suite_id nor case_ids provided (e.g. single-suite project)
|
|
32
|
+
params.include_all = true;
|
|
33
|
+
}
|
|
34
|
+
return client.addRun(project_id, params);
|
|
35
|
+
}, async (updatePayload) => {
|
|
36
|
+
const { run_id, action, ...data } = updatePayload;
|
|
37
|
+
return client.updateRun(run_id, data);
|
|
38
|
+
}, RunSchema);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { RunSchema, GetOneRunSchema, GetManyRunsSchema } from "./types.js";
|
|
3
|
+
import { handleQuery } from "../../utils/query_handler.js";
|
|
4
|
+
const parameters = {
|
|
5
|
+
payload: z.discriminatedUnion("action", [
|
|
6
|
+
GetOneRunSchema,
|
|
7
|
+
GetManyRunsSchema,
|
|
8
|
+
]).describe("The payload containing the action ('one' or 'many') and corresponding parameters"),
|
|
9
|
+
};
|
|
10
|
+
export const queryRunTool = {
|
|
11
|
+
name: "query_run",
|
|
12
|
+
mode: "read",
|
|
13
|
+
description: "Retrieve a single test run or all test runs for a project in TestRail. Set payload.action to 'one' or 'many' to specify the operation.",
|
|
14
|
+
parameters,
|
|
15
|
+
handler: async (args, client) => {
|
|
16
|
+
return handleQuery(args.payload, async (p) => {
|
|
17
|
+
const run = await client.getRun(p.run_id);
|
|
18
|
+
return {
|
|
19
|
+
run: RunSchema.parse(run),
|
|
20
|
+
};
|
|
21
|
+
}, async (p) => {
|
|
22
|
+
const { project_id, action, ...filters } = p;
|
|
23
|
+
const cleanFilters = {};
|
|
24
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
25
|
+
if (value !== undefined && value !== null) {
|
|
26
|
+
cleanFilters[key] = value.toString();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const runs = await client.getRuns(project_id, cleanFilters);
|
|
30
|
+
return {
|
|
31
|
+
runs: runs.map(r => RunSchema.parse(r)),
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const RunSchema = z.object({
|
|
3
|
+
id: z.number(),
|
|
4
|
+
name: z.string(),
|
|
5
|
+
description: z.string().nullable(),
|
|
6
|
+
suite_id: z.number().nullable(),
|
|
7
|
+
project_id: z.number(),
|
|
8
|
+
is_completed: z.boolean(),
|
|
9
|
+
passed_count: z.number(),
|
|
10
|
+
blocked_count: z.number(),
|
|
11
|
+
untested_count: z.number(),
|
|
12
|
+
retest_count: z.number(),
|
|
13
|
+
failed_count: z.number(),
|
|
14
|
+
url: z.string(),
|
|
15
|
+
});
|
|
16
|
+
export const BaseRunFields = z.object({
|
|
17
|
+
name: z.string().optional().describe("The name of the test run"),
|
|
18
|
+
description: z.string().optional().describe("The description of the test run"),
|
|
19
|
+
case_ids: z.array(z.number()).optional().describe("An array of case IDs for the custom case selection"),
|
|
20
|
+
});
|
|
21
|
+
export const CreateRunSchema = BaseRunFields.extend({
|
|
22
|
+
action: z.literal("create").describe("The operation to perform: create a new test run"),
|
|
23
|
+
project_id: z.number().describe("The ID of the project the test run should be added to"),
|
|
24
|
+
suite_id: z.number().optional().describe("The ID of the test suite for the test run"),
|
|
25
|
+
case_ids: z.array(z.number()).optional().describe("Array of case IDs to include in the run. If provided, the run will only include these cases; otherwise, all cases will be included."),
|
|
26
|
+
});
|
|
27
|
+
export const UpdateRunSchema = BaseRunFields.extend({
|
|
28
|
+
action: z.literal("update").describe("The operation to perform: update an existing test run"),
|
|
29
|
+
run_id: z.number().describe("The ID of the test run to update"),
|
|
30
|
+
});
|
|
31
|
+
export const GetOneRunSchema = z.object({
|
|
32
|
+
action: z.literal("one").describe("Retrieve a single test run by ID"),
|
|
33
|
+
run_id: z.number().int().describe("The ID of the test run"),
|
|
34
|
+
});
|
|
35
|
+
export const GetManyRunsSchema = z.object({
|
|
36
|
+
action: z.literal("many").describe("Retrieve all test runs for a project"),
|
|
37
|
+
project_id: z.number().int().describe("The ID of the project"),
|
|
38
|
+
suite_id: z.number().int().optional().describe("Filter by suite ID"),
|
|
39
|
+
is_completed: z.number().int().min(0).max(1).optional().describe("Filter by completion status (1 for completed, 0 for active)"),
|
|
40
|
+
});
|
|
41
|
+
export const AttachmentSchema = z.object({
|
|
42
|
+
attachment_id: z.number(),
|
|
43
|
+
});
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import fs from "fs";
|
|
3
|
-
import { SectionSchema } from "
|
|
3
|
+
import { SectionSchema } from "./types.js";
|
|
4
|
+
import { validateSuiteId } from "../../utils/validator.js";
|
|
4
5
|
const parameters = {
|
|
5
6
|
project_id: z.number().describe("The ID of the project. Use get_projects to find available projects"),
|
|
7
|
+
suite_id: z.number().optional().describe("The ID of the test suite (required for multi-suite projects, i.e. suite_mode=3). Use get_suites to find available suites"),
|
|
6
8
|
output_file: z.string().optional().describe("Absolute file path to save the JSON response to. Use this for large datasets to avoid blowing up context limits."),
|
|
7
9
|
};
|
|
8
10
|
export const getSectionsTool = {
|
|
9
11
|
name: "get_sections",
|
|
12
|
+
mode: "read",
|
|
10
13
|
description: "Get all sections for a project. Returns section IDs and names that can be used with add_case",
|
|
11
14
|
parameters,
|
|
12
|
-
handler: async ({ project_id, output_file }, client) => {
|
|
13
|
-
|
|
15
|
+
handler: async ({ project_id, suite_id, output_file }, client) => {
|
|
16
|
+
await validateSuiteId(client, project_id, suite_id);
|
|
17
|
+
const sections = await client.getSections(project_id, suite_id);
|
|
14
18
|
const response = {
|
|
15
19
|
sections: sections.map(s => SectionSchema.parse(s)),
|
|
16
20
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SectionSchema, CreateSectionSchema, UpdateSectionSchema } from "./types.js";
|
|
3
|
+
import { handleMutate } from "../../utils/mutate_handler.js";
|
|
4
|
+
const parameters = {
|
|
5
|
+
payload: z.discriminatedUnion("action", [
|
|
6
|
+
CreateSectionSchema,
|
|
7
|
+
UpdateSectionSchema,
|
|
8
|
+
]).describe("The mutation payload containing the action (create or update) and the corresponding section attributes"),
|
|
9
|
+
};
|
|
10
|
+
export const mutateSectionTool = {
|
|
11
|
+
name: "mutate_section",
|
|
12
|
+
mode: "write",
|
|
13
|
+
description: "Create a new section or update an existing section in TestRail. Set payload.action to 'create' or 'update' to specify the operation.",
|
|
14
|
+
parameters,
|
|
15
|
+
handler: async (args, client) => {
|
|
16
|
+
const { payload } = args;
|
|
17
|
+
return handleMutate(payload, async (createPayload) => {
|
|
18
|
+
const { project_id, action, ...data } = createPayload;
|
|
19
|
+
return client.addSection(project_id, data);
|
|
20
|
+
}, async (updatePayload) => {
|
|
21
|
+
const { section_id, action, ...data } = updatePayload;
|
|
22
|
+
return client.updateSection(section_id, data);
|
|
23
|
+
}, SectionSchema);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const SectionSchema = z.object({
|
|
3
|
+
id: z.number(),
|
|
4
|
+
name: z.string(),
|
|
5
|
+
description: z.string().nullish(),
|
|
6
|
+
parent_id: z.number().nullish(),
|
|
7
|
+
suite_id: z.number(),
|
|
8
|
+
});
|
|
9
|
+
export const BaseSectionFields = z.object({
|
|
10
|
+
name: z.string().describe("The name of the section"),
|
|
11
|
+
description: z.string().optional().describe("The description of the section"),
|
|
12
|
+
});
|
|
13
|
+
export const CreateSectionSchema = BaseSectionFields.extend({
|
|
14
|
+
action: z.literal("create").describe("The operation to perform: create a new section"),
|
|
15
|
+
project_id: z.number().describe("The ID of the project the section should be added to"),
|
|
16
|
+
parent_id: z.number().optional().describe("The ID of the parent section (to build section hierarchies)"),
|
|
17
|
+
});
|
|
18
|
+
export const UpdateSectionSchema = BaseSectionFields.partial().extend({
|
|
19
|
+
action: z.literal("update").describe("The operation to perform: update an existing section"),
|
|
20
|
+
section_id: z.number().describe("The ID of the section to update"),
|
|
21
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { SharedStepSchema } from "
|
|
2
|
+
import { SharedStepSchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
project_id: z.number().describe("The ID of the project where the shared step will be created"),
|
|
5
5
|
title: z.string().describe("The title of the shared test step"),
|
|
@@ -10,6 +10,7 @@ const parameters = {
|
|
|
10
10
|
};
|
|
11
11
|
export const addSharedStepTool = {
|
|
12
12
|
name: "add_shared_step",
|
|
13
|
+
mode: "write",
|
|
13
14
|
description: "Creates a new set of shared test steps.",
|
|
14
15
|
parameters,
|
|
15
16
|
handler: async (args, client) => {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { SharedStepSchema } from "
|
|
2
|
+
import { SharedStepSchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
shared_step_id: z.number().describe("The ID of the shared test step"),
|
|
5
5
|
};
|
|
6
6
|
export const getSharedStepTool = {
|
|
7
7
|
name: "get_shared_step",
|
|
8
|
+
mode: "read",
|
|
8
9
|
description: "Returns the details of a specific shared test step.",
|
|
9
10
|
parameters,
|
|
10
11
|
handler: async (args, client) => {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { SharedStepHistorySchema } from "
|
|
2
|
+
import { SharedStepHistorySchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
shared_step_id: z.number().describe("The ID of the shared test step"),
|
|
5
5
|
};
|
|
6
6
|
export const getSharedStepHistoryTool = {
|
|
7
7
|
name: "get_shared_step_history",
|
|
8
|
+
mode: "read",
|
|
8
9
|
description: "Returns the change history of a set of shared test steps.",
|
|
9
10
|
parameters,
|
|
10
11
|
handler: async (args, client) => {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { SharedStepSchema } from "
|
|
2
|
+
import { SharedStepSchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
project_id: z.number().describe("The ID of the project"),
|
|
5
5
|
refs: z.string().optional().describe("Filter by a single Reference ID"),
|
|
6
6
|
};
|
|
7
7
|
export const getSharedStepsTool = {
|
|
8
8
|
name: "get_shared_steps",
|
|
9
|
+
mode: "read",
|
|
9
10
|
description: "Returns a list of shared test steps for a specified project.",
|
|
10
11
|
parameters,
|
|
11
12
|
handler: async (args, client) => {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getSharedStepsTool } from "./get_shared_steps.js";
|
|
2
|
+
import { getSharedStepTool } from "./get_shared_step.js";
|
|
3
|
+
import { getSharedStepHistoryTool } from "./get_shared_step_history.js";
|
|
4
|
+
import { addSharedStepTool } from "./add_shared_step.js";
|
|
5
|
+
import { updateSharedStepTool } from "./update_shared_step.js";
|
|
6
|
+
export const sharedStepsTools = [
|
|
7
|
+
getSharedStepsTool,
|
|
8
|
+
getSharedStepTool,
|
|
9
|
+
getSharedStepHistoryTool,
|
|
10
|
+
addSharedStepTool,
|
|
11
|
+
updateSharedStepTool
|
|
12
|
+
];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const SharedStepContentSchema = z.object({
|
|
3
|
+
content: z.string(),
|
|
4
|
+
expected: z.string().nullable().optional(),
|
|
5
|
+
});
|
|
6
|
+
export const SharedStepSchema = z.object({
|
|
7
|
+
id: z.number(),
|
|
8
|
+
title: z.string(),
|
|
9
|
+
project_id: z.number().optional(),
|
|
10
|
+
custom_steps_separated: z.array(SharedStepContentSchema).optional(),
|
|
11
|
+
case_ids: z.array(z.number()).optional(),
|
|
12
|
+
});
|
|
13
|
+
export const SharedStepHistorySchema = z.object({
|
|
14
|
+
id: z.number(),
|
|
15
|
+
title: z.string(),
|
|
16
|
+
timestamp: z.number().optional(),
|
|
17
|
+
custom_steps_separated: z.array(SharedStepContentSchema).optional(),
|
|
18
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { SharedStepSchema } from "
|
|
2
|
+
import { SharedStepSchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
shared_step_id: z.number().describe("The ID of the shared test step to update"),
|
|
5
5
|
title: z.string().optional().describe("The updated title of the shared test step"),
|
|
@@ -14,6 +14,7 @@ Updates are propagated automatically to all test cases using these steps.
|
|
|
14
14
|
`;
|
|
15
15
|
export const updateSharedStepTool = {
|
|
16
16
|
name: "update_shared_step",
|
|
17
|
+
mode: "write",
|
|
17
18
|
description: description.trim(),
|
|
18
19
|
parameters,
|
|
19
20
|
handler: async (args, client) => {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SuiteSchema, CreateSuiteSchema, UpdateSuiteSchema } from "./types.js";
|
|
3
|
+
import { handleMutate } from "../../utils/mutate_handler.js";
|
|
4
|
+
const parameters = {
|
|
5
|
+
payload: z.discriminatedUnion("action", [
|
|
6
|
+
CreateSuiteSchema,
|
|
7
|
+
UpdateSuiteSchema,
|
|
8
|
+
]).describe("The mutation payload containing the action (create or update) and the corresponding suite attributes"),
|
|
9
|
+
};
|
|
10
|
+
export const mutateSuiteTool = {
|
|
11
|
+
name: "mutate_suite",
|
|
12
|
+
mode: "write",
|
|
13
|
+
description: "Create a new test suite or update an existing one in TestRail. Set payload.action to 'create' or 'update' to specify the operation.",
|
|
14
|
+
parameters,
|
|
15
|
+
handler: async (args, client) => {
|
|
16
|
+
const { payload } = args;
|
|
17
|
+
return handleMutate(payload, async (createPayload) => {
|
|
18
|
+
const { project_id, action, ...data } = createPayload;
|
|
19
|
+
return client.addSuite(project_id, data);
|
|
20
|
+
}, async (updatePayload) => {
|
|
21
|
+
const { suite_id, action, ...data } = updatePayload;
|
|
22
|
+
return client.updateSuite(suite_id, data);
|
|
23
|
+
}, SuiteSchema);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SuiteSchema, GetOneSuiteSchema, GetManySuitesSchema } from "./types.js";
|
|
3
|
+
import { handleQuery } from "../../utils/query_handler.js";
|
|
4
|
+
const parameters = {
|
|
5
|
+
payload: z.discriminatedUnion("action", [
|
|
6
|
+
GetOneSuiteSchema,
|
|
7
|
+
GetManySuitesSchema,
|
|
8
|
+
]).describe("The payload containing the action ('one' or 'many') and corresponding parameters"),
|
|
9
|
+
};
|
|
10
|
+
export const querySuiteTool = {
|
|
11
|
+
name: "query_suite",
|
|
12
|
+
mode: "read",
|
|
13
|
+
description: "Retrieve a single test suite or all test suites for a project in TestRail. Set payload.action to 'one' or 'many' to specify the operation.",
|
|
14
|
+
parameters,
|
|
15
|
+
handler: async (args, client) => {
|
|
16
|
+
return handleQuery(args.payload, async (p) => {
|
|
17
|
+
const suite = await client.getSuite(p.suite_id);
|
|
18
|
+
return {
|
|
19
|
+
suite: SuiteSchema.parse(suite),
|
|
20
|
+
};
|
|
21
|
+
}, async (p) => {
|
|
22
|
+
const suites = await client.getSuites(p.project_id);
|
|
23
|
+
return {
|
|
24
|
+
suites: suites.map(s => SuiteSchema.parse(s)),
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const SuiteSchema = z.object({
|
|
3
|
+
id: z.number(),
|
|
4
|
+
name: z.string(),
|
|
5
|
+
description: z.string().nullable().optional(),
|
|
6
|
+
project_id: z.number(),
|
|
7
|
+
url: z.string(),
|
|
8
|
+
}).loose();
|
|
9
|
+
export const BaseSuiteFields = z.object({
|
|
10
|
+
name: z.string().describe("The name of the test suite"),
|
|
11
|
+
description: z.string().optional().describe("The description of the test suite"),
|
|
12
|
+
});
|
|
13
|
+
export const CreateSuiteSchema = BaseSuiteFields.extend({
|
|
14
|
+
action: z.literal("create").describe("The operation to perform: create a new test suite"),
|
|
15
|
+
project_id: z.number().describe("The ID of the project the test suite should be added to"),
|
|
16
|
+
});
|
|
17
|
+
export const UpdateSuiteSchema = BaseSuiteFields.partial().extend({
|
|
18
|
+
action: z.literal("update").describe("The operation to perform: update an existing test suite"),
|
|
19
|
+
suite_id: z.number().describe("The ID of the test suite to update"),
|
|
20
|
+
});
|
|
21
|
+
export const GetOneSuiteSchema = z.object({
|
|
22
|
+
action: z.literal("one").describe("Retrieve a single test suite by ID"),
|
|
23
|
+
suite_id: z.number().int().describe("The ID of the test suite"),
|
|
24
|
+
});
|
|
25
|
+
export const GetManySuitesSchema = z.object({
|
|
26
|
+
action: z.literal("many").describe("Retrieve all test suites for a project"),
|
|
27
|
+
project_id: z.number().int().describe("The ID of the project"),
|
|
28
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized utility to handle mutation actions (create or update) in a type-safe manner.
|
|
3
|
+
*
|
|
4
|
+
* @param payload The incoming payload containing the action discriminator
|
|
5
|
+
* @param onCreate Callback executed when action is "create"
|
|
6
|
+
* @param onUpdate Callback executed when action is "update"
|
|
7
|
+
* @returns The mutated entity wrapped in a record
|
|
8
|
+
*/
|
|
9
|
+
export async function handleMutate(payload, onCreate, onUpdate, schema) {
|
|
10
|
+
let result;
|
|
11
|
+
if (payload.action === "create") {
|
|
12
|
+
result = await onCreate(payload);
|
|
13
|
+
}
|
|
14
|
+
else if (payload.action === "update") {
|
|
15
|
+
result = await onUpdate(payload);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const unknownAction = payload.action;
|
|
19
|
+
throw new Error(`Unsupported mutation action: ${unknownAction}`);
|
|
20
|
+
}
|
|
21
|
+
return schema ? schema.parse(result) : result;
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized utility to handle query actions (one or many) in a type-safe manner.
|
|
3
|
+
*
|
|
4
|
+
* @param payload The incoming payload containing the action discriminator
|
|
5
|
+
* @param onQueryOne Callback executed when action is "one"
|
|
6
|
+
* @param onQueryMany Callback executed when action is "many"
|
|
7
|
+
* @returns The queried entity or list of entities wrapped in a record
|
|
8
|
+
*/
|
|
9
|
+
export async function handleQuery(payload, onQueryOne, onQueryMany) {
|
|
10
|
+
if (payload.action === "one") {
|
|
11
|
+
return onQueryOne(payload);
|
|
12
|
+
}
|
|
13
|
+
else if (payload.action === "many") {
|
|
14
|
+
return onQueryMany(payload);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
const unknownAction = payload.action;
|
|
18
|
+
throw new Error(`Unsupported query action: ${unknownAction}`);
|
|
19
|
+
}
|
|
20
|
+
}
|