diff --git a/src/mcp/tools/testCaseValidate.ts b/src/mcp/tools/testCaseValidate.ts
index c103566..15f2b37 100644
--- a/src/mcp/tools/testCaseValidate.ts
+++ b/src/mcp/tools/testCaseValidate.ts
@@ -384,21 +384,18 @@ export function validateTestCaseXml(filePath: string, config: ServerConfig): Tes
/** TC_010/TC_011: validate testCase id and guid attributes. */
function checkTestCaseIdAndGuid(tcId: string | null, tcGuid: string | undefined, issues: ValidationIssue[]): void {
- if (!tcId) {
+ // The testCase `id` is OPTIONAL — the guid is the unique identifier (real Provar
+ // projects routinely omit id). When an id IS present it must be a non-negative
+ // integer: test case ids are project-unique sequential numbers (corpus shows 0…N),
+ // NOT the literal "1". Only a present-but-non-numeric id (e.g. a UUID) is rejected.
+ if (tcId !== null && !/^\d+$/.test(tcId)) {
issues.push({
rule_id: 'TC_010',
severity: 'ERROR',
- message: 'testCase missing required id attribute.',
+ message: `testCase id="${tcId}" is invalid — when present, the id must be a non-negative integer (test case ids are unique within a project, numbered sequentially). The guid is the cross-project unique identifier.`,
applies_to: 'testCase',
- suggestion: 'Add id="1" to testCase element (Provar requires the integer literal "1").',
- });
- } else if (tcId !== '1') {
- issues.push({
- rule_id: 'TC_010',
- severity: 'ERROR',
- message: `testCase id="${tcId}" is invalid — Provar requires id="1" (integer literal).`,
- applies_to: 'testCase',
- suggestion: 'Set id="1" on the testCase element. The unique identifier is the guid attribute, not id.',
+ suggestion:
+ 'Set id to a project-unique non-negative integer (typically the highest in use + 1), or omit the id attribute entirely and rely on the guid.',
});
}
if (!tcGuid) {
diff --git a/test/unit/mcp/testCaseValidate.test.ts b/test/unit/mcp/testCaseValidate.test.ts
index 6adc8b7..c3ca6d6 100644
--- a/test/unit/mcp/testCaseValidate.test.ts
+++ b/test/unit/mcp/testCaseValidate.test.ts
@@ -77,31 +77,48 @@ describe('validateTestCase', () => {
});
describe('testCase attribute rules', () => {
- it('TC_010: flags missing id', () => {
+ it('TC_010: does NOT fire for a missing id — id is optional, guid is the identifier', () => {
const r = validateTestCase(
``
);
assert.ok(
- r.issues.some((i) => i.rule_id === 'TC_010'),
- 'Expected TC_010'
+ !r.issues.some((i) => i.rule_id === 'TC_010'),
+ 'A missing id must not be flagged (real Provar test cases routinely omit it)'
);
});
- it('TC_010: flags non-"1" id (e.g. UUID used as id)', () => {
+ it('TC_010: flags a non-numeric id (e.g. UUID used as id)', () => {
const r = validateTestCase(
``
);
assert.ok(
r.issues.some((i) => i.rule_id === 'TC_010'),
- 'Expected TC_010 for UUID used as id'
+ 'Expected TC_010 for a non-numeric id'
+ );
+ });
+
+ it('TC_010: does not fire for id="0" — non-negative integers are valid (corpus uses 0)', () => {
+ const r = validateTestCase(
+ ``
);
+ assert.ok(!r.issues.some((i) => i.rule_id === 'TC_010'), 'id="0" must be accepted');
});
- it('TC_010: does not fire when id="1" (the correct literal)', () => {
+ it('TC_010: does not fire for a valid integer id (id="1")', () => {
const r = validateTestCase(VALID_TC);
assert.ok(!r.issues.some((i) => i.rule_id === 'TC_010'), 'TC_010 must not fire when id="1"');
});
+ it('TC_010: does not fire for a sequential project id (e.g. id="42") — ids are not literally "1"', () => {
+ const r = validateTestCase(
+ ``
+ );
+ assert.ok(
+ !r.issues.some((i) => i.rule_id === 'TC_010'),
+ 'TC_010 must accept any non-negative-integer id (test case ids are project-unique sequential numbers)'
+ );
+ });
+
it('TC_011: flags missing guid', () => {
const r = validateTestCase(
''