@uarlouski/testrail-mcp-server 1.1.0 → 1.3.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +62 -16
  3. package/package.json +3 -2
  4. package/src/client/testrail.ts +111 -56
  5. package/src/index.ts +32 -2
  6. package/src/tools/add_attachment_to_run.ts +62 -0
  7. package/src/tools/add_results.ts +28 -0
  8. package/src/tools/add_run.ts +24 -0
  9. package/src/tools/create_case.ts +10 -17
  10. package/src/tools/get_case.ts +5 -12
  11. package/src/tools/get_case_fields.ts +4 -11
  12. package/src/tools/get_cases.ts +9 -16
  13. package/src/tools/get_projects.ts +7 -27
  14. package/src/tools/get_sections.ts +7 -27
  15. package/src/tools/get_statuses.ts +18 -0
  16. package/src/tools/get_templates.ts +7 -27
  17. package/src/tools/get_tests.ts +22 -0
  18. package/src/tools/update_case.ts +7 -14
  19. package/src/tools/update_cases.ts +10 -16
  20. package/src/types/custom.ts +1 -16
  21. package/src/types/testrail.ts +140 -87
  22. package/src/utils/sanitizer.ts +29 -0
  23. package/test/client/testrail.test.ts +329 -141
  24. package/test/tools/add_results.test.ts +94 -0
  25. package/test/tools/add_run.test.ts +83 -0
  26. package/test/tools/create_case.test.ts +10 -16
  27. package/test/tools/get_case.test.ts +47 -62
  28. package/test/tools/get_case_fields.test.ts +7 -80
  29. package/test/tools/get_cases.test.ts +19 -108
  30. package/test/tools/get_projects.test.ts +4 -41
  31. package/test/tools/get_sections.test.ts +7 -36
  32. package/test/tools/get_statuses.test.ts +56 -0
  33. package/test/tools/get_templates.test.ts +1 -30
  34. package/test/tools/get_tests.test.ts +47 -0
  35. package/test/tools/update_case.test.ts +10 -16
  36. package/test/tools/update_cases.test.ts +29 -28
  37. package/test/utils/mapper.test.ts +13 -13
  38. package/test/utils/sanitizer.test.ts +85 -1
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { processCustomFields } from "../utils/mapper.js";
3
3
  import { TestRailClient } from "../client/testrail.js";
4
- import { TestCaseResponse, ToolDefinition, withErrorHandling } from "../types/custom.js";
4
+ import { TestCaseResponse, ToolDefinition } from "../types/custom.js";
5
5
 
6
6
  const parameters = {
7
7
  case_id: z.string().describe("The ID of the test case (e.g. '123' or 'C123')"),
@@ -9,9 +9,9 @@ const parameters = {
9
9
 
10
10
  export const getCaseTool: ToolDefinition<typeof parameters, TestRailClient> = {
11
11
  name: "get_case",
12
- description: "Get a test case from TestRail by ID",
12
+ description: "Get detailed information about a test case including its custom fields",
13
13
  parameters,
14
- handler: withErrorHandling<typeof parameters, TestRailClient>(async ({ case_id }, client) => {
14
+ handler: async ({ case_id }, client) => {
15
15
  const id = case_id.toUpperCase().startsWith("C") ? case_id.substring(1) : case_id;
16
16
  const testCase = await client.getCase(id);
17
17
 
@@ -39,13 +39,6 @@ export const getCaseTool: ToolDefinition<typeof parameters, TestRailClient> = {
39
39
  ...customFields
40
40
  };
41
41
 
42
- return {
43
- content: [
44
- {
45
- type: "text" as const,
46
- text: JSON.stringify(response, null, 2),
47
- },
48
- ],
49
- };
50
- })
42
+ return response;
43
+ }
51
44
  };
@@ -1,5 +1,5 @@
1
1
  import { TestRailClient } from "../client/testrail.js";
2
- import { ToolDefinition, withErrorHandling } from "../types/custom.js";
2
+ import { ToolDefinition } from "../types/custom.js";
3
3
  import { CaseField } from "../types/testrail.js";
4
4
  import { parseDropdownOptions } from "../utils/mapper.js";
5
5
 
@@ -76,7 +76,7 @@ export const getCaseFieldsTool: ToolDefinition<typeof parameters, TestRailClient
76
76
  name: "get_case_fields",
77
77
  description: "Get the field schema for test cases. Returns all available fields with their types, descriptions, and options (for dropdown fields). Use this to understand what fields can be set when creating or updating test cases.",
78
78
  parameters,
79
- handler: withErrorHandling<typeof parameters, TestRailClient>(async (_args, client) => {
79
+ handler: async (_args, client) => {
80
80
  const caseFields = await client.getCaseFields();
81
81
 
82
82
  const customFieldSchemas = caseFields.filter(field => field.is_active).map(mapToFieldSchema);
@@ -85,13 +85,6 @@ export const getCaseFieldsTool: ToolDefinition<typeof parameters, TestRailClient
85
85
  fields: [...SYSTEM_FIELDS, ...customFieldSchemas],
86
86
  };
87
87
 
88
- return {
89
- content: [
90
- {
91
- type: "text" as const,
92
- text: JSON.stringify(response, null, 2),
93
- },
94
- ],
95
- };
96
- })
88
+ return response;
89
+ }
97
90
  };
@@ -1,14 +1,14 @@
1
1
  import { z } from "zod";
2
2
  import { TestRailClient } from "../client/testrail.js";
3
- import { ToolDefinition, withErrorHandling } from "../types/custom.js";
3
+ import { ToolDefinition } from "../types/custom.js";
4
4
  import { Case } from "../types/testrail.js";
5
5
 
6
6
  const parameters = {
7
- project_id: z.string().describe("The ID of the project"),
8
- section_id: z.string().optional().describe("Optional section ID to filter cases by section"),
9
- filter: z.record(z.string(), z.string()).optional().describe("Optional API filter parameters. Supported filters: priority_id (comma-separated IDs), type_id (comma-separated IDs), created_by (comma-separated user IDs), updated_by, milestone_id, refs, created_after, created_before, updated_after, updated_before. Example: {\"priority_id\": \"1,2\", \"type_id\": \"3\"}"),
10
- where: z.record(z.string(), z.any()).optional().describe("Optional client-side filter for any field including custom fields. Cases are filtered after fetching. Supports exact match for values. Example: {\"custom_automation_status\": 1, \"priority_id\": 2}"),
11
- fields: z.array(z.string()).optional().describe("Optional list of additional fields to include in the response. Use get_case_fields to see available field names. Example: [\"priority_id\", \"type_id\", \"custom_automation_status\"]"),
7
+ project_id: z.string().describe("The ID of the project. Use get_projects to find available projects"),
8
+ section_id: z.string().optional().describe("Optional section ID to filter cases by section. Use get_sections to find available sections"),
9
+ filter: z.record(z.string(), z.string()).optional().describe("Optional API-side filters (more efficient for large datasets). Supported: priority_id, type_id, created_by, updated_by, milestone_id, refs, created_after, created_before, updated_after, updated_before. Use comma-separated values for IDs. Example: {\"priority_id\": \"1,2\", \"type_id\": \"3\"}"),
10
+ where: z.record(z.string(), z.any()).optional().describe("Optional client-side filter for any field including custom fields (filters after fetching all cases). Supports exact value matching. Example: {\"custom_automation_status\": 1, \"priority_id\": 2}"),
11
+ fields: z.array(z.string()).optional().describe("Additional fields to include in response beyond id, title, and suite_id. Use get_case_fields to see available fields. Example: [\"priority_id\", \"type_id\", \"custom_automation_status\"]"),
12
12
  };
13
13
 
14
14
  interface CasesResponse {
@@ -19,7 +19,7 @@ export const getCasesTool: ToolDefinition<typeof parameters, TestRailClient> = {
19
19
  name: "get_cases",
20
20
  description: "Get all test cases for a project. Filter by section, API params (priority, type), or any field including custom fields via 'where'. Returns case IDs, titles, and any additional requested fields.",
21
21
  parameters,
22
- handler: withErrorHandling<typeof parameters, TestRailClient>(async ({ project_id, section_id, filter, where, fields }, client) => {
22
+ handler: async ({ project_id, section_id, filter, where, fields }, client) => {
23
23
  let cases: Case[] = await client.getCases(project_id, section_id, filter);
24
24
 
25
25
  // Client-side filtering for custom fields and other unsupported API filters
@@ -55,13 +55,6 @@ export const getCasesTool: ToolDefinition<typeof parameters, TestRailClient> = {
55
55
  }),
56
56
  };
57
57
 
58
- return {
59
- content: [
60
- {
61
- type: "text" as const,
62
- text: JSON.stringify(response, null, 2),
63
- },
64
- ],
65
- };
66
- })
58
+ return response;
59
+ }
67
60
  };
@@ -1,40 +1,20 @@
1
1
  import { TestRailClient } from "../client/testrail.js";
2
- import { ToolDefinition, withErrorHandling } from "../types/custom.js";
2
+ import { ToolDefinition } from "../types/custom.js";
3
+ import { ProjectSchema } from "../types/testrail.js";
3
4
 
4
5
  const parameters = {};
5
6
 
6
- interface ProjectSchema {
7
- id: number;
8
- name: string;
9
- }
10
-
11
- interface ProjectsResponse {
12
- projects: ProjectSchema[];
13
- }
14
-
15
7
  export const getProjectsTool: ToolDefinition<typeof parameters, TestRailClient> = {
16
8
  name: "get_projects",
17
- description: "Get all available projects in TestRail. Returns project IDs and names. Use this to find the project_id needed for get_sections and get_templates.",
9
+ description: "Get all available projects in TestRail. Returns project IDs and names that can be used with get_sections, get_templates, get_cases, and add_run",
18
10
  parameters,
19
- handler: withErrorHandling<typeof parameters, TestRailClient>(async (_args, client) => {
11
+ handler: async (_args, client) => {
20
12
  const projects = await client.getProjects();
21
13
 
22
- const response: ProjectsResponse = {
14
+ return {
23
15
  projects: projects
24
16
  .filter(p => !p.is_completed)
25
- .map(p => ({
26
- id: p.id,
27
- name: p.name,
28
- })),
29
- };
30
-
31
- return {
32
- content: [
33
- {
34
- type: "text" as const,
35
- text: JSON.stringify(response, null, 2),
36
- },
37
- ],
17
+ .map(p => ProjectSchema.parse(p)),
38
18
  };
39
- })
19
+ }
40
20
  };
@@ -1,41 +1,21 @@
1
1
  import { z } from "zod";
2
2
  import { TestRailClient } from "../client/testrail.js";
3
- import { ToolDefinition, withErrorHandling } from "../types/custom.js";
3
+ import { ToolDefinition } from "../types/custom.js";
4
+ import { SectionSchema } from "../types/testrail.js";
4
5
 
5
6
  const parameters = {
6
- project_id: z.string().describe("The ID of the project"),
7
+ project_id: z.string().describe("The ID of the project. Use get_projects to find available projects"),
7
8
  };
8
9
 
9
- interface SectionSchema {
10
- id: number;
11
- name: string;
12
- }
13
-
14
- interface SectionsResponse {
15
- sections: SectionSchema[];
16
- }
17
-
18
10
  export const getSectionsTool: ToolDefinition<typeof parameters, TestRailClient> = {
19
11
  name: "get_sections",
20
- description: "Get all sections for a project. Returns section IDs and names. Use this to find the section_id needed for create_case.",
12
+ description: "Get all sections for a project. Returns section IDs and names that can be used with create_case",
21
13
  parameters,
22
- handler: withErrorHandling<typeof parameters, TestRailClient>(async ({ project_id }, client) => {
14
+ handler: async ({ project_id }, client) => {
23
15
  const sections = await client.getSections(project_id);
24
16
 
25
- const response: SectionsResponse = {
26
- sections: sections.map(s => ({
27
- id: s.id,
28
- name: s.name,
29
- })),
30
- };
31
-
32
17
  return {
33
- content: [
34
- {
35
- type: "text" as const,
36
- text: JSON.stringify(response, null, 2),
37
- },
38
- ],
18
+ sections: sections.map(s => SectionSchema.parse(s)),
39
19
  };
40
- })
20
+ }
41
21
  };
@@ -0,0 +1,18 @@
1
+ import { TestRailClient } from "../client/testrail.js";
2
+ import { z } from "zod";
3
+ import { StatusSchema } from "../types/testrail.js";
4
+ import { ToolDefinition } from "../types/custom.js";
5
+
6
+ const parameters = {};
7
+
8
+ export const getStatusesTool: ToolDefinition<typeof parameters, TestRailClient> = {
9
+ name: "get_statuses",
10
+ description: "Get all available test statuses (e.g. Passed, Failed, Blocked). Returns status IDs and names that can be used with add_result and get_tests",
11
+ parameters,
12
+ handler: async (args: any, client: TestRailClient) => {
13
+ const statuses = await client.getStatuses();
14
+ return {
15
+ statuses: statuses.map(s => StatusSchema.parse(s)),
16
+ };
17
+ },
18
+ };
@@ -1,41 +1,21 @@
1
1
  import { z } from "zod";
2
2
  import { TestRailClient } from "../client/testrail.js";
3
- import { ToolDefinition, withErrorHandling } from "../types/custom.js";
3
+ import { ToolDefinition } from "../types/custom.js";
4
+ import { TemplateSchema } from "../types/testrail.js";
4
5
 
5
6
  const parameters = {
6
- project_id: z.string().describe("The ID of the project"),
7
+ project_id: z.string().describe("The ID of the project. Use get_projects to find available projects"),
7
8
  };
8
9
 
9
- interface TemplateSchema {
10
- id: number;
11
- name: string;
12
- }
13
-
14
- interface TemplatesResponse {
15
- templates: TemplateSchema[];
16
- }
17
-
18
10
  export const getTemplatesTool: ToolDefinition<typeof parameters, TestRailClient> = {
19
11
  name: "get_templates",
20
- description: "Get available test case templates for a project. Returns template IDs and names. Use template IDs with get_case_fields to understand which fields apply to each template.",
12
+ description: "Get available test case templates for a project. Template IDs determine which fields are available when creating or updating test cases",
21
13
  parameters,
22
- handler: withErrorHandling<typeof parameters, TestRailClient>(async ({ project_id }, client) => {
14
+ handler: async ({ project_id }, client) => {
23
15
  const templates = await client.getTemplates(project_id);
24
16
 
25
- const response: TemplatesResponse = {
26
- templates: templates.map(t => ({
27
- id: t.id,
28
- name: t.name,
29
- })),
30
- };
31
-
32
17
  return {
33
- content: [
34
- {
35
- type: "text" as const,
36
- text: JSON.stringify(response, null, 2),
37
- },
38
- ],
18
+ templates: templates.map(t => TemplateSchema.parse(t)),
39
19
  };
40
- })
20
+ }
41
21
  };
@@ -0,0 +1,22 @@
1
+ import { TestRailClient } from "../client/testrail.js";
2
+ import { z } from "zod";
3
+ import { Test, TestSchema } from "../types/testrail.js";
4
+ import { ToolDefinition } from "../types/custom.js";
5
+
6
+ const parameters = {
7
+ run_id: z.number().describe("The ID of the test run"),
8
+ status_id: z.array(z.number()).optional().describe("Optional array of status IDs to filter by. Use get_statuses to retrieve available status IDs"),
9
+ }
10
+
11
+ export const getTestsTool: ToolDefinition<typeof parameters, TestRailClient> = {
12
+ name: "get_tests",
13
+ description: "Get tests for a test run, optionally filtered by status",
14
+ parameters,
15
+ handler: async ({ run_id, status_id }, client: TestRailClient) => {
16
+ const tests: Test[] = await client.getTests(run_id, status_id);
17
+
18
+ return {
19
+ tests: tests.map(test => TestSchema.parse(test)),
20
+ };
21
+ },
22
+ };
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { TestRailClient } from "../client/testrail.js";
3
- import { ToolDefinition, withErrorHandling } from "../types/custom.js";
3
+ import { ToolDefinition } from "../types/custom.js";
4
4
 
5
5
  const parameters = {
6
6
  case_id: z.string().describe("The ID of the test case to update (e.g. '123' or 'C123')"),
@@ -9,24 +9,17 @@ const parameters = {
9
9
 
10
10
  export const updateCaseTool: ToolDefinition<typeof parameters, TestRailClient> = {
11
11
  name: "update_case",
12
- description: "Update a test case in TestRail. Supports partial updates - only specify the fields you want to change. Use get_case_fields to see available fields and their types.",
12
+ description: "Update a test case in TestRail. Supports partial updates - only specify the fields you want to change",
13
13
  parameters,
14
- handler: withErrorHandling<typeof parameters, TestRailClient>(async ({ case_id, fields }, client) => {
14
+ handler: async ({ case_id, fields }, client) => {
15
15
  const id = case_id.toUpperCase().startsWith("C") ? case_id.substring(1) : case_id;
16
16
 
17
17
  const updatedCase = await client.updateCase(id, fields);
18
18
 
19
19
  return {
20
- content: [
21
- {
22
- type: "text" as const,
23
- text: JSON.stringify({
24
- success: true,
25
- case_id: updatedCase.id,
26
- message: `Test case C${updatedCase.id} updated successfully`,
27
- }, null, 2),
28
- },
29
- ],
20
+ success: true,
21
+ case_id: updatedCase.id,
22
+ message: `Test case C${updatedCase.id} updated successfully`,
30
23
  };
31
- })
24
+ }
32
25
  };
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { TestRailClient } from "../client/testrail.js";
3
- import { ToolDefinition, withErrorHandling } from "../types/custom.js";
3
+ import { ToolDefinition } from "../types/custom.js";
4
4
 
5
5
  const parameters = {
6
6
  case_ids: z.array(z.number()).describe("Array of case IDs to update (e.g. [123, 456, 789])"),
@@ -9,23 +9,17 @@ const parameters = {
9
9
 
10
10
  export const updateCasesTool: ToolDefinition<typeof parameters, TestRailClient> = {
11
11
  name: "update_cases",
12
- description: "Bulk update multiple test cases with the same field values. More efficient than calling update_case multiple times. All cases will receive the same field values.",
12
+ description: "Bulk update multiple test cases with the same field values. More efficient than calling update_case multiple times",
13
13
  parameters,
14
- handler: withErrorHandling<typeof parameters, TestRailClient>(async ({ case_ids, fields }, client) => {
15
- const updatedCases = await client.updateCases(case_ids, fields);
14
+ handler: async ({ case_ids, fields }, client) => {
15
+ const caseData = await client.getCase(String(case_ids[0]));
16
+ const updatedCases = await client.updateCases(caseData.suite_id, case_ids, fields);
16
17
 
17
18
  return {
18
- content: [
19
- {
20
- type: "text" as const,
21
- text: JSON.stringify({
22
- success: true,
23
- updated_count: updatedCases.length,
24
- case_ids: updatedCases.map(c => c.id),
25
- message: `Successfully updated ${updatedCases.length} test cases`,
26
- }, null, 2),
27
- },
28
- ],
19
+ success: true,
20
+ updated_count: updatedCases.length,
21
+ case_ids: updatedCases.map(c => c.id),
22
+ message: `Successfully updated ${updatedCases.length} test cases`,
29
23
  };
30
- })
24
+ }
31
25
  };
@@ -15,20 +15,5 @@ export interface ToolDefinition<T extends z.ZodRawShape, Context = any> {
15
15
  name: string;
16
16
  description: string;
17
17
  parameters: T;
18
- handler: (args: z.infer<z.ZodObject<T>>, context: Context) => Promise<any>;
19
- }
20
-
21
- export function withErrorHandling<T extends z.ZodRawShape, Context = any>(
22
- handler: (args: z.infer<z.ZodObject<T>>, context: Context) => Promise<any>
23
- ): (args: z.infer<z.ZodObject<T>>, context: Context) => Promise<any> {
24
- return async (args, context) => {
25
- try {
26
- return await handler(args, context);
27
- } catch (error: any) {
28
- return {
29
- content: [{ type: "text", text: `Error: ${error.message}` }],
30
- isError: true,
31
- };
32
- }
33
- };
18
+ handler: (args: z.infer<z.ZodObject<T>>, context: Context) => Promise<Record<string, any>>;
34
19
  }
@@ -1,87 +1,140 @@
1
- export interface Case {
2
- id: number;
3
- title: string;
4
- section_id: number;
5
- template_id: number;
6
- type_id: number;
7
- priority_id: number;
8
- milestone_id: number | null;
9
- refs: string | null;
10
- created_by: number;
11
- created_on: number;
12
- updated_by: number;
13
- updated_on: number;
14
- estimate: string | null;
15
- estimate_forecast: string | null;
16
- suite_id: number;
17
- display_order: number;
18
- is_deleted: number;
19
- labels: any[];
20
- [key: string]: any;
21
- }
22
-
23
- export interface Section {
24
- id: number;
25
- name: string;
26
- description: string | null;
27
- parent_id: number | null;
28
- depth: number;
29
- display_order: number;
30
- suite_id: number;
31
- }
32
-
33
- export interface Priority {
34
- id: number;
35
- is_default: boolean;
36
- name: string;
37
- priority: number;
38
- short_name: string;
39
- }
40
-
41
- export interface CaseType {
42
- id: number;
43
- is_default: boolean;
44
- name: string;
45
- }
46
-
47
- export interface CaseField {
48
- id: number;
49
- name: string;
50
- system_name: string;
51
- label: string;
52
- type_id: number;
53
- template_ids: number[];
54
- is_active: boolean;
55
- display_order: number;
56
- description: string | null;
57
- include_all: boolean;
58
- configs: CaseFieldConfig[];
59
- }
60
-
61
- export interface CaseFieldConfig {
62
- context: {
63
- is_global: boolean;
64
- project_ids: number[] | null;
65
- };
66
- options: {
67
- default_value?: string;
68
- format?: string;
69
- is_required?: boolean;
70
- rows?: string;
71
- items?: string;
72
- };
73
- }
74
-
75
- export interface Template {
76
- id: number;
77
- name: string;
78
- is_default: boolean;
79
- }
80
-
81
- export interface Project {
82
- id: number;
83
- name: string;
84
- announcement: string | null;
85
- is_completed: boolean;
86
- suite_mode: number;
87
- }
1
+ import { z } from "zod";
2
+
3
+ export const CaseFieldConfigSchema = z.object({
4
+ options: z.object({
5
+ default_value: z.string().optional(),
6
+ is_required: z.boolean().optional(),
7
+ items: z.string().optional(),
8
+ }),
9
+ });
10
+
11
+ export type CaseFieldConfig = z.infer<typeof CaseFieldConfigSchema>;
12
+
13
+ export const CaseFieldSchema = z.object({
14
+ id: z.number(),
15
+ name: z.string(),
16
+ system_name: z.string(),
17
+ label: z.string(),
18
+ type_id: z.number(),
19
+ template_ids: z.array(z.number()),
20
+ is_active: z.boolean(),
21
+ description: z.string().nullable(),
22
+ include_all: z.boolean(),
23
+ configs: z.array(CaseFieldConfigSchema),
24
+ });
25
+
26
+ export type CaseField = z.infer<typeof CaseFieldSchema>;
27
+
28
+ export const CaseSchema = z.object({
29
+ id: z.number(),
30
+ title: z.string(),
31
+ section_id: z.number(),
32
+ template_id: z.number(),
33
+ type_id: z.number(),
34
+ priority_id: z.number(),
35
+ milestone_id: z.number().nullable(),
36
+ refs: z.string().nullable(),
37
+ created_on: z.number(),
38
+ updated_on: z.number(),
39
+ estimate: z.string().nullable(),
40
+ suite_id: z.number(),
41
+ labels: z.array(z.any()),
42
+ }).loose();
43
+
44
+ export type Case = z.infer<typeof CaseSchema>;
45
+
46
+ export const SectionSchema = z.object({
47
+ id: z.number(),
48
+ name: z.string(),
49
+ description: z.string().nullish(),
50
+ parent_id: z.number().nullish(),
51
+ suite_id: z.number(),
52
+ });
53
+
54
+ export type Section = z.infer<typeof SectionSchema>;
55
+
56
+ export const PrioritySchema = z.object({
57
+ id: z.number(),
58
+ is_default: z.boolean(),
59
+ name: z.string(),
60
+ });
61
+
62
+ export type Priority = z.infer<typeof PrioritySchema>;
63
+
64
+ export const CaseTypeSchema = z.object({
65
+ id: z.number(),
66
+ is_default: z.boolean(),
67
+ name: z.string(),
68
+ });
69
+
70
+ export type CaseType = z.infer<typeof CaseTypeSchema>;
71
+
72
+ export const TemplateSchema = z.object({
73
+ id: z.number(),
74
+ name: z.string(),
75
+ is_default: z.boolean(),
76
+ });
77
+
78
+ export type Template = z.infer<typeof TemplateSchema>;
79
+
80
+ export const ProjectSchema = z.object({
81
+ id: z.number(),
82
+ name: z.string(),
83
+ is_completed: z.boolean(),
84
+ suite_mode: z.number(),
85
+ });
86
+
87
+ export type Project = z.infer<typeof ProjectSchema>;
88
+
89
+ export const RunSchema = z.object({
90
+ id: z.number(),
91
+ name: z.string(),
92
+ description: z.string().nullable(),
93
+ suite_id: z.number().nullable(),
94
+ project_id: z.number(),
95
+ is_completed: z.boolean(),
96
+ passed_count: z.number(),
97
+ blocked_count: z.number(),
98
+ untested_count: z.number(),
99
+ retest_count: z.number(),
100
+ failed_count: z.number(),
101
+ url: z.string(),
102
+ });
103
+
104
+ export type Run = z.infer<typeof RunSchema>;
105
+
106
+ export const StatusSchema = z.object({
107
+ id: z.number(),
108
+ name: z.string(),
109
+ label: z.string(),
110
+ });
111
+
112
+ export type Status = z.infer<typeof StatusSchema>;
113
+
114
+ export const TestSchema = z.object({
115
+ id: z.number(),
116
+ case_id: z.number(),
117
+ status_id: z.number(),
118
+ title: z.string(),
119
+ run_id: z.number(),
120
+ })
121
+
122
+ export const TestsSchema = z.array(TestSchema)
123
+
124
+ export type Test = z.infer<typeof TestSchema>
125
+
126
+ export const ResultSchema = z.object({
127
+ id: z.number(),
128
+ test_id: z.number(),
129
+ status_id: z.number(),
130
+ comment: z.string().nullable(),
131
+ defects: z.string().nullable(),
132
+ });
133
+
134
+ export type Result = z.infer<typeof ResultSchema>
135
+
136
+ export const AttachmentSchema = z.object({
137
+ attachment_id: z.number(),
138
+ });
139
+
140
+ export type Attachment = z.infer<typeof AttachmentSchema>