Skip to main content
Glama
public-api-test-runs.test.tsโ€ข15.8 kB
import { describe, it } from 'node:test'; import { strict as assert } from 'node:assert'; import { z } from 'zod'; import { PublicTestRunResourceSchema, PublicTestRunsResponseSchema, PublicTestRunResponseSchema, PublicTestRunTestCasesResponseSchema } from '../../src/types/core.js'; describe('Public API Test Run Tools Unit Tests', () => { describe('Public API Test Run Schemas', () => { it('should validate PublicTestRunResource schema', () => { const validTestRun = { id: 123, title: "Test Run 25.39.0", description: "Build 12345 test execution", milestone: { id: 556, name: "25.39.0", completed: false, description: "Release milestone", startDate: "2025-09-23T22:00:00Z", dueDate: "2025-09-30T22:00:00Z" }, environment: { id: 1, key: "staging", name: "Staging Environment" }, configurations: [ { group: { id: 1, name: "Browser" }, option: { id: 2, name: "Chrome" } } ], requirements: [ { source: "JIRA", reference: "PROJ-123" } ], closed: false, createdBy: { id: 604, username: "maksim.sarychau", email: "maksim.sarychau@example.com" }, createdAt: "2025-09-30T10:00:00Z", executionSummaries: [ { status: { id: 1, name: "PASSED", colorHex: "#28a745" }, testCasesCount: 15 }, { status: { id: 2, name: "FAILED", colorHex: "#dc3545" }, testCasesCount: 3 } ] }; assert.doesNotThrow(() => PublicTestRunResourceSchema.parse(validTestRun)); }); it('should validate PublicTestRunsResponse schema', () => { const validResponse = { items: [ { id: 123, title: "Test Run 1", description: null, milestone: null, environment: null, configurations: [], requirements: [], closed: false, createdBy: { id: 604, username: "test.user", email: "test@example.com" }, createdAt: "2025-09-30T10:00:00Z", executionSummaries: [] } ], _meta: { nextPageToken: "next_page_token_123" } }; assert.doesNotThrow(() => PublicTestRunsResponseSchema.parse(validResponse)); }); it('should validate PublicTestRunResponse schema', () => { const validResponse = { data: { id: 123, title: "Test Run Details", description: "Detailed test run", milestone: null, environment: null, configurations: [], requirements: [], closed: true, createdBy: { id: 604, username: "test.user", email: "test@example.com" }, createdAt: "2025-09-30T10:00:00Z", executionSummaries: [] } }; assert.doesNotThrow(() => PublicTestRunResponseSchema.parse(validResponse)); }); it('should validate PublicTestRunTestCasesResponse schema', () => { const validResponse = { items: [ { testCase: { id: 456, key: "MCP-123", title: "Test login functionality" }, assignee: { id: 604, username: "test.assignee", email: "assignee@example.com" }, result: { status: { id: 1, name: "PASSED", aliases: "pass" }, details: "Test executed successfully", issue: { type: "JIRA", id: "BUG-456" }, executionTimeInMillis: 5000, executionType: "AUTOMATED", attachments: [ { fileUuid: "uuid-123" } ] } } ] }; assert.doesNotThrow(() => PublicTestRunTestCasesResponseSchema.parse(validResponse)); }); it('should handle nullable and optional fields correctly', () => { const minimalTestRun = { id: 123, title: "Minimal Test Run", milestone: null, environment: null, configurations: [], requirements: [], closed: false, createdBy: { id: 604, username: "test.user", email: "test@example.com" }, createdAt: "2025-09-30T10:00:00Z", executionSummaries: [] }; assert.doesNotThrow(() => PublicTestRunResourceSchema.parse(minimalTestRun)); }); }); describe('Public API Tool Parameter Validation', () => { describe('list_test_runs tool parameters', () => { const listTestRunsSchema = z.object({ project: z.union([z.enum(["web","android","ios","api"]), z.string()]) .default("web"), pageToken: z.string().optional(), maxPageSize: z.number().int().positive().max(100).default(10), nameFilter: z.string().optional(), milestoneFilter: z.string().optional(), buildNumberFilter: z.string().optional(), closedFilter: z.boolean().optional(), sortBy: z.enum(["-createdAt", "createdAt", "-title", "title"]) .default("-createdAt"), format: z.enum(['raw', 'formatted']).default('formatted') }); it('should validate default parameters', () => { const result = listTestRunsSchema.parse({}); assert.strictEqual(result.project, "web"); assert.strictEqual(result.maxPageSize, 10); assert.strictEqual(result.sortBy, "-createdAt"); assert.strictEqual(result.format, "formatted"); }); it('should validate all filter parameters', () => { const input = { project: "android", pageToken: "token123", maxPageSize: 50, nameFilter: "regression", milestoneFilter: "25.39.0", buildNumberFilter: "12345", closedFilter: false, sortBy: "title" as const, format: "raw" as const }; const result = listTestRunsSchema.parse(input); assert.deepStrictEqual(result, input); }); it('should reject invalid maxPageSize', () => { assert.throws(() => listTestRunsSchema.parse({ maxPageSize: 101 })); assert.throws(() => listTestRunsSchema.parse({ maxPageSize: 0 })); assert.throws(() => listTestRunsSchema.parse({ maxPageSize: -5 })); }); it('should accept project keys and aliases', () => { assert.strictEqual(listTestRunsSchema.parse({ project: "web" }).project, "web"); assert.strictEqual(listTestRunsSchema.parse({ project: "MCP" }).project, "MCP"); assert.strictEqual(listTestRunsSchema.parse({ project: "CUSTOM_PROJECT" }).project, "CUSTOM_PROJECT"); }); }); describe('get_test_run_by_id tool parameters', () => { const getTestRunByIdSchema = z.object({ id: z.number().int().positive(), project: z.union([z.enum(["web","android","ios","api"]), z.string()]) .default("web"), format: z.enum(['raw', 'formatted']).default('formatted') }); it('should validate required id parameter', () => { const result = getTestRunByIdSchema.parse({ id: 123 }); assert.strictEqual(result.id, 123); assert.strictEqual(result.project, "web"); assert.strictEqual(result.format, "formatted"); }); it('should reject invalid id values', () => { assert.throws(() => getTestRunByIdSchema.parse({ id: 0 })); assert.throws(() => getTestRunByIdSchema.parse({ id: -1 })); assert.throws(() => getTestRunByIdSchema.parse({ id: 1.5 })); }); }); describe('list_test_run_test_cases tool parameters', () => { const listTestRunTestCasesSchema = z.object({ testRunId: z.number().int().positive(), project: z.union([z.enum(["web","android","ios","api"]), z.string()]) .default("web"), format: z.enum(['raw', 'formatted']).default('formatted') }); it('should validate required testRunId parameter', () => { const result = listTestRunTestCasesSchema.parse({ testRunId: 456 }); assert.strictEqual(result.testRunId, 456); assert.strictEqual(result.project, "web"); assert.strictEqual(result.format, "formatted"); }); it('should reject invalid testRunId values', () => { assert.throws(() => listTestRunTestCasesSchema.parse({ testRunId: 0 })); assert.throws(() => listTestRunTestCasesSchema.parse({ testRunId: -1 })); }); }); }); describe('Filter Expression Building', () => { it('should build correct filter expressions for Resource Query Language', () => { const buildFilters = (args: any) => { const filters: string[] = []; if (args.nameFilter) { filters.push(`title ~= '${args.nameFilter.replace(/'/g, "\\'")}'`); } if (args.milestoneFilter) { filters.push(`milestone.name ~= '${args.milestoneFilter.replace(/'/g, "\\'")}'`); } if (args.buildNumberFilter) { filters.push(`(title ~= '${args.buildNumberFilter.replace(/'/g, "\\'")}' OR description ~= '${args.buildNumberFilter.replace(/'/g, "\\'")}')`); } if (args.closedFilter !== undefined) { filters.push(`closed = ${args.closedFilter}`); } return filters.length > 0 ? filters.join(' AND ') : undefined; }; // Test individual filters assert.strictEqual(buildFilters({ nameFilter: "regression" }), "title ~= 'regression'"); assert.strictEqual(buildFilters({ milestoneFilter: "25.39.0" }), "milestone.name ~= '25.39.0'"); assert.strictEqual(buildFilters({ buildNumberFilter: "12345" }), "(title ~= '12345' OR description ~= '12345')"); assert.strictEqual(buildFilters({ closedFilter: false }), "closed = false"); // Test combined filters assert.strictEqual(buildFilters({ nameFilter: "smoke", milestoneFilter: "25.39.0", closedFilter: false }), "title ~= 'smoke' AND milestone.name ~= '25.39.0' AND closed = false"); // Test escaping single quotes assert.strictEqual(buildFilters({ nameFilter: "test's name" }), "title ~= 'test\\'s name'"); }); it('should handle empty filters', () => { const buildFilters = (args: any) => { const filters: string[] = []; return filters.length > 0 ? filters.join(' AND ') : undefined; }; assert.strictEqual(buildFilters({}), undefined); }); }); describe('Formatted Output Structure', () => { it('should format test runs list correctly', () => { const mockTestRun = { id: 123, title: "Test Run 25.39.0", description: "Build 12345 execution", milestone: { id: 556, name: "25.39.0", completed: false, dueDate: "2025-09-30T22:00:00Z" }, environment: { key: "staging", name: "Staging Environment" }, configurations: [ { group: { name: "Browser" }, option: { name: "Chrome" } } ], requirements: [ { source: "JIRA", reference: "PROJ-123" } ], closed: false, createdBy: { username: "test.user", email: "test@example.com" }, createdAt: "2025-09-30T10:00:00Z", executionSummaries: [ { status: { name: "PASSED" }, testCasesCount: 15 } ] }; const formatted = { id: mockTestRun.id, title: mockTestRun.title, description: mockTestRun.description, milestone: { id: mockTestRun.milestone.id, name: mockTestRun.milestone.name, completed: mockTestRun.milestone.completed, dueDate: mockTestRun.milestone.dueDate }, environment: { key: mockTestRun.environment.key, name: mockTestRun.environment.name }, configurations: ["Browser: Chrome"], requirements: ["JIRA: PROJ-123"], closed: mockTestRun.closed, createdBy: "test.user (test@example.com)", createdAt: mockTestRun.createdAt, testCasesSummary: "PASSED: 15" }; assert.deepStrictEqual(formatted.milestone, { id: 556, name: "25.39.0", completed: false, dueDate: "2025-09-30T22:00:00Z" }); assert.deepStrictEqual(formatted.configurations, ["Browser: Chrome"]); assert.deepStrictEqual(formatted.requirements, ["JIRA: PROJ-123"]); assert.strictEqual(formatted.createdBy, "test.user (test@example.com)"); assert.strictEqual(formatted.testCasesSummary, "PASSED: 15"); }); it('should format test run details correctly', () => { const mockTestRun = { id: 123, title: "Detailed Test Run", executionSummaries: [ { status: { name: "PASSED", colorHex: "#28a745" }, testCasesCount: 10 }, { status: { name: "FAILED", colorHex: "#dc3545" }, testCasesCount: 2 } ] }; const executionSummary = { totalStatuses: mockTestRun.executionSummaries.length, details: mockTestRun.executionSummaries.map(summary => ({ status: summary.status.name, count: summary.testCasesCount, color: summary.status.colorHex, display: `${summary.status.name}: ${summary.testCasesCount} test cases` })), totalTestCases: mockTestRun.executionSummaries.reduce((sum, summary) => sum + summary.testCasesCount, 0) }; assert.strictEqual(executionSummary.totalStatuses, 2); assert.strictEqual(executionSummary.totalTestCases, 12); assert.deepStrictEqual(executionSummary.details, [ { status: "PASSED", count: 10, color: "#28a745", display: "PASSED: 10 test cases" }, { status: "FAILED", count: 2, color: "#dc3545", display: "FAILED: 2 test cases" } ]); }); it('should format test cases list correctly', () => { const mockTestCases = [ { testCase: { id: 456, key: "MCP-123", title: "Login Test" }, assignee: { id: 604, username: "tester", email: "tester@example.com" }, result: { status: { id: 1, name: "PASSED", aliases: "pass" }, details: "Test passed", executionTimeInMillis: 5000, executionType: "AUTOMATED", attachments: [{ fileUuid: "uuid1" }] } }, { testCase: { id: 457, key: "MCP-124", title: "Logout Test" }, assignee: null, result: null } ]; const statusSummary = mockTestCases.reduce((acc: any, tc: any) => { const status = tc.result?.status.name || 'No Result'; acc[status] = (acc[status] || 0) + 1; return acc; }, {} as Record<string, number>); assert.deepStrictEqual(statusSummary, { "PASSED": 1, "No Result": 1 }); const withResults = mockTestCases.filter(tc => tc.result).length; const withAssignees = mockTestCases.filter(tc => tc.assignee).length; assert.strictEqual(withResults, 1); assert.strictEqual(withAssignees, 1); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/maksimsarychau/mcp-zebrunner'

If you have feedback or need assistance with the MCP directory API, please join our Discord server