Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve constraint coverage tests #633

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions features/fedramp_extensions.feature
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,17 @@ Examples:
#END_DYNAMIC_TEST_CASES

@full-coverage
Scenario: Ensuring full test coverage for each constraint
Given I have loaded all Metaschema extensions documents
And I have collected all YAML test files in the test directory
When I extract all constraint IDs from the Metaschema extensions
And I analyze the YAML test files for each constraint ID
Then I should have both FAIL and PASS tests for each constraint ID:
| Constraint ID |
Scenario: Preparing constraint coverage analysis
Given I have loaded all Metaschema extensions documents
And I have collected all YAML test files in the test directory
When I extract all constraint IDs from the Metaschema extensions
And I analyze the YAML test files for each constraint ID

@full-coverage
Scenario Outline: Ensuring full test coverage for "<constraint_id>"
Then I should have both FAIL and PASS tests for constraint ID "<constraint_id>"
Examples:
| constraint_id |
#BEGIN_DYNAMIC_CONSTRAINT_IDS
| address-type |
| attachment-type |
Expand Down
58 changes: 31 additions & 27 deletions features/steps/fedramp_extensions_steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,9 @@ async function getConstraintIds() {
const filePath = join(constraintDir, file);
const fileContent = readFileSync(filePath, "utf8");
const result = (await parseXmlString(fileContent)) as any;

const contexts = result["metaschema-meta-constraints"]?.context || [];
for (const context of contexts) {
const constraints = context.constraints?.[0] || {};
for (const constraintType in constraints) {
if (Array.isArray(constraints[constraintType])) {
const ids = constraints[constraintType]
.filter((constraint) => constraint.$ && constraint.$.id)
.map((constraint) => constraint.$.id);
allConstraintIds = [...allConstraintIds, ...ids];
}
}
}
const fileConstraints=extractConstraints(result)
allConstraintIds=[...allConstraintIds,...fileConstraints];
}

// Remove duplicates and sort
allConstraintIds = [...new Set(allConstraintIds)].sort();

Expand Down Expand Up @@ -437,10 +425,11 @@ When(
constraintIds = [...constraintIds, ...constraints];
}
constraintIds = [...new Set(constraintIds)].sort();

console.log(`Extracted ${constraintIds.length} unique constraint IDs`);
wandmagic marked this conversation as resolved.
Show resolved Hide resolved
console.log(`Extracted ${constraintIds.length} unique constraint IDs`);
}
);

function extractConstraints(xmlObject: any): string[] {
const constraints: string[] = [];

Expand All @@ -449,19 +438,11 @@ function extractConstraints(xmlObject: any): string[] {
if (Array.isArray(obj)) {
obj.forEach(searchForConstraints);
} else {
if (obj.constraints && Array.isArray(obj.constraints)) {
obj.constraints.forEach((constraint: any) => {
Object.values(constraint).forEach((value: any) => {
if (Array.isArray(value)) {
value.forEach((item: any) => {
if (item.$ && item.$.id) {
constraints.push(item.$.id);
}
});
}
});
});
// Check if the current object has an 'id' field
if (obj.$ && obj.$.id) {
constraints.push(obj.$.id);
}
// Recursively search all object properties
Object.values(obj).forEach(searchForConstraints);
}
}
Expand Down Expand Up @@ -611,3 +592,26 @@ When("I analyze the YAML test files for each constraint ID", function () {
console.log(`Analyzed ${yamlTestFiles.length} YAML test files`);
console.log("Test results:", testResults);
});

// New step definition for the "Ensuring full test coverage for "<constraint_id>"" scenario
Then("I should have both FAIL and PASS tests for constraint ID {string}", function (constraintId) {
const testCoverage = testResults[constraintId];

if (!testCoverage) {
console.log(`${constraintId}: No tests found`);
expect.fail(`Constraint ${constraintId} has no tests`);
} else if (!testCoverage.pass) {
console.log(`${constraintId}: Missing at least one positive test`);
expect.fail(`Constraint ${constraintId} is missing a positive test`);
} else if (!testCoverage.fail) {
console.log(`${constraintId}: Missing at least one negative test`);
expect.fail(`Constraint ${constraintId} is missing a negative test`);
} else {
console.log(`${constraintId}: Has minimal required coverage`);
}

expect(constraintIds).to.include(
constraintId,
`Constraint ${constraintId} is not in the extracted constraints list`
);
});
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"federalist": "make init-repo && npm run build:validation-ui && npm run link:validation-ui",
"link:validation-ui": "ln -sf ./src/web/dist _site",
"test": "cross-env NODE_OPTIONS=\"--loader ts-node/esm --no-warnings --experimental-specifier-resolution=node\" cucumber-js",
"test:constraints": "cross-env NODE_OPTIONS=\"--loader ts-node/esm --no-warnings --experimental-specifier-resolution=node\" cucumber-js --tags @constraints",
"test:coverage": "cross-env NODE_OPTIONS=\"--loader ts-node/esm --no-warnings --experimental-specifier-resolution=node\" cucumber-js --tags @full-coverage",
wandmagic marked this conversation as resolved.
Show resolved Hide resolved
"constraint": "node ./src/scripts/dev-constraint.cjs"
},
"type": "module",
Expand Down
4 changes: 2 additions & 2 deletions src/scripts/dev-constraint.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ async function scaffoldTest(constraintId) {
type: 'confirm',
name: 'confirm',
message: `Do you want to scaffold a test for ${constraintId}?`,
default: false
default: true
}
]);
const { model } = await prompt([
Expand Down Expand Up @@ -233,7 +233,7 @@ async function runCucumberTest(constraintId, testFiles) {

if (scenarioLines.length === 0) {
console.error(`No scenarios found for constraintId: ${constraintId}`);
console.error(`execute npm run test and try again if you haven't already`);
execSync("npm run test:coverage",{stdio:'inherit'});
return false;
}

Expand Down
Loading