lib/apiScenario/coverageCalculator.ts (106 lines of code) (raw):
// Copyright (c) 2021 Microsoft Corporation
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
import { Operation, Path, SwaggerSpec, LowerHttpMethods } from "../swagger/swaggerTypes";
import { traverseSwagger } from "../transform/traverseSwagger";
import { ScenarioDefinition } from "./apiScenarioTypes";
export interface OperationCoverageResult {
coveredOperationNumber: number;
totalOperationNumber: number;
coverage: number;
coveredOperationIds: string[];
uncoveredOperationIds: string[];
}
export class CoverageCalculator {
public static calculateOperationCoverage(
testDef: ScenarioDefinition,
swaggerSpecs: SwaggerSpec[]
): OperationCoverageResult {
const ret: OperationCoverageResult = {
coveredOperationNumber: 0,
totalOperationNumber: 0,
coverage: 0,
coveredOperationIds: [],
uncoveredOperationIds: [],
};
const allOperationIds = new Set<string>();
for (const swaggerSpec of swaggerSpecs) {
traverseSwagger(swaggerSpec, {
onOperation: (operation: Operation, _path: Path, _method: LowerHttpMethods) => {
allOperationIds.add(operation.operationId!);
},
});
}
const coverageOperationIds = new Set<string>();
for (const step of testDef.prepareSteps) {
if (step.type === "restCall" && allOperationIds.has(step.operationId)) {
coverageOperationIds.add(step.operationId);
}
}
for (const testScenario of testDef.scenarios) {
for (const step of testScenario.steps) {
if (step.type === "restCall" && allOperationIds.has(step.operationId)) {
coverageOperationIds.add(step.operationId);
}
}
}
for (const step of testDef.cleanUpSteps) {
if (step.type === "restCall" && allOperationIds.has(step.operationId)) {
coverageOperationIds.add(step.operationId);
}
}
ret.coverage =
allOperationIds.size === 0 ? 0 : coverageOperationIds.size / allOperationIds.size;
ret.coveredOperationNumber = coverageOperationIds.size;
ret.totalOperationNumber = allOperationIds.size;
ret.coveredOperationIds = [...coverageOperationIds];
const difference = [...allOperationIds].filter((x) => !coverageOperationIds.has(x));
ret.uncoveredOperationIds = difference;
return ret;
}
public static calculateOperationCoverageBySpec(
testDef: ScenarioDefinition,
swaggerSpecs: SwaggerSpec[]
): Map<string, OperationCoverageResult> {
const ret: Map<string, OperationCoverageResult> = new Map();
for (const swaggerSpec of swaggerSpecs) {
const result: OperationCoverageResult = {
coveredOperationNumber: 0,
totalOperationNumber: 0,
coverage: 0,
coveredOperationIds: [],
uncoveredOperationIds: [],
};
const allOperationIds = new Set<string>();
traverseSwagger(swaggerSpec, {
onOperation: (operation: Operation, _path: Path, _method: LowerHttpMethods) => {
allOperationIds.add(operation.operationId!);
},
});
const coverageOperationIds = new Set<string>();
for (const step of testDef.prepareSteps) {
if (step.type === "restCall" && allOperationIds.has(step.operationId)) {
coverageOperationIds.add(step.operationId);
}
}
for (const testScenario of testDef.scenarios) {
for (const step of testScenario.steps) {
if (step.type === "restCall" && allOperationIds.has(step.operationId)) {
coverageOperationIds.add(step.operationId);
}
}
}
for (const step of testDef.cleanUpSteps) {
if (step.type === "restCall" && allOperationIds.has(step.operationId)) {
coverageOperationIds.add(step.operationId);
}
}
result.coverage =
allOperationIds.size === 0 ? 0 : coverageOperationIds.size / allOperationIds.size;
result.coveredOperationNumber = coverageOperationIds.size;
result.totalOperationNumber = allOperationIds.size;
result.coveredOperationIds = [...coverageOperationIds];
const difference = [...allOperationIds].filter((x) => !coverageOperationIds.has(x));
result.uncoveredOperationIds = difference;
ret.set(swaggerSpec._filePath, result);
}
return ret;
}
}