qase-mcp-server
Version:
Model Context Protocol server for Qase TMS - Enables AI assistants to manage test cases, runs, and defects in Qase
120 lines (119 loc) • 5.16 kB
JavaScript
import { TestStepResultCreateStatusEnum, } from 'qaseio';
import { z } from 'zod';
import { toResult } from '../utils.js';
import { apply, pipe } from 'ramda';
import { client } from '../utils.js';
export const GetResultsSchema = z.object({
code: z.string(),
limit: z.string().optional(),
offset: z.string().optional(),
status: z.nativeEnum(TestStepResultCreateStatusEnum).optional(),
from: z.string().optional(),
to: z.string().optional(),
});
export const GetResultSchema = z.object({
code: z.string(),
hash: z.string(),
});
export const CreateResultSchema = z.object({
code: z.string(),
id: z.number(),
result: z.record(z.any()).transform((v) => v),
});
export const CreateResultBulkSchema = z.object({
code: z.string(),
id: z.number(),
results: z.record(z.any()).transform((v) => v),
});
export const UpdateResultSchema = z.object({
code: z.string(),
id: z.number(),
hash: z.string(),
result: z.record(z.any()).transform((v) => v),
});
export const GetResultsByStatusSchema = z.object({
code: z.string().describe('Project code'),
runId: z.number().describe('Test run ID to get results for'),
status: z
.string()
.describe('Status to filter by (e.g., failed, passed, skipped, blocked, invalid)'),
unique: z
.boolean()
.optional()
.describe('Return only unique test cases (removes duplicates from retries/reruns)'),
limit: z.string().optional().describe('Maximum number of results to return'),
offset: z
.string()
.optional()
.describe('Number of results to skip for pagination'),
from: z
.string()
.optional()
.describe('Filter results from this timestamp (format: YYYY-MM-DD HH:mm:ss)'),
to: z
.string()
.optional()
.describe('Filter results to this timestamp (format: YYYY-MM-DD HH:mm:ss)'),
});
export const getResults = pipe(apply(client.results.getResults.bind(client.results)), (promise) => toResult(promise));
export const getResult = pipe(client.results.getResult.bind(client.results), (promise) => toResult(promise));
export const createResult = pipe(client.results.createResult.bind(client.results), (promise) => toResult(promise));
export const createResultBulk = pipe(client.results.createResultBulk.bind(client.results), (promise) => toResult(promise));
export const updateResult = pipe(client.results.updateResult.bind(client.results), (promise) => toResult(promise));
export const getResultsByStatus = (code, runId, status, unique, limit, offset, from, to) => {
// If unique filtering is requested, we need to get all results first (ignore limit/offset)
// then apply unique filtering, then apply limit/offset to the filtered results
const apiLimit = unique ? 100 : limit ? parseInt(limit, 10) : undefined; // Use high limit for unique filtering
const apiOffset = unique ? 0 : offset ? parseInt(offset, 10) : undefined; // Start from beginning for unique
// Call the API with individual parameters
// getResults(code, status, run, caseId, member, api, fromEndTime, toEndTime, limit, offset, options)
const apiCall = pipe(apply(client.results.getResults.bind(client.results)), (promise) => toResult(promise))([
code, // code
status, // status (user-specified: failed, passed, skipped, etc.)
runId.toString(), // run
undefined, // caseId
undefined, // member
undefined, // api
from, // fromEndTime
to, // toEndTime
apiLimit, // limit
apiOffset, // offset
undefined, // options
]);
// If unique filtering is not requested, return the result as-is
if (!unique) {
return apiCall;
}
// Apply unique filtering using map on the result
return apiCall.map((data) => {
const entities = data.data?.result?.entities || [];
// Filter to unique case IDs (keep the latest result for each case)
const uniqueEntities = [];
const seenCaseIds = new Set();
// Process entities in reverse order to keep the latest result for each case
for (let i = entities.length - 1; i >= 0; i--) {
const entity = entities[i];
if (!seenCaseIds.has(entity.case_id)) {
seenCaseIds.add(entity.case_id);
uniqueEntities.unshift(entity); // Add to beginning to maintain original order
}
}
// Apply limit and offset to unique results
const startIndex = offset ? parseInt(offset, 10) : 0;
const endIndex = limit ? startIndex + parseInt(limit, 10) : undefined;
const paginatedEntities = uniqueEntities.slice(startIndex, endIndex);
// Return the modified result
return {
...data,
data: {
...data.data,
result: {
...data.data.result,
entities: paginatedEntities,
count: paginatedEntities.length,
filtered: uniqueEntities.length, // Total unique results available
},
},
};
});
};