@skyramp/mcp
Version:
Skyramp MCP (Model Context Protocol) Server - AI-powered test generation and execution
263 lines (262 loc) • 10.9 kB
JavaScript
import { BASE_SCORES, CONTEXT_RULES, } from "../types/TestMapping.js";
import { TestType } from "../types/TestTypes.js";
/**
* Scoring Engine
* Calculates priority scores for test types based on repository analysis
*/
export class ScoringEngine {
/**
* Calculate priority score for a specific test type
*/
static calculateTestScore(testType, analysis) {
const _baseScore = BASE_SCORES[testType];
const contextMultiplier = this.calculateContextMultiplier(testType, analysis);
const _finalScore = _baseScore * contextMultiplier;
const feasibility = this.assessFeasibility(testType, analysis);
const requiredArtifacts = this.identifyRequiredArtifacts(testType, analysis);
const reasoning = this.generateReasoning(testType, _baseScore, contextMultiplier, _finalScore, analysis);
return {
testType,
_baseScore,
contextMultiplier,
_finalScore: Math.round(_finalScore * 10) / 10,
feasibility,
requiredArtifacts,
reasoning,
};
}
/**
* Calculate context multiplier based on repository characteristics
*/
static calculateContextMultiplier(testType, analysis) {
const rules = CONTEXT_RULES[testType];
let multiplier = 1.0;
for (const rule of rules) {
if (this.evaluateCondition(rule.condition, analysis)) {
multiplier = rule.multiplier;
break; // Use first matching rule
}
}
return multiplier;
}
/**
* Evaluate a context condition against repository analysis
*/
static evaluateCondition(condition, analysis) {
const { projectClassification, infrastructure, existingTests, artifacts } = analysis;
switch (condition) {
// Project type conditions
case "full-stack":
return projectClassification.projectType === "full-stack";
case "backend-only":
return projectClassification.projectType === "rest-api";
case "frontend-spa":
return projectClassification.projectType === "frontend";
case "library":
return projectClassification.projectType === "library";
case "cli-or-library":
return ["library", "cli"].includes(projectClassification.projectType);
case "microservices":
return (projectClassification.projectType === "microservices" ||
projectClassification.deploymentPattern === "microservices");
case "monolith":
return (projectClassification.deploymentPattern === "containerized-monolith");
// Infrastructure conditions
case "hasKubernetes":
return infrastructure.hasKubernetes;
case "hasDockerCompose":
return infrastructure.hasDockerCompose;
case "has-cicd":
return infrastructure.hasCiCd;
// Testing conditions
case "no-existing-tests":
return Object.values(existingTests.coverage).every((v) => v === 0);
case "has-unit-missing-integration":
return (existingTests.coverage.unit > 0 &&
existingTests.coverage.integration === 0);
case "has-integration-tests":
return existingTests.coverage.integration > 0;
case "comprehensive-tests":
return (Object.values(existingTests.coverage).reduce((a, b) => a + b, 0) > 20);
// Artifact conditions
case "frontend-spa-or-fullstack":
return ["frontend", "full-stack"].includes(projectClassification.projectType);
case "multiple-services":
return projectClassification.deploymentPattern === "microservices";
// Security conditions (inferred)
case "handles-payments":
return (analysis.businessContext.mainPurpose
.toLowerCase()
.includes("payment") ||
analysis.businessContext.mainPurpose
.toLowerCase()
.includes("commerce"));
case "handles-pii":
return (analysis.businessContext.mainPurpose.toLowerCase().includes("user") ||
analysis.businessContext.mainPurpose.toLowerCase().includes("profile"));
case "oauth2":
return analysis.authentication.method === "oauth2";
case "internal-service":
case "internal-tool":
return analysis.businessContext.mainPurpose
.toLowerCase()
.includes("internal");
default:
return false;
}
}
/**
* Assess feasibility of generating a test type
*/
static assessFeasibility(testType, analysis) {
const { artifacts, projectClassification } = analysis;
// Check for N/A cases first
if (testType === TestType.UI &&
projectClassification.projectType === "rest-api") {
return "not-applicable";
}
if (testType === TestType.E2E &&
projectClassification.projectType === "library") {
return "not-applicable";
}
// Check artifact requirements
const requiredArtifacts = this.identifyRequiredArtifacts(testType, analysis);
const missingCount = requiredArtifacts.missing.length;
if (missingCount === 0) {
return "high";
}
else if (missingCount === 1) {
return "medium";
}
else {
return "low";
}
}
/**
* Identify required artifacts and their availability
*/
static identifyRequiredArtifacts(testType, analysis) {
const available = [];
const missing = [];
const { artifacts } = analysis;
// Check OpenAPI spec
if (artifacts.openApiSpecs.length > 0) {
available.push("openApiSpec");
}
else {
if ([
TestType.SMOKE,
TestType.CONTRACT,
TestType.FUZZ,
TestType.INTEGRATION,
TestType.LOAD,
].includes(testType)) {
missing.push("openApiSpec");
}
}
// Check Playwright recordings
if (artifacts.playwrightRecordings.length > 0) {
available.push("playwrightRecording");
}
else {
if ([TestType.UI, TestType.E2E].includes(testType)) {
missing.push("playwrightRecording");
}
}
// Check trace files
if (artifacts.traceFiles.length > 0) {
available.push("traceFile");
}
else {
if ([TestType.E2E, TestType.INTEGRATION].includes(testType)) {
// Trace files are helpful but not required if OpenAPI is available
if (artifacts.openApiSpecs.length === 0) {
missing.push("traceFile");
}
}
}
return { available, missing };
}
/**
* Generate reasoning explanation for the score
*/
static generateReasoning(testType, _baseScore, multiplier, _finalScore, analysis) {
const { projectClassification, existingTests, artifacts, infrastructure } = analysis;
let reasoning = "";
// Base reasoning by test type
switch (testType) {
case TestType.INTEGRATION:
if (existingTests.coverage.integration === 0) {
reasoning = `No integration tests exist for ${analysis.apiEndpoints.totalCount} endpoints. `;
}
else {
reasoning = `${existingTests.coverage.integration} integration tests exist. `;
}
if (projectClassification.deploymentPattern === "microservices") {
reasoning +=
"Microservices architecture makes integration testing critical. ";
}
break;
case TestType.E2E:
if (projectClassification.projectType === "full-stack") {
reasoning =
"Full-stack application - E2E tests validate complete user journeys. ";
}
else {
reasoning =
"E2E tests have limited applicability for this project type. ";
}
break;
case TestType.UI:
if (["frontend", "full-stack"].includes(projectClassification.projectType)) {
reasoning = "UI testing essential for frontend validation. ";
}
else {
reasoning = "No UI components detected. ";
}
break;
case TestType.SMOKE:
if (Object.values(existingTests.coverage).every((v) => v === 0)) {
reasoning = "No existing tests - smoke tests provide quick wins. ";
}
if (infrastructure.hasCiCd) {
reasoning += "CI/CD pipeline benefits from smoke test validation. ";
}
break;
case TestType.LOAD:
if (infrastructure.hasKubernetes) {
reasoning =
"Kubernetes deployment suggests high-traffic expectations. ";
}
else {
reasoning = "Load testing validates performance and scalability. ";
}
break;
case TestType.FUZZ:
if (analysis.authentication.method !== "none") {
reasoning =
"API handles authentication - security testing important. ";
}
reasoning += "Fuzz testing discovers input validation issues. ";
break;
case TestType.CONTRACT:
if (artifacts.openApiSpecs.length > 0) {
reasoning =
"OpenAPI spec available - contract validation straightforward. ";
}
else {
reasoning = "No OpenAPI spec - contract tests harder to generate. ";
}
break;
}
// Add artifact status
const requiredArtifacts = this.identifyRequiredArtifacts(testType, analysis);
if (requiredArtifacts.missing.length > 0) {
reasoning += `Missing: ${requiredArtifacts.missing.join(", ")}. `;
}
else if (requiredArtifacts.available.length > 0) {
reasoning += `All required artifacts available. `;
}
return reasoning.trim();
}
}