@skyramp/mcp 0.3.2-rc.pom-3 → 0.3.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/commands/commandLibrary.d.ts +1 -0
- package/build/commands/commandLibrary.js +19 -13
- package/build/commands/localDevTestChangesCommand.d.ts +15 -0
- package/build/commands/localDevTestChangesCommand.js +201 -0
- package/build/index.js +80 -6
- package/build/prompts/code-reuse.js +3 -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 +429 -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/pom-aware-code-reuse.js +12 -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 +37 -14
- package/build/prompts/sut-setup/shared.js +16 -12
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
- package/build/prompts/test-recommendation/scopeAssessment.d.ts +24 -7
- package/build/prompts/test-recommendation/scopeAssessment.js +111 -12
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
- package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
- package/build/prompts/testbot/testbot-prompts.js +32 -52
- package/build/recommendation/planRanker.d.ts +15 -2
- package/build/recommendation/planRanker.js +76 -5
- 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/TestGenerationService.d.ts +2 -2
- package/build/services/TestGenerationService.js +39 -21
- package/build/services/containerEnv.js +3 -1
- package/build/tool-phases.js +6 -0
- package/build/tools/code-refactor/codeReuseTool.js +43 -4
- package/build/tools/code-refactor/enhanceAssertionsTool.js +68 -18
- package/build/tools/code-refactor/reuse-outcome.d.ts +109 -0
- package/build/tools/code-refactor/reuse-outcome.js +158 -0
- package/build/tools/code-refactor/reuse-state.d.ts +45 -0
- package/build/tools/code-refactor/reuse-state.js +140 -0
- package/build/tools/enrichTestWithMocksTool.d.ts +28 -0
- package/build/tools/enrichTestWithMocksTool.js +726 -0
- package/build/tools/executeSkyrampTestTool.d.ts +11 -0
- package/build/tools/executeSkyrampTestTool.js +62 -21
- package/build/tools/generate-tests/batchMockGenerationTool.d.ts +106 -0
- package/build/tools/generate-tests/batchMockGenerationTool.js +545 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +25 -23
- package/build/tools/generate-tests/generateContractRestTool.js +2 -2
- package/build/tools/generate-tests/generateE2ERestTool.d.ts +1 -1
- package/build/tools/generate-tests/generateE2ERestTool.js +1 -1
- package/build/tools/generate-tests/generateLoadRestTool.d.ts +1 -1
- package/build/tools/generate-tests/generateLoadRestTool.js +1 -1
- package/build/tools/generate-tests/generateMockRestTool.d.ts +179 -6
- package/build/tools/generate-tests/generateMockRestTool.js +391 -22
- package/build/tools/generate-tests/loadTestSchema.d.ts +1 -1
- package/build/tools/generate-tests/planGuard.js +2 -22
- package/build/tools/generateEnrichedIntegrationTestTool.d.ts +25 -0
- package/build/tools/generateEnrichedIntegrationTestTool.js +235 -0
- package/build/tools/localDevWorkerComposeTool.d.ts +24 -0
- package/build/tools/localDevWorkerComposeTool.js +264 -0
- package/build/tools/one-click/oneClickTool.d.ts +13 -0
- package/build/tools/one-click/oneClickTool.js +195 -24
- 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/runExistingTestsTool.d.ts +31 -0
- package/build/tools/runExistingTestsTool.js +214 -13
- package/build/tools/submitReportTool.js +38 -3
- package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
- package/build/tools/test-management/analyzeChangesTool.js +63 -40
- package/build/tools/test-management/registerTestPlanTool.js +55 -7
- package/build/tools/trace/startTraceCollectionTool.js +3 -3
- package/build/types/FrontendIntegration.d.ts +3 -0
- package/build/types/FrontendIntegration.js +3 -0
- package/build/types/OneClickCommands.d.ts +1 -1
- package/build/types/Recommendation.d.ts +20 -0
- package/build/types/Recommendation.js +32 -6
- package/build/types/RepositoryAnalysis.d.ts +131 -14
- package/build/types/RepositoryAnalysis.js +16 -2
- package/build/types/ReuseOutcome.d.ts +63 -0
- package/build/types/ReuseOutcome.js +33 -0
- package/build/types/TestExecution.d.ts +1 -0
- package/build/types/TestTypes.d.ts +25 -7
- package/build/types/TestTypes.js +22 -6
- package/build/types/TestbotReport.d.ts +7 -0
- package/build/types/index.d.ts +2 -0
- package/build/types/index.js +1 -0
- package/build/utils/AnalysisStateManager.d.ts +26 -0
- package/build/utils/AnalysisStateManager.js +31 -1
- package/build/utils/analyze-openapi.js +18 -1
- package/build/utils/branchDiff.d.ts +17 -1
- package/build/utils/branchDiff.js +99 -14
- package/build/utils/featureFlags.d.ts +31 -0
- package/build/utils/featureFlags.js +37 -0
- package/build/utils/frontendIntegration.js +8 -1
- package/build/utils/grpcMockValidation.d.ts +1 -0
- package/build/utils/grpcMockValidation.js +49 -0
- package/build/utils/httpMethodValidation.d.ts +4 -0
- package/build/utils/httpMethodValidation.js +15 -0
- package/build/utils/logger.js +1 -1
- package/build/utils/mockCompatibility.d.ts +49 -0
- package/build/utils/mockCompatibility.js +82 -0
- package/build/utils/pom-scope/pom-files.js +7 -0
- package/build/utils/pom-verify/bindings.js +11 -1
- package/build/utils/pom-verify/re-exports.d.ts +10 -0
- package/build/utils/pom-verify/re-exports.js +61 -0
- package/build/utils/pom-verify/resolve.d.ts +4 -1
- package/build/utils/pom-verify/resolve.js +7 -4
- package/build/utils/pom-verify/verify.d.ts +5 -0
- package/build/utils/pom-verify/verify.js +25 -3
- package/build/utils/progress.js +10 -5
- package/build/utils/proxy-terminal.js +3 -3
- package/build/utils/routeParsers.d.ts +3 -9
- package/build/utils/routeParsers.js +79 -4
- package/build/utils/utils.js +2 -2
- package/build/utils/versions.d.ts +4 -3
- package/build/utils/versions.js +3 -1
- package/build/utils/workspaceAuth.d.ts +46 -0
- package/build/utils/workspaceAuth.js +156 -1
- package/build/workspace/testSuites.d.ts +2 -1
- package/build/workspace/testSuites.js +1 -1
- package/build/workspace/workspace.d.ts +108 -32
- package/build/workspace/workspace.js +32 -4
- package/package.json +5 -2
- package/build/adapters/jestAdapter.test.d.ts +0 -1
- package/build/adapters/jestAdapter.test.js +0 -93
- package/build/adapters/mochaAdapter.test.d.ts +0 -1
- package/build/adapters/mochaAdapter.test.js +0 -63
- package/build/adapters/playwrightAdapter.test.d.ts +0 -1
- package/build/adapters/playwrightAdapter.test.js +0 -169
- package/build/adapters/pytestAdapter.test.d.ts +0 -1
- package/build/adapters/pytestAdapter.test.js +0 -90
- package/build/prompts/code-reuse.test.d.ts +0 -1
- package/build/prompts/code-reuse.test.js +0 -62
- package/build/prompts/pom-aware-code-reuse.test.d.ts +0 -1
- package/build/prompts/pom-aware-code-reuse.test.js +0 -11
- package/build/prompts/test-maintenance/drift-analysis-prompt.test.d.ts +0 -1
- package/build/prompts/test-maintenance/drift-analysis-prompt.test.js +0 -51
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.d.ts +0 -1
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +0 -264
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.d.ts +0 -1
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +0 -126
- package/build/prompts/test-recommendation/promptPlan.test.d.ts +0 -1
- package/build/prompts/test-recommendation/promptPlan.test.js +0 -336
- package/build/prompts/test-recommendation/scopeAssessment.test.d.ts +0 -1
- package/build/prompts/test-recommendation/scopeAssessment.test.js +0 -371
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.d.ts +0 -1
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +0 -1925
- package/build/prompts/testbot/testbot-prompts.test.d.ts +0 -1
- package/build/prompts/testbot/testbot-prompts.test.js +0 -451
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.d.ts +0 -1
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.js +0 -138
- package/build/recommendation/budgeters/fixedNBudgeter.test.d.ts +0 -1
- package/build/recommendation/budgeters/fixedNBudgeter.test.js +0 -66
- package/build/recommendation/discriminators.test.d.ts +0 -1
- package/build/recommendation/discriminators.test.js +0 -324
- package/build/recommendation/diversity.test.d.ts +0 -1
- package/build/recommendation/diversity.test.js +0 -77
- package/build/recommendation/planRanker.test.d.ts +0 -1
- package/build/recommendation/planRanker.test.js +0 -110
- package/build/resources/testbotResource.test.d.ts +0 -1
- package/build/resources/testbotResource.test.js +0 -44
- package/build/services/AnalyticsService.test.d.ts +0 -1
- package/build/services/AnalyticsService.test.js +0 -86
- package/build/services/ScenarioGenerationService.integration.test.d.ts +0 -1
- package/build/services/ScenarioGenerationService.integration.test.js +0 -162
- package/build/services/ScenarioGenerationService.test.d.ts +0 -1
- package/build/services/ScenarioGenerationService.test.js +0 -414
- package/build/services/TestDiscoveryService.test.d.ts +0 -1
- package/build/services/TestDiscoveryService.test.js +0 -983
- package/build/services/TestExecutionService.test.d.ts +0 -1
- package/build/services/TestExecutionService.test.js +0 -1032
- package/build/services/TestGenerationService.test.d.ts +0 -1
- package/build/services/TestGenerationService.test.js +0 -578
- package/build/tool-phase-coverage.test.d.ts +0 -1
- package/build/tool-phase-coverage.test.js +0 -49
- package/build/tools/code-refactor/codeReuseTool.test.d.ts +0 -1
- package/build/tools/code-refactor/codeReuseTool.test.js +0 -572
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +0 -433
- package/build/tools/generate-tests/generateContractRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateContractRestTool.test.js +0 -142
- package/build/tools/generate-tests/generateIntegrationRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.test.js +0 -159
- package/build/tools/generate-tests/generateLoadRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateLoadRestTool.test.js +0 -169
- package/build/tools/generate-tests/generateUIRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateUIRestTool.test.js +0 -32
- package/build/tools/generate-tests/planGuard.test.d.ts +0 -1
- package/build/tools/generate-tests/planGuard.test.js +0 -185
- package/build/tools/generate-tests/scenarioLint.test.d.ts +0 -1
- package/build/tools/generate-tests/scenarioLint.test.js +0 -100
- package/build/tools/runExistingTestsTool.test.d.ts +0 -1
- package/build/tools/runExistingTestsTool.test.js +0 -379
- package/build/tools/submitReportTool.test.d.ts +0 -1
- package/build/tools/submitReportTool.test.js +0 -1428
- package/build/tools/test-management/actionsTool.test.d.ts +0 -1
- package/build/tools/test-management/actionsTool.test.js +0 -436
- package/build/tools/test-management/analyzeChangesTool.test.d.ts +0 -1
- package/build/tools/test-management/analyzeChangesTool.test.js +0 -522
- package/build/tools/test-management/analyzeTestHealthTool.test.d.ts +0 -1
- package/build/tools/test-management/analyzeTestHealthTool.test.js +0 -385
- package/build/tools/test-management/registerTestPlanTool.test.d.ts +0 -1
- package/build/tools/test-management/registerTestPlanTool.test.js +0 -296
- package/build/tools/trace/resolveSaveStoragePath.test.d.ts +0 -1
- package/build/tools/trace/resolveSaveStoragePath.test.js +0 -17
- package/build/tools/trace/resolveSessionPaths.test.d.ts +0 -1
- package/build/tools/trace/resolveSessionPaths.test.js +0 -104
- package/build/tools/trace/sessionState.test.d.ts +0 -1
- package/build/tools/trace/sessionState.test.js +0 -17
- package/build/tools/workspace/initializeWorkspaceTool.test.d.ts +0 -1
- package/build/tools/workspace/initializeWorkspaceTool.test.js +0 -139
- package/build/tools/workspace/serviceUpsert.test.d.ts +0 -1
- package/build/tools/workspace/serviceUpsert.test.js +0 -50
- package/build/utils/AnalysisStateManager.test.d.ts +0 -1
- package/build/utils/AnalysisStateManager.test.js +0 -134
- package/build/utils/dartRouteExtractor.test.d.ts +0 -1
- package/build/utils/dartRouteExtractor.test.js +0 -307
- package/build/utils/docker.test.d.ts +0 -1
- package/build/utils/docker.test.js +0 -114
- package/build/utils/featureFlags.test.d.ts +0 -1
- package/build/utils/featureFlags.test.js +0 -81
- package/build/utils/fileWalk.test.d.ts +0 -1
- package/build/utils/fileWalk.test.js +0 -252
- package/build/utils/frontendIntegration.test.d.ts +0 -1
- package/build/utils/frontendIntegration.test.js +0 -229
- package/build/utils/frontendSelectors.test.d.ts +0 -1
- package/build/utils/frontendSelectors.test.js +0 -118
- package/build/utils/gitStaging.test.d.ts +0 -1
- package/build/utils/gitStaging.test.js +0 -111
- package/build/utils/httpDefaults.test.d.ts +0 -1
- package/build/utils/httpDefaults.test.js +0 -21
- package/build/utils/importerHop.test.d.ts +0 -1
- package/build/utils/importerHop.test.js +0 -469
- package/build/utils/pathAffinityClassification.test.d.ts +0 -1
- package/build/utils/pathAffinityClassification.test.js +0 -208
- package/build/utils/planMatchKeys.test.d.ts +0 -1
- package/build/utils/planMatchKeys.test.js +0 -123
- package/build/utils/pom-scope/index.test.d.ts +0 -1
- package/build/utils/pom-scope/index.test.js +0 -278
- package/build/utils/pom-scope/pom-files.test.d.ts +0 -1
- package/build/utils/pom-scope/pom-files.test.js +0 -29
- package/build/utils/pom-scope/scoring.test.d.ts +0 -1
- package/build/utils/pom-scope/scoring.test.js +0 -39
- package/build/utils/pom-scope/selector-extractor.test.d.ts +0 -1
- package/build/utils/pom-scope/selector-extractor.test.js +0 -67
- package/build/utils/pom-verify/bindings.test.d.ts +0 -1
- package/build/utils/pom-verify/bindings.test.js +0 -164
- package/build/utils/pom-verify/calls.test.d.ts +0 -1
- package/build/utils/pom-verify/calls.test.js +0 -61
- package/build/utils/pom-verify/resolve.test.d.ts +0 -1
- package/build/utils/pom-verify/resolve.test.js +0 -114
- package/build/utils/pom-verify/verify.test.d.ts +0 -1
- package/build/utils/pom-verify/verify.test.js +0 -374
- package/build/utils/pr-comment-parser.test.d.ts +0 -1
- package/build/utils/pr-comment-parser.test.js +0 -428
- package/build/utils/progress.test.d.ts +0 -1
- package/build/utils/progress.test.js +0 -37
- package/build/utils/projectMetadata.test.d.ts +0 -1
- package/build/utils/projectMetadata.test.js +0 -172
- package/build/utils/pythonMountPrefixes.test.d.ts +0 -1
- package/build/utils/pythonMountPrefixes.test.js +0 -113
- package/build/utils/repoScanner.test.d.ts +0 -1
- package/build/utils/repoScanner.test.js +0 -190
- package/build/utils/reportVerification.test.d.ts +0 -1
- package/build/utils/reportVerification.test.js +0 -185
- package/build/utils/routeParsers.test.d.ts +0 -1
- package/build/utils/routeParsers.test.js +0 -1011
- package/build/utils/scenarioDrafting.test.d.ts +0 -1
- package/build/utils/scenarioDrafting.test.js +0 -876
- package/build/utils/sourceRouteExtractor.test.d.ts +0 -1
- package/build/utils/sourceRouteExtractor.test.js +0 -738
- package/build/utils/telemetry.test.d.ts +0 -1
- package/build/utils/telemetry.test.js +0 -70
- package/build/utils/trace-parser.test.d.ts +0 -6
- package/build/utils/trace-parser.test.js +0 -140
- package/build/utils/uiPageEnumerator.test.d.ts +0 -1
- package/build/utils/uiPageEnumerator.test.js +0 -824
- package/build/utils/utils.test.d.ts +0 -1
- package/build/utils/utils.test.js +0 -103
- package/build/utils/walkerCharacterization.test.d.ts +0 -1
- package/build/utils/walkerCharacterization.test.js +0 -233
- package/build/utils/workspaceAuth.test.d.ts +0 -1
- package/build/utils/workspaceAuth.test.js +0 -260
- package/build/workspace/testSuites.test.d.ts +0 -1
- package/build/workspace/testSuites.test.js +0 -23
- package/build/workspace/workspace.test.d.ts +0 -1
- package/build/workspace/workspace.test.js +0 -264
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck - Jest ESM type inference issues
|
|
2
|
-
import { jest } from "@jest/globals";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
jest.unstable_mockModule("../../services/ScenarioGenerationService.js", () => ({
|
|
5
|
-
ScenarioGenerationService: jest.fn().mockImplementation(() => ({
|
|
6
|
-
generateTraceRequestFromInput: jest.fn().mockReturnValue(null),
|
|
7
|
-
})),
|
|
8
|
-
}));
|
|
9
|
-
jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({ AnalyticsService: { pushMCPToolEvent: jest.fn().mockResolvedValue(undefined) } }));
|
|
10
|
-
jest.unstable_mockModule("../../utils/workspaceAuth.js", () => ({
|
|
11
|
-
getWorkspaceAuthConfig: jest.fn().mockResolvedValue({ authHeader: undefined, authType: "none" }),
|
|
12
|
-
WorkspaceAuthType: { None: "none" },
|
|
13
|
-
getDefaultAuthHeader: jest.fn().mockReturnValue(""),
|
|
14
|
-
isAuthorizationHeaderName: jest.fn().mockReturnValue(false),
|
|
15
|
-
getAuthScheme: jest.fn().mockReturnValue(""),
|
|
16
|
-
readWorkspaceConfigRaw: jest.fn().mockResolvedValue(null),
|
|
17
|
-
}));
|
|
18
|
-
jest.unstable_mockModule("../../utils/logger.js", () => ({ logger: { info: jest.fn(), warning: jest.fn(), error: jest.fn() } }));
|
|
19
|
-
jest.unstable_mockModule("../../prompts/personas.js", () => ({ getPersonaPrefix: () => "" }));
|
|
20
|
-
const { stepSchema, registerBatchScenarioTestTool } = await import("./generateBatchScenarioRestTool.js");
|
|
21
|
-
const { ScenarioGenerationService } = await import("../../services/ScenarioGenerationService.js");
|
|
22
|
-
/** Build a minimal mock McpServer, register the tool, and return the captured handler. */
|
|
23
|
-
function captureHandler() {
|
|
24
|
-
let capturedHandler;
|
|
25
|
-
const mockServer = {
|
|
26
|
-
registerTool: jest.fn((_name, _meta, handler) => {
|
|
27
|
-
capturedHandler = handler;
|
|
28
|
-
}),
|
|
29
|
-
};
|
|
30
|
-
registerBatchScenarioTestTool(mockServer);
|
|
31
|
-
return capturedHandler;
|
|
32
|
-
}
|
|
33
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
-
// stepSchema — requestBody validation
|
|
35
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
-
describe("stepSchema — requestBody validation", () => {
|
|
37
|
-
it("accepts a POST step with a non-empty requestBody", () => {
|
|
38
|
-
const result = stepSchema.safeParse({
|
|
39
|
-
method: "POST",
|
|
40
|
-
path: "/api/v1/products",
|
|
41
|
-
requestBody: JSON.stringify({ name: "Widget-123", price: 9.99 }),
|
|
42
|
-
});
|
|
43
|
-
expect(result.success).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
it("rejects a POST step with an empty requestBody {}", () => {
|
|
46
|
-
const result = stepSchema.safeParse({
|
|
47
|
-
method: "POST",
|
|
48
|
-
path: "/api/v1/products",
|
|
49
|
-
requestBody: "{}",
|
|
50
|
-
});
|
|
51
|
-
expect(result.success).toBe(false);
|
|
52
|
-
const issue = result.error?.issues.find(i => i.path.includes("requestBody"));
|
|
53
|
-
expect(issue?.message).toContain("empty object {}");
|
|
54
|
-
expect(issue?.message).toContain("POST");
|
|
55
|
-
});
|
|
56
|
-
it("rejects a PATCH step with an empty requestBody {}", () => {
|
|
57
|
-
const result = stepSchema.safeParse({
|
|
58
|
-
method: "PATCH",
|
|
59
|
-
path: "/api/v1/orders/1",
|
|
60
|
-
requestBody: "{}",
|
|
61
|
-
});
|
|
62
|
-
expect(result.success).toBe(false);
|
|
63
|
-
expect(result.error?.issues.some(i => i.message.includes("empty object {}"))).toBe(true);
|
|
64
|
-
});
|
|
65
|
-
it("rejects a PUT step with an empty requestBody {}", () => {
|
|
66
|
-
const result = stepSchema.safeParse({
|
|
67
|
-
method: "PUT",
|
|
68
|
-
path: "/api/v1/products/1",
|
|
69
|
-
requestBody: "{}",
|
|
70
|
-
});
|
|
71
|
-
expect(result.success).toBe(false);
|
|
72
|
-
const issue = result.error?.issues.find(i => i.path.includes("requestBody"));
|
|
73
|
-
expect(issue?.message).toContain("empty object {}");
|
|
74
|
-
expect(issue?.message).toContain("PUT");
|
|
75
|
-
});
|
|
76
|
-
it("accepts a POST step with requestBody omitted entirely", () => {
|
|
77
|
-
const result = stepSchema.safeParse({
|
|
78
|
-
method: "POST",
|
|
79
|
-
path: "/api/v1/products",
|
|
80
|
-
});
|
|
81
|
-
expect(result.success).toBe(true);
|
|
82
|
-
});
|
|
83
|
-
it("does not throw on requestBody: 'null' (valid JSON null)", () => {
|
|
84
|
-
const result = stepSchema.safeParse({
|
|
85
|
-
method: "POST",
|
|
86
|
-
path: "/api/v1/products",
|
|
87
|
-
requestBody: "null",
|
|
88
|
-
});
|
|
89
|
-
expect(result.success).toBe(true);
|
|
90
|
-
});
|
|
91
|
-
it("accepts a GET step with no requestBody", () => {
|
|
92
|
-
const result = stepSchema.safeParse({
|
|
93
|
-
method: "GET",
|
|
94
|
-
path: "/api/v1/products/1",
|
|
95
|
-
});
|
|
96
|
-
expect(result.success).toBe(true);
|
|
97
|
-
});
|
|
98
|
-
it("accepts a DELETE step with no requestBody", () => {
|
|
99
|
-
const result = stepSchema.safeParse({
|
|
100
|
-
method: "DELETE",
|
|
101
|
-
path: "/api/v1/products/1",
|
|
102
|
-
});
|
|
103
|
-
expect(result.success).toBe(true);
|
|
104
|
-
});
|
|
105
|
-
it("does not reject a GET step that happens to have an empty requestBody", () => {
|
|
106
|
-
const result = stepSchema.safeParse({
|
|
107
|
-
method: "GET",
|
|
108
|
-
path: "/api/v1/products",
|
|
109
|
-
requestBody: "{}",
|
|
110
|
-
});
|
|
111
|
-
expect(result.success).toBe(true);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
115
|
-
// GraphQL step rejection (Change 10b)
|
|
116
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
-
describe("generateBatchScenarioRestTool — GraphQL step rejection (Change 10b)", () => {
|
|
118
|
-
let handler;
|
|
119
|
-
const baseParams = {
|
|
120
|
-
scenarioName: "test_scenario",
|
|
121
|
-
destination: "localhost",
|
|
122
|
-
outputDir: "/tmp/skyramp-test",
|
|
123
|
-
};
|
|
124
|
-
beforeEach(() => {
|
|
125
|
-
jest.clearAllMocks();
|
|
126
|
-
handler = captureHandler();
|
|
127
|
-
});
|
|
128
|
-
it("rejects a step list containing /graphql", async () => {
|
|
129
|
-
const result = await handler({
|
|
130
|
-
...baseParams,
|
|
131
|
-
steps: [{ method: "POST", path: "/graphql", requestBody: '{"query":"{ users { id } }"}' }],
|
|
132
|
-
});
|
|
133
|
-
expect(result.isError).toBe(true);
|
|
134
|
-
expect(result.content[0].text).toContain("GraphQL endpoints are not supported");
|
|
135
|
-
expect(result.content[0].text).toContain("POST /graphql");
|
|
136
|
-
});
|
|
137
|
-
it("rejects a step list containing /api/graphql (nested segment)", async () => {
|
|
138
|
-
const result = await handler({
|
|
139
|
-
...baseParams,
|
|
140
|
-
steps: [
|
|
141
|
-
{ method: "GET", path: "/api/v1/users" },
|
|
142
|
-
{ method: "POST", path: "/api/graphql", requestBody: '{"query":"{ id }"}' },
|
|
143
|
-
],
|
|
144
|
-
});
|
|
145
|
-
expect(result.isError).toBe(true);
|
|
146
|
-
expect(result.content[0].text).toContain("GraphQL endpoints are not supported");
|
|
147
|
-
expect(result.content[0].text).toContain("POST /api/graphql");
|
|
148
|
-
});
|
|
149
|
-
it("does not reject a path whose segment is 'graphql-config' (not an exact segment match)", async () => {
|
|
150
|
-
// 'graphql-config' !== 'graphql' — the guard uses exact segment comparison.
|
|
151
|
-
// The GraphQL rejection message must never appear, regardless of whether the
|
|
152
|
-
// handler succeeds or fails for some other reason.
|
|
153
|
-
const result = await handler({
|
|
154
|
-
...baseParams,
|
|
155
|
-
steps: [{ method: "GET", path: "/api/v1/graphql-config" }],
|
|
156
|
-
});
|
|
157
|
-
expect(result.content[0].text).not.toContain("GraphQL endpoints are not supported");
|
|
158
|
-
});
|
|
159
|
-
it("does not throw on a non-string path (typeof guard)", async () => {
|
|
160
|
-
// typeof guard returns false for non-string — no crash from .replace()
|
|
161
|
-
const result = await handler({
|
|
162
|
-
...baseParams,
|
|
163
|
-
steps: [{ method: "GET", path: undefined }],
|
|
164
|
-
});
|
|
165
|
-
expect(result).toBeDefined();
|
|
166
|
-
if (result.isError) {
|
|
167
|
-
expect(result.content[0].text).not.toContain("Cannot read");
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
172
|
-
// Spec path validation (Change 3)
|
|
173
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
174
|
-
describe("generateBatchScenarioRestTool — spec path validation (Change 3)", () => {
|
|
175
|
-
let handler;
|
|
176
|
-
let readFileSyncSpy;
|
|
177
|
-
const fakeSpec = JSON.stringify({
|
|
178
|
-
openapi: "3.0.0",
|
|
179
|
-
paths: {
|
|
180
|
-
"/api/v1/users": {},
|
|
181
|
-
"/api/v1/users/{id}": {},
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
const baseParams = {
|
|
185
|
-
scenarioName: "test_scenario",
|
|
186
|
-
destination: "localhost",
|
|
187
|
-
outputDir: "/tmp/skyramp-test",
|
|
188
|
-
apiSchema: "/tmp/openapi.json",
|
|
189
|
-
};
|
|
190
|
-
let writeFileSyncSpy;
|
|
191
|
-
beforeEach(() => {
|
|
192
|
-
jest.clearAllMocks();
|
|
193
|
-
handler = captureHandler();
|
|
194
|
-
readFileSyncSpy = jest.spyOn(fs, "readFileSync").mockReturnValue(fakeSpec);
|
|
195
|
-
writeFileSyncSpy = jest.spyOn(fs, "writeFileSync").mockImplementation(() => { });
|
|
196
|
-
// Return a valid trace so tests reach the success path (where the warning is appended)
|
|
197
|
-
ScenarioGenerationService.mockImplementation(() => ({
|
|
198
|
-
generateTraceRequestFromInput: jest.fn().mockReturnValue({ method: "GET", path: "/mock" }),
|
|
199
|
-
}));
|
|
200
|
-
});
|
|
201
|
-
afterEach(() => {
|
|
202
|
-
readFileSyncSpy.mockRestore();
|
|
203
|
-
writeFileSyncSpy.mockRestore();
|
|
204
|
-
});
|
|
205
|
-
it("warns (does not hard-reject) when step paths are not in the OpenAPI spec", async () => {
|
|
206
|
-
// Spec may lag code — missing path != phantom path, so we warn and proceed.
|
|
207
|
-
const result = await handler({
|
|
208
|
-
...baseParams,
|
|
209
|
-
steps: [{ method: "GET", path: "/api/v1/phantom-endpoint" }],
|
|
210
|
-
});
|
|
211
|
-
expect(result.isError).toBeFalsy();
|
|
212
|
-
expect(result.content[0].text).toContain("Spec warning");
|
|
213
|
-
expect(result.content[0].text).toContain("/api/v1/phantom-endpoint");
|
|
214
|
-
expect(result.content[0].text).toContain("Known spec paths");
|
|
215
|
-
});
|
|
216
|
-
it("includes no spec warning when step path is present in the OpenAPI spec", async () => {
|
|
217
|
-
const result = await handler({
|
|
218
|
-
...baseParams,
|
|
219
|
-
steps: [{ method: "GET", path: "/api/v1/users" }],
|
|
220
|
-
});
|
|
221
|
-
expect(result.content[0].text).not.toContain("Spec warning");
|
|
222
|
-
});
|
|
223
|
-
it("does not warn for Express-style :param paths that normalise to a spec path", async () => {
|
|
224
|
-
const result = await handler({
|
|
225
|
-
...baseParams,
|
|
226
|
-
steps: [{ method: "GET", path: "/api/v1/users/:id" }],
|
|
227
|
-
});
|
|
228
|
-
expect(result.content[0].text).not.toContain("Spec warning");
|
|
229
|
-
});
|
|
230
|
-
it("does not throw on a non-string path (typeof guard)", async () => {
|
|
231
|
-
const result = await handler({
|
|
232
|
-
...baseParams,
|
|
233
|
-
steps: [{ method: "GET", path: undefined }],
|
|
234
|
-
});
|
|
235
|
-
expect(result).toBeDefined();
|
|
236
|
-
expect(result.content[0].text).not.toContain("TypeError");
|
|
237
|
-
expect(result.content[0].text).not.toContain("Cannot read");
|
|
238
|
-
});
|
|
239
|
-
it("includes known spec paths hint in the warning message", async () => {
|
|
240
|
-
const result = await handler({
|
|
241
|
-
...baseParams,
|
|
242
|
-
steps: [{ method: "POST", path: "/does/not/exist" }],
|
|
243
|
-
});
|
|
244
|
-
expect(result.isError).toBeFalsy();
|
|
245
|
-
expect(result.content[0].text).toContain("/api/v1/users");
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
describe("generateBatchScenarioRestTool — spec URL fetch safety (comment 3203006665)", () => {
|
|
249
|
-
let handler;
|
|
250
|
-
const baseParams = {
|
|
251
|
-
scenarioName: "test_scenario",
|
|
252
|
-
destination: "localhost",
|
|
253
|
-
outputDir: "/tmp/skyramp-test",
|
|
254
|
-
apiSchema: "https://example.com/openapi.json",
|
|
255
|
-
steps: [{ method: "GET", path: "/api/v1/users" }],
|
|
256
|
-
};
|
|
257
|
-
beforeEach(() => {
|
|
258
|
-
jest.clearAllMocks();
|
|
259
|
-
handler = captureHandler();
|
|
260
|
-
});
|
|
261
|
-
it("skips spec check (no warning emitted) when URL returns non-2xx", async () => {
|
|
262
|
-
global.fetch = jest.fn().mockResolvedValue({
|
|
263
|
-
ok: false,
|
|
264
|
-
status: 404,
|
|
265
|
-
statusText: "Not Found",
|
|
266
|
-
text: async () => "<html>Not Found</html>",
|
|
267
|
-
});
|
|
268
|
-
const result = await handler({ ...baseParams });
|
|
269
|
-
// Non-2xx throws → caught → spec check skipped entirely, no warning in output
|
|
270
|
-
expect(result.content[0].text).not.toContain("Spec warning");
|
|
271
|
-
});
|
|
272
|
-
it("skips spec check (no warning emitted) when spec has no paths", async () => {
|
|
273
|
-
global.fetch = jest.fn().mockResolvedValue({
|
|
274
|
-
ok: true,
|
|
275
|
-
status: 200,
|
|
276
|
-
statusText: "OK",
|
|
277
|
-
text: async () => JSON.stringify({ openapi: "3.0.0", info: { title: "Test", version: "1.0" } }),
|
|
278
|
-
});
|
|
279
|
-
const result = await handler({ ...baseParams });
|
|
280
|
-
expect(result.content[0].text).not.toContain("Spec warning");
|
|
281
|
-
});
|
|
282
|
-
afterEach(() => {
|
|
283
|
-
// Restore fetch
|
|
284
|
-
delete global.fetch;
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
describe("stepSchema uniqueFields validation (SKYR-3882)", () => {
|
|
288
|
-
const base = { method: "POST", path: "/api/v1/providers/", requestBody: '{"name":"x"}' };
|
|
289
|
-
it("accepts uniqueFields on a POST with a body", () => {
|
|
290
|
-
expect(stepSchema.safeParse({ ...base, uniqueFields: ["name"] }).success).toBe(true);
|
|
291
|
-
});
|
|
292
|
-
it("rejects an empty uniqueFields array", () => {
|
|
293
|
-
expect(stepSchema.safeParse({ ...base, uniqueFields: [] }).success).toBe(false);
|
|
294
|
-
});
|
|
295
|
-
it("rejects empty-string paths in uniqueFields", () => {
|
|
296
|
-
expect(stepSchema.safeParse({ ...base, uniqueFields: [""] }).success).toBe(false);
|
|
297
|
-
});
|
|
298
|
-
it("rejects uniqueFields on a non-body method (GET)", () => {
|
|
299
|
-
expect(stepSchema.safeParse({ method: "GET", path: "/x", uniqueFields: ["name"] }).success).toBe(false);
|
|
300
|
-
});
|
|
301
|
-
it("rejects uniqueFields on a POST with no requestBody", () => {
|
|
302
|
-
expect(stepSchema.safeParse({ method: "POST", path: "/api/v1/providers/", uniqueFields: ["name"] }).success).toBe(false);
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
306
|
-
// SKYR-3884 write-side validations
|
|
307
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
308
|
-
describe("stepSchema — responseBody JSON validation (SKYR-3884)", () => {
|
|
309
|
-
it("rejects a responseBody that is not valid JSON", () => {
|
|
310
|
-
const result = stepSchema.safeParse({
|
|
311
|
-
method: "POST",
|
|
312
|
-
path: "/api/v1/products",
|
|
313
|
-
requestBody: JSON.stringify({ name: "W" }),
|
|
314
|
-
responseBody: "{id: 1, oops",
|
|
315
|
-
});
|
|
316
|
-
expect(result.success).toBe(false);
|
|
317
|
-
const issue = result.error?.issues.find((i) => i.path.includes("responseBody"));
|
|
318
|
-
expect(issue?.message).toContain("valid JSON");
|
|
319
|
-
});
|
|
320
|
-
it("accepts a valid JSON responseBody and an omitted one", () => {
|
|
321
|
-
expect(stepSchema.safeParse({
|
|
322
|
-
method: "POST",
|
|
323
|
-
path: "/api/v1/products",
|
|
324
|
-
requestBody: JSON.stringify({ name: "W" }),
|
|
325
|
-
responseBody: JSON.stringify({ id: 1 }),
|
|
326
|
-
}).success).toBe(true);
|
|
327
|
-
expect(stepSchema.safeParse({ method: "GET", path: "/api/v1/products" }).success).toBe(true);
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
describe("stepSchema — uniqueFields paths must resolve in requestBody (SKYR-3884)", () => {
|
|
331
|
-
it("rejects a uniqueFields path that does not resolve in the requestBody", () => {
|
|
332
|
-
const result = stepSchema.safeParse({
|
|
333
|
-
method: "POST",
|
|
334
|
-
path: "/api/v1/products",
|
|
335
|
-
requestBody: JSON.stringify({ name: "W", slug: "w" }),
|
|
336
|
-
uniqueFields: ["nmae"],
|
|
337
|
-
});
|
|
338
|
-
expect(result.success).toBe(false);
|
|
339
|
-
const issue = result.error?.issues.find((i) => i.path.includes("uniqueFields"));
|
|
340
|
-
expect(issue?.message).toContain("nmae");
|
|
341
|
-
});
|
|
342
|
-
it("accepts top-level, nested, and array-indexed uniqueFields paths that resolve", () => {
|
|
343
|
-
const result = stepSchema.safeParse({
|
|
344
|
-
method: "POST",
|
|
345
|
-
path: "/api/v1/products",
|
|
346
|
-
requestBody: JSON.stringify({ name: "W", nested: { slug: "w" }, items: [{ sku: "S1" }] }),
|
|
347
|
-
uniqueFields: ["name", "nested.slug", "items.0.sku"],
|
|
348
|
-
});
|
|
349
|
-
expect(result.success).toBe(true);
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
describe("generateBatchScenarioRestTool — chaining lint (SKYR-3884)", () => {
|
|
353
|
-
let handler;
|
|
354
|
-
let writeFileSyncSpy;
|
|
355
|
-
const baseParams = {
|
|
356
|
-
scenarioName: "test_scenario",
|
|
357
|
-
destination: "localhost",
|
|
358
|
-
outputDir: "/tmp/skyramp-test",
|
|
359
|
-
};
|
|
360
|
-
beforeEach(() => {
|
|
361
|
-
jest.clearAllMocks();
|
|
362
|
-
handler = captureHandler();
|
|
363
|
-
writeFileSyncSpy = jest.spyOn(fs, "writeFileSync").mockImplementation(() => { });
|
|
364
|
-
// The lint inspects the BUILT TraceRequests (the artifact the Go reader
|
|
365
|
-
// consumes), so the mock must pass the step through in TraceRequest shape.
|
|
366
|
-
ScenarioGenerationService.mockImplementation(() => ({
|
|
367
|
-
generateTraceRequestFromInput: jest.fn((p) => ({
|
|
368
|
-
Method: p.method,
|
|
369
|
-
Path: p.path,
|
|
370
|
-
RequestBody: p.requestBody ?? "",
|
|
371
|
-
ResponseBody: p.responseBody ?? "",
|
|
372
|
-
})),
|
|
373
|
-
}));
|
|
374
|
-
});
|
|
375
|
-
afterEach(() => {
|
|
376
|
-
writeFileSyncSpy.mockRestore();
|
|
377
|
-
});
|
|
378
|
-
it("hard-rejects a scenario whose FK reference is produced only by a later step", async () => {
|
|
379
|
-
const result = await handler({
|
|
380
|
-
...baseParams,
|
|
381
|
-
steps: [
|
|
382
|
-
{
|
|
383
|
-
method: "POST",
|
|
384
|
-
path: "/api/circuits/provider-accounts/",
|
|
385
|
-
requestBody: JSON.stringify({ provider: 1, name: "A" }),
|
|
386
|
-
responseBody: JSON.stringify({ id: 7 }),
|
|
387
|
-
},
|
|
388
|
-
{
|
|
389
|
-
method: "POST",
|
|
390
|
-
path: "/api/circuits/providers/",
|
|
391
|
-
requestBody: JSON.stringify({ name: "P" }),
|
|
392
|
-
responseBody: JSON.stringify({ id: 1 }),
|
|
393
|
-
},
|
|
394
|
-
],
|
|
395
|
-
});
|
|
396
|
-
expect(result.isError).toBe(true);
|
|
397
|
-
expect(result.content[0].text).toContain("later step");
|
|
398
|
-
expect(writeFileSyncSpy).not.toHaveBeenCalled();
|
|
399
|
-
});
|
|
400
|
-
it("appends a chaining warning (does not reject) for unchainable path values", async () => {
|
|
401
|
-
const result = await handler({
|
|
402
|
-
...baseParams,
|
|
403
|
-
steps: [
|
|
404
|
-
{
|
|
405
|
-
method: "POST",
|
|
406
|
-
path: "/api/circuits/providers/",
|
|
407
|
-
requestBody: JSON.stringify({ name: "P" }),
|
|
408
|
-
responseBody: JSON.stringify({ id: 1 }),
|
|
409
|
-
},
|
|
410
|
-
{ method: "GET", path: "/api/circuits/providers/999/summary/" },
|
|
411
|
-
],
|
|
412
|
-
});
|
|
413
|
-
expect(result.isError).toBeFalsy();
|
|
414
|
-
expect(result.content[0].text).toContain("Chaining warning");
|
|
415
|
-
expect(result.content[0].text).toContain("999");
|
|
416
|
-
});
|
|
417
|
-
it("emits no chaining output for a well-chained scenario", async () => {
|
|
418
|
-
const result = await handler({
|
|
419
|
-
...baseParams,
|
|
420
|
-
steps: [
|
|
421
|
-
{
|
|
422
|
-
method: "POST",
|
|
423
|
-
path: "/api/circuits/providers/",
|
|
424
|
-
requestBody: JSON.stringify({ name: "P" }),
|
|
425
|
-
responseBody: JSON.stringify({ id: 1 }),
|
|
426
|
-
},
|
|
427
|
-
{ method: "GET", path: "/api/circuits/providers/1/summary/" },
|
|
428
|
-
],
|
|
429
|
-
});
|
|
430
|
-
expect(result.isError).toBeFalsy();
|
|
431
|
-
expect(result.content[0].text).not.toContain("Chaining warning");
|
|
432
|
-
});
|
|
433
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck - Jest ESM type inference issues
|
|
2
|
-
import { jest } from "@jest/globals";
|
|
3
|
-
import * as fs from "fs/promises";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import * as os from "os";
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
// TestGenerationService transitively loads the @skyramp/skyramp native library
|
|
8
|
-
// (blocks 180s waiting for the platform dylib) — stub the base class out.
|
|
9
|
-
jest.unstable_mockModule("../../services/TestGenerationService.js", () => ({
|
|
10
|
-
TestGenerationService: class {
|
|
11
|
-
async generateTest() {
|
|
12
|
-
return { content: [{ type: "text", text: "generated" }] };
|
|
13
|
-
}
|
|
14
|
-
buildBaseGenerationOptions() {
|
|
15
|
-
return {};
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
}));
|
|
19
|
-
jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
|
|
20
|
-
AnalyticsService: {
|
|
21
|
-
pushTestGenerationToolEvent: jest.fn().mockResolvedValue(undefined),
|
|
22
|
-
},
|
|
23
|
-
}));
|
|
24
|
-
jest.unstable_mockModule("../../utils/logger.js", () => ({
|
|
25
|
-
logger: {
|
|
26
|
-
debug: jest.fn(),
|
|
27
|
-
info: jest.fn(),
|
|
28
|
-
warning: jest.fn(),
|
|
29
|
-
error: jest.fn(),
|
|
30
|
-
},
|
|
31
|
-
}));
|
|
32
|
-
const { registerContractTestTool } = await import("./generateContractRestTool.js");
|
|
33
|
-
const { setActiveRunStatePath, clearActiveRunStatePath } = await import("../../utils/AnalysisStateManager.js");
|
|
34
|
-
const { CandidateSource } = await import("../../types/Recommendation.js");
|
|
35
|
-
/** Capture the tool handler AND its input schema so calls exercise the real
|
|
36
|
-
* Zod contract, mirroring the MCP SDK's registerTool validation. */
|
|
37
|
-
function captureHandler() {
|
|
38
|
-
let capturedHandler;
|
|
39
|
-
let capturedSchema;
|
|
40
|
-
const mockServer = {
|
|
41
|
-
registerTool: jest.fn((_name, meta, handler) => {
|
|
42
|
-
capturedHandler = handler;
|
|
43
|
-
capturedSchema = meta.inputSchema;
|
|
44
|
-
}),
|
|
45
|
-
};
|
|
46
|
-
registerContractTestTool(mockServer);
|
|
47
|
-
return async (params) => {
|
|
48
|
-
const parsed = z.object(capturedSchema).safeParse(params);
|
|
49
|
-
if (!parsed.success) {
|
|
50
|
-
return {
|
|
51
|
-
isError: true,
|
|
52
|
-
content: [{ type: "text", text: parsed.error.message }],
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
return capturedHandler(parsed.data);
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
async function writeStateFile(dir, approvedPlan) {
|
|
59
|
-
const stateFile = path.join(dir, "analyze-changes-state.json");
|
|
60
|
-
await fs.writeFile(stateFile, JSON.stringify({
|
|
61
|
-
existingTests: [],
|
|
62
|
-
analysisScope: "current_branch_diff",
|
|
63
|
-
newEndpoints: [],
|
|
64
|
-
approvedPlan,
|
|
65
|
-
metadata: {
|
|
66
|
-
sessionId: "test-session",
|
|
67
|
-
stateType: "analysis",
|
|
68
|
-
createdAt: "2026-01-01T00:00:00.000Z",
|
|
69
|
-
updatedAt: "2026-01-01T00:00:00.000Z",
|
|
70
|
-
},
|
|
71
|
-
}), "utf-8");
|
|
72
|
-
return stateFile;
|
|
73
|
-
}
|
|
74
|
-
// A plan item whose ep key points at a DIFFERENT resource than the calls
|
|
75
|
-
// below, so the only viable match channel is the scenarioName. Mirrors the
|
|
76
|
-
// SKYR-4019 shape: a server-drafted candidate whose step path drifted from
|
|
77
|
-
// the live endpoint the agent generates against.
|
|
78
|
-
const NAME_ONLY_ITEM = {
|
|
79
|
-
candidateId: "order-response-shape-key-673b87b1",
|
|
80
|
-
scenarioName: "order-response-shape-key",
|
|
81
|
-
testType: "contract",
|
|
82
|
-
category: "breaking_change",
|
|
83
|
-
source: CandidateSource.SERVER,
|
|
84
|
-
matchKeys: ["name:order-response-shape-key", "ep:contract:GET:/inventory/:p"],
|
|
85
|
-
};
|
|
86
|
-
const BASE_PARAMS = {
|
|
87
|
-
endpointURL: "http://localhost:8000/api/v1/orders/{order_id}",
|
|
88
|
-
method: "GET",
|
|
89
|
-
language: "python",
|
|
90
|
-
framework: "pytest",
|
|
91
|
-
outputDir: "/tmp/out",
|
|
92
|
-
prompt: "Verify GET /api/v1/orders/{order_id} response shape",
|
|
93
|
-
};
|
|
94
|
-
describe("skyramp_contract_test_generation — plan-guard scenarioName channel (SKYR-4019)", () => {
|
|
95
|
-
let tmpDirs = [];
|
|
96
|
-
afterEach(async () => {
|
|
97
|
-
clearActiveRunStatePath();
|
|
98
|
-
for (const dir of tmpDirs) {
|
|
99
|
-
await fs.rm(dir, { recursive: true, force: true });
|
|
100
|
-
}
|
|
101
|
-
tmpDirs = [];
|
|
102
|
-
});
|
|
103
|
-
async function mkStateDir(approvedPlan) {
|
|
104
|
-
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "contract-guard-test-"));
|
|
105
|
-
tmpDirs.push(dir);
|
|
106
|
-
const stateFile = await writeStateFile(dir, approvedPlan);
|
|
107
|
-
setActiveRunStatePath(stateFile);
|
|
108
|
-
}
|
|
109
|
-
it("rejects when neither name nor endpoint matches the plan", async () => {
|
|
110
|
-
await mkStateDir({
|
|
111
|
-
planId: "plan-1",
|
|
112
|
-
generate: [NAME_ONLY_ITEM],
|
|
113
|
-
additional: [],
|
|
114
|
-
demotions: [],
|
|
115
|
-
});
|
|
116
|
-
const handler = captureHandler();
|
|
117
|
-
const result = await handler(BASE_PARAMS);
|
|
118
|
-
expect(result.isError).toBe(true);
|
|
119
|
-
expect(result.content[0].text).toContain("not in the approved plan");
|
|
120
|
-
});
|
|
121
|
-
it("allows when the exact scenarioName from the plan is passed (SKYR-4019)", async () => {
|
|
122
|
-
await mkStateDir({
|
|
123
|
-
planId: "plan-1",
|
|
124
|
-
generate: [NAME_ONLY_ITEM],
|
|
125
|
-
additional: [],
|
|
126
|
-
demotions: [],
|
|
127
|
-
});
|
|
128
|
-
const handler = captureHandler();
|
|
129
|
-
const result = await handler({
|
|
130
|
-
...BASE_PARAMS,
|
|
131
|
-
scenarioName: "order-response-shape-key",
|
|
132
|
-
});
|
|
133
|
-
expect(result.isError).toBeUndefined();
|
|
134
|
-
expect(result.content[0].text).toBe("generated");
|
|
135
|
-
});
|
|
136
|
-
it("allows without scenarioName when no plan is active (standalone/IDE usage)", async () => {
|
|
137
|
-
const handler = captureHandler();
|
|
138
|
-
const result = await handler(BASE_PARAMS);
|
|
139
|
-
expect(result.isError).toBeUndefined();
|
|
140
|
-
expect(result.content[0].text).toBe("generated");
|
|
141
|
-
});
|
|
142
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|