@skyramp/mcp 0.2.150-rc.ldw- → 0.2.150-rc.ldw

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.
Files changed (41) hide show
  1. package/build/commands/commandLibrary.js +5 -2
  2. package/build/commands/commandLibrary.test.d.ts +1 -0
  3. package/build/commands/commandLibrary.test.js +59 -0
  4. package/build/index.js +10 -7
  5. package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +3 -3
  6. package/build/prompts/local-dev/local-dev-plan.d.ts +3 -3
  7. package/build/prompts/local-dev/local-dev-plan.js +30 -24
  8. package/build/prompts/local-dev/local-dev-prompts.js +4 -2
  9. package/build/services/AnalyticsService.d.ts +1 -1
  10. package/build/services/TestExecutionService.js +6 -1
  11. package/build/services/TestExecutionService.test.js +33 -0
  12. package/build/tools/code-refactor/enhanceAssertionsTool.js +6 -1
  13. package/build/tools/enrichTestWithMocksTool.d.ts +16 -3
  14. package/build/tools/enrichTestWithMocksTool.js +438 -125
  15. package/build/tools/enrichTestWithMocksTool.test.js +206 -0
  16. package/build/tools/executeSkyrampTestTool.js +9 -6
  17. package/build/tools/generate-tests/batchMockGenerationTool.d.ts +7 -1
  18. package/build/tools/generate-tests/batchMockGenerationTool.js +43 -6
  19. package/build/tools/generate-tests/generateMockRestTool.d.ts +77 -4
  20. package/build/tools/generate-tests/generateMockRestTool.js +43 -3
  21. package/build/tools/generate-tests/loadTestSchema.d.ts +1 -1
  22. package/build/tools/generateEnrichedIntegrationTestTool.js +34 -18
  23. package/build/tools/generateEnrichedIntegrationTestTool.test.d.ts +1 -0
  24. package/build/tools/generateEnrichedIntegrationTestTool.test.js +44 -0
  25. package/build/tools/localDevWorkflowFixes.test.js +118 -7
  26. package/build/tools/preflightMockCheckTool.js +6 -1
  27. package/build/tools/submitReportTool.d.ts +14 -14
  28. package/build/tools/trace/startTraceCollectionTool.js +3 -3
  29. package/build/types/TestExecution.d.ts +1 -0
  30. package/build/types/TestTypes.d.ts +9 -6
  31. package/build/types/TestTypes.js +8 -4
  32. package/build/utils/branchDiff.test.js +2 -0
  33. package/build/utils/featureFlags.d.ts +5 -0
  34. package/build/utils/featureFlags.js +7 -0
  35. package/build/utils/grpcMockValidation.js +1 -1
  36. package/build/utils/grpcMockValidation.test.js +1 -1
  37. package/build/utils/mockCompatibility.js +4 -2
  38. package/build/utils/mockCompatibility.test.js +0 -9
  39. package/build/workspace/workspace.d.ts +14 -14
  40. package/build/workspace/workspace.js +2 -1
  41. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { isLocalDevEnabled } from "../utils/featureFlags.js";
1
+ import { isLocalDevEnabled, isOneClickEnabled } from "../utils/featureFlags.js";
2
2
  import { TEST_GIVEN_ENDPOINT_COMPREHENSIVELY_COMMAND } from "./testThisEndpointCommand.js";
3
3
  import { FULLREPO_RECOMMEND_GENERATE_EXECUTE_TOPN_TESTS_COMMAND } from "./recommendTestsAndExecuteCommand.js";
4
4
  import { LOCAL_DEV_TEST_CHANGES_COMMAND } from "./localDevTestChangesCommand.js";
@@ -9,7 +9,10 @@ const BASE_COMMAND_LIBRARY = {
9
9
  };
10
10
  /** All enabled predefined one-click commands */
11
11
  function getCommandLibrary() {
12
- const commands = { ...BASE_COMMAND_LIBRARY };
12
+ const commands = {};
13
+ if (isOneClickEnabled()) {
14
+ Object.assign(commands, BASE_COMMAND_LIBRARY);
15
+ }
13
16
  if (isLocalDevEnabled()) {
14
17
  commands.local_dev_test_changes = LOCAL_DEV_TEST_CHANGES_COMMAND;
15
18
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,59 @@
1
+ import { afterEach, describe, expect, it } from "@jest/globals";
2
+ import { getCommandIds, lookupCommand } from "./commandLibrary.js";
3
+ const originalOneClick = process.env.SKYRAMP_FEATURE_ONE_CLICK;
4
+ const originalLocalDev = process.env.SKYRAMP_FEATURE_LOCAL_DEV;
5
+ function setFlags(oneClick, localDev) {
6
+ if (oneClick === undefined) {
7
+ delete process.env.SKYRAMP_FEATURE_ONE_CLICK;
8
+ }
9
+ else {
10
+ process.env.SKYRAMP_FEATURE_ONE_CLICK = oneClick;
11
+ }
12
+ if (localDev === undefined) {
13
+ delete process.env.SKYRAMP_FEATURE_LOCAL_DEV;
14
+ }
15
+ else {
16
+ process.env.SKYRAMP_FEATURE_LOCAL_DEV = localDev;
17
+ }
18
+ }
19
+ afterEach(() => {
20
+ if (originalOneClick === undefined) {
21
+ delete process.env.SKYRAMP_FEATURE_ONE_CLICK;
22
+ }
23
+ else {
24
+ process.env.SKYRAMP_FEATURE_ONE_CLICK = originalOneClick;
25
+ }
26
+ if (originalLocalDev === undefined) {
27
+ delete process.env.SKYRAMP_FEATURE_LOCAL_DEV;
28
+ }
29
+ else {
30
+ process.env.SKYRAMP_FEATURE_LOCAL_DEV = originalLocalDev;
31
+ }
32
+ });
33
+ describe("command library feature gates", () => {
34
+ it("exposes no one-click workflows when both one-click and local-dev are disabled", () => {
35
+ setFlags(undefined, undefined);
36
+ expect(getCommandIds()).toEqual([]);
37
+ });
38
+ it("exposes only base one-click workflows when one-click is enabled", () => {
39
+ setFlags("1", undefined);
40
+ expect(getCommandIds().sort()).toEqual([
41
+ "full_repo_scan_recommend_generate_and_execute_top_n_tests",
42
+ "test_given_endpoint_comprehensively",
43
+ ]);
44
+ });
45
+ it("exposes only local-dev when local-dev is enabled without general one-click", () => {
46
+ setFlags(undefined, "1");
47
+ expect(getCommandIds()).toEqual(["local_dev_test_changes"]);
48
+ expect(lookupCommand("local_dev_test_changes").id).toBe("local_dev_test_changes");
49
+ expect(() => lookupCommand("test_given_endpoint_comprehensively")).toThrow(/Unknown workflow/);
50
+ });
51
+ it("exposes base and local-dev workflows when both flags are enabled", () => {
52
+ setFlags("1", "1");
53
+ expect(getCommandIds().sort()).toEqual([
54
+ "full_repo_scan_recommend_generate_and_execute_top_n_tests",
55
+ "local_dev_test_changes",
56
+ "test_given_endpoint_comprehensively",
57
+ ]);
58
+ });
59
+ });
package/build/index.js CHANGED
@@ -52,13 +52,16 @@ const localDevInstructions = `\n- When the user asks to test local changes, vali
52
52
  const fullOneClickInstructions = `\n- When the user asks to comprehensively, thoroughly, or deeply test a specific endpoint: MUST call \`skyramp_one_click_tool\` with workflow \`test_given_endpoint_comprehensively\` first. Do NOT self-orchestrate the steps manually.\n- When the user asks to scan the full repo, recommend, generate, and execute top N tests: MUST call \`skyramp_one_click_tool\` with workflow \`full_repo_scan_recommend_generate_and_execute_top_n_tests\`.${localDevEnabled ? localDevInstructions : ""}`;
53
53
  // Testbot suppresses one-click workflows (it has its own orchestration via testbot prompt),
54
54
  // but local-dev routing is always active when the flag is set — they serve different use cases.
55
- const oneClickInstructions = isTestbotEnabled()
56
- ? (localDevEnabled ? localDevInstructions : "")
57
- : oneClickEnabled
58
- ? fullOneClickInstructions
59
- : localDevEnabled
60
- ? localDevInstructions
61
- : "";
55
+ let oneClickInstructions = "";
56
+ if (isTestbotEnabled()) {
57
+ oneClickInstructions = localDevEnabled ? localDevInstructions : "";
58
+ }
59
+ else if (oneClickEnabled) {
60
+ oneClickInstructions = fullOneClickInstructions;
61
+ }
62
+ else if (localDevEnabled) {
63
+ oneClickInstructions = localDevInstructions;
64
+ }
62
65
  const server = new McpServer({
63
66
  name: "Skyramp MCP Server",
64
67
  version: "1.0.0",
@@ -68,7 +68,7 @@ API schemas (look inside the service directory and check known framework default
68
68
  <runtime_config>
69
69
  Inspect the repo root (and subdirectories like .devcontainer/) for shared runtime configuration:
70
70
  1. CLAUDE.md or AGENTS.md: if either file exists at the repo root, check it for dev/test/CI setup instructions including how to start services, required environment variables, and runtime configuration.
71
- 2. Docker Compose files: scan for ALL compose files including docker-compose.yml, docker-compose.yaml, docker-compose.*.yml (such as docker-compose.testbot.yml or docker-compose.dev.yml), compose.yml, and compose.*.yaml in the repo root and subdirectories. Distinguish between application compose files and infrastructure-only compose files. Infrastructure compose files contain only supporting services like databases, Redis, Minio, mail servers, or message queues and do NOT run the application itself. Application compose files contain the actual application services that build from the repo source code (look for "build:" directives pointing to the repo, or services that map to discovered application directories). Only use application compose files for determining runtime, serverStartCommand, and serverStopCommand. When a non-default compose file is found, the serverStartCommand and serverStopCommand must reference it explicitly (such as "docker compose -f docker-compose.testbot.yml up -d <service-name>" and "docker compose -f docker-compose.testbot.yml stop <service-name>"). Docker Compose ALWAYS prefixes the network name with the project name. If compose has "networks: { my-net: ... }", the actual network name is "<project-name>_my-net". If there is no explicit networks section, the default network is "<project-name>_default". The project name is the basename of the working directory where docker compose runs.
71
+ 2. Docker Compose files: scan for ALL compose files including docker-compose.yml, docker-compose.yaml, docker-compose.*.yml (such as docker-compose.testbot.yml or docker-compose.dev.yml), compose.yml, and compose.*.yaml in the repo root and subdirectories. Distinguish between application compose files and infrastructure-only compose files. Infrastructure compose files contain only supporting services like databases, Redis, Minio, mail servers, or message queues and do NOT run the application itself. Application compose files contain the actual application services that build from the repo source code (look for "build:" directives pointing to the repo, or services that map to discovered application directories). Only use application compose files for determining runtime, serverStartCommand, and serverStopCommand. When a non-default compose file is found, the serverStartCommand and serverStopCommand must reference it explicitly (such as "docker compose -f docker-compose.testbot.yml up -d --build <service-name>" and "docker compose -f docker-compose.testbot.yml stop <service-name>"). Docker Compose ALWAYS prefixes the network name with the project name. If compose has "networks: { my-net: ... }", the actual network name is "<project-name>_my-net". If there is no explicit networks section, the default network is "<project-name>_default". The project name is the basename of the working directory where docker compose runs.
72
72
  3. Makefile: extract start and dev targets.
73
73
  4. Root package.json scripts: extract workspace-level commands.
74
74
  </runtime_config>
@@ -139,7 +139,7 @@ Create one service entry per deployable unit. You MUST include every backend/API
139
139
  A repo may have MIXED runtimes. A backend in docker-compose.yml uses "docker" while a frontend run with pnpm or npm locally uses "local". Include ALL services regardless of runtime.
140
140
  If the frontend is bundled inside the API Docker container (no separate frontend service in the compose file), both frontend and backend services should use runtime "docker", share the same baseUrl (the container's exposed URL), and use the same serverStartCommand since they run in the same container.
141
141
  2. runtimeDetails.serverStartCommand: The command to start or deploy the service. It MUST match the runtime:
142
- - For "docker" runtime: Use a Docker command such as "docker compose up -d <service-name>" for the default docker-compose.yml, or "docker compose -f <compose-file> up -d <service-name>" when using a non-default compose file. This is always derivable from the application docker compose file and service name.
142
+ - For "docker" runtime: Use a Docker command that rebuilds application services from the working tree before starting them, such as "docker compose up -d --build <service-name>" for the default docker-compose.yml, or "docker compose -f <compose-file> up -d --build <service-name>" when using a non-default compose file. This is always derivable from the application docker compose file and service name. If the compose service has no build context and only references an image, note that the command cannot rebuild local code.
143
143
  - For "k8s" runtime: Use a deploy command such as "kubectl apply -f deploy/", "helm install myrelease .", or "skaffold run". This is always derivable from the manifests or charts present in the repo.
144
144
  - For "local" runtime: Use an application command such as "uvicorn main:app", "npm run dev", or "java -jar app.jar". Derive from Makefile, package.json scripts, or README. If no start command is discoverable, omit this field entirely.
145
145
  NEVER mix runtime types with incompatible commands (for example, using "uvicorn" with runtime "docker" will cause errors). For "local" runtime, NEVER fabricate a command. Only use commands found in Makefile, package.json scripts, or README.
@@ -160,7 +160,7 @@ Before calling skyramp_init_workspace, confirm all of the following:
160
160
  6. framework matches language (python uses pytest or robot, typescript or javascript uses playwright, java uses junit).
161
161
  7. testDirectory follows the stable resolution rules above: framework config file when present (Playwright testDir in playwright.config.ts, pytest testpaths in pytest.ini or pyproject.toml, JUnit test source dir in pom.xml or build.gradle); otherwise the deterministic default (tests/skyramp for a single service, tests/skyramp/<serviceDirName> for multiple services).
162
162
  8. If serverStartCommand is provided, it matches the runtime. If serverStopCommand is provided, runtime is "docker" and the command is a Docker command.
163
- 9. For services in docker-compose.yml: runtime MUST be "docker" and the command MUST be a docker command such as "docker compose up -d <service-name>". Always include it since it is derivable from the service name.
163
+ 9. For services in docker-compose.yml: runtime MUST be "docker" and the command MUST be a docker command such as "docker compose up -d --build <service-name>" when the service has a build context. Always include it since it is derivable from the service name.
164
164
  10. NEVER use application-level commands (uvicorn, npm, node, python, java, etc.) with runtime "docker".
165
165
  11. For "local" runtime: if no start command is discoverable from Makefile, package.json scripts, or README, omit serverStartCommand rather than guessing.
166
166
  12. dockerNetwork is set only when runtime is "docker".
@@ -4,11 +4,11 @@
4
4
  * Phases: ANALYZE → RECOMMEND → GENERATE → VERIFY → DEPLOY → EXECUTE
5
5
  *
6
6
  * Key design decisions:
7
- * - Mock files are generated in the same language as tests (from workspace config) with get_all_mocks()
8
- * - Tests import get_all_mocks from each mock file, collect into MOCK_SERVICES, deploy collectively
7
+ * - Mock files are generated in the same language as tests (from workspace config) with get_all_mocks()/getAllMocks()
8
+ * - Tests import get_all_mocks()/getAllMocks() from each mock file, collect into MOCK_SERVICES, deploy collectively
9
9
  * - NO YAML mock files, NO CLI commands — ONLY MCP tools
10
10
  * - Execution uses skyramp_execute_test MCP tool exclusively
11
- * - The executor handles mock deployment from the test file automatically
11
+ * - Enriched tests apply mocks at runtime through their language-specific Skyramp client helper
12
12
  */
13
13
  import { PromptPlan } from "../test-recommendation/promptPlan.js";
14
14
  export interface LocalDevPlanCtx {
@@ -4,11 +4,11 @@
4
4
  * Phases: ANALYZE → RECOMMEND → GENERATE → VERIFY → DEPLOY → EXECUTE
5
5
  *
6
6
  * Key design decisions:
7
- * - Mock files are generated in the same language as tests (from workspace config) with get_all_mocks()
8
- * - Tests import get_all_mocks from each mock file, collect into MOCK_SERVICES, deploy collectively
7
+ * - Mock files are generated in the same language as tests (from workspace config) with get_all_mocks()/getAllMocks()
8
+ * - Tests import get_all_mocks()/getAllMocks() from each mock file, collect into MOCK_SERVICES, deploy collectively
9
9
  * - NO YAML mock files, NO CLI commands — ONLY MCP tools
10
10
  * - Execution uses skyramp_execute_test MCP tool exclusively
11
- * - The executor handles mock deployment from the test file automatically
11
+ * - Enriched tests apply mocks at runtime through their language-specific Skyramp client helper
12
12
  */
13
13
  import { PromptPlan } from "../test-recommendation/promptPlan.js";
14
14
  // ── The Plan ─────────────────────────────────────────────────────────────────
@@ -34,11 +34,7 @@ If 0 endpoints detected but changed files contain application code: manually tra
34
34
 
35
35
  If \`skyramp_analyze_changes\` returns an error: retry once for transient errors. If it fails again, report the error and stop.
36
36
 
37
- **If the output file is too large to read** (the Read tool returns a token-limit error): extract only the fields you need with a targeted Bash command, for example:
38
- \`\`\`bash
39
- python3 -c "import json,sys; d=json.load(open('<output_file>')); print(json.dumps({'endpoints': d.get('endpoints',[]), 'recommendations': d.get('recommendations',[])[:${ctx.maxRecommendations}]}, indent=2))"
40
- \`\`\`
41
- Do not retry the Read tool with progressively smaller limits — use the extraction approach on the first failure.`)
37
+ **If the analysis output is too large to inspect**: do not switch to direct shell commands. Use the inline recommendations returned by \`skyramp_analyze_changes\`, fetch the focused MCP resources for this session (summary, endpoints, scenarios, and diff), or rerun with a smaller \`topN\` / \`maxGenerate\` budget so the workflow remains MCP-driven.`)
42
38
  .step("DISCOVER_DEPS", "Discover downstream dependencies", (_ctx) => `For each changed endpoint, read the implementation code and identify ALL outbound service calls:
43
39
 
44
40
  **HTTP/REST** — look for: \`HTTParty\`, \`fetch\`, \`axios\`, \`http.NewRequestWithContext\`, SDK client calls
@@ -103,7 +99,7 @@ For each endpoint under test:
103
99
 
104
100
  5. **For API and downstream contracts**, include scenarios only when they catch a realistic bug:
105
101
  - Provider contract: response field added/renamed/removed, type/nullable mismatch, status-code semantic drift.
106
- - Consumer contract: our code sends the wrong downstream request shape/header/path, or parses a response field that may be missing/renamed.
102
+ - Consumer contract: our code sends the wrong downstream request body/header/path, or parses a response field that may be missing/renamed.
107
103
 
108
104
  6. **For full API flows**, treat API E2E coverage as integration coverage:
109
105
  - **API full-flow coverage is integration coverage** in this workflow. Do NOT label API-only workflows as E2E.
@@ -121,7 +117,7 @@ For each endpoint under test:
121
117
 
122
118
  9. **Output a unified scenario table** (do NOT generate code yet):
123
119
 
124
- | # | Bug-Catching Target | Best Test Type | Scenario Name | Mock Config | Expected Response | Priority |
120
+ | # | What this verifies | Best Test Type | Scenario Name | Mock Config | Expected Response | Priority |
125
121
  |---|---------------------|----------------|---------------|-------------|-------------------|----------|
126
122
  | 1 | Order creation applies downstream pricing rules | integration | happy-path-with-pricing | All required downstream success | 201 + calculated totals | high |
127
123
  | 2 | Payment rejection rolls back order creation | integration | payment-rejects | Payment service → 422 | expected error + no persisted order | high |
@@ -140,7 +136,7 @@ For each endpoint under test:
140
136
  })
141
137
  .step("GENERATE_MOCKS_AND_TESTS", "Generate highest-value tests", (ctx) => `**Preferred: Use batch tools when available.**
142
138
  - Call \`skyramp_batch_mock_generation\` with all mock specs in one array instead of individual \`skyramp_mock_generation\` calls.
143
- - Call \`skyramp_generate_enriched_integration_test\` instead of separate generate + enrich calls.
139
+ - For Python, TypeScript, JavaScript, and Java integration tests, call \`skyramp_generate_enriched_integration_test\` instead of separate generate + enrich calls when generated mocks must be wired into the test.
144
140
  - Call \`skyramp_enhance_assertions\` with \`autoApply: true\` for compact output format — the returned instructions must still be applied by you to the test file.
145
141
 
146
142
  **Run \`skyramp_preflight_mock_check\` FIRST, before reading any .proto files or constructing any mock spec.**
@@ -156,9 +152,11 @@ Work through the scenario list from Phase 2 in **bug-catching priority order**.
156
152
 
157
153
  **Side-effect rule:** Integration scenarios must account for all downstream side effects discovered for that scenario. Generate mocks only for side-effect dependencies selected for mocking by service routing, protocol routing, or the default third-party policy. If a Kafka-compatible broker is a real local infrastructure service, keep it real and document that decision instead of generating Kafka mocks. Full-scenario integration tests should cover the complete API workflow from trigger request through downstream calls and final observable result.
158
154
 
155
+ **Outbound assertion rule:** If the behavior being tested depends on what the service under test sends to a mocked dependency, the test must prove the outbound interaction, not just the final SUT response. For REST mocks, provide \`requestData\` for the expected body and \`requestAware: true\`; for gRPC, include the expected request fields in the scenario's verification notes when the mock/test API can assert them; for Kafka producers, use \`requestData\` and include the Kafka mock in \`MOCK_SERVICES\`. If Skyramp cannot assert the outbound body/header/path/event payload for the selected protocol, mark that scenario **Blocked — outbound assertion unsupported** instead of reporting a green pass.
156
+
159
157
  **REST mock routing rule:** Generated REST mock files may contain a fallback like \`URL = skyramp.get_base_url(..., "http://localhost:8080")\`. Do not manually edit the generated mock file for local-dev routing. The enrichment step parses the mock generation \`# Command\` line and overwrites each deployable REST mock's \`mock.url\` with the original service origin from \`endpointURL\` (for example, \`http://ams:4000\` or \`http://billing:5001\`). If the command target itself is loopback, regenerate the mock with the original Docker service hostname.
160
158
 
161
- **Mock semantics rule:** Kafka mock generation must use \`kafkaTopic\` for the topic name; do not pass \`method: "produce"\` or \`method: "consume"\`. Use exactly one of \`requestData\` for producer validation or \`responseData\` for consumer messages. Core Skyramp infers Kafka \`produce\` from \`request_body\` and \`consume\` from \`response_body\`; passing both or neither is invalid. Kafka mock files are generated, included in \`MOCK_SERVICES\`, included in \`MOCK_TARGET_URLS\`, and applied by \`apply_all_mocks(client)\` after enrichment sets \`mock.url\` to the original broker \`host:port\` from the generation command. If a gRPC failure scenario depends on \`GRPC::Unavailable\`, \`DeadlineExceeded\`, \`NOT_FOUND\`, or another status error, gRPC status errors cannot be represented as normal responseData. Use a supported failure mechanism if available; otherwise record the scenario as blocked instead of generating a mock body like \`{ "error": "UNAVAILABLE" }\`.
159
+ **Mock semantics rule:** Kafka mock generation must use \`kafkaTopic\` for the topic name; do not pass \`method: "produce"\` or \`method: "consume"\`. Use exactly one of \`requestData\` for producer validation or \`responseData\` for consumer messages. Core Skyramp infers Kafka \`produce\` from \`request_body\` and \`consume\` from \`response_body\`; passing both or neither is invalid. Kafka mock files are generated, included in \`MOCK_SERVICES\`, included in \`MOCK_TARGET_URLS\`, and applied by the language-specific apply helper after enrichment sets \`mock.url\` to the original broker \`host:port\` from the generation command. If a gRPC failure scenario depends on \`GRPC::Unavailable\`, \`DeadlineExceeded\`, \`NOT_FOUND\`, or another status error, gRPC status errors cannot be represented as normal responseData. Use a supported failure mechanism if available; otherwise record the scenario as blocked instead of generating a mock body like \`{ "error": "UNAVAILABLE" }\`.
162
160
 
163
161
  For each selected scenario, follow the workflow matching its best test type:
164
162
 
@@ -176,6 +174,7 @@ REST spec fields:
176
174
  - \`method\`: HTTP method
177
175
  - \`responseData\`: realistic JSON response for THIS scenario
178
176
  - \`requestAware\`: true
177
+ - \`requestData\`: required when this scenario's bug-catching value depends on outbound request-body correctness. It must contain the exact fields the service under test should send to the downstream service (for example, \`billing_email\`, not an adjacent field such as \`adverse_action_email\`). For response-only downstream scenarios where body correctness is not what "What this verifies" covers, omit \`requestData\` unless it is needed for request-aware routing. If required request-body matching cannot be generated for the mock, record the scenario as blocked instead of producing a status-only test.
179
178
 
180
179
  gRPC spec fields:
181
180
  - \`protocol\`: "grpc"
@@ -241,7 +240,9 @@ Call \`skyramp_contract_test_generation\` with:
241
240
 
242
241
  Consumer contract tests have mocks wired **inline by default** — the generated test file includes \`MockV2\` creation and \`client.apply_mock(mock)\` directly in the test function. **No separate \`skyramp_enrich_test_with_mocks\` call is needed for contract tests.** The mock defines the expected downstream response shape and the test validates the contract against it.
243
242
 
244
- Consumer contracts catch: field renamed downstream, new required header added, response shape changed, status code semantics changed.
243
+ Consumer contract tests are incomplete until they invoke real consumer/SUT code that triggers the outbound request. A generated stub that calls \`client.send_request\` directly against the mock only proves the mock responds; it does not prove the application sends the right body/header/path. If you cannot wire real consumer code, do not execute or report the contract as PASS — mark the scenario **Blocked — consumer wiring unavailable**.
244
+
245
+ Consumer contracts catch: wrong outbound request body/header/path, field renamed downstream, new required header added, response shape changed, status code semantics changed.
245
246
 
246
247
  ---
247
248
 
@@ -266,7 +267,8 @@ Generate UI tests only when the diff includes frontend changes, a changed UI rou
266
267
  - **Idempotent test data** — use unique values that won't collide on re-run (UUID suffixes for emails, random names, timestamps). Do NOT use static emails like "test@example.com" that will hit uniqueness constraints.
267
268
  - **Auth headers** — generated tests should read auth from \`SKYRAMP_TEST_TOKEN\` only. Do not add literal token fallbacks to test files; pass the discovered token via \`skyramp_execute_test.token\` or the MCP server environment before execution. A 401 caused by an unset token is an execution setup bug, not a reason to hardcode credentials.
268
269
  - **Contract responseData** — always provide the FULL expected shape so the test asserts on field presence, types, and values — not just status codes.
269
- - **Assertion depth (integration tests)** — every integration test must assert the specific fields that prove the scenario's Bug-Catching Target, not just the HTTP status code. Use \`skyramp.get_response_value(response, "field.path")\` to assert field values from the Expected Response column of the scenario table. Example: for a "customer config applied" scenario, assert \`skyramp.get_response_value(response, "tax_exempt") == True\` and \`skyramp.get_response_value(response, "default_compliance_state") == "<expected_value>"\` where \`<expected_value>\` is the actual value the mock returns for that scenario (e.g., a jurisdiction code like \`"CA"\`, a status string, or a numeric value — derive it from the mock's responseData, not from a guess). Status-code-only assertions never satisfy this rule.
270
+ - **Assertion depth (integration tests)** — every integration test must assert the specific fields that prove the scenario's "What this verifies" column, not just the HTTP status code. Use \`skyramp.get_response_value(response, "field.path")\` to assert field values from the Expected Response column of the scenario table. Example: for a "customer config applied" scenario, assert \`skyramp.get_response_value(response, "tax_exempt") == True\` and \`skyramp.get_response_value(response, "default_compliance_state") == "<expected_value>"\` where \`<expected_value>\` is the actual value the mock returns for that scenario (e.g., a jurisdiction code like \`"CA"\`, a status string, or a numeric value — derive it from the mock's responseData, not from a guess). Status-code-only assertions never satisfy this rule.
271
+ - **No hollow tests** — never execute or report a generated test as PASS if it contains unresolved placeholders such as \`TODO\`, a standalone \`pass\` statement in a test body, \`NotImplemented\`, "replace with real consumer code", or an executable consumer-contract stub that directly calls the mocked endpoint instead of application/consumer code. A direct-mock stub may remain only when it is explicitly skipped or strict xfail and a separate executable real consumer/SUT test exists. Implement the missing check or mark the scenario blocked.
270
272
  - Failure recovery: retry once, then skip and log in issuesFound.
271
273
 
272
274
  Keep advancing in bug-catching priority order until the highest-value scenarios have test files (up to ${ctx.maxGenerate} total).`)
@@ -285,12 +287,14 @@ This injects import statements from the generated mock files — no data duplica
285
287
  .step("VALIDATE_MOCKS", "Confirm test setup is complete", (ctx) => `Verify:
286
288
 
287
289
  1. Every scenario from Phase 2 has a corresponding test file in \`${ctx.repositoryPath}/tests/skyramp/\`
288
- 2. For integration tests with selected downstream mocks: mock files exist in \`mocks/\` for each selected service/protocol and test files import them via a non-empty \`MOCK_SERVICES\` plus \`apply_all_mocks(client)\`; if \`MOCK_SERVICES = {}\` or any expected mock import is missing for a mocked dependency, return to \`GENERATE_MOCKS_AND_TESTS\` or \`ENRICH_TESTS\` before any edits or execution
290
+ 2. For integration tests with selected downstream mocks: mock files exist in \`mocks/\` for each selected service/protocol and test files import them via a non-empty \`MOCK_SERVICES\` plus the language-specific apply helper (\`apply_all_mocks(client)\` for Python or \`applyAllMocks(client)\` for TypeScript/JavaScript/Java); if \`MOCK_SERVICES = {}\` or any expected mock import is missing for a mocked dependency, return to \`GENERATE_MOCKS_AND_TESTS\` or \`ENRICH_TESTS\` before any edits or execution
289
291
  3. For consumer contract tests: mocks are wired inline (\`client.apply_mock(mock)\` present in the test function) — no separate mock files needed
290
292
  4. For provider contract tests: no mocks — test hits the real endpoint directly
291
293
  5. Test files use idempotent data (UUID-suffixed emails, random values — not hardcoded strings)
292
294
  6. Auth headers are present in all test requests
293
- 7. **Integration tests have scenario-driven assertions** — each test asserts the specific response fields that prove the Bug-Catching Target from the scenario table, not only the status code. If a test only asserts \`response.status_code == 201\` (or equivalent) without any field-level assertion on the behavior being tested, it is incomplete. Add \`skyramp.get_response_value()\` assertions for the key fields before executing.
295
+ 7. **Integration tests have scenario-driven assertions** — each test asserts the specific response fields that prove the "What this verifies" column from the scenario table, not only the status code. If a test only asserts \`response.status_code == 201\` (or equivalent) without any field-level assertion on the behavior being tested, it is incomplete. Add \`skyramp.get_response_value()\` assertions for the key fields before executing.
296
+ 8. **Outbound side effects are verified** — for every selected REST/gRPC/Kafka mock whose request body/header/path/event payload is the behavior under test, the mock spec or test includes a request/event assertion. If the toolchain cannot express that assertion, mark the scenario blocked instead of treating a response-only test as coverage.
297
+ 9. **No unresolved stubs** — no generated test file contains \`TODO\`, a standalone \`pass\` statement in a test body, \`NotImplemented\`, "replace with real consumer code", or an executable consumer-contract test that only calls the mock directly. A skipped or strict xfail direct-mock reference is allowed only when the file also contains a separate executable real consumer/SUT test. These are blocking defects, not passing tests.
294
298
 
295
299
  **If any mock file is missing for integration tests:** re-run \`skyramp_mock_generation\` for that service/endpoint.`)
296
300
  .step("ENHANCE_ASSERTIONS", "Enhance assertions for each test file", (ctx) => `For each test file in \`${ctx.repositoryPath}/tests/skyramp/\`, call \`skyramp_enhance_assertions\` with:
@@ -323,12 +327,12 @@ Steps:
323
327
  2. Identify which docker-compose service names correspond to services selected for mocking (e.g., if \`ams\` is selected for mocking, do not start the \`ams\` container).
324
328
  3. Stop any mocked service containers that are already running:
325
329
  \`docker compose stop <mocked-service-1> <mocked-service-2> ...\`
326
- 4. Bring up only the SUT and its real dependencies — start selectively:
327
- \`docker compose up -d <sut-service> <db> <cache> <broker> <real-first-party-services>\`
328
- Do **not** run plain \`docker compose up -d\` when it would start mocked downstream services. If the compose file cannot start selectively, start the minimum required profile/services and then re-check that mocked service containers are stopped before executing tests.
330
+ 4. Rebuild and bring up only the SUT and its real dependencies — start selectively:
331
+ \`docker compose up -d --build <sut-service> <db> <cache> <broker> <real-first-party-services>\`
332
+ Do **not** run plain \`docker compose up -d\` when it would start mocked downstream services or reuse a stale SUT image. If the compose file cannot start selectively, start the minimum required profile/services and then re-check that mocked service containers are stopped before executing tests. If the SUT service has no compose \`build\` context, record that local code cannot be rebuilt by compose before execution.
329
333
  5. Wait for health checks to pass and verify the service under test responds (e.g., curl the health endpoint or send a minimal request).
330
334
 
331
- The \`skyramp_execute_test\` tool handles mock deployment automatically it reads \`MOCK_SERVICES\` from the enriched test file, deploys selected mocks to the Skyramp worker, then runs the test.
335
+ The \`skyramp_execute_test\` tool runs the enriched test in the language runner. The enriched test applies selected mocks at runtime through its language-specific helper before exercising the service.
332
336
 
333
337
  **Do NOT proceed to Execute until: (a) the service under test responds, and (b) all mocked service containers are confirmed stopped.**`)
334
338
  .subStep("DEPLOY_GRPC", "Handle gRPC dependencies", (_ctx) => `If the dependency map from Phase 1 includes gRPC services selected for mocking:
@@ -364,9 +368,11 @@ To regenerate a single missing Kafka mock, call \`skyramp_mock_generation\` with
364
368
 
365
369
  **If \`skyramp_execute_test\` is unavailable** (tool not found, MCP disconnected, or context compaction removed it): do NOT fall back to any direct runtime. Stop execution immediately, list the test files that could not be executed, and tell the user: "The skyramp_execute_test tool is unavailable. Please reconnect the Skyramp MCP server and re-run. Do not use pytest or any direct runtime — doing so bypasses mock deployment and Skyramp telemetry."
366
370
 
367
- The \`skyramp_execute_test\` MCP tool handles execution. For enriched integration tests, it reads \`MOCK_SERVICES\`, deploys selected mocks to the Skyramp worker, then runs the test in a container. Contract tests run without \`MOCK_SERVICES\`.
371
+ The \`skyramp_execute_test\` MCP tool handles execution. For enriched integration tests, it runs the test in a container; the test's generated helper reads \`MOCK_SERVICES\`, clears stale mocks, and applies selected mocks before requests execute. Contract tests run without \`MOCK_SERVICES\`.
372
+
373
+ **Pre-execution gate:** Before every integration test execution, inspect the test file. If it does not contain \`MOCK_SERVICES\` and the language-specific apply helper (\`apply_all_mocks(client)\` for Python or \`applyAllMocks(client)\` for TypeScript/JavaScript/Java), or if it contains an empty \`MOCK_SERVICES\` collection for a scenario with downstream mocks, do NOT execute it. Call \`skyramp_enrich_test_with_mocks\` first and re-check the file. If \`MOCK_SERVICES\` is non-empty, execution is invalid until every real service selected for mocking is stopped.
368
374
 
369
- **Pre-execution gate:** Before every integration test execution, inspect the test file. If it does not contain both \`MOCK_SERVICES\` and \`apply_all_mocks(client)\`, or if it contains \`MOCK_SERVICES = {}\` for a scenario with downstream mocks, do NOT execute it. Call \`skyramp_enrich_test_with_mocks\` first and re-check the file. If \`MOCK_SERVICES\` is non-empty, execution is invalid until every real service selected for mocking is stopped.
375
+ **No-hollow-pass gate:** Before executing any generated test, inspect it for unresolved placeholders or stub-only behavior. Do NOT execute or report PASS for files containing \`TODO\`, a standalone \`pass\` statement in a test body, \`NotImplemented\`, "replace with real consumer code", or executable consumer-contract code that calls the mocked endpoint directly without invoking application/consumer code. Skipped or strict xfail direct-mock reference stubs may remain only when a separate executable real consumer/SUT test exists; the skipped or strict xfail stub never counts as PASS. Strict xfail must fail the run on XPASS (for pytest, \`@pytest.mark.xfail(strict=True)\`). Fix the test or report the scenario as blocked.
370
376
 
371
377
  **Execution — one call per test file:**
372
378
  \`\`\`
@@ -379,7 +385,7 @@ skyramp_execute_test({
379
385
  })
380
386
  \`\`\`
381
387
 
382
- That's it. Pass \`token: ""\` so the executor injects \`SKYRAMP_TEST_TOKEN\` from the environment when auth is configured. For provider contract tests, omit \`contractMode\` or pass \`contractMode: "provider"\` so \`SKYRAMP_TEST_BASE_URL\` is injected. For consumer contract tests with inline mocks, pass \`contractMode: "consumer"\`. For integration tests, mock deployment is automatic the tool reads \`MOCK_SERVICES\` from the enriched test file and deploys before execution. Provider contract tests hit the service under test directly; consumer contract tests use any inline mocks generated in the contract test itself; UI tests replay the generated browser workflow.
388
+ That's it. Pass \`token: ""\` so the executor injects \`SKYRAMP_TEST_TOKEN\` from the environment when auth is configured. For provider contract tests, omit \`contractMode\` or pass \`contractMode: "provider"\` so \`SKYRAMP_TEST_BASE_URL\` is injected. For consumer contract tests with inline mocks, pass \`contractMode: "consumer"\`. For integration tests, mock deployment is automatic because the enriched test helper reads \`MOCK_SERVICES\`, clears stale mocks, and applies them before exercising the service. Provider contract tests hit the service under test directly; consumer contract tests use any inline mocks generated in the contract test itself; UI tests replay the generated browser workflow.
383
389
 
384
390
  **On failure:**
385
391
  1. Read the error output to diagnose the root cause.
@@ -393,7 +399,7 @@ That's it. Pass \`token: ""\` so the executor injects \`SKYRAMP_TEST_TOKEN\` fro
393
399
 
394
400
  **IMPORTANT: Execute tests SEQUENTIALLY (one at a time).** Do NOT send multiple \`skyramp_execute_test\` calls in parallel — concurrent execution overwhelms the stdio transport and causes MCP disconnection. Wait for each test to complete before starting the next.
395
401
 
396
- **Completion gate:** Do not report success after only one passing test if more generated test files or high-priority scenario rows remain. A passing integration test with selected mocked downstreams is not valid if the test has empty \`MOCK_SERVICES\` or if required selected mocks were removed and replaced by live Compose stubs. Execute every generated test file and report any high-priority scenarios that could not be generated or executed, including the reason.`)
402
+ **Completion gate:** Do not report success after only one passing test if more generated test files or high-priority scenario rows remain. A passing integration test with selected mocked downstreams is not valid if the test has empty \`MOCK_SERVICES\`, if required selected mocks were removed and replaced by live Compose stubs, or if the scenario's value depends on an outbound request/event that was not asserted. Execute every generated test file and report any high-priority scenarios that could not be generated or executed, including the reason.`)
397
403
  .step("REPORT_RESULTS", "Report results to user", (_ctx) => `Present a summary to the user covering:
398
404
 
399
405
  - **What changed**: 2-3 sentence summary of the diff
@@ -55,12 +55,12 @@ ${sandboxWorkerBlock ? sandboxWorkerBlock + "\n" : ""}${serviceContext ? service
55
55
  - Always generate NEW tests targeting the behavior introduced in this diff — do not skip generation because an existing test file covers the same endpoint.
56
56
  - For every endpoint under test, discover ALL downstream dependencies (HTTP, gRPC, Kafka) by reading the implementation code. Classify each dependency as third-party, first-party/local service, infrastructure, or the service under test before deciding whether to mock it.
57
57
  - **Default mock scope** — mock only true third-party downstream services by default. Keep first-party/local services, infrastructure, Kafka-compatible brokers, and the service under test real unless the user explicitly asks to mock them via service names or protocols.
58
- - **Mock files are code in the same language as tests** — \`skyramp_mock_generation\` produces mock files (language from workspace config) in \`tests/skyramp/mocks/\` with \`get_all_mocks()\` functions that return MockV2 objects. The enrichment tool imports these, builds a collective \`MOCK_SERVICES\` dict, reuses the integration test client, clears stale mocks, and applies the full mock set in one atomic call. Do NOT create YAML mock files.
58
+ - **Mock files are code in the same language as tests** — \`skyramp_mock_generation\` produces mock files (language from workspace config) in \`tests/skyramp/mocks/\` with language-native mock factory functions (\`get_all_mocks()\` for Python, \`getAllMocks()\` for TypeScript/JavaScript/Java) that return MockV2 objects. The enrichment tool imports these, builds a collective \`MOCK_SERVICES\` collection, reuses the integration test client, clears stale mocks, and applies the full mock set in one atomic call. Do NOT create YAML mock files.
59
59
  - **NO Skyramp CLI** — do NOT use \`skyramp mocker apply\`, \`skyramp mocker generate\`, or any other Skyramp CLI command. Use ONLY MCP tools for Skyramp operations.
60
60
  - **NO duplicate mock data** — the generated mock files are the single source of truth. Do NOT duplicate response bodies or status codes in the test file. The test references mock files, not copies of them.
61
61
  - **Mock generation → enrich flow (integration tests only)** — use \`skyramp_mock_generation\` to generate mock files, then call \`skyramp_enrich_test_with_mocks\` to wire mock references into the test. The enrichment uses the same data from generation — never invent different values. Consumer contract tests do NOT need enrichment — mocks are wired inline by the generation tool.
62
62
  - **MANDATORY enrichment gate** — after every \`skyramp_integration_test_generation\` call for a scenario with any generated mock files, immediately call \`skyramp_enrich_test_with_mocks\` before reading, editing, enhancing, or executing that test. Do not batch all enrichment for later.
63
- - **Enrichment validation** — an integration test that has downstream mocks but lacks \`MOCK_SERVICES\` and \`apply_all_mocks(client)\` is incomplete. Stop and enrich it; never call \`skyramp_execute_test\` on an incomplete integration test.
63
+ - **Enrichment validation** — an integration test that has downstream mocks but lacks \`MOCK_SERVICES\` and the language-specific apply helper (\`apply_all_mocks(client)\` for Python or \`applyAllMocks(client)\` for TypeScript/JavaScript/Java) is incomplete. Stop and enrich it; never call \`skyramp_execute_test\` on an incomplete integration test.
64
64
  - **Do not delete mocks to pass** — if mock deployment fails, do NOT remove mock imports, empty \`MOCK_SERVICES\`, switch mocked downstreams to live Compose stubs, or report the resulting test as passed. Fix the mock wiring once; if still blocked, report the scenario as failed/blocked with the mock deployment error.
65
65
  - **Mocked services must not be running** — before execution, stop every Docker container for a service selected for mocking (\`docker compose stop <mocked-service>\`). The Skyramp worker takes over that service's DNS alias on the Docker network; a real container running on the same hostname and port will conflict, causing traffic to reach the real service instead of the mock or causing port binding failure. Bring up only the SUT and its real infrastructure (DB, cache, real broker, real first-party services); do NOT run \`docker compose up -d\` without immediately stopping mocked service containers.
66
66
  - **Mock URL format** — the \`endpointURL\` in \`skyramp_mock_generation\` MUST use the original Docker service hostname (e.g., \`http://identity-service:4000\`, \`http://profile-service:50052\`). The Skyramp executor uses DNS alias hijacking — it takes over the service's DNS name on the Docker network so the service under test's requests are intercepted transparently. NEVER use \`localhost\`, \`127.0.0.1\`, \`0.0.0.0\`, \`host.docker.internal\`, or the worker address as the mock URL. For REST mocks, \`skyramp_preflight_mock_check\` returns blocking \`REST_LOOPBACK_URL\` when a deployable mock uses these hosts.
@@ -68,6 +68,8 @@ ${sandboxWorkerBlock ? sandboxWorkerBlock + "\n" : ""}${serviceContext ? service
68
68
  - **gRPC mock routing** — gRPC mocks may target the real downstream service port (e.g., \`partner-accounts:50051\`). Apply gRPC mocks before the SUT starts or restart the SUT after applying them, and verify the Skyramp worker has the original endpoint host alias from \`endpointURL\` (\`partner-accounts\`, not the protobuf service name like \`PartnerAccountsService\`).
69
69
  - **Execution** — ALWAYS use \`skyramp_execute_test\` MCP tool to run tests. NEVER use direct language runtimes (\`python3\`, \`pytest\`, \`node\`, \`npx jest\`, etc.) or Skyramp CLI commands.
70
70
  - **Generated test hardening** — before execution, make generated tests rerunnable and assertion-rich: use \`int(os.getenv("SKYRAMP_WORKER_PORT", "35142"))\` instead of a hardcoded worker port, keep auth token lookup environment-only (\`os.getenv("SKYRAMP_TEST_TOKEN")\`) with no literal fallback token in test source; pass the discovered token via \`skyramp_execute_test.token\` or the MCP server \`SKYRAMP_TEST_TOKEN\` environment before execution, use \`json.dumps({...})\` for JSON request bodies instead of f-string triple-quoted strings, use unique data for persistent DBs (UUID-suffixed emails/names), access \`ResponseV2\` via \`.status_code\` and \`skyramp.get_response_value(...)\` (never \`.json()\`), remember \`check_schema\` validates concrete field values (not just shape — a wrong value fails the assertion), and assert the changed behavior rather than status code alone.
71
+ - **Outbound side-effect proof** — when a scenario is meant to verify what the service under test sends to a mocked dependency, require a REST request body/header/path assertion, a Kafka producer payload assertion, or a documented gRPC request assertion. If Skyramp cannot express the outbound assertion, mark the scenario blocked instead of reporting a response-only test as passing.
72
+ - **No hollow green tests** — unresolved placeholders (\`TODO\`, a standalone \`pass\` statement in a test body, \`NotImplemented\`, or "replace with real consumer code") and executable consumer-contract stubs that call only the mock are blocking defects. Skipped or strict xfail direct-mock reference stubs are allowed only when paired with a separate executable real consumer/SUT test. Implement the missing application/consumer call or report the scenario blocked; do not execute or report it as PASS.
71
73
  - **Downstream dependency handling is explicit** — HTTP/REST, gRPC, and Kafka dependencies can be mocked with Skyramp when selected for mocking. For gRPC, call \`skyramp_mock_generation\` with \`protocol: "grpc"\`, \`endpointURL\` as \`host:port\`, plus \`protoPath\` and \`grpcServiceName\`. For Kafka, call \`skyramp_mock_generation\` with \`protocol: "kafka"\`, \`endpointURL\` as broker \`host:port\`, and \`kafkaTopic\` as the topic name.
72
74
  - **Service routing** — if a \`<SERVICE_ROUTING>\` block is provided above, respect it strictly. Resolve routing in this order:
73
75
  1. \`REAL_SERVICES\` wins over every other rule. A real service should receive real traffic even if its service name or protocol is also listed for mocking.
@@ -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, string>): Promise<void>;
4
+ static pushMCPToolEvent(toolName: string, result: CallToolResult | undefined, params: Record<string, any>): Promise<void>;
5
5
  /**
6
6
  * Track server crash events
7
7
  */
@@ -470,10 +470,15 @@ export class TestExecutionService {
470
470
  testFilePath,
471
471
  options.testType,
472
472
  ];
473
+ const networkMode = options.dockerNetwork?.trim()
474
+ ? options.dockerNetwork.trim()
475
+ : options.useHostNetwork && process.platform === "linux"
476
+ ? "host"
477
+ : undefined;
473
478
  // Prepare host config with mounts
474
479
  const hostConfig = {
475
480
  ExtraHosts: ["host.docker.internal:host-gateway"],
476
- ...(options.useHostNetwork && process.platform === "linux" ? { NetworkMode: "host" } : {}),
481
+ ...(networkMode ? { NetworkMode: networkMode } : {}),
477
482
  Mounts: [
478
483
  {
479
484
  Type: "bind",
@@ -303,6 +303,23 @@ describe("TestExecutionService.executeTest - Docker env forwarding", () => {
303
303
  delete process.env.SKYRAMP_TEST_SERVICE_URL_BACKEND;
304
304
  delete process.env.SKYRAMP_TEST_SERVICE_URL_FRONTEND;
305
305
  });
306
+ it.each(["python", "typescript", "javascript", "java"])("passes %s integration tests through to runner.sh", async (language) => {
307
+ const mockContainer = { remove: jest.fn().mockResolvedValue(undefined) };
308
+ mockRun.mockResolvedValue([{ StatusCode: 0 }, mockContainer]);
309
+ const service = new TestExecutionService();
310
+ await service.executeTest({
311
+ testFile: `/workspace/${language}_integration_test.txt`,
312
+ workspacePath: "/workspace",
313
+ language,
314
+ testType: "integration",
315
+ });
316
+ const command = mockRun.mock.calls[0][1];
317
+ expect(command[0]).toBe("/root/runner.sh");
318
+ expect(command[1]).toBe(language);
319
+ expect(command[2]).toBe(`/home/user/${language}_integration_test.txt`);
320
+ expect(command[3]).toBe("integration");
321
+ mockRun.mockClear();
322
+ });
306
323
  it("passes SKYRAMP_TEST_BASE_URL to Docker container Env", async () => {
307
324
  process.env.SKYRAMP_TEST_BASE_URL = "http://external-host:8000";
308
325
  const mockContainer = { remove: jest.fn().mockResolvedValue(undefined) };
@@ -349,6 +366,22 @@ describe("TestExecutionService.executeTest - Docker env forwarding", () => {
349
366
  e.startsWith("SKYRAMP_TEST_SERVICE_URL_"));
350
367
  expect(envWithBaseUrl).toHaveLength(0);
351
368
  });
369
+ it("attaches executor to dockerNetwork without treating it as host networking", async () => {
370
+ process.env.SKYRAMP_TEST_BASE_URL = "http://localhost:3000";
371
+ const mockContainer = { remove: jest.fn().mockResolvedValue(undefined) };
372
+ mockRun.mockResolvedValue([{ StatusCode: 0 }, mockContainer]);
373
+ const service = new TestExecutionService();
374
+ await service.executeTest({
375
+ testFile: "/workspace/test_file.py",
376
+ workspacePath: "/workspace",
377
+ language: "python",
378
+ testType: "smoke",
379
+ dockerNetwork: "checkr-demo_checkr_default",
380
+ });
381
+ const dockerOptions = mockRun.mock.calls[0][3];
382
+ expect(dockerOptions.HostConfig.NetworkMode).toBe("checkr-demo_checkr_default");
383
+ expect(dockerOptions.Env).toContain("SKYRAMP_TEST_BASE_URL=http://host.docker.internal:3000");
384
+ });
352
385
  // Approach B: every workspace mount is mirrored at the host-absolute path so
353
386
  // tests that embed absolute references (storageState, fixtures, snapshots)
354
387
  // resolve correctly inside the executor regardless of which path-shape the
@@ -87,7 +87,12 @@ export function registerEnhanceAssertionsTool(server) {
87
87
  ],
88
88
  isError: false,
89
89
  };
90
- AnalyticsService.pushMCPToolEvent(TOOL_NAME, undefined, { testFile: params.testFile, testType: params.testType, enhanceType: params.enhanceType, autoApply: String(params.autoApply) }).catch(() => { });
90
+ AnalyticsService.pushMCPToolEvent(TOOL_NAME, undefined, {
91
+ testFile: params.testFile,
92
+ testType: params.testType,
93
+ enhanceType: params.enhanceType,
94
+ autoApply: params.autoApply,
95
+ }).catch(() => { });
91
96
  return result;
92
97
  });
93
98
  }
@@ -1,5 +1,5 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { ProgrammingLanguage } from "../types/TestTypes.js";
2
+ import { ProgrammingLanguage, MockProtocol } from "../types/TestTypes.js";
3
3
  /**
4
4
  * skyramp_enrich_test_with_mocks
5
5
  *
@@ -11,5 +11,18 @@ import { ProgrammingLanguage } from "../types/TestTypes.js";
11
11
  */
12
12
  export declare function registerEnrichTestWithMocksTool(server: McpServer): void;
13
13
  declare function injectMockImports(original: string, testFile: string, scenarioName: string, mockFiles: string[], traceComments?: string[], language?: ProgrammingLanguage): string;
14
- declare function buildMockImportBlock(testFile: string, scenarioName: string, mockFiles: string[], traceComments?: string[], language?: ProgrammingLanguage): string;
15
- export { injectMockImports, buildMockImportBlock };
14
+ declare function isSupportedMockEnrichmentLanguage(language: ProgrammingLanguage | string): language is ProgrammingLanguage;
15
+ type MockEntry = {
16
+ serviceName: string;
17
+ moduleName: string;
18
+ relativePath: string;
19
+ importPath: string;
20
+ importLabel: string;
21
+ mockFile: string;
22
+ className: string;
23
+ classReference: string;
24
+ targetUrl?: string;
25
+ protocol: MockProtocol;
26
+ };
27
+ declare function buildMockImportBlock(testFile: string, scenarioName: string, mockFiles: string[], traceComments?: string[], language?: ProgrammingLanguage, mockEntries?: MockEntry[]): string;
28
+ export { injectMockImports, buildMockImportBlock, isSupportedMockEnrichmentLanguage };