@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
|
@@ -4,9 +4,9 @@ import { AnalyticsService } from "../../services/AnalyticsService.js";
|
|
|
4
4
|
import { MAX_TESTS_TO_GENERATE, MAX_RECOMMENDATIONS, MAX_CRITICAL_TESTS, PATH_PARAM_UUID_GUIDANCE, AUTH_CONFLICT_ERROR_MSG, } from "../test-recommendation/recommendationSections.js";
|
|
5
5
|
import { TASK_ANALYZE_MAINTAIN, TASK_GENERATE, TASK_SUBMIT, taskRef } from "../test-recommendation/recommendationShared.js";
|
|
6
6
|
import { getTraceRecordingPromptText } from "../../playwright/traceRecordingPrompt.js";
|
|
7
|
-
import { isContractConsumerModeEnabled } from "../../utils/featureFlags.js";
|
|
7
|
+
import { isContractConsumerModeEnabled, isPomReuseEnabled } from "../../utils/featureFlags.js";
|
|
8
8
|
import { resolveServiceDetailsRef } from "../../utils/utils.js";
|
|
9
|
-
import {
|
|
9
|
+
import { buildServiceContext, readWorkspaceServices, } from "../prompt-utils.js";
|
|
10
10
|
// Cached at module-load — flags are process-wide and cannot change per call.
|
|
11
11
|
const CONSUMER_MODE_ENABLED = isContractConsumerModeEnabled();
|
|
12
12
|
const SERVICE_REFS = resolveServiceDetailsRef();
|
|
@@ -18,6 +18,14 @@ const CONTRACT_MODE_GUIDANCE = CONSUMER_MODE_ENABLED
|
|
|
18
18
|
For client-facing APIs consumed by frontend: add \`consumerMode: true\`.
|
|
19
19
|
Both modes (\`providerMode: true, consumerMode: true\`): For diff that contains BOTH provider signals (such as new/modified endpoint handlers, route changes this service owns) AND consumer signals (outbound HTTP client calls to another service, no new endpoint handlers).`
|
|
20
20
|
: ` Always add \`providerMode: true\` — the tool generates provider-side contract tests only.`;
|
|
21
|
+
const POM_REUSE_ENABLED = isPomReuseEnabled();
|
|
22
|
+
// Post-generation code-reuse step for UI tests. The POM catalog, the `verify: true`
|
|
23
|
+
// loop and the `.raw.bak` restore only exist on the POM-aware path — when that path
|
|
24
|
+
// is flagged off, `skyramp_reuse_code` returns the SkyrampUtils workflow instead, so
|
|
25
|
+
// the agent must not be sent looking for artifacts nothing produces.
|
|
26
|
+
const UI_CODE_REUSE_STEP = POM_REUSE_ENABLED
|
|
27
|
+
? `3. **[MANDATORY] After \`skyramp_ui_test_generation\` with \`codeReuse: true\`**: the generation result directs you to call \`skyramp_reuse_code\` — do this BEFORE \`skyramp_enhance_assertions\`. Two outcomes: (1) a response starting "No reusable POM layer detected" — this is a normal outcome, continue immediately (do NOT retry); (2) a refactoring workflow — follow it to completion INCLUDING its verification loop (\`skyramp_reuse_code\` with \`verify: true\`), finish only when it reports PASSED. If a reused test later fails execution and the failure points at a substituted POM call, restore the saved \`<testFile>.raw.bak\` over the test file and re-run — do NOT hand-edit the customer's POM methods (this re-run counts toward the 2-attempt execution cap — prefer this restore over the generic timeout fix-up when the failing locator came from a POM substitution). **Cleanup before reporting:** \`.raw.bak\` files are internal scratch — after ALL test executions are complete (pass or fail) and before calling \`skyramp_submit_report\`, delete every \`*.raw.bak\` you created so they are not committed to the customer-facing branch. The \`skyramp-pom-catalog.md\` is NOT scratch — leave it in place (later runs reuse it).`
|
|
28
|
+
: `3. **[MANDATORY] After \`skyramp_ui_test_generation\` with \`codeReuse: true\`**: the generation result directs you to call \`skyramp_reuse_code\` — do this BEFORE \`skyramp_enhance_assertions\`. Follow the returned steps exactly. If it finds no existing helper functions to reuse, that is a normal outcome — leave the test file unchanged and continue immediately (do NOT retry).`;
|
|
21
29
|
/**
|
|
22
30
|
* Parse the JSON-encoded `relatedRepositories` argument passed via the testbot
|
|
23
31
|
* prompt/resource. Returns undefined for missing/blank input or malformed JSON so the
|
|
@@ -84,7 +92,7 @@ prNumber, userPrompt, services, uiCredentials, testsRepoDir, relatedRepositories
|
|
|
84
92
|
maintenanceBeforeExecStep = ` d. Plan-only run: skip the pre-edit baseline execution — the application is not running. Record every maintenance verdict from static drift analysis alone; execution statuses simply remain unrecorded.`;
|
|
85
93
|
}
|
|
86
94
|
else {
|
|
87
|
-
maintenanceBeforeExecStep = ` d. Call \`skyramp_execute_test\` with \`phase: "before"\` and \`stateFile\` for every UPDATE/REGENERATE/DELETE test. Exclude tests marked \`[external]\` — those are baselined in step (a) via \`skyramp_run_existing_tests\`. Run them sequentially, not in parallel. This captures the pre-edit baseline — do not skip even if you expect the test to fail.`;
|
|
95
|
+
maintenanceBeforeExecStep = ` d. Call \`skyramp_execute_test\` with \`phase: "before"\` and \`stateFile\` for every UPDATE/REGENERATE/DELETE test. Exclude tests marked \`[external]\` — those are baselined in step 2(a) via \`skyramp_run_existing_tests\`. Run them sequentially, not in parallel. This captures the pre-edit baseline — do not skip even if you expect the test to fail.`;
|
|
88
96
|
}
|
|
89
97
|
// For follow-up requests: emit the @skyramp-testbot header + guardrails + retrieve-recommendations step.
|
|
90
98
|
// For first-run prompts: emit the full Task 1 analysis + maintenance section.
|
|
@@ -143,12 +151,12 @@ ${hasRelatedRepos ? `
|
|
|
143
151
|
4. **Spillover:** when a type's candidates run out, its freed slots go to the next-highest-scored candidate of any remaining type.
|
|
144
152
|
Within a type, higher score wins regardless of which repo it came from. Everything not selected becomes an ADDITIONAL recommendation. This guarantees a frontend-only primary repo cannot starve a related backend repo's contract/integration tests of GENERATE slots (and vice versa). When you generate a test for a related repo's endpoint:
|
|
145
153
|
- **Execute it only if that repo's service is already running and reachable.** The workflow's setup may have started multiple services; before generating an API test for a related repo, confirm its \`base_url\` (from that repo's workspace/Execution Plan) responds. If the service is unreachable, still GENERATE the test but mark its \`testResults\` status as \`Skipped\` with details "service not running in this run" — do NOT count an unreachable service as a failure.
|
|
146
|
-
- **Write the test file into that service's own \`testDirectory\`** — the one declared for the service in the unified workspace.yml (the related repo's services were registered there in step (a), each with its \`repository\`). The \`testDirectory\` is interpreted relative to the **single delivery root** (the configured test repo if set, otherwise the primary repo), so all generated tests are delivered together by the existing single-target delivery. Do NOT invent a per-source-repo subdirectory, and do NOT write into the related repo's own checkout — it is read-only context. (If two repos happen to declare the same \`testDirectory\`, their files coexist there; the \`repository\` field on each report item — below — is what attributes ownership, not the path.)
|
|
154
|
+
- **Write the test file into that service's own \`testDirectory\`** — the one declared for the service in the unified workspace.yml (the related repo's services were registered there in step 1(a), each with its \`repository\`). The \`testDirectory\` is interpreted relative to the **single delivery root** (the configured test repo if set, otherwise the primary repo), so all generated tests are delivered together by the existing single-target delivery. Do NOT invent a per-source-repo subdirectory, and do NOT write into the related repo's own checkout — it is read-only context. (If two repos happen to declare the same \`testDirectory\`, their files coexist there; the \`repository\` field on each report item — below — is what attributes ownership, not the path.)
|
|
147
155
|
- **Set the \`repository\` field** (\`owner/repo\`) on every such \`newTestsCreated\` / \`testResults\` item so the report attributes it to the originating repo (see Report Guidelines).` : ""}
|
|
148
156
|
|
|
149
157
|
2. **Maintain existing tests:**
|
|
150
158
|
|
|
151
|
-
a. Confirm external-test breakage before assessment. If any service in \`${repositoryPath}/.skyramp/workspace.yml\` declares test-env config (\`runtimeDetails.test*\`), call \`skyramp_run_existing_tests\` (\`mode: "confirm"\`, \`stateFile\`) on the tests \`skyramp_analyze_changes\` marked \`[external]\` — the repo's OWN suite, not the Skyramp-generated tests — before \`skyramp_analyze_test_health\`, so the run-confirmed failures fold into its assessment. Do NOT read the \`[external]\` test files to *guess* the change's impact — RUN them with \`skyramp_run_existing_tests\` to get real pass/fail; reading them is not a substitute for running them. These \`[external]\` suites also do not run through \`skyramp_execute_test\`, so skipping this leaves their status \`Unknown\`. Selector, health-gate, and self-skip behavior are in the tool description. (Skyramp-generated tests are
|
|
159
|
+
a. Confirm external-test breakage before assessment. If any service in \`${repositoryPath}/.skyramp/workspace.yml\` declares test-env config (\`runtimeDetails.test*\`), call \`skyramp_run_existing_tests\` (\`mode: "confirm"\`, \`stateFile\`) on the tests \`skyramp_analyze_changes\` marked \`[external]\` — the repo's OWN suite, not the Skyramp-generated tests — before \`skyramp_analyze_test_health\`, so the run-confirmed failures fold into its assessment. Do NOT read the \`[external]\` test files to *guess* the change's impact — RUN them with \`skyramp_run_existing_tests\` to get real pass/fail; reading them is not a substitute for running them. These \`[external]\` suites also do not run through \`skyramp_execute_test\`, so skipping this leaves their status \`Unknown\`. Selector, health-gate, and self-skip behavior are in the tool description. (Skyramp-generated tests are handled in step 2(d), not here.)
|
|
152
160
|
|
|
153
161
|
b. Call \`skyramp_analyze_test_health\` with \`stateFile\` (from \`skyramp_analyze_changes\` output). Pass \`blueprintCaptured: true\` when \`browser_blueprint\` was called successfully earlier in this session — see the parameter description for when this applies. **Do NOT read application source files** (routes, models, controllers) — all change information you need is in the \`skyramp_analyze_changes\` output and the diff. Exception: the UI drift pre-scan (\`UI_SYMBOL_PRESCAN\`) may instruct you to read changed frontend files to extract exported symbols — follow those instructions when present.
|
|
154
162
|
|
|
@@ -158,7 +166,7 @@ ${maintenanceBeforeExecStep}
|
|
|
158
166
|
|
|
159
167
|
e. Call \`skyramp_actions\` with \`stateFile\` (from \`skyramp_analyze_changes\` output) and apply the edits it returns.
|
|
160
168
|
|
|
161
|
-
f. Verify external-test fixes.
|
|
169
|
+
f. Verify external-test fixes. **This step is not optional and it is the easiest one to forget — you have just edited files in step 2(e), so come back here before you move on to anything else.** It applies whenever step 2(a) reported a real pass/fail result for a file you then edited. It does NOT apply when step 2(a) returned \`skipped: true\` or \`ran: 0\` for every suite — there is no baseline to compare against, so say so in your report instead of re-running. When it applies: re-run those \`[external]\` files with \`skyramp_run_existing_tests\` (\`mode: "verify"\`, \`stateFile\`) and record each file's result as its \`afterStatus\`. Editing an \`[external]\` file that step 2(a) confirmed failing and NOT re-running it leaves your own fix unverified — you would be reporting a repair you never saw work. A still-failing verify is surfaced in the report — do not loop.
|
|
162
170
|
|
|
163
171
|
3. **Code review:** From the \`skyramp_analyze_changes\` output and the existing test files you read for maintenance, note any logic bugs. Do NOT read additional source files just for code review — use what is already available from the analysis and test file reads. Common patterns to flag:
|
|
164
172
|
- Computed fields not recalculated after mutation (e.g. \`total_amount\` unchanged after items are added/removed)
|
|
@@ -400,7 +408,7 @@ This is a plan-only evaluation run: the application under test is NOT running, a
|
|
|
400
408
|
${userPrompt ? "Generate only the tests that the user requested from the Additional Recommendations. The rules below still apply." : "Drift-based maintenance (Task 1) is complete. This step only processes the GENERATE list. Exception: if a GENERATE item targets a resource with an existing `[skyramp]` contract test, UPDATE that test file (see covered-resource handling below) — a new test case added to an existing file counts toward the budget and is reported in `newTestsCreated`."}
|
|
401
409
|
|
|
402
410
|
- **MANDATORY — use the plan returned by \`skyramp_register_test_plan\` as-is**: Before generating anything, call \`skyramp_register_test_plan\` (\`stateFile\` required) with your complete candidate list — every test you would generate OR recommend, including the Execution Plan's own pre-ranked GENERATE/ADDITIONAL items and any candidate you drafted yourself, with a \`discriminator\` claim \`{kind, changedCodeAnchor}\` for candidates probing changed logic. Its returned GENERATE list — not the Execution Plan's raw pre-ranked GENERATE section — governs ADD actions from this point on. You MUST generate exactly those scenarios in the exact order listed, keeping each item's \`scenarioName\` exactly as registered — the generation tools match on it and reject renamed or substituted scenarios. If parameter grounding uncovers a distinct bug-catching scenario not already registered, generate it after all planned GENERATE items are complete and report it in \`newTestsCreated\` — this is an additional test driven by source-code analysis and does not count against the GENERATE budget.${hasRelatedRepos ? `\n - **Multi-repo exception:** this run has related repositories, so the per-repo GENERATE lists are NOT final — they are candidates re-selected by the cross-repo round-robin described in Task 1's "Cross-repo test generation". Register the pooled, type-distributed selection instead of any single repo's GENERATE list. (In single-repo runs, register the GENERATE list exactly as-is.)` : ""}
|
|
403
|
-
- **Do not fabricate tests outside the GENERATE list
|
|
411
|
+
- **Do not fabricate tests outside the GENERATE list provided by \`skyramp_analyze_changes\`.** Changes that only modify, delete, or add fields to an EXISTING covered endpoint or component are maintenance: handle them in ${taskRef(TASK_ANALYZE_MAINTAIN)} by UPDATE/DELETE of the existing test, never by creating a new spec. If the GENERATE list is empty, create zero new tests and proceed to ${taskRef(TASK_SUBMIT)}.
|
|
404
412
|
- Scenario JSON files are always new files — always generate them for new methods. Every generated scenario JSON must have a corresponding new integration test generated from it via \`skyramp_integration_test_generation\`.
|
|
405
413
|
- Covered-resource handling (aligns with Execution Plan Step 0): When a GENERATE item targets a resource that already has an existing test file covering the same endpoint:
|
|
406
414
|
- If the existing test source is \`[external]\`, skip the resource entirely — the external test already provides coverage. Do NOT UPDATE, REGENERATE, or DELETE external tests.
|
|
@@ -410,11 +418,11 @@ ${userPrompt ? "Generate only the tests that the user requested from the Additio
|
|
|
410
418
|
- UI tests: Always generate as a new file. Report in \`newTestsCreated\`.
|
|
411
419
|
Keep advancing until you have created exactly as many new test files as your committed Budget Plan's generate count (at most ${maxGenerate}) OR exhausted all candidates. If your Budget Plan is 0 total, ${taskRef(TASK_GENERATE)} produces zero tests.
|
|
412
420
|
- Example: If enrichment reveals that sending \`discount_value\` without \`discount_type\` silently orphans the value (a concrete bug), complete all planned GENERATE items first, then generate this discovered scenario as an extra test and report it in \`newTestsCreated\`.
|
|
413
|
-
- Total generated: your committed Budget Plan's generate count (from the Execution Plan's Scope Assessment, at most ${maxGenerate}) is the single source of truth for how many tests to create. Process every GENERATE-tagged item in order, then backfill from ADDITIONAL candidates (highest-ranked first) until \`newTestsCreated\` reaches that generate count or all candidates are exhausted. If your Budget Plan is 0 total
|
|
414
|
-
- **UI test priority**:
|
|
421
|
+
- Total generated: your committed Budget Plan's generate count (from the Execution Plan's Scope Assessment, at most ${maxGenerate}) is the single source of truth for how many tests to create. Process every GENERATE-tagged item in order, then backfill from ADDITIONAL candidates (highest-ranked first) until \`newTestsCreated\` reaches that generate count or all candidates are exhausted. If your Budget Plan is 0 total, skip generation and backfilling entirely and proceed to ${taskRef(TASK_SUBMIT)}'s zero-test report path.
|
|
422
|
+
- **UI test priority**: \`skyramp_register_test_plan\` ends its output with a **Generation directive** that states whether this run generates UI/E2E tests and how many. Follow it as given — do not re-derive the decision from the diff or from \`uiContext\`. When it says UI/E2E generation is REQUIRED, use \`browser_navigate\` to the app's base URL, record a trace, and generate the test. (\`uiContext.changedFrontendFiles\` — the deterministic server signal, populated for all supported frontend file types including \`.tsx\`/\`.jsx\`/\`.vue\`/\`.svelte\`/\`.dart\` — tells you the PR touched the frontend, so a UI candidate belongs in the list you *register*. It never obliges you to generate.)
|
|
415
423
|
**Flutter web apps:** Skyramp's Playwright tools automatically enable Flutter's accessibility semantics tree on every \`browser_navigate\` call — you do NOT need to manually click \`flt-semantics-placeholder\` or add any activation step to the trace. Do NOT log an \`issuesFound\` entry about Flutter canvas rendering or accessibility activation — this is handled transparently. **Do NOT skip test generation or abstain from recording based on what you see in the Flutter source code** (e.g. \`SemanticsBinding.ensureSemantics()\` commented out, \`IS_TESTING\` flag absent, or similar) — Skyramp enables accessibility from the browser side regardless of the app's Dart code. Proceed with \`browser_navigate\` and test recording as normal. **Start at the app's root URL** (e.g. \`{baseUrl}/\`) — do NOT \`browser_navigate\` straight to a deep sub-route (e.g. \`/authors\`, \`/orders/13\`). Flutter \`go_router\` SPAs route from the root: deep-linking on a cold page load often fails to render the expected screen (the route's widgets never mount, so the trace captures the wrong page). Load the root, let the app's own routing/auth-redirect render, then reach target screens by interaction. **After the initial login, navigate using in-app controls only** (tab buttons, links, back buttons) — do NOT call \`browser_navigate\` to a different URL after login. Flutter web apps are SPAs: a \`browser_navigate\` to a new URL after login triggers a full page reload which clears the auth session, causing redundant re-login cycles in the generated test. Use button clicks to reach target screens instead.
|
|
416
|
-
**
|
|
417
|
-
- **(a) App is unreachable** — \`browser_navigate\` fails or connection is refused.
|
|
424
|
+
**When the directive requires UI/E2E generation, skip only if one of these runtime conditions is met** (the directive already settles allocation; these are the two things the server cannot know ahead of time):
|
|
425
|
+
- **(a) App is unreachable** — \`browser_navigate\` fails or connection is refused. This is an environment failure, NOT a decision: you were required to generate and could not. Record it in \`issuesFound\`, move the intended UI test to \`additionalRecommendations\` with the failure reason, and say plainly in the report that the test could not be recorded because the app was down, so the empty \`newTestsCreated\` is not mistaken for a deliberate no-test verdict.
|
|
418
426
|
- **(b) Unintegrated non-route component** — the changed file is a leaf component (not a framework route/entrypoint) that has no integration point in the running app. **The server already computes this** — check \`uiContext.frontendFileIntegration\` in the \`skyramp_analyze_changes\` output: if it marks the changed file \`integrated: false\`, treat the component as unintegrated WITHOUT re-running the grep below (the tool output's accompanying instruction block already tells you what to do — do not substitute another page or trace). Only fall back to the manual grep procedure when \`frontendFileIntegration\` is absent (older MCP versions) or doesn't cover the changed file:
|
|
419
427
|
1. Grep for the component's exported name AND its module path/filename across all production source files (excluding \`*.test.*\`, \`*.spec.*\`, \`*.stories.*\`, \`__tests__/\` directories — only production code imports count).
|
|
420
428
|
2. If no production file imports, re-exports, or renders it, the component has no DOM node in the running app → unintegrated.
|
|
@@ -464,18 +472,20 @@ ${CONTRACT_MODE_GUIDANCE}
|
|
|
464
472
|
If NO relevant trace exists, **you MUST write out your full trace plan as text BEFORE calling \`browser_navigate\`**. Do not touch the browser until the plan is written.
|
|
465
473
|
|
|
466
474
|
**Browser authentication (check BEFORE navigating)**: If \`<ui-credentials>\` appears in your context above, the app requires login. Parse the credentials — one per line, two supported formats:
|
|
467
|
-
- New format: \`username=<value>;password=<value>\` or \`username=<value>;password=<value>;role=<value>\`
|
|
475
|
+
- New format: \`;\`-delimited \`key=value\` pairs, e.g. \`username=<value>;password=<value>\` or \`username=<value>;password=<value>;tenantId=<value>\`. \`username\` and \`password\` are the standard keys; \`role=<value>\` (optional) selects among multiple credentials; **any other key is the value for an additional login-form field** (e.g. \`tenantId\`, \`companyCode\`, \`domain\`), matched to the form field by its name/label/placeholder.
|
|
476
|
+
- JSON format: a line may instead be a JSON object, e.g. \`{"username":"u","password":"p;=x","tenantId":"1"}\` — preferred when a value itself contains \`=\` or \`;\`.
|
|
468
477
|
- Legacy format: \`username:password\` — the first \`:\` splits username from password.
|
|
478
|
+
These are format hints, not a strict grammar — apply judgment on ambiguous input (e.g. a \`=\` or \`;\` inside a value of the key=value form: split on the \`;\` that precedes a plausible login-field key).
|
|
469
479
|
|
|
470
480
|
**Credential selection**: Use the first credential by default. When the scenario requires a specific role, find the credential whose \`role\` field matches (e.g. \`role=admin\`). If no credential matches the required role, use the first credential and add a note to \`issuesFound\` that no matching role was found.
|
|
471
481
|
|
|
472
482
|
Type all values verbatim. Before navigating to ANY feature URL:
|
|
473
483
|
1. \`browser_navigate\` to the login URL (e.g. \`{baseUrl}/login\`, \`/user/login\`, \`/signin\` — infer from the app's base URL and framework)
|
|
474
|
-
2. \`browser_snapshot\`
|
|
484
|
+
2. \`browser_snapshot\` and enumerate every **visible, user-editable** input field in the login form — not just username/password. Match each field to a credential key by its name/label/placeholder (e.g. a tenant-ID field ↔ \`tenantId=<value>\`) BEFORE clicking anything.
|
|
475
485
|
3. \`browser_type\` the username into the email/username field
|
|
476
486
|
4. \`browser_type\` the password into the password field
|
|
477
|
-
5. If a role selector is present and a \`role\` was specified in the credential, select it before submitting
|
|
478
|
-
6. \`browser_click\` the submit button, then \`browser_wait_for\` redirect away from the login page
|
|
487
|
+
5. \`browser_type\` each remaining form field's value from its matching credential key. If a role selector is present and a \`role\` was specified in the credential, select it before submitting.
|
|
488
|
+
6. Only once **every visible required field is filled**: \`browser_click\` the submit button, then \`browser_wait_for\` redirect away from the login page. NEVER click submit with a required field still empty to "see what happens" — the recorder is a faithful capture, so a failed submit attempt gets baked into the trace and replayed by the generated test. If a visible **required** field has no matching credential key, do NOT submit: skip recording this flow and add an \`issuesFound\` entry naming the form field and the \`key=value\` pair the customer must add to the \`uiCredentials\` input in their workflow file. Optional fields with no matching key are simply left empty — they are never a reason to abort.
|
|
479
489
|
7. Now navigate directly to the feature URL and begin recording
|
|
480
490
|
The login steps ARE part of the trace — the generated test will authenticate automatically.
|
|
481
491
|
|
|
@@ -508,7 +518,7 @@ ${CONTRACT_MODE_GUIDANCE}
|
|
|
508
518
|
- **\`browser_assert\` — MANDATORY**: at least one per page navigated. Call multiple assertions in the same tool call batch when checking independent elements. If you navigate to 2 pages, assert on both. Each assertion should verify a business outcome (state change, computed value, error condition) — not just that an element is visible.
|
|
509
519
|
- **\`browser_visual_snapshot\` — for visual/appearance checks**: when the instruction asks to take a screenshot, capture a baseline, or verify how a page/element/region *looks* (not its text or value), call \`browser_visual_snapshot\` — it records a \`toHaveScreenshot()\` assertion so the generated test pixel-compares against a baseline on every run. Do NOT use \`browser_take_screenshot\` for this: it captures a throwaway image that is dropped at export and never appears in the generated test (use it only to view the page yourself).
|
|
510
520
|
- **Wait for stable state before the second capture**: After performing an action that affects computed fields (filling a discount, submitting a form, adding an item), check the current page state before calling the second \`browser_blueprint\` (the capture after the action). If a computed field — total, price, count, derived text — still shows its initial empty or zero value (e.g. \`$0.00\`, \`0\`, \`Loading...\`, empty string), that means async data hasn't finished loading yet. Use \`browser_wait_for\` to wait up to 10 seconds for the field to update to a real value (for example, wait for the total to show a non-zero amount like \`$799.99\` instead of \`$0.00\`). Once the field shows a real value, THEN call the second \`browser_blueprint\` to capture stable state. If after 10 seconds the field still hasn't updated, skip the assertion on that field — don't capture and assert a value that hasn't loaded.
|
|
511
|
-
If \`browser_navigate\` fails (app not running / connection refused), move to \`additionalRecommendations\` with the failure reason
|
|
521
|
+
If \`browser_navigate\` fails (app not running / connection refused), apply skip condition (a) above: move the intended test to \`additionalRecommendations\` with the failure reason AND record the outage in \`issuesFound\`.
|
|
512
522
|
Record at most 2-3 UI traces per run to stay within tool call budget. Quality over quantity: 1 great test is better than 3 mediocre ones — do not pad to reach the count.
|
|
513
523
|
**Strategic assertions** — key checkpoints only, 3 to 5 per test:
|
|
514
524
|
- **After the main action completes**: verify the outcome is visible (new item appears, form saves, confirmation shows)
|
|
@@ -522,7 +532,7 @@ ${CONTRACT_MODE_GUIDANCE}
|
|
|
522
532
|
|
|
523
533
|
**Skip this entire section if \`uiContext\` was absent or \`changedFrontendFiles\` was empty in the \`skyramp_analyze_changes\` response** (backend-only PR). The capture-act-capture pattern is for UI trace recording only — there's no UI trace to record on a backend-only PR. Continue to the non-UI test-type instructions below.
|
|
524
534
|
|
|
525
|
-
**Reminder — the UI test priority rule above still applies.** If the
|
|
535
|
+
**Reminder — the UI test priority rule above still applies.** If the plan's Generation directive requires UI/E2E generation, you still MUST attempt to record a trace. Capture-act-capture is **how** you record that test, not **whether** you record one — do not substitute UI recommendations for actually recording a trace. (If the directive allocates no UI/E2E generation, there is nothing to record here — that is the plan's decision, not a shortcut.) UI recommendation reasoning was already grounded in the blueprints you captured from the UI Blueprint Capture section of \`skyramp_analyze_changes\`; Task 2's capture-act-capture is for the trace's own assertions, not for retroactively rewriting recommendation reasoning.
|
|
526
536
|
|
|
527
537
|
This pattern produces delta-derived assertions from blueprint diffs. Diff-derived assertions catch state changes more reliably than author-inference — the diff tells you what actually changed on the page so the assertion is grounded in observable state, not in guessing what "success" looks like.
|
|
528
538
|
|
|
@@ -589,12 +599,14 @@ Do NOT use \`page.waitForTimeout()\` with fixed delays. Do NOT retry more than o
|
|
|
589
599
|
**After generation, you MUST do exactly these steps — nothing more, nothing less:**
|
|
590
600
|
1. **[MANDATORY] After \`skyramp_integration_test_generation\`**: Call \`skyramp_enhance_assertions\` with \`testFile\` set to the absolute path of the generated integration test file, \`testType: "integration"\`, and \`enhanceType: "generation"\`. Apply every instruction returned to that file.
|
|
591
601
|
2. **[MANDATORY] After \`skyramp_contract_test_generation\` with \`providerMode\`**: Call \`skyramp_enhance_assertions\` with \`testFile\` set to the absolute path of the generated provider contract test file, \`testType: "contract"\`, and \`enhanceType: "generation"\`. Apply every instruction returned to that file.
|
|
592
|
-
|
|
602
|
+
${UI_CODE_REUSE_STEP}
|
|
593
603
|
4. **[MANDATORY] After \`skyramp_ui_test_generation\`**: Call \`skyramp_enhance_assertions\` with \`testFile\` set to the absolute path of the generated UI test file, \`testType: "ui"\`, and \`enhanceType: "generation"\`. Apply every instruction returned to that file. The HIGH-tier \`possibleAssertions\` from your second \`browser_blueprint\` captures (after each action) during trace recording are in your context — when the enhance instructions ask you to add assertions for state-changing actions, use those grounded candidates first (they contain exact computed values from the DOM delta, e.g. \`toHaveText('Total: $899.98')\`). Only fall back to deriving values from the test file or source code when no HIGH-tier candidate covers the action.
|
|
594
604
|
5. **Wait**: Do NOT proceed to test execution until steps 1–4 are complete and the verification checklist in the \`skyramp_enhance_assertions\` tool result has been validated for EVERY generated test file.
|
|
595
605
|
Do not make any changes other than the code-reuse refactoring (step 3) and the assertion enhancements described above. For example: do not modify auth headers, cookies, tokens, env vars, or imports that the generation tool already set correctly — those are correct by construction and changing them breaks auth or execution.
|
|
596
606
|
|
|
597
|
-
**
|
|
607
|
+
**Execution timing:**
|
|
608
|
+
- **beforeStatus** (maintained tests only): execute each maintained test file **once at the start** (before any edits) to capture \`beforeStatus\`. This is the only execution allowed before edits.
|
|
609
|
+
- **Final execution**: Do NOT call \`skyramp_execute_test\` again until ALL maintenance edits AND ALL new test generation/enhancement are complete. Then execute every test file once — maintained files (for \`afterStatus\`) and new files together. **Execute tests SEQUENTIALLY (one at a time)** — do NOT send multiple \`skyramp_execute_test\` calls in the same tool call batch, as concurrent execution overwhelms the stdio transport and causes MCP disconnection. Exclude tests marked \`[external]\`.
|
|
598
610
|
- Only report test results for files you actually ran.
|
|
599
611
|
**Auth**: If \`skyramp_analyze_changes\` reports an auth token or \`SKYRAMP_TEST_TOKEN\` is set, pass it in **every** \`skyramp_execute_test\` call from the first attempt — do NOT wait for a 401/403 to discover auth is needed.`;
|
|
600
612
|
}
|
|
@@ -647,38 +659,6 @@ ${getTraceRecordingPromptText({ outputDir: `${repositoryPath}/.skyramp`, modular
|
|
|
647
659
|
// removing stateOutputFile from the prompt schema. Remove the RUNNER_TEMP branch in
|
|
648
660
|
// AnalysisStateManager.ts when this is done.
|
|
649
661
|
}
|
|
650
|
-
function escapeXml(value) {
|
|
651
|
-
return value
|
|
652
|
-
.replaceAll('&', '&')
|
|
653
|
-
.replaceAll('<', '<')
|
|
654
|
-
.replaceAll('>', '>')
|
|
655
|
-
.replaceAll('"', '"')
|
|
656
|
-
.replaceAll("'", ''');
|
|
657
|
-
}
|
|
658
|
-
function buildServiceContext(services) {
|
|
659
|
-
const blocks = services.map(svc => {
|
|
660
|
-
const parts = [`<service name="${escapeXml(svc.serviceName)}">`];
|
|
661
|
-
if (svc.language)
|
|
662
|
-
parts.push(` <language>${escapeXml(svc.language)}</language>`);
|
|
663
|
-
if (svc.framework)
|
|
664
|
-
parts.push(` <framework>${escapeXml(svc.framework)}</framework>`);
|
|
665
|
-
if (svc.api?.baseUrl)
|
|
666
|
-
parts.push(` <base_url>${escapeXml(svc.api.baseUrl)}</base_url>`);
|
|
667
|
-
if (svc.testDirectory)
|
|
668
|
-
parts.push(` <test_directory>${escapeXml(svc.testDirectory)}</test_directory>`);
|
|
669
|
-
parts.push('</service>');
|
|
670
|
-
return parts.join('\n');
|
|
671
|
-
});
|
|
672
|
-
return `<services>\n${blocks.join('\n')}\n</services>`;
|
|
673
|
-
}
|
|
674
|
-
/**
|
|
675
|
-
* Read services from .skyramp/workspace.yml. Returns empty array if
|
|
676
|
-
* the workspace file doesn't exist or can't be parsed.
|
|
677
|
-
*/
|
|
678
|
-
export async function readWorkspaceServices(repositoryPath) {
|
|
679
|
-
const rawConfig = await readWorkspaceConfigRaw(repositoryPath);
|
|
680
|
-
return (rawConfig?.services ?? []);
|
|
681
|
-
}
|
|
682
662
|
export function buildWorkspaceRecoveryPrefix(repositoryPath) {
|
|
683
663
|
return `IMPORTANT: The existing .skyramp/workspace.yml failed to parse or validate. Before proceeding with any tasks below, you MUST call skyramp_init_scan with workspacePath "${repositoryPath}" and force: true, then call skyramp_init_workspace with workspacePath "${repositoryPath}", the discovered services, scanToken, and force: true to regenerate the workspace file.\n\n`;
|
|
684
664
|
}
|
|
@@ -723,7 +703,7 @@ export function registerTestbotPrompt(server) {
|
|
|
723
703
|
uiCredentials: z
|
|
724
704
|
.string()
|
|
725
705
|
.optional()
|
|
726
|
-
.describe("Browser login credentials for UI test recording. One credential per line. Supported formats: 'username=<val>;password=<val>'
|
|
706
|
+
.describe("Browser login credentials for UI test recording. One credential per line. Supported formats: ';'-delimited key=value pairs — 'username=<val>;password=<val>' plus any additional login-form fields the app needs (e.g. ';tenantId=<val>') and optional ';role=<val>' for credential selection — a JSON object per line (preferred when a value contains = or ;), or legacy 'username:password'. The string is passed to the agent opaquely; formats are hints the agent interprets, not a parsed grammar. Injected into the prompt as a <ui-credentials> block so the agent fills every login-form field and logs in before recording traces."),
|
|
727
707
|
workspaceValidationFailed: z
|
|
728
708
|
.boolean()
|
|
729
709
|
.default(false)
|
|
@@ -9,12 +9,22 @@ export interface RankOptions {
|
|
|
9
9
|
* the agent's priority tag as a ranking input.
|
|
10
10
|
*/
|
|
11
11
|
carveOutCategories?: ScenarioCategory[];
|
|
12
|
+
/**
|
|
13
|
+
* Raw PR diff text. When supplied, a candidate whose scenario references a
|
|
14
|
+
* method+path that appears on an actual changed diff line (`+`/`-`) floats
|
|
15
|
+
* ahead of a same-tier candidate that doesn't — e.g. the one endpoint really
|
|
16
|
+
* removed by a PR outranks other same-category "verify-removed-*" candidates
|
|
17
|
+
* for endpoints merely adjacent in the same file (SKYR-4026).
|
|
18
|
+
*/
|
|
19
|
+
diffText?: string;
|
|
12
20
|
}
|
|
13
21
|
/** Context for {@link selectPlan}: the budget context plus the demotion channel
|
|
14
22
|
* the register-plan tool fills from discriminator verification. */
|
|
15
23
|
export interface SelectPlanContext extends BudgetContext {
|
|
16
24
|
/** Demoted claims (failed discriminator verification) to surface on the result. */
|
|
17
25
|
demotions?: Demotion[];
|
|
26
|
+
/** Threaded into {@link rankCandidates} as `RankOptions.diffText`. */
|
|
27
|
+
diffText?: string;
|
|
18
28
|
}
|
|
19
29
|
/**
|
|
20
30
|
* Rank test candidates for the register-plan checkpoint. Pure and fully
|
|
@@ -28,9 +38,12 @@ export interface SelectPlanContext extends BudgetContext {
|
|
|
28
38
|
* 2. Verified discriminators — candidates whose declared discriminator survived
|
|
29
39
|
* `validateDiscriminator` (marked via `verifiedDiscriminator`) float ahead of
|
|
30
40
|
* unverified peers in the same tier.
|
|
31
|
-
* 3.
|
|
41
|
+
* 3. Diff-hunk proximity — candidates referencing a method+path that appears
|
|
42
|
+
* on an actual changed diff line float ahead of same-tier candidates that
|
|
43
|
+
* don't (SKYR-4026). Only applied when `opts.diffText` is supplied.
|
|
44
|
+
* 4. `CATEGORY_PRIORITY` mapped over `scenario.category` (CRITICAL > HIGH >
|
|
32
45
|
* MEDIUM > LOW).
|
|
33
|
-
*
|
|
46
|
+
* 5. Stable tiebreak on `candidateId` for determinism.
|
|
34
47
|
*
|
|
35
48
|
* IMPORTANT: the agent-supplied `priority` field (`scenario.priority`) is
|
|
36
49
|
* DELIBERATELY IGNORED here. Round-1 experiments found it systematically
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CATEGORY_PRIORITY, PriorityTier } from "../types/TestRecommendation.js";
|
|
2
2
|
import { diversityBalancedBudgeter } from "./budgeters/diversityBalancedBudgeter.js";
|
|
3
|
+
import { parseRouteLine, normalizeDiffPath } from "../utils/routeParsers.js";
|
|
3
4
|
const PRIORITY_RANK = {
|
|
4
5
|
CRITICAL: 0,
|
|
5
6
|
HIGH: 1,
|
|
@@ -19,9 +20,12 @@ const DEFAULT_CARVE_OUT_CATEGORIES = Object.keys(CATEGORY_PRIORITY).filter((cate
|
|
|
19
20
|
* 2. Verified discriminators — candidates whose declared discriminator survived
|
|
20
21
|
* `validateDiscriminator` (marked via `verifiedDiscriminator`) float ahead of
|
|
21
22
|
* unverified peers in the same tier.
|
|
22
|
-
* 3.
|
|
23
|
+
* 3. Diff-hunk proximity — candidates referencing a method+path that appears
|
|
24
|
+
* on an actual changed diff line float ahead of same-tier candidates that
|
|
25
|
+
* don't (SKYR-4026). Only applied when `opts.diffText` is supplied.
|
|
26
|
+
* 4. `CATEGORY_PRIORITY` mapped over `scenario.category` (CRITICAL > HIGH >
|
|
23
27
|
* MEDIUM > LOW).
|
|
24
|
-
*
|
|
28
|
+
* 5. Stable tiebreak on `candidateId` for determinism.
|
|
25
29
|
*
|
|
26
30
|
* IMPORTANT: the agent-supplied `priority` field (`scenario.priority`) is
|
|
27
31
|
* DELIBERATELY IGNORED here. Round-1 experiments found it systematically
|
|
@@ -32,7 +36,11 @@ const DEFAULT_CARVE_OUT_CATEGORIES = Object.keys(CATEGORY_PRIORITY).filter((cate
|
|
|
32
36
|
*/
|
|
33
37
|
export function rankCandidates(candidates, opts = {}) {
|
|
34
38
|
const carveOutSet = new Set(opts.carveOutCategories ?? DEFAULT_CARVE_OUT_CATEGORIES);
|
|
35
|
-
|
|
39
|
+
const changedRoutes = opts.diffText ? collectChangedRouteLines(opts.diffText) : [];
|
|
40
|
+
const onHunk = new Set(candidates
|
|
41
|
+
.filter((c) => scenarioOnChangedHunk(c.scenario, changedRoutes))
|
|
42
|
+
.map((c) => c.candidateId));
|
|
43
|
+
return [...candidates].sort((a, b) => compareRank(a, b, carveOutSet, onHunk));
|
|
36
44
|
}
|
|
37
45
|
/**
|
|
38
46
|
* Convenience composition: rank the candidates, split GENERATE vs ADDITIONAL via
|
|
@@ -42,11 +50,11 @@ export function rankCandidates(candidates, opts = {}) {
|
|
|
42
50
|
* passing the failed claims through `ctx.demotions`.
|
|
43
51
|
*/
|
|
44
52
|
export function selectPlan(candidates, ctx, opts = {}) {
|
|
45
|
-
const ranked = rankCandidates(candidates, opts);
|
|
53
|
+
const ranked = rankCandidates(candidates, { ...opts, diffText: opts.diffText ?? ctx.diffText });
|
|
46
54
|
const result = diversityBalancedBudgeter.select(ranked, ctx);
|
|
47
55
|
return { ...result, demotions: ctx.demotions ?? [] };
|
|
48
56
|
}
|
|
49
|
-
function compareRank(a, b, carveOutSet) {
|
|
57
|
+
function compareRank(a, b, carveOutSet, onHunk) {
|
|
50
58
|
const carveA = carveOutSet.has(a.scenario?.category) ? 0 : 1;
|
|
51
59
|
const carveB = carveOutSet.has(b.scenario?.category) ? 0 : 1;
|
|
52
60
|
if (carveA !== carveB)
|
|
@@ -55,6 +63,10 @@ function compareRank(a, b, carveOutSet) {
|
|
|
55
63
|
const verifiedB = b.verifiedDiscriminator ? 0 : 1;
|
|
56
64
|
if (verifiedA !== verifiedB)
|
|
57
65
|
return verifiedA - verifiedB;
|
|
66
|
+
const hunkA = onHunk.has(a.candidateId) ? 0 : 1;
|
|
67
|
+
const hunkB = onHunk.has(b.candidateId) ? 0 : 1;
|
|
68
|
+
if (hunkA !== hunkB)
|
|
69
|
+
return hunkA - hunkB;
|
|
58
70
|
const catRankA = categoryRank(a);
|
|
59
71
|
const catRankB = categoryRank(b);
|
|
60
72
|
if (catRankA !== catRankB)
|
|
@@ -65,3 +77,62 @@ function categoryRank(candidate) {
|
|
|
65
77
|
const tier = CATEGORY_PRIORITY[candidate.scenario?.category] ?? PriorityTier.LOW;
|
|
66
78
|
return PRIORITY_RANK[tier];
|
|
67
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Extract method+path from every changed (`+`/`-`, non-header) line of a raw
|
|
82
|
+
* unified diff. Reuses `parseRouteLine`, which already strips the leading
|
|
83
|
+
* `+`/`-` marker and matches the same route-decorator patterns the endpoint
|
|
84
|
+
* scanner does.
|
|
85
|
+
*/
|
|
86
|
+
function collectChangedRouteLines(diffText) {
|
|
87
|
+
const routes = [];
|
|
88
|
+
let currentFile = "";
|
|
89
|
+
for (const line of diffText.split("\n")) {
|
|
90
|
+
// Track the current file from the unified-diff header so parseRouteLine
|
|
91
|
+
// gets the real path — its UI-component guard (UI_COMPONENT_EXT) depends
|
|
92
|
+
// on it, or a route-shaped line inside a .tsx/.jsx file (e.g. a client
|
|
93
|
+
// router registration) gets misparsed as a changed backend route.
|
|
94
|
+
if (line.startsWith("+++ ")) {
|
|
95
|
+
const spec = line.slice(4).trim().split("\t")[0];
|
|
96
|
+
currentFile = spec === "/dev/null" ? "" : normalizeDiffPath(spec);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (line.startsWith("--- ") || line.startsWith("diff --git") || line.startsWith("index "))
|
|
100
|
+
continue;
|
|
101
|
+
if (!(line.startsWith("+") || line.startsWith("-")))
|
|
102
|
+
continue;
|
|
103
|
+
const parsed = parseRouteLine(line, currentFile);
|
|
104
|
+
if (parsed)
|
|
105
|
+
routes.push({ method: parsed.method, path: parsed.path });
|
|
106
|
+
}
|
|
107
|
+
return routes;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Whether any step of `scenario` targets a method+path on the changed hunk.
|
|
111
|
+
* Diff-extracted paths are local to the file's own router declaration (e.g.
|
|
112
|
+
* "/suggestions"); scenario step paths are fully mounted (e.g.
|
|
113
|
+
* "/api/recipes/suggestions") once drafted from a scanned/recovered endpoint.
|
|
114
|
+
* A local path matches when the step path ends with it, so the cross-file
|
|
115
|
+
* mount prefix difference (see recoverRemovedEndpointsFromBase, SKYR-4026)
|
|
116
|
+
* doesn't prevent the match.
|
|
117
|
+
*/
|
|
118
|
+
function scenarioOnChangedHunk(scenario, changedRoutes) {
|
|
119
|
+
if (changedRoutes.length === 0)
|
|
120
|
+
return false;
|
|
121
|
+
for (const step of scenario.steps ?? []) {
|
|
122
|
+
const stepMethod = (step.method ?? "").toUpperCase();
|
|
123
|
+
const stepPath = (step.path ?? "").replace(/\/+$/, "");
|
|
124
|
+
for (const route of changedRoutes) {
|
|
125
|
+
if (route.method.toUpperCase() !== stepMethod)
|
|
126
|
+
continue;
|
|
127
|
+
const routePath = route.path.replace(/\/+$/, "");
|
|
128
|
+
if (routePath === "") {
|
|
129
|
+
if (stepPath === "" || stepPath === "/")
|
|
130
|
+
return true;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (stepPath === routePath || stepPath.endsWith(routePath))
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
@@ -2,7 +2,8 @@ import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { logger } from "../utils/logger.js";
|
|
3
3
|
import { AnalyticsService } from "../services/AnalyticsService.js";
|
|
4
4
|
import { MAX_TESTS_TO_GENERATE, MAX_RECOMMENDATIONS, MAX_CRITICAL_TESTS, } from "../prompts/test-recommendation/recommendationSections.js";
|
|
5
|
-
import { getTestbotPrompt,
|
|
5
|
+
import { getTestbotPrompt, parseRelatedRepositories, } from "../prompts/testbot/testbot-prompts.js";
|
|
6
|
+
import { readWorkspaceServices } from "../prompts/prompt-utils.js";
|
|
6
7
|
export function registerTestbotResource(server) {
|
|
7
8
|
logger.info("Registering testbot resource");
|
|
8
9
|
// RFC 6570 {+rest} (reserved expansion) captures the entire query string
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
export declare class AnalyticsService {
|
|
3
3
|
static pushTestGenerationToolEvent(toolName: string, result: CallToolResult, params: Record<string, any>): Promise<void>;
|
|
4
|
-
static pushMCPToolEvent(toolName: string, result: CallToolResult | undefined, params: Record<string,
|
|
4
|
+
static pushMCPToolEvent(toolName: string, result: CallToolResult | undefined, params: Record<string, any>): Promise<void>;
|
|
5
5
|
/**
|
|
6
6
|
* Track server crash events
|
|
7
7
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TestExecutionResult, BatchExecutionResult, TestExecutionOptions, ProgressCallback } from "../types/TestExecution.js";
|
|
2
|
-
|
|
2
|
+
import { EXECUTOR_DOCKER_IMAGE } from "../utils/versions.js";
|
|
3
|
+
export { EXECUTOR_DOCKER_IMAGE };
|
|
3
4
|
export declare const PLAYWRIGHT_CONFIG_FILES: string[];
|
|
4
5
|
export declare const EXCLUDED_MOUNT_ITEMS: string[];
|
|
5
6
|
export declare const TEST_REPO_CONTAINER_SUBDIR = ".skyramp-test-repo";
|
|
@@ -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
|
|
@@ -565,10 +565,15 @@ export class TestExecutionService {
|
|
|
565
565
|
testFilePath,
|
|
566
566
|
options.testType,
|
|
567
567
|
];
|
|
568
|
+
const networkMode = options.dockerNetwork?.trim()
|
|
569
|
+
? options.dockerNetwork.trim()
|
|
570
|
+
: options.useHostNetwork && process.platform === "linux"
|
|
571
|
+
? "host"
|
|
572
|
+
: undefined;
|
|
568
573
|
// Prepare host config with mounts
|
|
569
574
|
const hostConfig = {
|
|
570
575
|
ExtraHosts: ["host.docker.internal:host-gateway"],
|
|
571
|
-
...(
|
|
576
|
+
...(networkMode ? { NetworkMode: networkMode } : {}),
|
|
572
577
|
Mounts: [
|
|
573
578
|
{
|
|
574
579
|
Type: "bind",
|
|
@@ -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,10 +2,11 @@ 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, getWorkspaceSkipTLSVerify, getWorkspaceDefaultQueryParams, mergeQueryParamsString, } 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";
|
|
9
|
+
import { TestType } from "../types/TestTypes.js";
|
|
9
10
|
import { logger } from "../utils/logger.js";
|
|
10
11
|
import { normalizeLanguageParams } from "../utils/normalizeParams.js";
|
|
11
12
|
import { stageGeneratedPaths, resolveOutputDir } from "../utils/gitStaging.js";
|
|
@@ -24,6 +25,19 @@ export function deriveEffectiveFramework(language, framework) {
|
|
|
24
25
|
const lang = (language ?? "").trim().toLowerCase();
|
|
25
26
|
return lang === "typescript" || lang === "javascript" ? "playwright" : "";
|
|
26
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Test types whose generation is driven by a recorded Playwright trace, not a
|
|
30
|
+
* live query-string request the same way contract/fuzz/smoke/load/integration
|
|
31
|
+
* are. Their schemas (uiTestSchema, e2eTestSchema) don't expose a queryParams
|
|
32
|
+
* field at all, so there's no way for a caller to override or even know this
|
|
33
|
+
* field exists — injecting an unrequested, uneditable workspace
|
|
34
|
+
* defaultQueryParams value into that path is not what the design intends
|
|
35
|
+
* (SKYR-4050).
|
|
36
|
+
*/
|
|
37
|
+
const SKIP_DEFAULT_QUERY_PARAMS_TEST_TYPES = new Set([
|
|
38
|
+
TestType.UI,
|
|
39
|
+
TestType.E2E,
|
|
40
|
+
]);
|
|
27
41
|
export class TestGenerationService {
|
|
28
42
|
client;
|
|
29
43
|
constructor() {
|
|
@@ -316,29 +330,15 @@ The generated test file remains unchanged and ready to use as-is.
|
|
|
316
330
|
async executeGeneration(generateOptions) {
|
|
317
331
|
try {
|
|
318
332
|
// Auto-resolve auth from workspace config when authHeader was not explicitly provided.
|
|
319
|
-
// undefined =
|
|
333
|
+
// undefined = auto-resolve; "" = caller explicitly said skip-auth.
|
|
320
334
|
if (generateOptions.authHeader === undefined) {
|
|
321
335
|
try {
|
|
322
336
|
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);
|
|
337
|
+
const resolved = await resolveAuthFromWorkspace(repoPath, generateOptions.authHeader, generateOptions.authScheme);
|
|
338
|
+
if (resolved) {
|
|
339
|
+
generateOptions.authHeader = resolved.authHeader;
|
|
340
|
+
if (resolved.authScheme !== undefined) {
|
|
341
|
+
generateOptions.authScheme = resolved.authScheme;
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
344
|
}
|
|
@@ -364,6 +364,24 @@ The generated test file remains unchanged and ready to use as-is.
|
|
|
364
364
|
logger.warning("Could not resolve skipTLSVerify from workspace config");
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
|
+
// Workspace-declared default query params (SKYR-4050): api.defaultQueryParams
|
|
368
|
+
// are attached to every generated request for the service. Existing
|
|
369
|
+
// queryParams entries (explicit caller values) always win on a key collision.
|
|
370
|
+
if (!SKIP_DEFAULT_QUERY_PARAMS_TEST_TYPES.has(this.getTestType())) {
|
|
371
|
+
try {
|
|
372
|
+
const repoPath = generateOptions.outputDir || process.cwd();
|
|
373
|
+
const defaults = await getWorkspaceDefaultQueryParams(repoPath);
|
|
374
|
+
if (defaults) {
|
|
375
|
+
generateOptions.queryParams = mergeQueryParamsString(generateOptions.queryParams, defaults);
|
|
376
|
+
logger.info("Merged workspace defaultQueryParams into queryParams", {
|
|
377
|
+
keys: Object.keys(defaults),
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
logger.warning("Could not resolve default query params from workspace config");
|
|
383
|
+
}
|
|
384
|
+
}
|
|
367
385
|
if (generateOptions.traceFilePath) {
|
|
368
386
|
const traceAuth = this.extractAuthFromTrace(generateOptions.traceFilePath, generateOptions.generateInclude, generateOptions.generateExclude);
|
|
369
387
|
if (traceAuth) {
|
|
@@ -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
|
@@ -11,6 +11,9 @@ export const TOOL_PHASE_MAP = {
|
|
|
11
11
|
skyramp_ui_test_generation: "generating",
|
|
12
12
|
skyramp_batch_scenario_test_generation: "generating",
|
|
13
13
|
skyramp_mock_generation: "generating",
|
|
14
|
+
skyramp_batch_mock_generation: "generating",
|
|
15
|
+
skyramp_generate_enriched_integration_test: "generating",
|
|
16
|
+
skyramp_enrich_test_with_mocks: "generating",
|
|
14
17
|
skyramp_execute_test: { before: "maintaining", after: "executing" },
|
|
15
18
|
skyramp_run_existing_tests: { before: "maintaining", after: "executing" },
|
|
16
19
|
skyramp_analyze_test_health: "maintaining",
|
|
@@ -31,6 +34,7 @@ export const TOOLS_WITHOUT_PHASE = new Set([
|
|
|
31
34
|
"skyramp_init_scan",
|
|
32
35
|
"skyramp_init_workspace",
|
|
33
36
|
"skyramp_one_click_tool",
|
|
37
|
+
"skyramp_setup_local_dev_worker",
|
|
34
38
|
"skyramp_actions",
|
|
35
39
|
"skyramp_start_trace_collection",
|
|
36
40
|
"skyramp_stop_trace_collection",
|
|
@@ -38,4 +42,6 @@ export const TOOLS_WITHOUT_PHASE = new Set([
|
|
|
38
42
|
"skyramp_modularization",
|
|
39
43
|
"skyramp_reuse_code",
|
|
40
44
|
"skyramp_enhance_assertions",
|
|
45
|
+
"skyramp_preflight_mock_check",
|
|
46
|
+
"skyramp_query_proxy_mocks",
|
|
41
47
|
]);
|