@skyramp/mcp 0.3.0 → 0.3.1-ldw-beta
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 +19 -13
- package/build/commands/commandLibrary.test.d.ts +1 -0
- package/build/commands/commandLibrary.test.js +59 -0
- package/build/commands/localDevTestChangesCommand.d.ts +15 -0
- package/build/commands/localDevTestChangesCommand.js +201 -0
- package/build/index.js +80 -6
- package/build/playwright/traceRecordingPrompt.js +2 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +17 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +11 -9
- package/build/prompts/local-dev/local-dev-plan.d.ts +35 -0
- package/build/prompts/local-dev/local-dev-plan.js +466 -0
- package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
- package/build/prompts/local-dev/local-dev-prompts.js +190 -0
- package/build/prompts/prompt-utils.d.ts +8 -0
- package/build/prompts/prompt-utils.js +33 -0
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +0 -28
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +16 -1
- package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
- package/build/prompts/testbot/testbot-prompts.js +5 -34
- package/build/resources/testbotResource.js +2 -1
- package/build/services/AnalyticsService.d.ts +1 -1
- package/build/services/TestExecutionService.d.ts +2 -1
- package/build/services/TestExecutionService.js +8 -3
- package/build/services/TestExecutionService.test.js +83 -3
- package/build/services/TestGenerationService.d.ts +2 -2
- package/build/services/TestGenerationService.js +7 -21
- package/build/services/containerEnv.js +3 -1
- package/build/tool-phases.js +6 -0
- package/build/tools/code-refactor/enhanceAssertionsTool.js +47 -18
- package/build/tools/enrichTestWithMocksTool.d.ts +28 -0
- package/build/tools/enrichTestWithMocksTool.js +726 -0
- package/build/tools/enrichTestWithMocksTool.test.d.ts +1 -0
- package/build/tools/enrichTestWithMocksTool.test.js +266 -0
- package/build/tools/executeSkyrampTestTool.d.ts +11 -0
- package/build/tools/executeSkyrampTestTool.js +63 -22
- package/build/tools/executeSkyrampTestTool.test.d.ts +1 -0
- package/build/tools/executeSkyrampTestTool.test.js +36 -0
- package/build/tools/generate-tests/batchMockGenerationTool.d.ts +85 -0
- package/build/tools/generate-tests/batchMockGenerationTool.js +432 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +6 -21
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +1 -0
- package/build/tools/generate-tests/generateContractRestTool.js +2 -2
- package/build/tools/generate-tests/generateMockRestTool.d.ts +129 -6
- package/build/tools/generate-tests/generateMockRestTool.js +234 -22
- package/build/tools/generate-tests/loadTestSchema.d.ts +1 -1
- package/build/tools/generateEnrichedIntegrationTestTool.d.ts +25 -0
- package/build/tools/generateEnrichedIntegrationTestTool.js +235 -0
- package/build/tools/generateEnrichedIntegrationTestTool.test.d.ts +1 -0
- package/build/tools/generateEnrichedIntegrationTestTool.test.js +44 -0
- package/build/tools/localDevWorkerComposeTool.d.ts +24 -0
- package/build/tools/localDevWorkerComposeTool.js +224 -0
- package/build/tools/localDevWorkerComposeTool.test.d.ts +1 -0
- package/build/tools/localDevWorkerComposeTool.test.js +129 -0
- package/build/tools/localDevWorkflowFixes.test.d.ts +1 -0
- package/build/tools/localDevWorkflowFixes.test.js +255 -0
- package/build/tools/one-click/oneClickTool.d.ts +13 -0
- package/build/tools/one-click/oneClickTool.js +195 -24
- package/build/tools/one-click/oneClickTool.test.d.ts +1 -0
- package/build/tools/one-click/oneClickTool.test.js +172 -0
- package/build/tools/preflightMockCheckTool.d.ts +2 -0
- package/build/tools/preflightMockCheckTool.js +96 -0
- package/build/tools/queryProxyMocksTool.d.ts +70 -0
- package/build/tools/queryProxyMocksTool.js +522 -0
- package/build/tools/queryProxyMocksTool.test.d.ts +1 -0
- package/build/tools/queryProxyMocksTool.test.js +164 -0
- package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
- package/build/tools/test-management/analyzeChangesTool.js +61 -38
- package/build/tools/trace/startTraceCollectionTool.js +3 -3
- 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/TestExecution.d.ts +1 -0
- package/build/types/TestTypes.d.ts +25 -7
- package/build/types/TestTypes.js +19 -5
- 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 +99 -14
- package/build/utils/branchDiff.test.d.ts +1 -0
- package/build/utils/branchDiff.test.js +109 -0
- package/build/utils/featureFlags.d.ts +16 -0
- package/build/utils/featureFlags.js +20 -0
- 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 +15 -0
- package/build/utils/mockCompatibility.d.ts +49 -0
- package/build/utils/mockCompatibility.js +82 -0
- package/build/utils/mockCompatibility.test.d.ts +1 -0
- package/build/utils/mockCompatibility.test.js +79 -0
- package/build/utils/progress.js +10 -5
- package/build/utils/versions.d.ts +1 -0
- package/build/utils/versions.js +2 -0
- package/build/utils/workspaceAuth.d.ts +9 -0
- package/build/utils/workspaceAuth.js +51 -1
- package/build/workspace/workspace.d.ts +28 -14
- package/build/workspace/workspace.js +3 -1
- package/node_modules/playwright/lib/mcp/browser/tools/screenshot.js +1 -1
- package/node_modules/playwright/lib/mcp/skyramp/resultCode.js +45 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +6 -22
- package/node_modules/playwright/lib/mcp/test/resultCode.test.js +48 -0
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +2 -2
|
@@ -9,11 +9,11 @@ import { logger } from "../utils/logger.js";
|
|
|
9
9
|
import { TestExecutionStatus, } from "../types/TestExecution.js";
|
|
10
10
|
import { TestType } from "../types/TestTypes.js";
|
|
11
11
|
import { buildContainerEnv } from "./containerEnv.js";
|
|
12
|
-
import {
|
|
12
|
+
import { EXECUTOR_DOCKER_IMAGE } from "../utils/versions.js";
|
|
13
13
|
import { walkDir } from "../utils/fileWalk.js";
|
|
14
|
+
export { EXECUTOR_DOCKER_IMAGE };
|
|
14
15
|
const DEFAULT_TIMEOUT = 300000; // 5 minutes
|
|
15
16
|
const MAX_CONCURRENT_EXECUTIONS = 5;
|
|
16
|
-
export const EXECUTOR_DOCKER_IMAGE = `skyramp/executor:${SKYRAMP_IMAGE_VERSION}`;
|
|
17
17
|
const DOCKER_PLATFORM = "linux/amd64";
|
|
18
18
|
const EXECUTION_PROGRESS_INTERVAL = 10000; // 10 seconds between progress updates during execution
|
|
19
19
|
// Temp file with valid empty JSON — used instead of /dev/null for .json config files
|
|
@@ -454,10 +454,15 @@ export class TestExecutionService {
|
|
|
454
454
|
testFilePath,
|
|
455
455
|
options.testType,
|
|
456
456
|
];
|
|
457
|
+
const networkMode = options.dockerNetwork?.trim()
|
|
458
|
+
? options.dockerNetwork.trim()
|
|
459
|
+
: options.useHostNetwork && process.platform === "linux"
|
|
460
|
+
? "host"
|
|
461
|
+
: undefined;
|
|
457
462
|
// Prepare host config with mounts
|
|
458
463
|
const hostConfig = {
|
|
459
464
|
ExtraHosts: ["host.docker.internal:host-gateway"],
|
|
460
|
-
...(
|
|
465
|
+
...(networkMode ? { NetworkMode: networkMode } : {}),
|
|
461
466
|
Mounts: [
|
|
462
467
|
{
|
|
463
468
|
Type: "bind",
|
|
@@ -83,14 +83,23 @@ describe("rewriteLocalhostForDocker", () => {
|
|
|
83
83
|
describe("buildContainerEnv", () => {
|
|
84
84
|
const baseOptions = { token: "test-token", language: "python" };
|
|
85
85
|
const emptyHostEnv = {};
|
|
86
|
-
it("
|
|
86
|
+
it("includes SKYRAMP_TEST_TOKEN when token is provided", () => {
|
|
87
87
|
const env = buildContainerEnv(baseOptions, undefined, emptyHostEnv);
|
|
88
88
|
expect(env).toContain("SKYRAMP_TEST_TOKEN=test-token");
|
|
89
89
|
expect(env).toContain("SKYRAMP_IN_DOCKER=true");
|
|
90
90
|
});
|
|
91
|
-
it("
|
|
91
|
+
it("omits SKYRAMP_TEST_TOKEN when token is not provided", () => {
|
|
92
92
|
const env = buildContainerEnv({ language: "typescript" }, undefined, emptyHostEnv);
|
|
93
|
-
expect(env).
|
|
93
|
+
expect(env.some((e) => e.startsWith("SKYRAMP_TEST_TOKEN="))).toBe(false);
|
|
94
|
+
expect(env).toContain("SKYRAMP_IN_DOCKER=true");
|
|
95
|
+
});
|
|
96
|
+
it("omits SKYRAMP_TEST_TOKEN when token is empty string (E7: unauthenticated endpoint)", () => {
|
|
97
|
+
// When unauthenticated: true, executeSkyrampTestTool resolves effectiveToken to "".
|
|
98
|
+
// containerEnv must NOT inject SKYRAMP_TEST_TOKEN="" because the generated test
|
|
99
|
+
// would then send an empty Authorization header, hitting the SUT's auth middleware
|
|
100
|
+
// and causing encoding errors (ASCII-8BIT → UTF-8).
|
|
101
|
+
const env = buildContainerEnv({ language: "python", token: "" }, undefined, emptyHostEnv);
|
|
102
|
+
expect(env.some((e) => e.startsWith("SKYRAMP_TEST_TOKEN="))).toBe(false);
|
|
94
103
|
});
|
|
95
104
|
it("adds PYTEST_ADDOPTS for python language", () => {
|
|
96
105
|
const env = buildContainerEnv(baseOptions, undefined, emptyHostEnv);
|
|
@@ -303,6 +312,23 @@ describe("TestExecutionService.executeTest - Docker env forwarding", () => {
|
|
|
303
312
|
delete process.env.SKYRAMP_TEST_SERVICE_URL_BACKEND;
|
|
304
313
|
delete process.env.SKYRAMP_TEST_SERVICE_URL_FRONTEND;
|
|
305
314
|
});
|
|
315
|
+
it.each(["python", "typescript", "javascript", "java"])("passes %s integration tests through to runner.sh", async (language) => {
|
|
316
|
+
const mockContainer = { remove: jest.fn().mockResolvedValue(undefined) };
|
|
317
|
+
mockRun.mockResolvedValue([{ StatusCode: 0 }, mockContainer]);
|
|
318
|
+
const service = new TestExecutionService();
|
|
319
|
+
await service.executeTest({
|
|
320
|
+
testFile: `/workspace/${language}_integration_test.txt`,
|
|
321
|
+
workspacePath: "/workspace",
|
|
322
|
+
language,
|
|
323
|
+
testType: "integration",
|
|
324
|
+
});
|
|
325
|
+
const command = mockRun.mock.calls[0][1];
|
|
326
|
+
expect(command[0]).toBe("/root/runner.sh");
|
|
327
|
+
expect(command[1]).toBe(language);
|
|
328
|
+
expect(command[2]).toBe(`/home/user/${language}_integration_test.txt`);
|
|
329
|
+
expect(command[3]).toBe("integration");
|
|
330
|
+
mockRun.mockClear();
|
|
331
|
+
});
|
|
306
332
|
it("passes SKYRAMP_TEST_BASE_URL to Docker container Env", async () => {
|
|
307
333
|
process.env.SKYRAMP_TEST_BASE_URL = "http://external-host:8000";
|
|
308
334
|
const mockContainer = { remove: jest.fn().mockResolvedValue(undefined) };
|
|
@@ -349,6 +375,22 @@ describe("TestExecutionService.executeTest - Docker env forwarding", () => {
|
|
|
349
375
|
e.startsWith("SKYRAMP_TEST_SERVICE_URL_"));
|
|
350
376
|
expect(envWithBaseUrl).toHaveLength(0);
|
|
351
377
|
});
|
|
378
|
+
it("attaches executor to dockerNetwork without treating it as host networking", async () => {
|
|
379
|
+
process.env.SKYRAMP_TEST_BASE_URL = "http://localhost:3000";
|
|
380
|
+
const mockContainer = { remove: jest.fn().mockResolvedValue(undefined) };
|
|
381
|
+
mockRun.mockResolvedValue([{ StatusCode: 0 }, mockContainer]);
|
|
382
|
+
const service = new TestExecutionService();
|
|
383
|
+
await service.executeTest({
|
|
384
|
+
testFile: "/workspace/test_file.py",
|
|
385
|
+
workspacePath: "/workspace",
|
|
386
|
+
language: "python",
|
|
387
|
+
testType: "smoke",
|
|
388
|
+
dockerNetwork: "checkr-demo_checkr_default",
|
|
389
|
+
});
|
|
390
|
+
const dockerOptions = mockRun.mock.calls[0][3];
|
|
391
|
+
expect(dockerOptions.HostConfig.NetworkMode).toBe("checkr-demo_checkr_default");
|
|
392
|
+
expect(dockerOptions.Env).toContain("SKYRAMP_TEST_BASE_URL=http://host.docker.internal:3000");
|
|
393
|
+
});
|
|
352
394
|
// Approach B: every workspace mount is mirrored at the host-absolute path so
|
|
353
395
|
// tests that embed absolute references (storageState, fixtures, snapshots)
|
|
354
396
|
// resolve correctly inside the executor regardless of which path-shape the
|
|
@@ -408,6 +450,44 @@ describe("TestExecutionService.executeTest - Docker env forwarding", () => {
|
|
|
408
450
|
}
|
|
409
451
|
});
|
|
410
452
|
});
|
|
453
|
+
describe("TestExecutionService.executeBatch", () => {
|
|
454
|
+
let TestExecutionService;
|
|
455
|
+
beforeAll(async () => {
|
|
456
|
+
const mod = await import("./TestExecutionService.js");
|
|
457
|
+
TestExecutionService = mod.TestExecutionService;
|
|
458
|
+
});
|
|
459
|
+
it("executes tests in parallel batches", async () => {
|
|
460
|
+
const service = new TestExecutionService();
|
|
461
|
+
const order = [];
|
|
462
|
+
let resolveFirst;
|
|
463
|
+
jest.spyOn(service, "executeTest").mockImplementation(async (options) => {
|
|
464
|
+
order.push(`start:${options.testFile}`);
|
|
465
|
+
if (options.testFile === "one.py") {
|
|
466
|
+
await new Promise((resolve) => {
|
|
467
|
+
resolveFirst = resolve;
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
order.push(`end:${options.testFile}`);
|
|
471
|
+
return {
|
|
472
|
+
testFile: options.testFile,
|
|
473
|
+
status: "pass",
|
|
474
|
+
executedAt: new Date().toISOString(),
|
|
475
|
+
duration: 1,
|
|
476
|
+
errors: [],
|
|
477
|
+
warnings: [],
|
|
478
|
+
};
|
|
479
|
+
});
|
|
480
|
+
const batchPromise = service.executeBatch([
|
|
481
|
+
{ testFile: "one.py", workspacePath: "/workspace", language: "python", testType: "smoke" },
|
|
482
|
+
{ testFile: "two.py", workspacePath: "/workspace", language: "python", testType: "smoke" },
|
|
483
|
+
]);
|
|
484
|
+
await Promise.resolve();
|
|
485
|
+
expect(order).toEqual(["start:one.py", "start:two.py", "end:two.py"]);
|
|
486
|
+
resolveFirst?.();
|
|
487
|
+
await batchPromise;
|
|
488
|
+
expect(order).toEqual(["start:one.py", "start:two.py", "end:two.py", "end:one.py"]);
|
|
489
|
+
});
|
|
490
|
+
});
|
|
411
491
|
describe("TestExecutionService.executeTest - Video capture for browser tests", () => {
|
|
412
492
|
let TestExecutionService;
|
|
413
493
|
let getVideoSubdir;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SkyrampClient } from "@skyramp/skyramp";
|
|
2
2
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
-
import { TestType } from "../types/TestTypes.js";
|
|
3
|
+
import { TestType, MOCK_TYPE } from "../types/TestTypes.js";
|
|
4
4
|
export interface BaseTestParams {
|
|
5
5
|
endpointURL?: string;
|
|
6
6
|
method?: string;
|
|
@@ -50,7 +50,7 @@ export declare abstract class TestGenerationService {
|
|
|
50
50
|
generateTest(params: BaseTestParams & Record<string, any>): Promise<CallToolResult>;
|
|
51
51
|
protected validateInputs(params: BaseTestParams): CallToolResult;
|
|
52
52
|
protected abstract buildGenerationOptions(params: BaseTestParams & Record<string, any>): any;
|
|
53
|
-
protected abstract getTestType(): TestType;
|
|
53
|
+
protected abstract getTestType(): TestType | typeof MOCK_TYPE;
|
|
54
54
|
protected handleApiAnalysis(params: BaseTestParams): Promise<CallToolResult | null>;
|
|
55
55
|
private static readonly STANDARD_HEADERS;
|
|
56
56
|
private static simpleWildcardMatch;
|
|
@@ -2,7 +2,7 @@ import path from "path";
|
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import { SkyrampClient } from "@skyramp/skyramp";
|
|
4
4
|
import { analyzeOpenAPIWithGivenEndpoint } from "../utils/analyze-openapi.js";
|
|
5
|
-
import { isAuthorizationHeaderName, KNOWN_AUTH_HEADERS,
|
|
5
|
+
import { isAuthorizationHeaderName, KNOWN_AUTH_HEADERS, resolveAuthFromWorkspace, } from "../utils/workspaceAuth.js";
|
|
6
6
|
import { getPathParameterValidationError, OUTPUT_DIR_FIELD_NAME, PATH_PARAMS_FIELD_NAME, QUERY_PARAMS_FIELD_NAME, FORM_PARAMS_FIELD_NAME, validateParams, validatePath, validateRequestData, } from "../utils/utils.js";
|
|
7
7
|
import { getEntryPoint } from "../utils/telemetry.js";
|
|
8
8
|
import { getLanguageSteps } from "../utils/language-helper.js";
|
|
@@ -316,29 +316,15 @@ The generated test file remains unchanged and ready to use as-is.
|
|
|
316
316
|
async executeGeneration(generateOptions) {
|
|
317
317
|
try {
|
|
318
318
|
// Auto-resolve auth from workspace config when authHeader was not explicitly provided.
|
|
319
|
-
// undefined =
|
|
319
|
+
// undefined = auto-resolve; "" = caller explicitly said skip-auth.
|
|
320
320
|
if (generateOptions.authHeader === undefined) {
|
|
321
321
|
try {
|
|
322
322
|
const repoPath = generateOptions.outputDir || process.cwd();
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (wsAuth.authType === WorkspaceAuthType.ApiKey && !resolvedHeader) {
|
|
329
|
-
logger.warning("workspace.yml has authType: apiKey but no authHeader is set — " +
|
|
330
|
-
"requests will be sent without an auth header and will likely receive 401/403. " +
|
|
331
|
-
"Add authHeader: <X-Your-Key-Header> to .skyramp/workspace.yml.");
|
|
332
|
-
}
|
|
333
|
-
if (resolvedHeader && wsAuth.authType !== WorkspaceAuthType.None) {
|
|
334
|
-
logger.info("authHeader not provided — resolved from workspace config", {
|
|
335
|
-
authHeader: resolvedHeader,
|
|
336
|
-
authType: wsAuth.authType,
|
|
337
|
-
});
|
|
338
|
-
generateOptions.authHeader = resolvedHeader;
|
|
339
|
-
if (isAuthorizationHeaderName(resolvedHeader)) {
|
|
340
|
-
generateOptions.authScheme =
|
|
341
|
-
generateOptions.authScheme ?? wsAuth.authScheme ?? getAuthScheme(wsAuth.authType);
|
|
323
|
+
const resolved = await resolveAuthFromWorkspace(repoPath, generateOptions.authHeader, generateOptions.authScheme);
|
|
324
|
+
if (resolved) {
|
|
325
|
+
generateOptions.authHeader = resolved.authHeader;
|
|
326
|
+
if (resolved.authScheme !== undefined) {
|
|
327
|
+
generateOptions.authScheme = resolved.authScheme;
|
|
342
328
|
}
|
|
343
329
|
}
|
|
344
330
|
}
|
|
@@ -14,7 +14,9 @@ export function rewriteLocalhostForDocker(url) {
|
|
|
14
14
|
*/
|
|
15
15
|
export function buildContainerEnv(options, saveStoragePath, hostEnv = process.env) {
|
|
16
16
|
const env = [
|
|
17
|
-
|
|
17
|
+
// Omit entirely when empty so os.getenv() returns None in the test —
|
|
18
|
+
// unauthenticated endpoints won't send an empty auth header (E7).
|
|
19
|
+
...(options.token ? [`SKYRAMP_TEST_TOKEN=${options.token}`] : []),
|
|
18
20
|
"SKYRAMP_IN_DOCKER=true",
|
|
19
21
|
];
|
|
20
22
|
// Skyramp-generated tests are standalone HTTP tests that never need host repo
|
package/build/tool-phases.js
CHANGED
|
@@ -10,6 +10,9 @@ export const TOOL_PHASE_MAP = {
|
|
|
10
10
|
skyramp_ui_test_generation: "generating",
|
|
11
11
|
skyramp_batch_scenario_test_generation: "generating",
|
|
12
12
|
skyramp_mock_generation: "generating",
|
|
13
|
+
skyramp_batch_mock_generation: "generating",
|
|
14
|
+
skyramp_generate_enriched_integration_test: "generating",
|
|
15
|
+
skyramp_enrich_test_with_mocks: "generating",
|
|
13
16
|
skyramp_execute_test: { before: "maintaining", after: "executing" },
|
|
14
17
|
skyramp_analyze_test_health: "maintaining",
|
|
15
18
|
skyramp_submit_report: "reporting",
|
|
@@ -29,6 +32,7 @@ export const TOOLS_WITHOUT_PHASE = new Set([
|
|
|
29
32
|
"skyramp_init_scan",
|
|
30
33
|
"skyramp_init_workspace",
|
|
31
34
|
"skyramp_one_click_tool",
|
|
35
|
+
"skyramp_setup_local_dev_worker",
|
|
32
36
|
"skyramp_actions",
|
|
33
37
|
"skyramp_start_trace_collection",
|
|
34
38
|
"skyramp_stop_trace_collection",
|
|
@@ -36,4 +40,6 @@ export const TOOLS_WITHOUT_PHASE = new Set([
|
|
|
36
40
|
"skyramp_modularization",
|
|
37
41
|
"skyramp_reuse_code",
|
|
38
42
|
"skyramp_enhance_assertions",
|
|
43
|
+
"skyramp_preflight_mock_check",
|
|
44
|
+
"skyramp_query_proxy_mocks",
|
|
39
45
|
]);
|
|
@@ -12,6 +12,37 @@ const TESTBOT_UI_CHECKS = `
|
|
|
12
12
|
- If no suitable selector exists in the generated file for an assertion you need to add, go back and call \`browser_assert\` on the live page to record it with a valid selector, then re-export and regenerate.
|
|
13
13
|
- **After executing a UI test that documents a bug from \`issuesFound\`**: if it passed when you expected it to fail, the assertions are too weak — add a stronger \`expect()\` that directly targets the buggy behavior. This counts as the single allowed retry under the 2-attempt cap — do NOT re-run more than once.
|
|
14
14
|
`;
|
|
15
|
+
function buildAssertionInstructions(testFile, testType, enhanceType) {
|
|
16
|
+
if (testType === TestType.UI) {
|
|
17
|
+
let instructions = getUIAssertionsPrompt(testFile, enhanceType);
|
|
18
|
+
if (isTestbotEnabled()) {
|
|
19
|
+
instructions += TESTBOT_UI_CHECKS;
|
|
20
|
+
}
|
|
21
|
+
return instructions;
|
|
22
|
+
}
|
|
23
|
+
if (testType === TestType.CONTRACT) {
|
|
24
|
+
return getContractProviderAssertionsPrompt(testFile, enhanceType);
|
|
25
|
+
}
|
|
26
|
+
if (testType === TestType.INTEGRATION) {
|
|
27
|
+
return getIntegrationAssertionsPrompt(testFile, enhanceType);
|
|
28
|
+
}
|
|
29
|
+
throw new Error(`Unsupported testType for ${TOOL_NAME}: ${testType}`);
|
|
30
|
+
}
|
|
31
|
+
function buildAutoApplyInstructions(testFile, testType, enhanceType) {
|
|
32
|
+
const typeSpecificInstructions = buildAssertionInstructions(testFile, testType, enhanceType);
|
|
33
|
+
return [
|
|
34
|
+
`Enhance response body assertions in: \`${testFile}\``,
|
|
35
|
+
`Test type: ${testType} | Context: ${enhanceType}`,
|
|
36
|
+
``,
|
|
37
|
+
`Read the file, apply the type-specific assertion guidance below, and write the file back directly.`,
|
|
38
|
+
`Add assertions after each send_request/sendRequest status-code assertion.`,
|
|
39
|
+
`Use SDK helper: Python \`skyramp.get_response_value(response, "json.path")\`, JS \`getValue(response, "json.path")\`.`,
|
|
40
|
+
`Do NOT restructure, reformat, add comments, or change imports. Only add assertion lines.`,
|
|
41
|
+
``,
|
|
42
|
+
`Type-specific assertion guidance:`,
|
|
43
|
+
typeSpecificInstructions,
|
|
44
|
+
].join("\n");
|
|
45
|
+
}
|
|
15
46
|
const enhanceAssertionsSchema = {
|
|
16
47
|
testFile: z
|
|
17
48
|
.string()
|
|
@@ -24,6 +55,12 @@ const enhanceAssertionsSchema = {
|
|
|
24
55
|
.describe("The context of the enhancement. " +
|
|
25
56
|
"Use 'generation' after generating a new test file (applies to every test function). " +
|
|
26
57
|
"Use 'maintenance' during drift UPDATE (applies only to new or diff-affected functions)."),
|
|
58
|
+
autoApply: z
|
|
59
|
+
.boolean()
|
|
60
|
+
.default(false)
|
|
61
|
+
.describe("When true, returns a compact instruction set (10 lines) that tells the agent to " +
|
|
62
|
+
"read the test file, enhance assertions per standard rules, and write it back directly. " +
|
|
63
|
+
"When false (default), returns the full verbose instruction set."),
|
|
27
64
|
};
|
|
28
65
|
export function registerEnhanceAssertionsTool(server) {
|
|
29
66
|
server.registerTool(TOOL_NAME, {
|
|
@@ -34,26 +71,13 @@ export function registerEnhanceAssertionsTool(server) {
|
|
|
34
71
|
- After updating an existing supported test file during maintenance`,
|
|
35
72
|
inputSchema: enhanceAssertionsSchema,
|
|
36
73
|
}, async (params) => {
|
|
37
|
-
const { testFile, testType, enhanceType } = params;
|
|
74
|
+
const { testFile, testType, enhanceType, autoApply } = params;
|
|
38
75
|
// Stage so testbot includes the generated files in its output commit.
|
|
39
76
|
await stageGeneratedPaths(testFile);
|
|
40
77
|
const enhanceCtx = enhanceType;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (isTestbotEnabled()) {
|
|
45
|
-
instructions += TESTBOT_UI_CHECKS;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
else if (testType === TestType.CONTRACT) {
|
|
49
|
-
instructions = getContractProviderAssertionsPrompt(testFile, enhanceCtx);
|
|
50
|
-
}
|
|
51
|
-
else if (testType === TestType.INTEGRATION) {
|
|
52
|
-
instructions = getIntegrationAssertionsPrompt(testFile, enhanceCtx);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
throw new Error(`Unsupported testType for ${TOOL_NAME}: ${testType}`);
|
|
56
|
-
}
|
|
78
|
+
const instructions = autoApply
|
|
79
|
+
? buildAutoApplyInstructions(testFile, testType, enhanceCtx)
|
|
80
|
+
: buildAssertionInstructions(testFile, testType, enhanceCtx);
|
|
57
81
|
const result = {
|
|
58
82
|
content: [
|
|
59
83
|
{
|
|
@@ -63,7 +87,12 @@ export function registerEnhanceAssertionsTool(server) {
|
|
|
63
87
|
],
|
|
64
88
|
isError: false,
|
|
65
89
|
};
|
|
66
|
-
AnalyticsService.pushMCPToolEvent(TOOL_NAME, undefined,
|
|
90
|
+
AnalyticsService.pushMCPToolEvent(TOOL_NAME, undefined, {
|
|
91
|
+
testFile: params.testFile,
|
|
92
|
+
testType: params.testType,
|
|
93
|
+
enhanceType: params.enhanceType,
|
|
94
|
+
autoApply: Boolean(params.autoApply),
|
|
95
|
+
}).catch(() => { });
|
|
67
96
|
return result;
|
|
68
97
|
});
|
|
69
98
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { ProgrammingLanguage, MockProtocol } from "../types/TestTypes.js";
|
|
3
|
+
/**
|
|
4
|
+
* skyramp_enrich_test_with_mocks
|
|
5
|
+
*
|
|
6
|
+
* Injects mock imports and apply_mock() calls into a generated test file.
|
|
7
|
+
* The mock files are generated by skyramp_mock_generation — this tool wires
|
|
8
|
+
* them into the test so the mocks are applied before the test runs.
|
|
9
|
+
*
|
|
10
|
+
* No data duplication — the test imports from the generated mock files.
|
|
11
|
+
*/
|
|
12
|
+
export declare function registerEnrichTestWithMocksTool(server: McpServer): void;
|
|
13
|
+
declare function injectMockImports(original: string, testFile: string, scenarioName: string, mockFiles: string[], traceComments?: string[], language?: ProgrammingLanguage): string;
|
|
14
|
+
declare function isSupportedMockEnrichmentLanguage(language: ProgrammingLanguage | string): language is ProgrammingLanguage;
|
|
15
|
+
type MockEntry = {
|
|
16
|
+
serviceName: string;
|
|
17
|
+
moduleName: string;
|
|
18
|
+
relativePath: string;
|
|
19
|
+
importPath: string;
|
|
20
|
+
importLabel: string;
|
|
21
|
+
mockFile: string;
|
|
22
|
+
className: string;
|
|
23
|
+
classReference: string;
|
|
24
|
+
targetUrl?: string;
|
|
25
|
+
protocol: MockProtocol;
|
|
26
|
+
};
|
|
27
|
+
declare function buildMockImportBlock(testFile: string, scenarioName: string, mockFiles: string[], traceComments?: string[], language?: ProgrammingLanguage, mockEntries?: MockEntry[]): string;
|
|
28
|
+
export { injectMockImports, buildMockImportBlock, isSupportedMockEnrichmentLanguage };
|