@skyramp/mcp 0.0.59 → 0.0.60-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.js +32 -5
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +98 -0
- package/build/prompts/test-recommendation/recommendationSections.js +226 -0
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +71 -0
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +166 -104
- package/build/prompts/testGenerationPrompt.js +2 -3
- package/build/prompts/testbot/testbot-prompts.js +96 -93
- package/build/resources/analysisResources.js +254 -0
- package/build/services/ScenarioGenerationService.js +70 -26
- package/build/tools/generate-tests/generateIntegrationRestTool.js +54 -1
- package/build/tools/generate-tests/generateScenarioRestTool.js +8 -5
- package/build/tools/submitReportTool.js +28 -0
- package/build/tools/test-maintenance/stateCleanupTool.js +8 -0
- package/build/tools/test-recommendation/analyzeRepositoryTool.js +349 -217
- package/build/tools/test-recommendation/recommendTestsTool.js +163 -159
- package/build/tools/workspace/initializeWorkspaceTool.js +1 -1
- package/build/types/RepositoryAnalysis.js +99 -12
- package/build/utils/AnalysisStateManager.js +40 -23
- package/build/utils/branchDiff.js +47 -0
- package/build/utils/pr-comment-parser.js +124 -0
- package/build/utils/projectMetadata.js +188 -0
- package/build/utils/projectMetadata.test.js +81 -0
- package/build/utils/repoScanner.js +378 -0
- package/build/utils/routeParsers.js +213 -0
- package/build/utils/routeParsers.test.js +87 -0
- package/build/utils/scenarioDrafting.js +119 -0
- package/build/utils/scenarioDrafting.test.js +66 -0
- package/build/utils/trace-parser.js +166 -0
- package/build/utils/workspaceAuth.js +16 -0
- package/package.json +1 -1
- package/build/prompts/test-recommendation/repository-analysis-prompt.js +0 -326
- package/build/prompts/test-recommendation/test-mapping-prompt.js +0 -266
- package/build/tools/test-recommendation/mapTestsTool.js +0 -243
- package/build/types/TestMapping.js +0 -173
- package/build/utils/scoring-engine.js +0 -380
|
@@ -1,137 +1,199 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { buildPrioritizationDimensions, buildTestExamples, buildTestPatternGuidelines, buildTestQualityCriteria, buildGenerationRules, buildToolWorkflows, buildCoverageChecklist, } from "./recommendationSections.js";
|
|
2
|
+
function formatTestLocations(locs) {
|
|
3
|
+
const entries = Object.entries(locs || {});
|
|
4
|
+
if (entries.length === 0)
|
|
5
|
+
return "";
|
|
6
|
+
return "\n**Existing test files (do NOT duplicate these):**\n" +
|
|
7
|
+
entries.map(([type, files]) => " - [" + type + "] " + files).join("\n");
|
|
8
|
+
}
|
|
9
|
+
export function buildRecommendationPrompt(analysis, analysisScope = "full_repo", focus = "all", topN = 10, prContext, workspaceAuthHeader) {
|
|
6
10
|
const isDiffScope = analysisScope === "current_branch_diff";
|
|
7
11
|
const diffContext = analysis.branchDiffContext;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
const openApiSpec = analysis.artifacts?.openApiSpecs?.[0];
|
|
13
|
+
// ── Frontend / UI change detection ──
|
|
14
|
+
const FRONTEND_FILE_PATTERN = /\.(tsx?|jsx?|vue|svelte|css|scss|less|html)$/;
|
|
15
|
+
const API_DIR_PATTERN = /\/(api|routes?|controllers?|routers?|handlers?|endpoints?|server)\//;
|
|
16
|
+
const hasFrontendChanges = isDiffScope && diffContext
|
|
17
|
+
? diffContext.changedFiles.some(f => FRONTEND_FILE_PATTERN.test(f) &&
|
|
18
|
+
!API_DIR_PATTERN.test(f) &&
|
|
19
|
+
/\/(components?|pages?|views?|layouts?|app|src\/app|frontend|client|public|styles?)\//i.test(f))
|
|
20
|
+
: false;
|
|
21
|
+
const hasApiChanges = isDiffScope && diffContext
|
|
22
|
+
? (diffContext.newEndpoints.length > 0 || diffContext.modifiedEndpoints.length > 0)
|
|
23
|
+
: false;
|
|
24
|
+
const isUIOnlyPR = hasFrontendChanges && !hasApiChanges;
|
|
25
|
+
// ── Mode preamble ──
|
|
26
|
+
const modePreamble = isDiffScope
|
|
27
|
+
? `You are in **PR mode**. Maximize test coverage for the branch changes.
|
|
28
|
+
Focus on tests that validate the changed fields, endpoints, and their interactions.
|
|
29
|
+
**CRITICAL**: Recommend MULTIPLE integration tests — at minimum one multi-resource workflow
|
|
30
|
+
AND one CRUD lifecycle.
|
|
31
|
+
${isUIOnlyPR ? `\n**UI-only PR.** Prioritize UI tests and E2E. Skip fuzz/contract.`
|
|
32
|
+
: hasFrontendChanges ? `\n**Mixed PR (frontend + API).** Integration > E2E > UI > fuzz/contract.`
|
|
33
|
+
: ``}
|
|
34
|
+
Output should be concise and immediately actionable.`
|
|
35
|
+
: `You are in **Repo mode**. Comprehensive test strategy across all endpoints.`;
|
|
36
|
+
// ── Endpoint listing ──
|
|
37
|
+
const allEndpoints = analysis.apiEndpoints.endpoints;
|
|
38
|
+
const endpointLines = allEndpoints
|
|
39
|
+
.flatMap((ep) => (ep.methods ?? []).map((m) => ` ${m.method} ${ep.path}${m.authRequired ? " [auth]" : ""} (${(m.interactions ?? []).length} interactions)`))
|
|
26
40
|
.join("\n");
|
|
41
|
+
const authMethod = analysis.authentication.method || "unknown";
|
|
42
|
+
const authHeaderValue = workspaceAuthHeader
|
|
43
|
+
? workspaceAuthHeader
|
|
44
|
+
: /session|cookie|nextauth/i.test(authMethod) ? "Cookie"
|
|
45
|
+
: /api[_-]?key/i.test(authMethod) ? "X-API-Key"
|
|
46
|
+
: "Authorization";
|
|
27
47
|
const repoContext = `
|
|
28
48
|
Repository: ${analysis.metadata.repositoryName}
|
|
29
49
|
Framework: ${analysis.projectClassification.primaryFramework} (${analysis.projectClassification.primaryLanguage})
|
|
30
50
|
Project type: ${analysis.projectClassification.projectType}
|
|
31
|
-
Auth: ${
|
|
51
|
+
Auth: ${authMethod} (header: ${authHeaderValue})
|
|
32
52
|
Base URL: ${analysis.apiEndpoints.baseUrl}
|
|
33
|
-
Endpoints (${analysis.apiEndpoints.totalCount}
|
|
34
|
-
${
|
|
53
|
+
Endpoints (${analysis.apiEndpoints.totalCount}):
|
|
54
|
+
${endpointLines}
|
|
35
55
|
`.trim();
|
|
36
|
-
//
|
|
56
|
+
// ── Branch diff ──
|
|
37
57
|
let diffSection = "";
|
|
38
58
|
if (isDiffScope && diffContext) {
|
|
39
|
-
const
|
|
40
|
-
.map((e) => ` ${e.method} ${e.path} (${e.sourceFile})`)
|
|
41
|
-
.join("\n") || " none";
|
|
42
|
-
const modEps = diffContext.modifiedEndpoints
|
|
43
|
-
.map((e) => ` ${e.method} ${e.path} (${e.sourceFile})`)
|
|
44
|
-
.join("\n") || " none";
|
|
59
|
+
const fmtEps = (eps, detail) => eps.flatMap((ep) => (ep.methods ?? []).map((m) => ` ${m.method} ${ep.path} (${detail(m)})`)).join("\n") || " none";
|
|
45
60
|
diffSection = `
|
|
46
61
|
## Branch Diff Context
|
|
47
62
|
Branch: \`${diffContext.currentBranch}\` → base: \`${diffContext.baseBranch}\`
|
|
48
63
|
Changed files: ${diffContext.changedFiles.join(", ")}
|
|
49
64
|
New endpoints:
|
|
50
|
-
${
|
|
65
|
+
${fmtEps(diffContext.newEndpoints, (m) => `${m.sourceFile}, ${m.interactionCount} interactions`)}
|
|
51
66
|
Modified endpoints:
|
|
52
|
-
${
|
|
67
|
+
${fmtEps(diffContext.modifiedEndpoints, (m) => `${m.sourceFile}, ${m.changeType}`)}
|
|
53
68
|
Affected services: ${diffContext.affectedServices.join(", ") || "N/A"}
|
|
54
|
-
Summary: ${diffContext?.summary ?? "N/A"}
|
|
55
69
|
|
|
56
|
-
**CRITICAL**: Focus
|
|
70
|
+
**CRITICAL**: Focus on tests that validate these changes and how they interact with existing resources.
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
// ── Interactions ──
|
|
74
|
+
let interactionSection = "";
|
|
75
|
+
if (focus === "all" || focus === "interactions") {
|
|
76
|
+
const diffEndpointKeys = new Set();
|
|
77
|
+
if (isDiffScope && diffContext) {
|
|
78
|
+
for (const ep of [...(diffContext.newEndpoints || []), ...(diffContext.modifiedEndpoints || [])]) {
|
|
79
|
+
for (const m of (ep.methods ?? []))
|
|
80
|
+
diffEndpointKeys.add(`${m.method} ${ep.path}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const summaryLines = allEndpoints
|
|
84
|
+
.flatMap((ep) => (ep.methods ?? []).flatMap((m) => (m.interactions ?? []).map((i) => ` ${m.method} ${ep.path} → ${i.response.statusCode} (${i.type}): ${i.description}`)))
|
|
85
|
+
.join("\n");
|
|
86
|
+
const detailEndpoints = isDiffScope
|
|
87
|
+
? allEndpoints.filter((ep) => (ep.methods ?? []).some((m) => diffEndpointKeys.has(`${m.method} ${ep.path}`)))
|
|
88
|
+
: allEndpoints;
|
|
89
|
+
const detailBlocks = detailEndpoints
|
|
90
|
+
.flatMap((ep) => (ep.methods ?? []).flatMap((m) => (m.interactions ?? []).map((i) => {
|
|
91
|
+
const reqBody = i.request.body ? `\n requestBody: ${JSON.stringify(i.request.body)}` : "";
|
|
92
|
+
const resBody = i.response.body ? `\n responseBody: ${JSON.stringify(i.response.body)}` : "";
|
|
93
|
+
const headers = i.request.headers ? `\n headers: ${JSON.stringify(i.request.headers)}` : "";
|
|
94
|
+
return ` ${m.method} ${ep.path} → ${i.response.statusCode} (${i.type}): ${i.description}${reqBody}${resBody}${headers}`;
|
|
95
|
+
})))
|
|
96
|
+
.join("\n");
|
|
97
|
+
interactionSection = `
|
|
98
|
+
## Endpoint Interactions
|
|
99
|
+
${summaryLines}
|
|
100
|
+
|
|
101
|
+
### Detailed (request/response bodies)
|
|
102
|
+
${isDiffScope ? "Changed endpoints only. " : ""}Use source code schemas (Zod/Pydantic/DTOs) for actual request bodies.
|
|
103
|
+
${detailBlocks}
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
// ── Scenarios ──
|
|
107
|
+
let scenarioSection = "";
|
|
108
|
+
if (focus === "all" || focus === "scenarios") {
|
|
109
|
+
const scenarios = analysis.businessContext.draftedScenarios;
|
|
110
|
+
if (scenarios.length > 0) {
|
|
111
|
+
const baseUrl = analysis.apiEndpoints.baseUrl;
|
|
112
|
+
const scenarioBlocks = scenarios
|
|
113
|
+
.map((s) => {
|
|
114
|
+
const stepLines = s.steps.map((st) => ` ${st.order ?? ""}. **${st.method} ${st.path}** → ${st.expectedStatusCode ?? 200}: ${st.description || ""}`).join("\n");
|
|
115
|
+
const toolCalls = s.steps.map((st) => ` skyramp_scenario_test_generation({ scenarioName: "${s.scenarioName}", destination: "${s.scenarioName}", baseURL: "${baseUrl}", method: "${st.method}", path: "${st.path}", statusCode: ${st.expectedStatusCode ?? 200}, authHeader: "${authHeaderValue}", requestBody: <from source schemas for ${st.method} ${st.path}> })`).join("\n");
|
|
116
|
+
return (` ### ${s.scenarioName} (${s.category}, ${s.priority})\n` +
|
|
117
|
+
` ${s.description}\n` +
|
|
118
|
+
` **Steps:**\n${stepLines}\n` +
|
|
119
|
+
` **Chaining keys:** ${s.chainingKeys.join(", ") || "none"}\n` +
|
|
120
|
+
` **Tool calls:**\n${toolCalls}\n` +
|
|
121
|
+
` Then: skyramp_integration_test_generation({ scenarioFile: "scenario_${s.scenarioName}.json", authHeader: "${authHeaderValue}" })`);
|
|
122
|
+
})
|
|
123
|
+
.join("\n\n");
|
|
124
|
+
scenarioSection = `
|
|
125
|
+
## Drafted Scenarios — EACH = MANDATORY Integration Test
|
|
126
|
+
**Base URL:** \`${baseUrl}\` | **Auth:** \`${authHeaderValue}\`
|
|
127
|
+
|
|
128
|
+
Only use scenarios where resources are ACTUALLY related in the codebase. Replace any
|
|
129
|
+
scenario that pairs unrelated resources with one reflecting real foreign key relationships.
|
|
130
|
+
|
|
131
|
+
**Quality bar:** Realistic request bodies, actual foreign keys for chaining, response data
|
|
132
|
+
verification (not just status codes), realistic test data (not "test product").
|
|
133
|
+
**Path verification:** Cross-reference paths against Router Mounting context — use correct
|
|
134
|
+
nested paths. **Request bodies:** Replace placeholders with actual schemas from source code.
|
|
135
|
+
|
|
136
|
+
${scenarioBlocks}
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
scenarioSection = `
|
|
141
|
+
## Scenarios — Draft From Your Analysis
|
|
142
|
+
|
|
143
|
+
Draft at least 2-3 MEANINGFUL scenarios based on your codebase analysis:
|
|
144
|
+
1. **Cross-resource workflow** — resources referencing each other via foreign keys
|
|
145
|
+
2. **Search/filter + verify** — create data, search, verify results
|
|
146
|
+
3. **Error handling** — invalid cross-resource references → appropriate errors
|
|
147
|
+
|
|
148
|
+
Use base URL: \`${analysis.apiEndpoints.baseUrl}\` and auth: \`${authHeaderValue}\`.
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// ── PR History ──
|
|
153
|
+
let prHistorySection = "";
|
|
154
|
+
if (prContext && prContext.previousRecommendations.length > 0) {
|
|
155
|
+
const recLines = prContext.previousRecommendations
|
|
156
|
+
.map((r) => ` - [${r.status.toUpperCase()}] ${r.testType} — ${r.endpoint}${r.scenarioName ? ` (scenario: ${r.scenarioName})` : ""}`)
|
|
157
|
+
.join("\n");
|
|
158
|
+
prHistorySection = `
|
|
159
|
+
## PR History (PR #${prContext.prNumber})
|
|
160
|
+
${recLines}
|
|
161
|
+
**Do NOT re-recommend tests already listed above.**
|
|
57
162
|
`;
|
|
58
163
|
}
|
|
164
|
+
// ── Compose all sections ──
|
|
59
165
|
const scopeNote = isDiffScope
|
|
60
|
-
? "
|
|
61
|
-
: "
|
|
166
|
+
? "Scoped to current branch changes."
|
|
167
|
+
: "Covers the full repository.";
|
|
62
168
|
return `
|
|
63
|
-
|
|
169
|
+
${modePreamble}
|
|
64
170
|
|
|
65
171
|
Scope: ${scopeNote}
|
|
172
|
+
|
|
173
|
+
${buildTestQualityCriteria()}
|
|
174
|
+
|
|
175
|
+
${buildPrioritizationDimensions()}
|
|
176
|
+
|
|
177
|
+
${buildTestExamples()}
|
|
178
|
+
|
|
179
|
+
${buildTestPatternGuidelines()}
|
|
180
|
+
|
|
181
|
+
${buildGenerationRules(isUIOnlyPR)}
|
|
182
|
+
|
|
66
183
|
${diffSection}
|
|
67
184
|
## Repository Context
|
|
68
185
|
|
|
69
186
|
${repoContext}
|
|
70
187
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
${
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
## Your Task
|
|
81
|
-
|
|
82
|
-
Generate specific, actionable test recommendations for the top ${topN} test types listed above.
|
|
83
|
-
|
|
84
|
-
**MANDATORY RULES**:
|
|
85
|
-
1. **NO numeric scores** in output — use only: "high", "medium", "low".
|
|
86
|
-
2. **DO NOT mark any test "blocked"** even if artifacts are missing.
|
|
87
|
-
3. **Branch scope**: All specific test suggestions MUST target endpoints/code that changed in the branch diff.
|
|
88
|
-
4. **DO NOT create or save any files** — output everything inline in your response.
|
|
89
|
-
|
|
90
|
-
## Output Structure
|
|
91
|
-
|
|
92
|
-
Return a JSON object with this structure:
|
|
93
|
-
|
|
94
|
-
\`\`\`json
|
|
95
|
-
{
|
|
96
|
-
"summary": {
|
|
97
|
-
"totalRecommended": 3,
|
|
98
|
-
"highPriorityCount": 2,
|
|
99
|
-
"estimatedEffort": "4-6 hours for top 3 tests",
|
|
100
|
-
"quickWins": ["Smoke tests (OpenAPI available, 30 min)"]
|
|
101
|
-
},
|
|
102
|
-
"recommendations": [
|
|
103
|
-
{
|
|
104
|
-
"priority": "high",
|
|
105
|
-
"testType": "contract",
|
|
106
|
-
"rationale": "Why this is high priority (DO NOT mention numeric scores)",
|
|
107
|
-
"specificTests": [
|
|
108
|
-
{
|
|
109
|
-
"testName": "Order quantity validation contract test",
|
|
110
|
-
"description": "Verify POST /api/v1/orders rejects quantity < 1",
|
|
111
|
-
"targetEndpoint": "POST /api/v1/orders",
|
|
112
|
-
"targetFlow": "Order creation validation",
|
|
113
|
-
"requiredInputs": {
|
|
114
|
-
"available": [{"name": "openApiSpec", "path": "./openapi.json"}],
|
|
115
|
-
"missing": []
|
|
116
|
-
},
|
|
117
|
-
"estimatedValue": "High - catches regression in validation rule change"
|
|
118
|
-
}
|
|
119
|
-
],
|
|
120
|
-
"gettingStarted": {
|
|
121
|
-
"prerequisites": ["Service running at localhost:8000"],
|
|
122
|
-
"quickStartCommand": "Use Skyramp MCP generate tools",
|
|
123
|
-
"documentationUrl": "https://www.skyramp.dev/docs"
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
],
|
|
127
|
-
"nextSteps": [
|
|
128
|
-
"1. Start with highest priority test type",
|
|
129
|
-
"2. Use Skyramp MCP generate tools to create each test",
|
|
130
|
-
"3. Execute and validate results"
|
|
131
|
-
]
|
|
132
|
-
}
|
|
133
|
-
\`\`\`
|
|
188
|
+
${interactionSection}
|
|
189
|
+
${scenarioSection}
|
|
190
|
+
${prHistorySection}
|
|
191
|
+
## Existing Tests
|
|
192
|
+
- Frameworks: ${analysis.existingTests.frameworks.join(", ") || "none"}
|
|
193
|
+
${formatTestLocations(analysis.existingTests.testLocations)}
|
|
194
|
+
|
|
195
|
+
${buildToolWorkflows(authHeaderValue)}
|
|
134
196
|
|
|
135
|
-
|
|
197
|
+
${buildCoverageChecklist(openApiSpec, isUIOnlyPR, hasFrontendChanges, authHeaderValue, topN)}
|
|
136
198
|
`;
|
|
137
199
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// src/prompts/skyrampPrompt.ts
|
|
2
1
|
import { logger } from "../utils/logger.js";
|
|
3
2
|
export function registerTestGenerationPrompt(mcpServer) {
|
|
4
3
|
logger.info("test generation prompt");
|
|
@@ -26,7 +25,7 @@ export function registerTestGenerationPrompt(mcpServer) {
|
|
|
26
25
|
1. **Priority Scores Must Remain Unchanged**: When a test type is missing required inputs (e.g., Playwright recordings, traces), **DO NOT**:
|
|
27
26
|
- Mark the test as "blocked" in the output
|
|
28
27
|
- Adjust or reduce the priority score
|
|
29
|
-
- Exclude it from recommendations
|
|
28
|
+
- Exclude it from recommendations
|
|
30
29
|
2. DO NOT CREATE ANY .json or .md file during repository analysis, test mapping, or test recommendation.
|
|
31
30
|
|
|
32
31
|
**CONTRACT TEST:**
|
|
@@ -115,7 +114,7 @@ Let us generate a UI test with frontend playwright trace file skyramp_playwright
|
|
|
115
114
|
|
|
116
115
|
**SCENARIO PARSING:**
|
|
117
116
|
- Purpose: Parse natural language scenarios into structured JSON arrays of API requests
|
|
118
|
-
- Requirements: A natural language scenario description
|
|
117
|
+
- Requirements: A natural language scenario description. API schema (OpenAPI/Swagger file or URL) is OPTIONAL — scenarios work without it using endpointURL and requestBody from source code
|
|
119
118
|
- Features: Automatically breaks down any user-provided scenario into sequential API calls with request chaining
|
|
120
119
|
- Output: Returns a JSON array where each element represents an API request with method, endpoint, parameters, and dependencies
|
|
121
120
|
|
|
@@ -9,99 +9,102 @@ function getTestbotPrompt(prTitle, prDescription, diffFile, testDirectory, summa
|
|
|
9
9
|
<TEST DIRECTORY>${testDirectory}</TEST DIRECTORY>
|
|
10
10
|
<REPOSITORY PATH>${repositoryPath}</REPOSITORY PATH>
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- \`
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
12
|
+
Use the Skyramp MCP server tools for all tasks below.
|
|
13
|
+
|
|
14
|
+
## Task 1: Recommend & Generate New Tests
|
|
15
|
+
|
|
16
|
+
Read the diff at \`${diffFile}\`. Skip Task 1 if all changed files are non-application
|
|
17
|
+
(CI/CD, docs, lock files, config). Otherwise proceed:
|
|
18
|
+
|
|
19
|
+
### Steps
|
|
20
|
+
|
|
21
|
+
1. Call \`skyramp_analyze_repository\` with \`repositoryPath\`: "${repositoryPath}", \`analysisScope\`: "current_branch_diff"${baseBranch ? `\n , \`baseBranch\`: "${baseBranch}"` : ''}
|
|
22
|
+
2. Call \`skyramp_recommend_tests\` with the returned \`sessionId\`.
|
|
23
|
+
It returns 7 ranked recommendations. Generate the top 4, report the remaining 3
|
|
24
|
+
as \`additionalRecommendations\`.
|
|
25
|
+
|
|
26
|
+
3. **Generate** at most 4 tests from the top 4 recommendations. Stop after 4.
|
|
27
|
+
Keep a list of every file the CLI creates (test files AND scenario JSON files).
|
|
28
|
+
|
|
29
|
+
**Frontend-only PRs** (no backend/API changes): only generate tests if relevant
|
|
30
|
+
Playwright traces exist. If no traces are available, skip generation entirely and
|
|
31
|
+
move all 7 recommendations to \`additionalRecommendations\` with scenario steps and
|
|
32
|
+
trace recording instructions. Do not generate integration tests for unchanged backend
|
|
33
|
+
APIs just to fill the quota — those tests don't validate the PR's changes.
|
|
34
|
+
|
|
35
|
+
**How to generate each type:**
|
|
36
|
+
- **Integration**: call \`skyramp_scenario_test_generation\` per step, then
|
|
37
|
+
\`skyramp_integration_test_generation\` with the scenario file.
|
|
38
|
+
The scenario JSON is written to the same \`outputDir\` as the test files
|
|
39
|
+
(e.g. \`tests/scenario_<name>.json\`), not \`.skyramp/\`.
|
|
40
|
+
- **Contract**: call \`skyramp_contract_test_generation\` with \`endpointURL\`, \`method\`,
|
|
41
|
+
and \`requestData\` for POST/PUT endpoints.
|
|
42
|
+
Pass \`apiSchema\` if an OpenAPI spec exists — it validates response structure.
|
|
43
|
+
- **Fuzz**: call \`skyramp_fuzz_test_generation\` with \`endpointURL\`, \`method\`, \`requestData\`.
|
|
44
|
+
Pass \`apiSchema\` if available — it generates smarter boundary values.
|
|
45
|
+
- **E2E/UI**: only generate when relevant Playwright traces exist (see step 5).
|
|
46
|
+
Without traces, move the test to \`additionalRecommendations\` with scenario steps
|
|
47
|
+
and trace recording instructions instead.
|
|
48
|
+
- Skip smoke tests entirely.
|
|
49
|
+
|
|
50
|
+
**Scenario quality:** Before generating, verify each step's preconditions are met by
|
|
51
|
+
prior steps. For example, you can't update a membership that was never created — check
|
|
52
|
+
the controller code for existence checks and ensure the scenario creates records first.
|
|
53
|
+
|
|
54
|
+
**Filenames:** Pass a descriptive \`--output\` name per test to avoid CLI overwrites.
|
|
55
|
+
|
|
56
|
+
4. **Execute** the generated tests and record results.
|
|
57
|
+
|
|
58
|
+
5. **Trace search** for E2E/UI: look in \`\${testDirectory}\`, repo root, and \`.skyramp/\` for
|
|
59
|
+
trace files (\`*trace*.json\`, \`*playwright*.zip\`). Only use a trace if it covers code
|
|
60
|
+
changed in this PR and targets localhost — skip traces for external hosts or unrelated code.
|
|
61
|
+
|
|
62
|
+
With relevant traces: backend + Playwright → \`skyramp_e2e_test_generation\`,
|
|
63
|
+
Playwright only → \`skyramp_ui_test_generation\`.
|
|
64
|
+
|
|
65
|
+
**After generation, fix chaining only.** The CLI may use literal/hardcoded IDs instead
|
|
66
|
+
of dynamic values from prior responses. Fix these two cases:
|
|
67
|
+
1. **Path params:** variables like \`product_id = 'product_id'\` → use the response accessor
|
|
68
|
+
(e.g. \`getResponseValue(response, "response.id")\` in TS, \`skyramp.get_response_value(response, "id")\` in Python).
|
|
69
|
+
2. **Request body refs:** hardcoded IDs in request bodies (e.g. \`"product_id": 1\`) → replace
|
|
70
|
+
with the dynamic ID extracted from the prior POST response (e.g. \`product_id\` variable or
|
|
71
|
+
\`dataOverride\`/\`data_override\` for the field).
|
|
72
|
+
|
|
73
|
+
Change ONLY chaining-related values (path param assignments and body ID references).
|
|
74
|
+
Preserve everything else exactly as the CLI generated it — headers, auth code, assertions,
|
|
75
|
+
imports, and all other request body fields.
|
|
76
|
+
|
|
77
|
+
## Task 2: Existing Test Maintenance
|
|
78
|
+
|
|
79
|
+
Run this task regardless of Task 1 outcome — even if Task 1 was skipped or generated zero tests.
|
|
80
|
+
|
|
81
|
+
1. Call \`skyramp_discover_tests\` with \`repositoryPath\`: "${repositoryPath}".
|
|
82
|
+
2. If zero Skyramp tests found, report \`testMaintenance\` as an empty array with
|
|
83
|
+
a note in \`issuesFound\`: "No existing Skyramp tests found for maintenance."
|
|
84
|
+
3. If tests exist:
|
|
85
|
+
a. Baseline them (from CI status or by executing).
|
|
86
|
+
b. Run \`skyramp_analyze_test_drift\` → \`skyramp_calculate_health_scores\` → \`skyramp_actions\`.
|
|
87
|
+
c. Apply actions (path renames, schema updates) in-place. Do not regenerate.
|
|
88
|
+
d. Execute modified tests. Report before/after in \`testMaintenance\`.
|
|
89
|
+
|
|
90
|
+
## Task 3: Submit Report
|
|
91
|
+
|
|
92
|
+
Verify Tasks 1 and 2 are complete, then call \`skyramp_submit_report\` with
|
|
93
|
+
\`summaryOutputFile\`: "${summaryOutputFile}".
|
|
94
|
+
|
|
95
|
+
\`commitMessage\`: under 72 chars, e.g. "add integration tests for /products and /orders"
|
|
96
|
+
|
|
97
|
+
**newTestsCreated** — list every generated test file (at most 4):
|
|
98
|
+
\`testType\`, \`endpoint\`, \`fileName\`, \`description\`, \`scenarioFile\`, \`traceFile\`, \`frontendTrace\`
|
|
99
|
+
Use the actual file path returned by the generation tool for \`scenarioFile\`.
|
|
100
|
+
Include scenario JSON files in the git commit alongside test files.
|
|
101
|
+
Every test file in the commit should appear here. If you over-generated, delete extras first.
|
|
102
|
+
If no tests were generated (e.g. frontend-only PR without traces), pass an empty array.
|
|
103
|
+
|
|
104
|
+
**additionalRecommendations** — remaining recommendations not generated:
|
|
105
|
+
\`testType\`, \`scenarioName\`, \`priority\`, \`description\`, \`steps\`, artifact paths
|
|
106
|
+
|
|
107
|
+
**businessCaseAnalysis** — based only on PR data and tool outputs.`;
|
|
105
108
|
}
|
|
106
109
|
export function registerTestbotPrompt(server) {
|
|
107
110
|
logger.info("Registering testbot prompt");
|