@skyramp/mcp 0.2.8 → 0.2.150-rc.local.de
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/commands/commandLibrary.d.ts +1 -0
- package/build/commands/commandLibrary.js +18 -17
- package/build/commands/localDevTestChangesCommand.d.ts +15 -0
- package/build/commands/localDevTestChangesCommand.js +175 -0
- package/build/index.js +74 -13
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +1 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +7 -6
- package/build/prompts/local-dev/local-dev-plan.d.ts +25 -0
- package/build/prompts/local-dev/local-dev-plan.js +414 -0
- package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
- package/build/prompts/local-dev/local-dev-prompts.js +152 -0
- package/build/prompts/prompt-utils.d.ts +8 -0
- package/build/prompts/prompt-utils.js +33 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +3 -1
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +3 -1
- package/build/prompts/sut-setup/shared.d.ts +20 -0
- package/build/prompts/sut-setup/shared.js +69 -7
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +79 -16
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +34 -1
- package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
- package/build/prompts/testbot/testbot-prompts.js +4 -34
- package/build/resources/testbotResource.js +2 -1
- package/build/services/TestExecutionService.d.ts +10 -2
- package/build/services/TestExecutionService.js +220 -28
- package/build/services/TestExecutionService.test.js +447 -16
- package/build/services/TestGenerationService.d.ts +2 -2
- package/build/tool-phases.js +4 -0
- package/build/tools/code-refactor/enhanceAssertionsTool.js +34 -2
- package/build/tools/enrichTestWithMocksTool.d.ts +15 -0
- package/build/tools/enrichTestWithMocksTool.js +414 -0
- package/build/tools/enrichTestWithMocksTool.test.d.ts +1 -0
- package/build/tools/enrichTestWithMocksTool.test.js +397 -0
- package/build/tools/executeSkyrampTestTool.d.ts +3 -0
- package/build/tools/executeSkyrampTestTool.js +73 -18
- package/build/tools/executeSkyrampTestTool.test.d.ts +1 -0
- package/build/tools/executeSkyrampTestTool.test.js +111 -0
- package/build/tools/generate-tests/batchMockGenerationTool.d.ts +72 -0
- package/build/tools/generate-tests/batchMockGenerationTool.js +382 -0
- package/build/tools/generate-tests/batchMockGenerationTool.test.d.ts +1 -0
- package/build/tools/generate-tests/batchMockGenerationTool.test.js +514 -0
- package/build/tools/generate-tests/generateContractRestTool.js +2 -2
- package/build/tools/generate-tests/generateMockRestTool.d.ts +56 -6
- package/build/tools/generate-tests/generateMockRestTool.js +193 -21
- package/build/tools/generate-tests/generateMockRestTool.test.d.ts +1 -0
- package/build/tools/generate-tests/generateMockRestTool.test.js +340 -0
- package/build/tools/generateEnrichedIntegrationTestTool.d.ts +24 -0
- package/build/tools/generateEnrichedIntegrationTestTool.js +209 -0
- package/build/tools/generateEnrichedIntegrationTestTool.test.d.ts +1 -0
- package/build/tools/generateEnrichedIntegrationTestTool.test.js +199 -0
- package/build/tools/one-click/oneClickTool.d.ts +10 -0
- package/build/tools/one-click/oneClickTool.js +182 -40
- package/build/tools/one-click/oneClickTool.test.d.ts +1 -0
- package/build/tools/one-click/oneClickTool.test.js +204 -0
- package/build/tools/preflightMockCheckTool.d.ts +2 -0
- package/build/tools/preflightMockCheckTool.js +91 -0
- package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
- package/build/tools/test-management/analyzeChangesTool.js +59 -38
- package/build/tools/workspace/initializeWorkspaceTool.js +2 -9
- package/build/tools/workspace/initializeWorkspaceTool.test.js +9 -4
- package/build/types/OneClickCommands.d.ts +1 -1
- package/build/types/RepositoryAnalysis.d.ts +117 -0
- package/build/types/RepositoryAnalysis.js +16 -2
- package/build/types/TestTypes.d.ts +11 -2
- package/build/types/TestTypes.js +11 -1
- package/build/utils/analyze-openapi.js +18 -1
- package/build/utils/analyze-openapi.test.d.ts +1 -0
- package/build/utils/analyze-openapi.test.js +19 -0
- package/build/utils/branchDiff.d.ts +17 -1
- package/build/utils/branchDiff.js +96 -14
- package/build/utils/branchDiff.test.d.ts +1 -0
- package/build/utils/branchDiff.test.js +107 -0
- package/build/utils/docker.test.js +1 -1
- package/build/utils/featureFlags.d.ts +15 -1
- package/build/utils/featureFlags.js +19 -2
- package/build/utils/featureFlags.test.js +57 -2
- package/build/utils/grpcMockValidation.d.ts +1 -0
- package/build/utils/grpcMockValidation.js +49 -0
- package/build/utils/grpcMockValidation.test.d.ts +1 -0
- package/build/utils/grpcMockValidation.test.js +41 -0
- package/build/utils/httpMethodValidation.d.ts +4 -0
- package/build/utils/httpMethodValidation.js +13 -0
- package/build/utils/mockCompatibility.d.ts +49 -0
- package/build/utils/mockCompatibility.js +80 -0
- package/build/utils/mockCompatibility.test.d.ts +1 -0
- package/build/utils/mockCompatibility.test.js +88 -0
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +35 -21
- package/build/workspace/workspace.js +8 -4
- package/build/workspace/workspace.test.js +65 -6
- package/package.json +2 -2
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SkyrampClient } from "@skyramp/skyramp";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
import { injectMockImports } from "./enrichTestWithMocksTool.js";
|
|
5
|
+
import { isAuthorizationHeaderName } from "../utils/workspaceAuth.js";
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
const TOOL_NAME = "skyramp_generate_enriched_integration_test";
|
|
9
|
+
const enrichedIntegrationTestSchema = {
|
|
10
|
+
scenarioFile: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe("Absolute path to the scenario JSON file (from skyramp_batch_scenario_test_generation). Used as trace input for integration test generation."),
|
|
13
|
+
mockFiles: z
|
|
14
|
+
.array(z.string())
|
|
15
|
+
.describe("Absolute paths to the generated mock files (from skyramp_mock_generation or skyramp_batch_mock_generation) that this test needs."),
|
|
16
|
+
scenarioName: z
|
|
17
|
+
.string()
|
|
18
|
+
.describe("Human-readable scenario name (e.g., order-happy-path). Used as the SCENARIO_NAME constant in the enriched test."),
|
|
19
|
+
outputDir: z
|
|
20
|
+
.string()
|
|
21
|
+
.describe("Absolute path to the directory where the test file will be written."),
|
|
22
|
+
output: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Output test file name (e.g., order_happy_path_integration_test.py). Derived from scenario if omitted."),
|
|
26
|
+
runtime: z
|
|
27
|
+
.string()
|
|
28
|
+
.optional()
|
|
29
|
+
.default("docker")
|
|
30
|
+
.describe("Runtime environment: 'docker' or 'k8s'. Defaults to docker."),
|
|
31
|
+
dockerNetwork: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Docker network name for the test runtime."),
|
|
35
|
+
authHeader: z
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("HTTP header that carries the auth credential (e.g., Authorization, X-Api-Key)."),
|
|
39
|
+
authScheme: z
|
|
40
|
+
.string()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Auth scheme prefix (e.g., Bearer). Mapped to authType for the backend."),
|
|
43
|
+
language: z
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Programming language for the generated test (e.g., python, typescript)."),
|
|
47
|
+
framework: z
|
|
48
|
+
.string()
|
|
49
|
+
.optional()
|
|
50
|
+
.describe("Test framework (e.g., pytest, playwright)."),
|
|
51
|
+
traceComments: z
|
|
52
|
+
.array(z.string())
|
|
53
|
+
.optional()
|
|
54
|
+
.describe("Optional trace comments showing how mock values flow to assertions."),
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* skyramp_generate_enriched_integration_test
|
|
58
|
+
*
|
|
59
|
+
* Combines integration test generation (from a scenario file) and mock enrichment
|
|
60
|
+
* into a single atomic operation. Eliminates the need to call
|
|
61
|
+
* skyramp_integration_test_generation and skyramp_enrich_test_with_mocks separately.
|
|
62
|
+
*/
|
|
63
|
+
export function registerGenerateEnrichedIntegrationTestTool(server) {
|
|
64
|
+
server.registerTool(TOOL_NAME, {
|
|
65
|
+
description: "Generate an integration test from a scenario file AND enrich it with mock imports in one step. " +
|
|
66
|
+
"Combines skyramp_integration_test_generation + skyramp_enrich_test_with_mocks into a single call. " +
|
|
67
|
+
"Use this after skyramp_batch_scenario_test_generation and skyramp_batch_mock_generation to " +
|
|
68
|
+
"produce a fully wired test file ready for execution.",
|
|
69
|
+
inputSchema: enrichedIntegrationTestSchema,
|
|
70
|
+
}, async (params) => {
|
|
71
|
+
return executeGenerateEnrichedIntegrationTest(params);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export async function executeGenerateEnrichedIntegrationTest(params) {
|
|
75
|
+
const { scenarioFile, mockFiles, scenarioName, outputDir, output, runtime, dockerNetwork, authHeader, authScheme, language, framework, traceComments, } = params;
|
|
76
|
+
try {
|
|
77
|
+
// Step 1: Validate language — enrichment currently supports Python only
|
|
78
|
+
if (language && language !== "python") {
|
|
79
|
+
return {
|
|
80
|
+
content: [
|
|
81
|
+
{
|
|
82
|
+
type: "text",
|
|
83
|
+
text: `**Error:** Mock enrichment is currently supported only for Python tests; received language "${language}". Use \`skyramp_integration_test_generation\` followed by \`skyramp_enrich_test_with_mocks\` for non-Python languages.`,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
isError: true,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// Step 1b: Validate scenarioFile exists
|
|
90
|
+
if (!fs.existsSync(scenarioFile)) {
|
|
91
|
+
return {
|
|
92
|
+
content: [
|
|
93
|
+
{
|
|
94
|
+
type: "text",
|
|
95
|
+
text: `**Error:** Scenario file not found: ${scenarioFile}`,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
isError: true,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Step 1b: Validate mock files exist
|
|
102
|
+
const missingFiles = mockFiles.filter((f) => !fs.existsSync(f));
|
|
103
|
+
if (missingFiles.length > 0) {
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: "text",
|
|
108
|
+
text: `**Error:** Mock files not found:\n` +
|
|
109
|
+
missingFiles.map((f) => ` - ${f}`).join("\n") +
|
|
110
|
+
`\n\nGenerate them first with \`skyramp_batch_mock_generation\`.`,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
isError: true,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Step 2: Generate the integration test
|
|
117
|
+
// Normalize auth params: the Go CLI treats --auth-type and --auth-header as
|
|
118
|
+
// mutually exclusive when authHeader is the standard Authorization header.
|
|
119
|
+
// Mirror TestGenerationService.executeGeneration() normalization:
|
|
120
|
+
// if authScheme is set and authHeader is Authorization, use authType only.
|
|
121
|
+
let resolvedAuthHeader = authHeader;
|
|
122
|
+
const resolvedAuthType = authScheme;
|
|
123
|
+
if (resolvedAuthType !== undefined && isAuthorizationHeaderName(resolvedAuthHeader || "")) {
|
|
124
|
+
resolvedAuthHeader = undefined;
|
|
125
|
+
}
|
|
126
|
+
const client = new SkyrampClient();
|
|
127
|
+
const result = await client.generateRestTest({
|
|
128
|
+
traceFilePath: scenarioFile,
|
|
129
|
+
outputDir,
|
|
130
|
+
output,
|
|
131
|
+
runtime,
|
|
132
|
+
force: true,
|
|
133
|
+
dockerNetwork,
|
|
134
|
+
authHeader: resolvedAuthHeader,
|
|
135
|
+
authType: resolvedAuthType,
|
|
136
|
+
language,
|
|
137
|
+
framework,
|
|
138
|
+
testType: "integration",
|
|
139
|
+
});
|
|
140
|
+
// Check if generation succeeded
|
|
141
|
+
if (!result || !result.toLowerCase().includes("success")) {
|
|
142
|
+
return {
|
|
143
|
+
content: [
|
|
144
|
+
{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: `**Error:** Integration test generation failed: ${result || "no output from backend"}`,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
isError: true,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// Step 3: Determine the test file path and read it
|
|
153
|
+
const testFileName = output || deriveTestFileName(scenarioFile, language);
|
|
154
|
+
const testFilePath = path.join(outputDir, testFileName);
|
|
155
|
+
if (!fs.existsSync(testFilePath)) {
|
|
156
|
+
return {
|
|
157
|
+
content: [
|
|
158
|
+
{
|
|
159
|
+
type: "text",
|
|
160
|
+
text: `**Error:** Generated test file not found at expected path: ${testFilePath}\n` +
|
|
161
|
+
`Backend output: ${result}`,
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
isError: true,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const testContent = fs.readFileSync(testFilePath, "utf-8");
|
|
168
|
+
// Step 4: Enrich with mock imports
|
|
169
|
+
const enrichedContent = injectMockImports(testContent, testFilePath, scenarioName, mockFiles, traceComments);
|
|
170
|
+
// Step 5: Write the enriched content back
|
|
171
|
+
fs.writeFileSync(testFilePath, enrichedContent);
|
|
172
|
+
logger.info(`Generated and enriched integration test: ${testFilePath}`, {
|
|
173
|
+
scenarioName,
|
|
174
|
+
mockFileCount: mockFiles.length,
|
|
175
|
+
});
|
|
176
|
+
return {
|
|
177
|
+
content: [
|
|
178
|
+
{
|
|
179
|
+
type: "text",
|
|
180
|
+
text: [
|
|
181
|
+
`**Integration test generated and enriched with mocks for scenario "${scenarioName}".**\n`,
|
|
182
|
+
`**Test file:** ${testFilePath}`,
|
|
183
|
+
`**Mock files imported:** ${mockFiles.length}`,
|
|
184
|
+
...mockFiles.map((f) => ` - ${path.basename(f)}`),
|
|
185
|
+
`\nThe test is ready for execution with: \`skyramp_execute_test({testFile: "${testFilePath}", ...})\``,
|
|
186
|
+
].join("\n"),
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
logger.error(`Failed to generate enriched integration test: ${error.message}`);
|
|
193
|
+
return {
|
|
194
|
+
content: [
|
|
195
|
+
{
|
|
196
|
+
type: "text",
|
|
197
|
+
text: `**Failed to generate enriched integration test:** ${error.message}`,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
isError: true,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function deriveTestFileName(scenarioFile, language) {
|
|
205
|
+
const base = path.basename(scenarioFile, ".json");
|
|
206
|
+
const name = base.replace(/^scenario_/, "").replace(/[^a-zA-Z0-9]/g, "_");
|
|
207
|
+
const ext = language === "typescript" || language === "javascript" ? ".spec.ts" : ".py";
|
|
208
|
+
return `${name}_integration_test${ext}`;
|
|
209
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { jest, describe, it, expect, beforeEach, afterEach } from "@jest/globals";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
// Mock the SkyrampClient
|
|
6
|
+
const mockGenerateRestTest = jest.fn();
|
|
7
|
+
jest.unstable_mockModule("@skyramp/skyramp", () => ({
|
|
8
|
+
SkyrampClient: jest.fn().mockImplementation(() => ({
|
|
9
|
+
generateRestTest: mockGenerateRestTest,
|
|
10
|
+
})),
|
|
11
|
+
}));
|
|
12
|
+
jest.unstable_mockModule("../utils/logger.js", () => ({
|
|
13
|
+
logger: {
|
|
14
|
+
info: jest.fn(),
|
|
15
|
+
error: jest.fn(),
|
|
16
|
+
warning: jest.fn(),
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
const { executeGenerateEnrichedIntegrationTest } = await import("./generateEnrichedIntegrationTestTool.js");
|
|
20
|
+
describe("generateEnrichedIntegrationTestTool", () => {
|
|
21
|
+
let tempDir;
|
|
22
|
+
let scenarioFile;
|
|
23
|
+
let mockFile1;
|
|
24
|
+
let mockFile2;
|
|
25
|
+
let mocksDir;
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "skyramp-enriched-test-"));
|
|
28
|
+
mocksDir = path.join(tempDir, "mocks");
|
|
29
|
+
fs.mkdirSync(mocksDir, { recursive: true });
|
|
30
|
+
// Create a scenario file
|
|
31
|
+
scenarioFile = path.join(tempDir, "scenario_partner-happy-path.json");
|
|
32
|
+
fs.writeFileSync(scenarioFile, JSON.stringify({ steps: [] }));
|
|
33
|
+
// Create mock files
|
|
34
|
+
mockFile1 = path.join(mocksDir, "accounts_GET_mock.py");
|
|
35
|
+
fs.writeFileSync(mockFile1, '# Command: skyramp generate mock rest http://accounts-service:4000/api/v1/accounts\ndef get_all_mocks():\n return []\n');
|
|
36
|
+
mockFile2 = path.join(mocksDir, "payments_POST_mock.py");
|
|
37
|
+
fs.writeFileSync(mockFile2, '# Command: skyramp generate mock rest http://payments-service:4001/api/v1/payments\ndef get_all_mocks():\n return []\n');
|
|
38
|
+
mockGenerateRestTest.mockReset();
|
|
39
|
+
});
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
42
|
+
});
|
|
43
|
+
it("generates and enriches successfully", async () => {
|
|
44
|
+
// The backend writes the test file on success
|
|
45
|
+
const testFileName = "partner_happy_path_integration_test.py";
|
|
46
|
+
const testFilePath = path.join(tempDir, testFileName);
|
|
47
|
+
mockGenerateRestTest.mockImplementation(async () => {
|
|
48
|
+
// Simulate backend writing the test file
|
|
49
|
+
fs.writeFileSync(testFilePath, [
|
|
50
|
+
"import skyramp",
|
|
51
|
+
"",
|
|
52
|
+
"def test_integration():",
|
|
53
|
+
" client = skyramp.Client(",
|
|
54
|
+
' "docker"',
|
|
55
|
+
" )",
|
|
56
|
+
" response = client.send_request()",
|
|
57
|
+
" assert response.status_code == 200",
|
|
58
|
+
"",
|
|
59
|
+
].join("\n"));
|
|
60
|
+
return "success: integration test generated";
|
|
61
|
+
});
|
|
62
|
+
const result = await executeGenerateEnrichedIntegrationTest({
|
|
63
|
+
scenarioFile,
|
|
64
|
+
mockFiles: [mockFile1, mockFile2],
|
|
65
|
+
scenarioName: "partner-happy-path",
|
|
66
|
+
outputDir: tempDir,
|
|
67
|
+
output: testFileName,
|
|
68
|
+
runtime: "docker",
|
|
69
|
+
dockerNetwork: "test-network",
|
|
70
|
+
language: "python",
|
|
71
|
+
framework: "pytest",
|
|
72
|
+
});
|
|
73
|
+
expect(result.isError).toBeUndefined();
|
|
74
|
+
const text = result.content[0].type === "text" ? result.content[0].text : "";
|
|
75
|
+
expect(text).toContain("partner-happy-path");
|
|
76
|
+
expect(text).toContain(testFilePath);
|
|
77
|
+
expect(text).toContain("Mock files imported:** 2");
|
|
78
|
+
// Verify the file was enriched with mock imports
|
|
79
|
+
const written = fs.readFileSync(testFilePath, "utf-8");
|
|
80
|
+
expect(written).toContain("MOCK_SERVICES = {");
|
|
81
|
+
expect(written).toContain("apply_all_mocks(client)");
|
|
82
|
+
// Verify the client was called with correct options
|
|
83
|
+
expect(mockGenerateRestTest).toHaveBeenCalledWith(expect.objectContaining({
|
|
84
|
+
traceFilePath: scenarioFile,
|
|
85
|
+
outputDir: tempDir,
|
|
86
|
+
output: testFileName,
|
|
87
|
+
runtime: "docker",
|
|
88
|
+
force: true,
|
|
89
|
+
dockerNetwork: "test-network",
|
|
90
|
+
language: "python",
|
|
91
|
+
framework: "pytest",
|
|
92
|
+
testType: "integration",
|
|
93
|
+
}));
|
|
94
|
+
});
|
|
95
|
+
it("returns error when scenario file missing", async () => {
|
|
96
|
+
const result = await executeGenerateEnrichedIntegrationTest({
|
|
97
|
+
scenarioFile: "/nonexistent/scenario.json",
|
|
98
|
+
mockFiles: [mockFile1],
|
|
99
|
+
scenarioName: "test",
|
|
100
|
+
outputDir: tempDir,
|
|
101
|
+
});
|
|
102
|
+
expect(result.isError).toBe(true);
|
|
103
|
+
const text = result.content[0].type === "text" ? result.content[0].text : "";
|
|
104
|
+
expect(text).toContain("Scenario file not found");
|
|
105
|
+
expect(text).toContain("/nonexistent/scenario.json");
|
|
106
|
+
expect(mockGenerateRestTest).not.toHaveBeenCalled();
|
|
107
|
+
});
|
|
108
|
+
it("returns error when mock files missing", async () => {
|
|
109
|
+
const result = await executeGenerateEnrichedIntegrationTest({
|
|
110
|
+
scenarioFile,
|
|
111
|
+
mockFiles: [mockFile1, "/nonexistent/mock.py"],
|
|
112
|
+
scenarioName: "test",
|
|
113
|
+
outputDir: tempDir,
|
|
114
|
+
});
|
|
115
|
+
expect(result.isError).toBe(true);
|
|
116
|
+
const text = result.content[0].type === "text" ? result.content[0].text : "";
|
|
117
|
+
expect(text).toContain("Mock files not found");
|
|
118
|
+
expect(text).toContain("/nonexistent/mock.py");
|
|
119
|
+
expect(mockGenerateRestTest).not.toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
it("returns error when backend generation fails", async () => {
|
|
122
|
+
mockGenerateRestTest.mockRejectedValue(new Error("Backend connection refused"));
|
|
123
|
+
const result = await executeGenerateEnrichedIntegrationTest({
|
|
124
|
+
scenarioFile,
|
|
125
|
+
mockFiles: [mockFile1],
|
|
126
|
+
scenarioName: "test",
|
|
127
|
+
outputDir: tempDir,
|
|
128
|
+
output: "test_integration_test.py",
|
|
129
|
+
});
|
|
130
|
+
expect(result.isError).toBe(true);
|
|
131
|
+
const text = result.content[0].type === "text" ? result.content[0].text : "";
|
|
132
|
+
expect(text).toContain("Backend connection refused");
|
|
133
|
+
});
|
|
134
|
+
it("returns error when generation result does not indicate success", async () => {
|
|
135
|
+
mockGenerateRestTest.mockResolvedValue("error: failed to parse scenario");
|
|
136
|
+
const result = await executeGenerateEnrichedIntegrationTest({
|
|
137
|
+
scenarioFile,
|
|
138
|
+
mockFiles: [mockFile1],
|
|
139
|
+
scenarioName: "test",
|
|
140
|
+
outputDir: tempDir,
|
|
141
|
+
output: "test_integration_test.py",
|
|
142
|
+
});
|
|
143
|
+
expect(result.isError).toBe(true);
|
|
144
|
+
const text = result.content[0].type === "text" ? result.content[0].text : "";
|
|
145
|
+
expect(text).toContain("Integration test generation failed");
|
|
146
|
+
});
|
|
147
|
+
it("maps authScheme to authType and drops Authorization header to avoid CLI conflict", async () => {
|
|
148
|
+
// The Go CLI treats --auth-type and --auth-header as mutually exclusive.
|
|
149
|
+
// When authScheme is set and authHeader is the standard Authorization header,
|
|
150
|
+
// authHeader must be omitted so the backend doesn't return
|
|
151
|
+
// "Auth header and auth type cannot be supported at the same time".
|
|
152
|
+
const testFileName = "auth_test_integration_test.py";
|
|
153
|
+
const testFilePath = path.join(tempDir, testFileName);
|
|
154
|
+
mockGenerateRestTest.mockImplementation(async () => {
|
|
155
|
+
fs.writeFileSync(testFilePath, [
|
|
156
|
+
"import skyramp",
|
|
157
|
+
"",
|
|
158
|
+
"def test_integration():",
|
|
159
|
+
" client = skyramp.Client(",
|
|
160
|
+
' "docker"',
|
|
161
|
+
" )",
|
|
162
|
+
" response = client.send_request()",
|
|
163
|
+
"",
|
|
164
|
+
].join("\n"));
|
|
165
|
+
return "success: test generated";
|
|
166
|
+
});
|
|
167
|
+
await executeGenerateEnrichedIntegrationTest({
|
|
168
|
+
scenarioFile,
|
|
169
|
+
mockFiles: [mockFile1],
|
|
170
|
+
scenarioName: "auth-test",
|
|
171
|
+
outputDir: tempDir,
|
|
172
|
+
output: testFileName,
|
|
173
|
+
authScheme: "Bearer",
|
|
174
|
+
authHeader: "Authorization",
|
|
175
|
+
});
|
|
176
|
+
// authType: "Bearer" present, authHeader must NOT be present (mutually exclusive)
|
|
177
|
+
expect(mockGenerateRestTest).toHaveBeenCalledWith(expect.objectContaining({ authType: "Bearer" }));
|
|
178
|
+
expect(mockGenerateRestTest).not.toHaveBeenCalledWith(expect.objectContaining({ authHeader: "Authorization" }));
|
|
179
|
+
});
|
|
180
|
+
it("preserves non-Authorization authHeader alongside authType", async () => {
|
|
181
|
+
// Non-standard auth headers (X-Api-Key, etc.) are not mutually exclusive with authType.
|
|
182
|
+
const testFileName = "apikey_test_integration_test.py";
|
|
183
|
+
const testFilePath = path.join(tempDir, testFileName);
|
|
184
|
+
mockGenerateRestTest.mockImplementation(async () => {
|
|
185
|
+
fs.writeFileSync(testFilePath, "import skyramp\ndef test_integration(): pass\n");
|
|
186
|
+
return "success: test generated";
|
|
187
|
+
});
|
|
188
|
+
await executeGenerateEnrichedIntegrationTest({
|
|
189
|
+
scenarioFile,
|
|
190
|
+
mockFiles: [mockFile1],
|
|
191
|
+
scenarioName: "apikey-test",
|
|
192
|
+
outputDir: tempDir,
|
|
193
|
+
output: testFileName,
|
|
194
|
+
authScheme: "ApiKey",
|
|
195
|
+
authHeader: "X-Api-Key",
|
|
196
|
+
});
|
|
197
|
+
expect(mockGenerateRestTest).toHaveBeenCalledWith(expect.objectContaining({ authType: "ApiKey", authHeader: "X-Api-Key" }));
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export declare enum CallingAgentIde {
|
|
3
|
+
Cursor = "cursor",
|
|
4
|
+
Claude = "claude"
|
|
5
|
+
}
|
|
2
6
|
/** User params passed to workflow builder (optional fields may be undefined) */
|
|
3
7
|
export type OneClickParams = {
|
|
4
8
|
workflow: string;
|
|
@@ -7,5 +11,11 @@ export type OneClickParams = {
|
|
|
7
11
|
apiSchema?: string;
|
|
8
12
|
topN?: number;
|
|
9
13
|
token?: string;
|
|
14
|
+
baseBranch?: string;
|
|
15
|
+
servicesToMock?: string;
|
|
16
|
+
realServices?: string;
|
|
17
|
+
mockProtocols?: string;
|
|
18
|
+
callingAgent?: CallingAgentIde;
|
|
19
|
+
rawCallingAgent?: string;
|
|
10
20
|
};
|
|
11
21
|
export declare function registerOneClickTool(server: McpServer): void;
|