import { describe, it } from 'node:test';
import { strict as assert } from 'node:assert';
/**
* Unit tests for test case-related MCP tools
*
* Tests the following tools:
* - get_test_case_by_key
* - get_test_cases_advanced
* - get_all_tcm_test_cases_by_project
* - get_all_tcm_test_cases_with_root_suite_id
* - get_test_cases_by_suite_id
* - get_test_case_details_by_key
* - generate_draft_test_by_key
*/
describe('Test Case Tools Unit Tests', () => {
describe('get_test_case_by_key Tool', () => {
it('should validate required parameters', () => {
const validParams = {
project_key: 'MCP',
case_key: 'MCP-1',
format: 'json'
};
assert.ok(validParams.project_key, 'project_key should be required');
assert.ok(validParams.case_key, 'case_key should be required');
assert.ok(validParams.project_key.length > 0, 'project_key should not be empty');
assert.ok(validParams.case_key.length > 0, 'case_key should not be empty');
});
it('should validate case_key format', () => {
const validCaseKey = 'MCP-1';
const invalidCaseKey = 'invalid-key';
assert.ok(validCaseKey.includes('-'), 'valid case key should contain hyphen');
assert.ok(validCaseKey.match(/^[A-Z]+-\d+$/), 'valid case key should match PROJECT-NUMBER pattern');
assert.ok(!invalidCaseKey.match(/^[A-Z]+-\d+$/), 'invalid case key should not match pattern');
});
it('should validate format parameter', () => {
const validFormats = ['dto', 'json', 'string', 'markdown'];
const invalidFormat = 'xml';
assert.ok(validFormats.includes('json'), 'json should be valid format');
assert.ok(validFormats.includes('markdown'), 'markdown should be valid format');
assert.ok(!validFormats.includes(invalidFormat), 'xml should not be valid format');
});
it('should handle include_steps parameter', () => {
const params = {
project_key: 'MCP',
case_key: 'MCP-1',
include_steps: true
};
assert.equal(typeof params.include_steps, 'boolean', 'include_steps should be boolean');
});
});
describe('get_test_cases_advanced Tool', () => {
it('should validate required parameters', () => {
const validParams = {
project_key: 'MCP',
format: 'json'
};
assert.ok(validParams.project_key, 'project_key should be required');
});
it('should validate pagination parameters', () => {
const validPagination = {
page: 0,
size: 50,
page_token: 'optional-token'
};
assert.ok(validPagination.page >= 0, 'page should be non-negative');
assert.ok(validPagination.size > 0, 'size should be positive');
assert.ok(validPagination.size <= 1000, 'size should not exceed maximum');
});
it('should handle suite_id filter', () => {
const params = {
project_key: 'MCP',
suite_id: 17470
};
assert.ok(params.suite_id > 0, 'suite_id should be positive when provided');
});
it('should handle automation_status filter', () => {
const validStatuses = ['AUTOMATED', 'NOT_AUTOMATED', 'PARTIALLY_AUTOMATED'];
const params = {
project_key: 'MCP',
automation_status: 'AUTOMATED'
};
assert.ok(validStatuses.includes(params.automation_status), 'automation_status should be valid value');
});
it('should handle priority filter', () => {
const validPriorities = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'];
const params = {
project_key: 'MCP',
priority: 'HIGH'
};
assert.ok(validPriorities.includes(params.priority), 'priority should be valid value');
});
});
describe('get_all_tcm_test_cases_by_project Tool', () => {
it('should validate required parameters', () => {
const validParams = {
project_key: 'MCP',
max_page_size: 100,
format: 'json'
};
assert.ok(validParams.project_key, 'project_key should be required');
assert.ok(validParams.max_page_size > 0, 'max_page_size should be positive');
});
it('should handle page_token for pagination', () => {
const params = {
project_key: 'MCP',
page_token: 'abc123token'
};
assert.equal(typeof params.page_token, 'string', 'page_token should be string when provided');
});
it('should validate max_page_size limits', () => {
const MAX_PAGE_SIZE = 100;
const validSize = 50;
const invalidSize = 150;
assert.ok(validSize <= MAX_PAGE_SIZE, 'valid size should not exceed limit');
assert.ok(invalidSize > MAX_PAGE_SIZE, 'invalid size should exceed limit');
});
it('should handle include_steps parameter', () => {
const params = {
project_key: 'MCP',
include_steps: false
};
assert.equal(typeof params.include_steps, 'boolean', 'include_steps should be boolean');
});
});
describe('get_all_tcm_test_cases_with_root_suite_id Tool', () => {
it('should validate required parameters', () => {
const validParams = {
project_key: 'MCP',
max_page_size: 100,
format: 'json'
};
assert.ok(validParams.project_key, 'project_key should be required');
assert.ok(validParams.max_page_size > 0, 'max_page_size should be positive');
});
it('should handle root_suite_id filter', () => {
const params = {
project_key: 'MCP',
root_suite_id: 18659
};
assert.ok(params.root_suite_id > 0, 'root_suite_id should be positive when provided');
});
it('should validate pagination with token', () => {
const params = {
project_key: 'MCP',
page_token: 'next-page-token',
max_page_size: 50
};
assert.equal(typeof params.page_token, 'string', 'page_token should be string');
assert.ok(params.max_page_size <= 100, 'max_page_size should not exceed limit');
});
});
describe('get_test_cases_by_suite_id Tool', () => {
it('should validate required parameters', () => {
const validParams = {
project_key: 'MCP',
suite_id: 17470,
format: 'json'
};
assert.ok(validParams.project_key, 'project_key should be required');
assert.ok(validParams.suite_id > 0, 'suite_id should be positive');
});
it('should validate pagination parameters', () => {
const validPagination = {
page: 0,
size: 50
};
assert.ok(validPagination.page >= 0, 'page should be non-negative');
assert.ok(validPagination.size > 0, 'size should be positive');
assert.ok(validPagination.size <= 1000, 'size should not exceed maximum');
});
it('should handle include_steps parameter', () => {
const params = {
project_key: 'MCP',
suite_id: 17470,
include_steps: true
};
assert.equal(typeof params.include_steps, 'boolean', 'include_steps should be boolean');
});
});
describe('get_test_case_details_by_key Tool', () => {
it('should validate required parameters', () => {
const validParams = {
project_key: 'MCP',
case_key: 'MCP-1',
format: 'json'
};
assert.ok(validParams.project_key, 'project_key should be required');
assert.ok(validParams.case_key, 'case_key should be required');
assert.ok(validParams.case_key.match(/^[A-Z]+-\d+$/), 'case_key should match pattern');
});
it('should handle include_hierarchy parameter', () => {
const params = {
project_key: 'MCP',
case_key: 'MCP-1',
include_hierarchy: true
};
assert.equal(typeof params.include_hierarchy, 'boolean', 'include_hierarchy should be boolean');
});
it('should handle include_custom_fields parameter', () => {
const params = {
project_key: 'MCP',
case_key: 'MCP-1',
include_custom_fields: true
};
assert.equal(typeof params.include_custom_fields, 'boolean', 'include_custom_fields should be boolean');
});
});
describe('generate_draft_test_by_key Tool', () => {
it('should validate required parameters', () => {
const validParams = {
project_key: 'MCP',
case_key: 'MCP-1',
implementation_context: 'Test implementation context',
target_framework: 'auto'
};
assert.ok(validParams.project_key, 'project_key should be required');
assert.ok(validParams.case_key, 'case_key should be required');
assert.ok(validParams.implementation_context, 'implementation_context should be required');
});
it('should validate case_key format', () => {
const validCaseKey = 'MCP-1';
const invalidCaseKey = 'invalid';
assert.ok(validCaseKey.match(/^[A-Z]+-\d+$/), 'valid case key should match pattern');
assert.ok(!invalidCaseKey.match(/^[A-Z]+-\d+$/), 'invalid case key should not match pattern');
});
it('should validate target_framework options', () => {
const validFrameworks = ['auto', 'selenium', 'appium', 'playwright', 'cypress', 'testng', 'junit', 'pytest', 'mocha', 'jest'];
const params = {
target_framework: 'selenium'
};
assert.ok(validFrameworks.includes(params.target_framework), 'target_framework should be valid option');
});
it('should handle optional parameters', () => {
const params = {
project_key: 'MCP',
case_key: 'MCP-1',
implementation_context: 'Context',
target_framework: 'auto',
include_page_objects: true,
include_test_data: false,
output_format: 'code_with_explanation'
};
assert.equal(typeof params.include_page_objects, 'boolean', 'include_page_objects should be boolean');
assert.equal(typeof params.include_test_data, 'boolean', 'include_test_data should be boolean');
assert.ok(['code_only', 'code_with_explanation', 'detailed_analysis'].includes(params.output_format), 'output_format should be valid');
});
it('should validate implementation_context content', () => {
const validContext = 'Test implementation found at: /path/to/test.java\n\nKey implementation details:\n1. Test creates account';
const emptyContext = '';
const shortContext = 'test';
assert.ok(validContext.length > 10, 'valid context should be meaningful');
assert.ok(emptyContext.length === 0, 'empty context should be detected');
assert.ok(shortContext.length < 10, 'short context should be detected');
});
});
describe('Test Case Response Validation', () => {
it('should validate test case object structure', () => {
const mockTestCase = {
id: 123456,
key: 'MCP-1',
title: 'Test case title',
description: 'Test case description',
suiteId: 17470,
automationState: { name: 'NOT_AUTOMATED' },
priority: { name: 'HIGH' },
draft: false,
deprecated: false
};
assert.ok(typeof mockTestCase.id === 'number', 'id should be number');
assert.ok(typeof mockTestCase.key === 'string', 'key should be string');
assert.ok(typeof mockTestCase.title === 'string', 'title should be string');
assert.ok(mockTestCase.automationState && mockTestCase.automationState.name, 'should have automation state');
assert.ok(mockTestCase.priority && mockTestCase.priority.name, 'should have priority');
});
it('should validate test case with steps', () => {
const testCaseWithSteps = {
id: 123456,
key: 'MCP-1',
title: 'Test case title',
steps: [
{ id: 1, description: 'Step 1', expectedResult: 'Expected 1' },
{ id: 2, description: 'Step 2', expectedResult: 'Expected 2' }
]
};
assert.ok(Array.isArray(testCaseWithSteps.steps), 'steps should be array');
assert.ok(testCaseWithSteps.steps.length > 0, 'should have steps');
assert.ok(testCaseWithSteps.steps[0].description, 'step should have description');
});
it('should validate test case with custom fields', () => {
const testCaseWithCustomFields = {
id: 123456,
key: 'MCP-1',
title: 'Test case title',
customFields: [
{ name: 'Manual Only', value: 'Yes' },
{ name: 'Component', value: 'Login' }
]
};
assert.ok(Array.isArray(testCaseWithCustomFields.customFields), 'customFields should be array');
assert.ok(testCaseWithCustomFields.customFields[0].name, 'custom field should have name');
assert.ok(testCaseWithCustomFields.customFields[0].value, 'custom field should have value');
});
it('should validate paged test case response', () => {
const pagedResponse = {
items: [
{ id: 1, key: 'MCP-1', title: 'Test 1' },
{ id: 2, key: 'MCP-2', title: 'Test 2' }
],
_meta: {
nextPageToken: 'token123',
totalElements: 3,
currentPage: 0,
pageSize: 50
}
};
assert.ok(Array.isArray(pagedResponse.items), 'items should be array');
assert.ok(pagedResponse._meta, 'response should have metadata');
assert.ok(typeof pagedResponse._meta.totalElements === 'number', 'totalElements should be number');
assert.equal(pagedResponse._meta.totalElements, 3, 'should match expected total for MCP');
});
});
describe('Error Handling', () => {
it('should handle missing project_key', () => {
const invalidParams = {
case_key: 'MCP-1',
format: 'json'
};
assert.ok(!invalidParams.hasOwnProperty('project_key'), 'should detect missing project_key');
});
it('should handle missing case_key', () => {
const invalidParams = {
project_key: 'MCP',
format: 'json'
};
assert.ok(!invalidParams.hasOwnProperty('case_key'), 'should detect missing case_key');
});
it('should handle invalid case_key format', () => {
const invalidCaseKeys = ['', 'invalid', '123', 'MCP', 'MCP-', '-4678'];
invalidCaseKeys.forEach(key => {
assert.ok(!key.match(/^[A-Z]+-\d+$/), `"${key}" should be invalid case key format`);
});
});
it('should handle invalid suite_id', () => {
const invalidSuiteIds = [0, -1, 'not-a-number', null];
invalidSuiteIds.forEach(id => {
if (typeof id === 'number') {
assert.ok(id <= 0, `${id} should be invalid suite_id`);
} else {
assert.ok(typeof id !== 'number', `${id} should not be a number`);
}
});
});
it('should provide helpful error messages for generate_draft_test_by_key', () => {
const errorScenarios = [
{
type: '500_error',
message: 'Internal server error occurred',
tips: 'Check Implementation Context: Ensure your implementation_context parameter contains meaningful information'
},
{
type: 'not_found',
message: 'Test case not found',
tips: 'Verify Test Case Key: Ensure MCP-1 exists in project MCP'
},
{
type: 'rules_engine',
message: 'Rules engine configuration issue',
tips: 'Set ENABLE_RULES_ENGINE=true in your .env file'
}
];
errorScenarios.forEach(scenario => {
assert.ok(scenario.message.length > 0, 'Error message should not be empty');
assert.ok(scenario.tips.length > 0, 'Troubleshooting tips should be provided');
});
});
it('should validate pagination error handling', () => {
const MAX_PAGE_SIZE = 100;
const requestedSize = 200;
const shouldThrowError = requestedSize > MAX_PAGE_SIZE;
assert.ok(shouldThrowError, 'Should detect page size exceeding limit');
const errorMessage = `Requested page size (${requestedSize}) exceeds configured maximum (${MAX_PAGE_SIZE})`;
assert.ok(errorMessage.includes('exceeds configured maximum'), 'Error should explain the limit');
});
});
describe('Performance Considerations', () => {
it('should validate large dataset handling', () => {
const EXPECTED_MCP_TOTAL = 3;
const MAX_PAGE_SIZE = 100;
const expectedPages = Math.ceil(EXPECTED_MCP_TOTAL / MAX_PAGE_SIZE);
assert.ok(expectedPages >= 1, 'Should require at least one page for all test cases');
assert.ok(expectedPages <= 50, 'Should not require excessive pagination');
});
it('should validate token-based pagination logic', () => {
const paginationState = {
pageToken: 'abc123',
pageCount: 5,
maxPages: 50
};
const shouldContinue = !!(paginationState.pageToken && paginationState.pageCount < paginationState.maxPages);
assert.equal(typeof shouldContinue, 'boolean', 'shouldContinue should be boolean');
assert.ok(shouldContinue, 'Should continue when token exists and under max pages');
});
it('should validate memory-efficient processing', () => {
const BATCH_SIZE = 100;
const TOTAL_ITEMS = 3;
const batches = Math.ceil(TOTAL_ITEMS / BATCH_SIZE);
assert.ok(batches >= 1, 'Should process in at least one batch');
assert.ok(BATCH_SIZE <= 100, 'Batch size should be reasonable for memory usage');
});
});
});