@skyramp/mcp 0.3.0 → 0.3.1-ldw-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/commands/commandLibrary.d.ts +1 -0
- package/build/commands/commandLibrary.js +19 -13
- package/build/commands/commandLibrary.test.d.ts +1 -0
- package/build/commands/commandLibrary.test.js +59 -0
- package/build/commands/localDevTestChangesCommand.d.ts +15 -0
- package/build/commands/localDevTestChangesCommand.js +201 -0
- package/build/index.js +80 -6
- package/build/playwright/traceRecordingPrompt.js +2 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +17 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +11 -9
- package/build/prompts/local-dev/local-dev-plan.d.ts +35 -0
- package/build/prompts/local-dev/local-dev-plan.js +466 -0
- package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
- package/build/prompts/local-dev/local-dev-prompts.js +190 -0
- package/build/prompts/prompt-utils.d.ts +8 -0
- package/build/prompts/prompt-utils.js +33 -0
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +0 -28
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +16 -1
- package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
- package/build/prompts/testbot/testbot-prompts.js +5 -34
- package/build/resources/testbotResource.js +2 -1
- package/build/services/AnalyticsService.d.ts +1 -1
- package/build/services/TestExecutionService.d.ts +2 -1
- package/build/services/TestExecutionService.js +8 -3
- package/build/services/TestExecutionService.test.js +83 -3
- package/build/services/TestGenerationService.d.ts +2 -2
- package/build/services/TestGenerationService.js +7 -21
- package/build/services/containerEnv.js +3 -1
- package/build/tool-phases.js +6 -0
- package/build/tools/code-refactor/enhanceAssertionsTool.js +47 -18
- package/build/tools/enrichTestWithMocksTool.d.ts +28 -0
- package/build/tools/enrichTestWithMocksTool.js +726 -0
- package/build/tools/enrichTestWithMocksTool.test.d.ts +1 -0
- package/build/tools/enrichTestWithMocksTool.test.js +266 -0
- package/build/tools/executeSkyrampTestTool.d.ts +11 -0
- package/build/tools/executeSkyrampTestTool.js +63 -22
- package/build/tools/executeSkyrampTestTool.test.d.ts +1 -0
- package/build/tools/executeSkyrampTestTool.test.js +36 -0
- package/build/tools/generate-tests/batchMockGenerationTool.d.ts +85 -0
- package/build/tools/generate-tests/batchMockGenerationTool.js +432 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +6 -21
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +1 -0
- package/build/tools/generate-tests/generateContractRestTool.js +2 -2
- package/build/tools/generate-tests/generateMockRestTool.d.ts +129 -6
- package/build/tools/generate-tests/generateMockRestTool.js +234 -22
- package/build/tools/generate-tests/loadTestSchema.d.ts +1 -1
- package/build/tools/generateEnrichedIntegrationTestTool.d.ts +25 -0
- package/build/tools/generateEnrichedIntegrationTestTool.js +235 -0
- package/build/tools/generateEnrichedIntegrationTestTool.test.d.ts +1 -0
- package/build/tools/generateEnrichedIntegrationTestTool.test.js +44 -0
- package/build/tools/localDevWorkerComposeTool.d.ts +24 -0
- package/build/tools/localDevWorkerComposeTool.js +224 -0
- package/build/tools/localDevWorkerComposeTool.test.d.ts +1 -0
- package/build/tools/localDevWorkerComposeTool.test.js +129 -0
- package/build/tools/localDevWorkflowFixes.test.d.ts +1 -0
- package/build/tools/localDevWorkflowFixes.test.js +255 -0
- package/build/tools/one-click/oneClickTool.d.ts +13 -0
- package/build/tools/one-click/oneClickTool.js +195 -24
- package/build/tools/one-click/oneClickTool.test.d.ts +1 -0
- package/build/tools/one-click/oneClickTool.test.js +172 -0
- package/build/tools/preflightMockCheckTool.d.ts +2 -0
- package/build/tools/preflightMockCheckTool.js +96 -0
- package/build/tools/queryProxyMocksTool.d.ts +70 -0
- package/build/tools/queryProxyMocksTool.js +522 -0
- package/build/tools/queryProxyMocksTool.test.d.ts +1 -0
- package/build/tools/queryProxyMocksTool.test.js +164 -0
- package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
- package/build/tools/test-management/analyzeChangesTool.js +61 -38
- package/build/tools/trace/startTraceCollectionTool.js +3 -3
- package/build/types/OneClickCommands.d.ts +1 -1
- package/build/types/RepositoryAnalysis.d.ts +117 -0
- package/build/types/RepositoryAnalysis.js +16 -2
- package/build/types/TestExecution.d.ts +1 -0
- package/build/types/TestTypes.d.ts +25 -7
- package/build/types/TestTypes.js +19 -5
- package/build/utils/analyze-openapi.js +18 -1
- package/build/utils/analyze-openapi.test.d.ts +1 -0
- package/build/utils/analyze-openapi.test.js +19 -0
- package/build/utils/branchDiff.d.ts +17 -1
- package/build/utils/branchDiff.js +99 -14
- package/build/utils/branchDiff.test.d.ts +1 -0
- package/build/utils/branchDiff.test.js +109 -0
- package/build/utils/featureFlags.d.ts +16 -0
- package/build/utils/featureFlags.js +20 -0
- package/build/utils/featureFlags.test.js +57 -2
- package/build/utils/grpcMockValidation.d.ts +1 -0
- package/build/utils/grpcMockValidation.js +49 -0
- package/build/utils/grpcMockValidation.test.d.ts +1 -0
- package/build/utils/grpcMockValidation.test.js +41 -0
- package/build/utils/httpMethodValidation.d.ts +4 -0
- package/build/utils/httpMethodValidation.js +15 -0
- package/build/utils/mockCompatibility.d.ts +49 -0
- package/build/utils/mockCompatibility.js +82 -0
- package/build/utils/mockCompatibility.test.d.ts +1 -0
- package/build/utils/mockCompatibility.test.js +79 -0
- package/build/utils/progress.js +10 -5
- package/build/utils/versions.d.ts +1 -0
- package/build/utils/versions.js +2 -0
- package/build/utils/workspaceAuth.d.ts +9 -0
- package/build/utils/workspaceAuth.js +51 -1
- package/build/workspace/workspace.d.ts +28 -14
- package/build/workspace/workspace.js +3 -1
- package/node_modules/playwright/lib/mcp/browser/tools/screenshot.js +1 -1
- package/node_modules/playwright/lib/mcp/skyramp/resultCode.js +45 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +6 -22
- package/node_modules/playwright/lib/mcp/test/resultCode.test.js +48 -0
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PromptPlan-based local-dev workflow — 6 phases.
|
|
3
|
+
*
|
|
4
|
+
* Phases: ANALYZE → RECOMMEND → GENERATE → VERIFY → DEPLOY → EXECUTE
|
|
5
|
+
*
|
|
6
|
+
* Key design decisions:
|
|
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
|
+
* - NO YAML mock files, NO CLI commands — ONLY MCP tools
|
|
10
|
+
* - Execution uses skyramp_execute_test MCP tool exclusively
|
|
11
|
+
* - Enriched tests apply mocks at runtime through their language-specific Skyramp client helper
|
|
12
|
+
*/
|
|
13
|
+
import { PromptPlan } from "../test-recommendation/promptPlan.js";
|
|
14
|
+
/** gRPC service name guidance — use bare name from proto, not package-qualified. */
|
|
15
|
+
export declare const GRPC_SERVICE_NAME_GUIDANCE = "the bare service name as declared in the proto `service` block (e.g., `CartService`, NOT `package.CartService`). Do NOT include the proto package prefix \u2014 the tool looks up the service by its unqualified name in the proto file. If proxy-derived specs (`skyramp_query_proxy_mocks`) return a fully-qualified name like `idl.PartnerAccounts`, strip the package prefix before passing it to mock generation";
|
|
16
|
+
/** Assertion API guidance — use SDK helper with JSON path for field access. */
|
|
17
|
+
export declare const ASSERTION_API_GUIDANCE = "Use the SDK helper for all field-level assertions: Python `skyramp.get_response_value(response, \"json.path\")`, TypeScript `getResponseValue(response, \"json.path\")`. The helper supports dot-path notation for nested access (e.g., `\"data.items[0].name\"`). Do NOT use dict/attribute access on the response variable (`response[\"field\"]`, `response.field`) or `json.loads(response.response_body)`";
|
|
18
|
+
export interface LocalDevPlanCtx {
|
|
19
|
+
repositoryPath: string;
|
|
20
|
+
baseCommit: string;
|
|
21
|
+
maxRecommendations: number;
|
|
22
|
+
maxGenerate: number;
|
|
23
|
+
stateOutputFile?: string;
|
|
24
|
+
sandboxWorkerUrl?: string;
|
|
25
|
+
schemaSourceGuidance: string;
|
|
26
|
+
serviceRoutingBlock: string;
|
|
27
|
+
serviceContext: string;
|
|
28
|
+
/**
|
|
29
|
+
* True when proxy access parameters were supplied (a URL, or kubeconfig+namespace)
|
|
30
|
+
* — NOT a reachability check. skyramp_query_proxy_mocks decides reachability at
|
|
31
|
+
* call time and falls back to code/OpenAPI inference if the proxy is unreachable.
|
|
32
|
+
*/
|
|
33
|
+
hasProxyAccess?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare const localDevPlan: PromptPlan<LocalDevPlanCtx>;
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PromptPlan-based local-dev workflow — 6 phases.
|
|
3
|
+
*
|
|
4
|
+
* Phases: ANALYZE → RECOMMEND → GENERATE → VERIFY → DEPLOY → EXECUTE
|
|
5
|
+
*
|
|
6
|
+
* Key design decisions:
|
|
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
|
+
* - NO YAML mock files, NO CLI commands — ONLY MCP tools
|
|
10
|
+
* - Execution uses skyramp_execute_test MCP tool exclusively
|
|
11
|
+
* - Enriched tests apply mocks at runtime through their language-specific Skyramp client helper
|
|
12
|
+
*/
|
|
13
|
+
import { PromptPlan } from "../test-recommendation/promptPlan.js";
|
|
14
|
+
// ── Shared guidance constants (used by both the plan and the prompt header) ──
|
|
15
|
+
/** gRPC service name guidance — use bare name from proto, not package-qualified. */
|
|
16
|
+
export const GRPC_SERVICE_NAME_GUIDANCE = `the bare service name as declared in the proto \`service\` block (e.g., \`CartService\`, NOT \`package.CartService\`). Do NOT include the proto package prefix — the tool looks up the service by its unqualified name in the proto file. If proxy-derived specs (\`skyramp_query_proxy_mocks\`) return a fully-qualified name like \`idl.PartnerAccounts\`, strip the package prefix before passing it to mock generation`;
|
|
17
|
+
/** Assertion API guidance — use SDK helper with JSON path for field access. */
|
|
18
|
+
export const ASSERTION_API_GUIDANCE = `Use the SDK helper for all field-level assertions: Python \`skyramp.get_response_value(response, "json.path")\`, TypeScript \`getResponseValue(response, "json.path")\`. The helper supports dot-path notation for nested access (e.g., \`"data.items[0].name"\`). Do NOT use dict/attribute access on the response variable (\`response["field"]\`, \`response.field\`) or \`json.loads(response.response_body)\``;
|
|
19
|
+
// ── The Plan ─────────────────────────────────────────────────────────────────
|
|
20
|
+
export const localDevPlan = new PromptPlan()
|
|
21
|
+
// ─── Phase 1: Analyze ───────────────────────────────────────────────────────
|
|
22
|
+
.addPhase("analyze", "Analyze Changes", {
|
|
23
|
+
headerLevel: "##",
|
|
24
|
+
stepFormat: "hash",
|
|
25
|
+
})
|
|
26
|
+
.step("ANALYZE_DIFF", "Analyze branch diff", (ctx) => `Call \`skyramp_analyze_changes\` with:
|
|
27
|
+
- \`repositoryPath\`: "${ctx.repositoryPath}"
|
|
28
|
+
- \`scope\`: "branch_diff"
|
|
29
|
+
- \`baseBranch\`: "${ctx.baseCommit}"
|
|
30
|
+
- \`includeUncommitted\`: true
|
|
31
|
+
- \`topN\`: ${ctx.maxRecommendations}
|
|
32
|
+
- \`maxGenerate\`: ${ctx.maxGenerate}${ctx.stateOutputFile ? `\n- \`stateOutputFile\`: "${ctx.stateOutputFile}"` : ""}
|
|
33
|
+
|
|
34
|
+
**IMPORTANT — This is a local-dev workflow, NOT a testbot run.** IGNORE any suggestions about \`skyramp_analyze_test_health\`, \`skyramp_actions\`, or \`skyramp_recommend_tests\`. Proceed directly to dependency discovery.
|
|
35
|
+
|
|
36
|
+
**Reading the output efficiently.** The tool returns a large output. Read ONLY the JSON summary block at the top (between \`\`\`json fences) — it contains \`newEndpoints\` and \`modifiedEndpoints\` arrays with the actual endpoint method and path (e.g., \`"GET /api/cart/estimate"\`). Use these to identify what changed. Skip the recommendation catalog and UI blueprint sections — do not spend turns trying to read or process the full output. If you need endpoint details, use the MCP resources (\`skyramp://analysis/{sessionId}/endpoints\`). Do NOT re-read the persisted state file — the result is already in your context from the tool call; re-reading it will fail with a token limit error.
|
|
37
|
+
|
|
38
|
+
**Cross-check recommendations against the actual diff.** \`skyramp_analyze_changes\` can return scenario recommendations for endpoints that do not exist in the changed files (wrong-repo suggestions, hallucinated paths). Before acting on any recommendation, verify that the referenced endpoint or file path appears in the diff output. Discard any recommendation whose endpoint is not traceable to a changed file — use those only as inspiration, not as authoritative coverage guidance.
|
|
39
|
+
|
|
40
|
+
If 0 endpoints detected but changed files contain application code: manually trace changed files upward to discover which API endpoint(s) they serve.
|
|
41
|
+
|
|
42
|
+
If \`skyramp_analyze_changes\` returns an error: retry once for transient errors. If it fails again, report the error and stop.`)
|
|
43
|
+
.step("DISCOVER_DEPS", "Discover downstream dependencies", (ctx) => `${ctx.hasProxyAccess
|
|
44
|
+
? `**Proxy-backed mock discovery (preferred source of truth).** A Skyramp worker proxy is available (see \`<PROXY_ACCESS>\` in the header). **First**, call \`skyramp_query_proxy_mocks\` passing the proxy access from \`<PROXY_ACCESS>\` — use \`proxyUrl\` from \`<PROXY_URL>\`, or \`kubeconfig\`/\`namespace\` from \`<PROXY_KUBECONFIG>\`/\`<PROXY_NAMESPACE>\`. It returns \`mockSpecs\` derived from REAL recorded traffic (actual request/response bodies and status codes). Treat these as the AUTHORITATIVE request/response shapes for the downstream dependencies they cover, and feed them directly into \`skyramp_batch_mock_generation\` in Phase 3. Only if \`proxyReachable\` is false, or a specific dependency has no matching sample, fall back to the OpenAPI/code inference below for that dependency. Still read the implementation code to classify each dependency (third-party vs first-party vs infra) and to map dependencies the proxy did not observe.
|
|
45
|
+
|
|
46
|
+
`
|
|
47
|
+
: ""}For each changed endpoint, read the implementation code and identify ALL outbound service calls:
|
|
48
|
+
|
|
49
|
+
**HTTP/REST** — look for: \`HTTParty\`, \`fetch\`, \`axios\`, \`http.NewRequestWithContext\`, SDK client calls
|
|
50
|
+
**gRPC** — look for: proto-generated stubs, \`.Stub.new()\`, \`pb.New<Service>Client(conn)\`
|
|
51
|
+
**Kafka/Events** — look for: \`Kafka.new\`, \`deliver_message\`, \`producer.Send\`, event publishing
|
|
52
|
+
|
|
53
|
+
**Trace the FULL call chain** — do not stop at the endpoint handler. Follow through service objects, callbacks, side-effect hooks (e.g., \`after_save\`, \`after_commit\`, background jobs enqueued synchronously), and nested method calls. Dependencies buried in side-effect callbacks (e.g., a metrics-recache RPC called inside a post-creation hook) are the most commonly missed — if they are not mocked, the SUT returns 500 at runtime.
|
|
54
|
+
|
|
55
|
+
**gRPC dependency handling:** Record the service hostname, port, and ALL RPC methods the SUT calls on that service — not just the primary one. A single gRPC service may host multiple RPCs; missing any one causes \`UNIMPLEMENTED\` errors at runtime. Do NOT read .proto files yet — defer to Phase 3 after \`skyramp_preflight_mock_check\`.
|
|
56
|
+
|
|
57
|
+
Classify each dependency as **third-party**, **first-party/local service**, **infrastructure**, or **service under test** before deciding whether to mock it. By default, only third-party downstream services are mocked; first-party/local services and infrastructure remain real unless the user explicitly requests them in service routing.
|
|
58
|
+
|
|
59
|
+
Record as a **dependency map**:
|
|
60
|
+
\`\`\`
|
|
61
|
+
service_name:
|
|
62
|
+
protocol: http | grpc | kafka
|
|
63
|
+
port: <port from env var or config>
|
|
64
|
+
interactions: [method, path, request shape, response shape]
|
|
65
|
+
\`\`\`
|
|
66
|
+
|
|
67
|
+
Downstream dependency handling must be explicit: HTTP/REST, gRPC, and Kafka dependencies can be mocked by Skyramp when selected for mocking. For unselected first-party/local services and infrastructure, use the real local service and document why it is real. For Kafka selected for mocking, call \`skyramp_mock_generation\` with \`protocol: "kafka"\`, \`endpointURL\` as broker \`host:port\`, and \`kafkaTopic\` as the topic name. The \`skyramp_execute_test\` tool handles execution and integration mock deployment from enriched test files.
|
|
68
|
+
|
|
69
|
+
**Auth discovery (do this NOW, not later):**
|
|
70
|
+
- Read the endpoint's auth middleware/filters to understand the token format (e.g., Base64 of "client_id:secret", JWT, API key)
|
|
71
|
+
- Read docker-compose env vars for valid credentials
|
|
72
|
+
- Determine the correct token value ONCE for execution, but do not write literal token values into generated test files
|
|
73
|
+
|
|
74
|
+
**Existing test discovery (do this NOW, not later):**
|
|
75
|
+
- Search the repo for existing tests that exercise the same endpoint: look in \`spec/\`, \`tests/skyramp/\`, \`spec/skyramp_local_dev/\`, and any \`*_spec.rb\` / \`*_test.py\` files whose path or name matches the endpoint.
|
|
76
|
+
- Read any matches before generating new tests. Extract: request body shape, required fields, auth header value, and expected response fields already exercised.
|
|
77
|
+
- **Auth token precedence — use in this order:**
|
|
78
|
+
1. **Captured scenario or trace data** — if a recorded trace or captured scenario file contains an Authorization header for this specific scenario, use that value as the execution token source. Do not paste it into generated test source.
|
|
79
|
+
2. **Existing matching test file for the same scenario or client** — if a test file exercises this endpoint AND clearly targets the same client/fixture (same scenario name, same client_id, same credential class), reuse that credential for execution via \`skyramp_execute_test.token\` or \`SKYRAMP_TEST_TOKEN\`, not as a hardcoded fallback in new tests.
|
|
80
|
+
3. **Derive from env/middleware** — only if neither of the above exists: read docker-compose env vars for valid credentials and the auth middleware for token format, then compute the token once.
|
|
81
|
+
Do NOT recompute the token when a literal value exists from step 1 or 2. The same endpoint can be exercised by different client credentials that load different fixture data from stubs — using the wrong credential produces a test that passes structurally but asserts against the wrong fixture.`)
|
|
82
|
+
.done()
|
|
83
|
+
// ─── Phase 2: Recommend ─────────────────────────────────────────────────────
|
|
84
|
+
.addPhase("recommend", "Recommend Scenarios", {
|
|
85
|
+
headerLevel: "##",
|
|
86
|
+
stepFormat: "hash",
|
|
87
|
+
})
|
|
88
|
+
.step("ENUMERATE_SCENARIOS", "Enumerate bug-catching scenarios", (_ctx) => `Based on the analysis output, enumerate an **exhaustive** list of bug-catching scenarios BEFORE generating any code. This is a planning step — produce the scenario list, not test files. Rank scenarios by the production bug they would catch; test type is only the implementation mechanism.
|
|
89
|
+
|
|
90
|
+
For each endpoint under test:
|
|
91
|
+
|
|
92
|
+
**Integration test scenarios:**
|
|
93
|
+
|
|
94
|
+
1. **Read the implementation code** (handler, orchestrator, adapter). For every \`if/else\`, \`case/when\`, \`rescue/catch\`, conditional return, or early exit — that's a scenario.
|
|
95
|
+
|
|
96
|
+
2. **For each mocked downstream service**, enumerate ALL distinct responses:
|
|
97
|
+
- Success (200/201) — happy path
|
|
98
|
+
- Validation rejection (400/422) — service says "no"
|
|
99
|
+
- Auth failure (401/403) — credentials wrong or expired
|
|
100
|
+
- Not found (404) — resource doesn't exist
|
|
101
|
+
- Timeout / unavailable (503/504) — service is down
|
|
102
|
+
|
|
103
|
+
3. **For outbound-dependent behaviors** — when the scenario's value depends on what the SUT *sends* to a downstream service (not just the SUT's own response):
|
|
104
|
+
- Trace the data flow in the implementation: identify where an input or config value is transformed before being sent downstream (e.g., config \`shipping_priority=express\` → mapped to \`carrier_class=next_day\` → sent to fulfillment-service).
|
|
105
|
+
- Mocks return the same response regardless of what request body the SUT sends — they cannot distinguish correct from incorrect outbound values. If the SUT's response and the outbound call derive from different code paths (response echoes the raw config value, outbound call uses a transformed or separately-sourced value), the mock always returns success, the SUT's response looks correct, and the test passes even when the outbound value is wrong.
|
|
106
|
+
- In the Mock Config column, recommend the outbound target as **real (outbound validation)**. If the user has explicitly requested mocking it (via \`MOCK_ALL_DOWNSTREAM\`, \`MOCK_SERVICES\`, or \`MOCK_PROTOCOLS\`), honor the request but note the outbound coverage gap — the test will pass regardless of whether the SUT sent the correct or incorrect value.
|
|
107
|
+
- The test must assert something whose value depends on what the SUT actually sent. Prefer fields in the SUT's response that originate from the real downstream's reply; when the SUT response is decoupled from the outbound call (fire-and-forget, async), assert via the real downstream's request log, debug endpoint, or — for Kafka — by consuming from the topic and checking the message payload.
|
|
108
|
+
|
|
109
|
+
4. **For DB/state interactions**, enumerate:
|
|
110
|
+
- Uniqueness violations (duplicate email, duplicate name)
|
|
111
|
+
- Foreign key failures, constraint violations
|
|
112
|
+
|
|
113
|
+
5. **For auth/input paths**, enumerate:
|
|
114
|
+
- Missing auth entirely, invalid auth
|
|
115
|
+
- Missing required fields, invalid field values
|
|
116
|
+
|
|
117
|
+
6. **For API and downstream contracts**, include scenarios only when they catch a realistic bug:
|
|
118
|
+
- Provider contract: response field added/renamed/removed, type/nullable mismatch, status-code semantic drift.
|
|
119
|
+
|
|
120
|
+
7. **For full API flows**, treat API E2E coverage as integration coverage:
|
|
121
|
+
- **API full-flow coverage is integration coverage** in this workflow. Do NOT label API-only workflows as E2E.
|
|
122
|
+
- When a changed behavior spans multiple API calls, downstream services, DB state, async side effects, or a cross-service workflow, include full-scenario integration tests that cover each distinct workflow path (happy path, error paths, side-effect variations).
|
|
123
|
+
- Example: order creation with identity validation, inventory lookup, pricing calculation, payment authorization, fulfillment scheduling, and message-broker side effects is a cross-service workflow — generate an integration test for the happy path AND one for each high-value failure branch (e.g., payment rejection, identity validation failure), not a separate API E2E test type.
|
|
124
|
+
|
|
125
|
+
8. **For frontend/UI changes**, do NOT generate UI tests. Instead, identify the backend APIs that the frontend calls and generate integration or contract tests for those APIs. Trace frontend component changes back to the API endpoints they consume and include those endpoints in the scenario list.
|
|
126
|
+
|
|
127
|
+
**Prioritization (applies across ALL test types):**
|
|
128
|
+
|
|
129
|
+
9. **Rank by bug-catching value, not by test type**: security paths > data-loss or money-moving behavior > externally visible API shape breakage > important business branches > dependency failures > edge cases. Prefer the cheapest reliable test type that catches the bug. Do NOT generate a contract test just to satisfy a quota, and do NOT generate an integration test if a focused contract catches the same bug more directly.
|
|
130
|
+
|
|
131
|
+
10. **Design scenarios within mock engine constraints**: mocks provide one static response per endpoint+method, are stateless (no call-count or sequential responses), and cannot validate outbound request bodies/headers/query params. When a scenario's value depends on different responses for different inputs, sequential state, or verifying what the SUT sent to a dependency, do NOT design a mock-based test — keep the dependency real (outbound validation rule), split the scenario, or mark it blocked. See the full mock limitations list in the Generate phase.
|
|
132
|
+
|
|
133
|
+
11. **Output a unified scenario table** (do NOT generate code yet):
|
|
134
|
+
|
|
135
|
+
| # | What this verifies | Best Test Type | Scenario Name | Mock Config | Expected Response | Priority |
|
|
136
|
+
|---|---------------------|----------------|---------------|-------------|-------------------|----------|
|
|
137
|
+
| 1 | Order creation applies downstream pricing rules | integration | happy-path-with-pricing | All required downstream success | 201 + calculated totals | high |
|
|
138
|
+
| 2 | Payment rejection rolls back order creation | integration | payment-rejects | Payment service → 422 | expected error + no persisted order | high |
|
|
139
|
+
| 3 | Config-driven value reaches downstream correctly | integration | express-shipping-carrier | fulfillment: real (outbound validation); others mocked | 201 + carrier confirmation matches next_day | high |
|
|
140
|
+
| 4 | Public order response shape does not regress | provider-contract | order-response-shape | N/A | required fields typed | medium |
|
|
141
|
+
| 5 | Complete API order workflow remains coherent | integration | order-creation-full-flow | All downstreams for the cross-service workflow | final response + side effects observed | high |
|
|
142
|
+
| 6 | Backend checkout API still works after frontend-related changes | integration | checkout-api-flow | Downstream service mocks | 201 + expected response fields | high when frontend changed |
|
|
143
|
+
| ...| ... | ... | ... | ... | ... | ... |
|
|
144
|
+
|
|
145
|
+
**IMPORTANT:** Only proceed to the next phase once you have the complete scenario list. The total number should reflect distinct bug-catching outcomes and API boundaries — skip redundant tests even if they are a different type.`)
|
|
146
|
+
.done()
|
|
147
|
+
// ─── Phase 3: Generate ──────────────────────────────────────────────────────
|
|
148
|
+
.addPhase("generate", "Generate Mocks & Tests", {
|
|
149
|
+
headerLevel: "##",
|
|
150
|
+
stepFormat: "hash",
|
|
151
|
+
})
|
|
152
|
+
.step("GENERATE_MOCKS_AND_TESTS", "Generate highest-value tests", (ctx) => `**Preferred: Use batch tools when available.**
|
|
153
|
+
- Call \`skyramp_batch_mock_generation\` with all mock specs in one array instead of individual \`skyramp_mock_generation\` calls.${ctx.hasProxyAccess
|
|
154
|
+
? `\n- **Use the proxy-derived specs from \`skyramp_query_proxy_mocks\` (Phase 1) as the mock inputs** — their \`endpointURL\`, \`method\`, \`responseData\`, \`responseStatusCode\`, \`requestData\`, \`grpcServiceName\`, and \`kafkaTopic\` map directly onto batch mock generation. Use the recorded values verbatim (real bodies/status codes); do NOT invent different response data when a proxy sample exists.`
|
|
155
|
+
: ""}
|
|
156
|
+
- 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.
|
|
157
|
+
- Call \`skyramp_enhance_assertions\` with \`autoApply: true\` for compact output format — the returned instructions must still be applied by you to the test file.
|
|
158
|
+
|
|
159
|
+
**Run \`skyramp_preflight_mock_check\` FIRST, before reading any .proto files or constructing any mock spec.**
|
|
160
|
+
- For each gRPC service in the dependency map, preflight surfaces advisory issues such as persistent pre-existing gRPC connections. gRPC mocks may target the real downstream service port (for example, \`partner-accounts:50051\`).
|
|
161
|
+
- For REST services, preflight confirms the mock URL uses a Docker-service hostname. If it returns \`REST_LOOPBACK_URL\`, fix \`endpointURL\` to the original service origin (for example, \`http://ams:4000\`), not \`localhost\` or \`host.docker.internal\`.
|
|
162
|
+
- For Docker gRPC mocks, ensure the real downstream service container is stopped, apply mocks before the SUT starts, restart the SUT after applying mocks, and verify the worker has the original endpoint host alias from \`endpointURL\` (for example, \`partner-accounts\`).
|
|
163
|
+
|
|
164
|
+
Work through the scenario list from Phase 2 in **bug-catching priority order**. The budget is ${ctx.maxGenerate} total tests across all types.
|
|
165
|
+
|
|
166
|
+
**Selection rule:** Generate the tests most likely to catch real regressions and edge-case bugs. Choose integration or provider contract based on which one best proves the behavior. There is no required quota by test type. If the highest-value ${ctx.maxGenerate} scenarios are all integration tests, generate integrations; if focused provider contracts catch the riskiest issues more directly, generate contracts. For frontend/UI changes, trace them back to the backend APIs they consume and generate integration or contract tests for those APIs.
|
|
167
|
+
|
|
168
|
+
**Coverage floor:** If Phase 2 identifies multiple high-priority scenarios, generate and execute each high-priority scenario up to ${ctx.maxGenerate}. Do not stop after the first passing test while other high-priority scenarios remain ungenerated or unexecuted.
|
|
169
|
+
|
|
170
|
+
**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.
|
|
171
|
+
|
|
172
|
+
**Outbound validation rule:** Per the outbound validation rule above, do NOT mock a downstream service whose inbound request is the thing being validated. Keep it real, mock everything else, and assert fields that depend on what the SUT actually sent. If the real downstream cannot be kept running, mark the scenario as **Blocked — outbound validation target unavailable**.
|
|
173
|
+
|
|
174
|
+
**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.
|
|
175
|
+
|
|
176
|
+
**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" }\`.
|
|
177
|
+
|
|
178
|
+
For each selected scenario, follow the workflow matching its best test type:
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
**For \`integration\` scenarios:**
|
|
183
|
+
|
|
184
|
+
**A — Generate mock files for each downstream service:**
|
|
185
|
+
Call \`skyramp_batch_mock_generation\` once with all downstream services as specs in a single array. Fields per spec type:
|
|
186
|
+
|
|
187
|
+
REST spec fields:
|
|
188
|
+
- \`protocol\`: "rest"
|
|
189
|
+
- \`endpointURL\`: full URL using the ORIGINAL Docker service hostname and port (e.g., \`http://identity-service:4000/api/internal/v1/users/validate\`). NEVER use \`localhost\`, \`127.0.0.1\`, \`0.0.0.0\`, \`host.docker.internal\`, or the Skyramp worker address. The worker parses the hostname from this URL (e.g. \`identity-service\`) and adds it as a Docker network alias on the worker container so the SUT's DNS resolves to the mock — always use the real Docker service hostname. Preflight returns a blocking \`REST_LOOPBACK_URL\` warning for deployable REST mocks that violate this.
|
|
190
|
+
- Dynamic path segments: if the service under test generates an id at runtime (order id, resource id, UUID, user id), use a Skyramp route parameter in the mock path (e.g., \`/orders/:order_id/payment_authorizations\`) rather than a sentinel value like \`order_test\`, \`resource_placeholder\`, or a sample UUID.
|
|
191
|
+
- \`method\`: HTTP method
|
|
192
|
+
- \`responseData\`: realistic JSON response for THIS scenario
|
|
193
|
+
- \`requestData\`: sample request body for mock generation context. Does not enforce request-body matching at runtime — do NOT rely on it to validate what the SUT sent.
|
|
194
|
+
|
|
195
|
+
gRPC spec fields:
|
|
196
|
+
- \`protocol\`: "grpc"
|
|
197
|
+
- \`endpointURL\`: original Docker service target as \`host:port\` (e.g., \`profile-service:50051\`), without \`http://\`
|
|
198
|
+
- \`protoPath\`: absolute or repo-relative path to the .proto defining the service
|
|
199
|
+
- \`grpcServiceName\`: ${GRPC_SERVICE_NAME_GUIDANCE}.
|
|
200
|
+
- \`method\`: RPC method name, or empty string to generate all methods for the service
|
|
201
|
+
- \`protobufExtraFiles\` / \`protobufExtraImportPath\`: include imports needed to compile the proto
|
|
202
|
+
- \`responseData\`: realistic JSON matching the proto response message shape. For empty proto responses (\`google.protobuf.Empty\` or messages with no fields), use \`{}\` — an empty JSON object, NOT a quoted string like \`"{}"\`. A quoted string causes the proto parser to fail with \`syntax error: unexpected token "{}"\`.
|
|
203
|
+
|
|
204
|
+
Kafka spec fields:
|
|
205
|
+
- \`protocol\`: "kafka"
|
|
206
|
+
- \`endpointURL\`: original broker target as \`host:port\` (e.g., \`broker:9092\`), without \`http://\`
|
|
207
|
+
- \`kafkaTopic\`: topic name
|
|
208
|
+
- \`requestData\`: producer message schema/shape when validating published messages
|
|
209
|
+
- \`responseData\`: message payload to inject for consumer scenarios
|
|
210
|
+
|
|
211
|
+
All specs share these fields:
|
|
212
|
+
- \`outputDir\`: "${ctx.repositoryPath}/tests/skyramp/mocks/"
|
|
213
|
+
- \`runtime\`: "docker"
|
|
214
|
+
- \`dockerNetwork\`: from workspace config
|
|
215
|
+
- \`output\`: (optional) **REQUIRED whenever two or more mock files for the same endpoint and HTTP method would land in the same \`outputDir\`** — whether they come from the same batch call or separate \`skyramp_mock_generation\` calls (e.g., a 201 success mock and a 500 error mock both targeting \`PUT /accounts/:id/billing_preferences\`). Without a distinct \`output\` name, the later file silently overwrites the earlier one. Use scenario-descriptive names like \`billing_preferences_success_mock.py\` and \`billing_preferences_error_mock.py\`. Before calling any mock generation tool, check the target \`outputDir\` for existing files with the default name the tool would produce; if a collision is possible, set \`output\` explicitly.
|
|
216
|
+
|
|
217
|
+
**Mock engine limitations — design scenarios around these constraints:**
|
|
218
|
+
1. **One static response per endpoint+method** — no conditional branching on request body, path, query params, or headers. If a scenario needs different responses for different inputs to the same endpoint, split into separate tests with separate mock files, or keep the downstream real.
|
|
219
|
+
2. **Stateless** — no call-count tracking, no "1st call returns X, 2nd call returns Y". Scenarios that depend on sequential state changes in a dependency cannot be validated with mocks.
|
|
220
|
+
3. **No outbound request validation** — the engine supports request-aware blobs internally, but this is not reliably exposed via MockV2/codegen. Do NOT design scenarios that assert what the SUT sent to a mocked service — use the outbound validation rule (keep the target real) instead.
|
|
221
|
+
4. **Mock request validation is schema-shape only** — the mock engine checks field presence and non-null, but does not support exact-value, range, enum, or regex matching on request body fields. This limitation applies to mock-side request validation, not to test-side assertions (e.g., \`check_schema\` in tests validates concrete field values).
|
|
222
|
+
5. **No header/query/cookie validation** — only form-params get exact-value checks.
|
|
223
|
+
6. **Validation failures hardcoded to 400** — cannot simulate custom error status codes from validation.
|
|
224
|
+
|
|
225
|
+
When a scenario's bug-catching value depends on a capability the mock engine lacks (e.g., different responses per request body, sequential state, outbound request assertion), do NOT generate a mock-based test that silently passes. Instead: keep the downstream real (outbound validation rule), split the scenario to work within the one-response-per-endpoint constraint, or mark the scenario as blocked with the specific limitation.
|
|
226
|
+
|
|
227
|
+
For producer side effects, include the Kafka mock file in the integration test enrichment alongside REST/gRPC mocks so \`MOCK_SERVICES\` proves the event path was exercised.
|
|
228
|
+
|
|
229
|
+
**B — Generate the integration test:**
|
|
230
|
+
Call \`skyramp_batch_scenario_test_generation\`:
|
|
231
|
+
- \`destination\`: app hostname (e.g., "localhost")
|
|
232
|
+
- \`baseURL\`: app URL (e.g., "http://localhost:3000")
|
|
233
|
+
- \`steps\`: target the SERVICE UNDER TEST, NOT downstream ports
|
|
234
|
+
- In the \`prompt\` parameter:
|
|
235
|
+
- Describe the scenario and expected outcome
|
|
236
|
+
- Include the auth header discovered in Phase 1
|
|
237
|
+
- Describe the mock response data from step A for each downstream service
|
|
238
|
+
|
|
239
|
+
Then call \`skyramp_integration_test_generation\`:
|
|
240
|
+
- Pass ONLY \`scenarioFile\` (mutually exclusive with endpointURL/apiSchema)
|
|
241
|
+
- \`outputDir\`: "${ctx.repositoryPath}/tests/skyramp/"
|
|
242
|
+
- \`output\`: descriptive name (e.g., "order_happy_path_integration_test.py")
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
**For \`provider-contract\` scenarios:**
|
|
247
|
+
|
|
248
|
+
Call \`skyramp_contract_test_generation\` with:
|
|
249
|
+
- \`endpointURL\`: full URL of OUR endpoint (e.g., "http://localhost:3000/api/v1/orders")
|
|
250
|
+
- \`method\`: the HTTP method
|
|
251
|
+
- \`responseData\`: the FULL expected response shape including ALL new fields from this diff
|
|
252
|
+
- \`outputDir\`: "${ctx.repositoryPath}/tests/skyramp/"
|
|
253
|
+
|
|
254
|
+
Provider contracts assert: status codes, required fields present, field types correct, nullable vs non-nullable.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
**Rules (all test types):**
|
|
259
|
+
- All \`outputDir\` values MUST be absolute paths inside the repository — NEVER \`/tmp\`.
|
|
260
|
+
- ONE test file per scenario row.
|
|
261
|
+
- **API full-flow scenarios** — generate as integration tests using scenario traces that include every API step needed to exercise the workflow. Do not label API-only workflows as E2E.
|
|
262
|
+
- **Frontend/UI changes** — do NOT generate UI tests. Trace frontend changes back to the backend APIs they consume and generate integration or contract tests for those APIs instead.
|
|
263
|
+
- **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.
|
|
264
|
+
- **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.
|
|
265
|
+
- **Contract responseData** — always provide the FULL expected shape so the test asserts on field presence, types, and values — not just status codes.
|
|
266
|
+
- **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. ${ASSERTION_API_GUIDANCE}. Status-code-only assertions never satisfy this rule.
|
|
267
|
+
- **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.
|
|
268
|
+
- Failure recovery: retry once, then skip and log in issuesFound.
|
|
269
|
+
|
|
270
|
+
Keep advancing in bug-catching priority order until the highest-value scenarios have test files (up to ${ctx.maxGenerate} total).`)
|
|
271
|
+
.step("ENRICH_TESTS", "Wire mock imports into integration test files", (ctx) => `For each **integration** test file only, call \`skyramp_enrich_test_with_mocks\` with:
|
|
272
|
+
- \`testFile\`: absolute path to the generated test file
|
|
273
|
+
- \`scenarioName\`: name from the scenario table
|
|
274
|
+
- \`mockFiles\`: absolute paths to the mock files generated in step A (e.g., ["${ctx.repositoryPath}/tests/skyramp/mocks/validate_POST_mock.py"])
|
|
275
|
+
- \`traceComments\`: (optional) how mock values flow through implementation to assertions
|
|
276
|
+
|
|
277
|
+
This injects import statements from the generated mock files — no data duplication. The test imports directly from the mock files.
|
|
278
|
+
|
|
279
|
+
**Skip contract tests** — provider contract tests don't use mocks at all.`)
|
|
280
|
+
.done()
|
|
281
|
+
// ─── Phase 4: Verify ───────────────────────────────────────────────────────
|
|
282
|
+
.addPhase("verify", "Verify Tests", { headerLevel: "##", stepFormat: "hash" })
|
|
283
|
+
.step("VALIDATE_MOCKS", "Confirm test setup is complete", (ctx) => `Verify:
|
|
284
|
+
|
|
285
|
+
1. Every scenario from Phase 2 has a corresponding test file in \`${ctx.repositoryPath}/tests/skyramp/\`
|
|
286
|
+
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
|
|
287
|
+
3. For provider contract tests: no mocks — test hits the real endpoint directly
|
|
288
|
+
4. Test files use idempotent data (UUID-suffixed emails, random values — not hardcoded strings)
|
|
289
|
+
5. Auth headers are present in all test requests
|
|
290
|
+
6. **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. ${ASSERTION_API_GUIDANCE}.
|
|
291
|
+
7. **Outbound coverage check** — for every scenario whose "What this verifies" depends on outbound request correctness, check whether the outbound target is mocked or real. If real, outbound validation is covered. If mocked (because the user explicitly requested it), note the outbound coverage gap for the report — the test validates the SUT's response but cannot validate what was sent to the mocked service. For Kafka producer scenarios with a real broker, confirm the test includes a consumer assertion on the published message.
|
|
292
|
+
8. **No unresolved stubs** — no generated test file contains \`TODO\`, a standalone \`pass\` statement in a test body, \`NotImplemented\`, or "replace with real consumer code". These are blocking defects, not passing tests.
|
|
293
|
+
|
|
294
|
+
**If any mock file is missing for integration tests:** re-run \`skyramp_mock_generation\` for that service/endpoint.`)
|
|
295
|
+
.step("ENHANCE_ASSERTIONS", "Enhance assertions for each test file", (ctx) => `For each test file in \`${ctx.repositoryPath}/tests/skyramp/\`, call \`skyramp_enhance_assertions\` with:
|
|
296
|
+
- \`testFile\`: the test file path
|
|
297
|
+
- \`testType\`: "integration", "contract", or "ui" (match the scenario table)
|
|
298
|
+
- \`enhanceType\`: "generation"
|
|
299
|
+
|
|
300
|
+
The tool returns instructions — it does NOT edit the file automatically. Apply every returned instruction yourself: read the test file and add the suggested assertions. For assertions on server-generated IDs (e.g., account IDs, order IDs), prefer a domain prefix check (e.g., \`.startswith("acc_")\`) over \`is not None\` when the application uses a known ID format.`)
|
|
301
|
+
.step("CROSS_REFERENCE", "Cross-reference scenarios vs test files", (_ctx) => `Compare the scenario table from Phase 2 against the generated test files:
|
|
302
|
+
|
|
303
|
+
1. For each generated scenario, verify a corresponding test file exists with the correct SCENARIO_NAME or generated test name.
|
|
304
|
+
2. For integration tests, verify MOCK_SERVICES is non-empty and exactly matches the scenario's mock configuration. Empty MOCK_SERVICES is invalid for any scenario with downstream mocks.
|
|
305
|
+
3. For contract tests, verify responseData covers the full shape (all fields, types, nullability).
|
|
306
|
+
4. For frontend/UI changes, verify the backend APIs behind those changes have integration or contract test coverage.
|
|
307
|
+
5. Verify the generated set covers the highest-risk bug-catching targets from the scenario table regardless of test type, including full API workflows as integration scenarios when applicable.
|
|
308
|
+
6. Document any gaps:
|
|
309
|
+
- Scenarios without test files → add to issuesFound with reason (generation failed, protocol unsupported, frontend not present, etc.)
|
|
310
|
+
- Test files without matching scenarios → investigate and either map to a scenario or remove
|
|
311
|
+
|
|
312
|
+
**Only proceed to Deploy when all generable scenarios have verified test files.**`)
|
|
313
|
+
.done()
|
|
314
|
+
// ─── Phase 5: Deploy ────────────────────────────────────────────────────────
|
|
315
|
+
.addPhase("deploy", "Deploy", { headerLevel: "##", stepFormat: "hash" })
|
|
316
|
+
.step("START_ENVIRONMENT", "Bring up the service under test and the Skyramp worker", (ctx) => `Bring up the service under test and its real infrastructure (DB, cache, real broker, real first-party services) together with the Skyramp mock worker — **but mocked downstream services must NOT be running.**
|
|
317
|
+
|
|
318
|
+
**Why this matters:** The Skyramp worker runs as a Docker Compose service on the SUT's network and intercepts traffic via Docker DNS alias hijacking at mock-apply time — it takes over the DNS alias for each mocked service's hostname. If the real service container is also up on the same hostname and port, the two conflict: traffic may reach the real service instead of the mock, or port binding fails. This is the most common cause of mocks not being applied correctly. Running the worker as a compose service (rather than spawning it ad hoc) guarantees it joins the SUT's network and is torn down with the stack.
|
|
319
|
+
|
|
320
|
+
**Docker readiness is checked automatically** by \`skyramp_setup_local_dev_worker\` (step 2 below). If it returns an error about Docker not running, **stop immediately and report the error to the user** — do NOT manually run \`docker info\`, \`docker context\`, \`open -a Docker\`, probe socket paths, or attempt any Docker debugging. The user must start Docker Desktop themselves.
|
|
321
|
+
|
|
322
|
+
Steps:
|
|
323
|
+
1. **Clean stale environment.** Before anything else, tear down leftover containers, networks, and port-forwards that will collide with the new stack:
|
|
324
|
+
\`\`\`bash
|
|
325
|
+
# Stop and remove any containers from a previous run on this compose project
|
|
326
|
+
docker compose down -v --remove-orphans 2>/dev/null || true
|
|
327
|
+
# Kill any port-forward or process holding the SUT port (commonly 8080) or the worker port (35142)
|
|
328
|
+
# Adjust 8080 to the SUT's actual port if different
|
|
329
|
+
for port in 8080 35142; do
|
|
330
|
+
lsof -ti :$port 2>/dev/null | xargs kill 2>/dev/null || true
|
|
331
|
+
done
|
|
332
|
+
\`\`\`
|
|
333
|
+
Stale containers, zombie port-forwards (e.g. from a prior K8s \`kubectl port-forward\`), or leftover Docker networks cause the most common deploy failures: port-binding conflicts, traffic reaching a wrong backend, or \`apply_mock\` failing with "failed to get containers in network". Always start from a clean state.
|
|
334
|
+
2. Call \`skyramp_setup_local_dev_worker\` with \`repositoryPath\` = \`${ctx.repositoryPath}\` (pass \`composeFile\` if the base docker-compose file is not at the repo root). It writes a worker compose override and returns \`composeFilePrefix\` (the \`-f <base> -f <override>\` flags), the resolved \`dockerNetwork\`, and the worker port. **Use the returned \`composeFilePrefix\` for every \`docker compose\` command below, and pass the returned \`dockerNetwork\` to test/mock generation (see the Generate phase).**
|
|
335
|
+
3. Run \`docker compose <composeFilePrefix> ps\` to check current container state.
|
|
336
|
+
4. 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).
|
|
337
|
+
5. Stop any mocked service containers that are already running — but do NOT stop services marked as \`real (outbound validation)\` in the scenario table; those must remain running so integration tests can validate outbound behavior through real responses:
|
|
338
|
+
\`docker compose <composeFilePrefix> stop <mocked-service-1> <mocked-service-2> ...\`
|
|
339
|
+
6. **Ensure the SUT builds from local source.** Before starting, check whether the SUT's compose service has a \`build:\` context. If it only has \`image:\` (pre-built), the diff's code changes will NOT be in the running container and tests will hit 404 or stale behavior. Fix this by finding the service's Dockerfile in the repo (look in the service directory, or \`src/<service>/Dockerfile\`, or the repo root) and adding a compose override that sets the build context:
|
|
340
|
+
\`\`\`yaml
|
|
341
|
+
# write to a file like .skyramp/sut-build-override.yml
|
|
342
|
+
services:
|
|
343
|
+
<sut-service>:
|
|
344
|
+
build:
|
|
345
|
+
context: <repo-root>
|
|
346
|
+
dockerfile: <path-to-Dockerfile>
|
|
347
|
+
\`\`\`
|
|
348
|
+
Then include it in the compose prefix: \`-f <base> -f <worker-override> -f <sut-build-override>\`.
|
|
349
|
+
7. Rebuild and bring up only the SUT, its real dependencies, and the worker — start selectively:
|
|
350
|
+
\`docker compose <composeFilePrefix> up -d --build <sut-service> <db> <cache> <broker> <real-first-party-services> skyramp\`
|
|
351
|
+
Always include the \`skyramp\` service. 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 plus \`skyramp\`, then re-check that mocked service containers are stopped before executing tests.
|
|
352
|
+
8. **Network verification gate** — after \`docker compose up\`, verify the Docker network exists and the worker container is on it:
|
|
353
|
+
\`\`\`bash
|
|
354
|
+
docker network inspect <dockerNetwork> --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null || echo "NETWORK MISSING"
|
|
355
|
+
\`\`\`
|
|
356
|
+
If the network does not exist or the \`skyramp\` container is not listed, \`apply_mock\` will fail with "failed to get containers in network". Do NOT proceed — re-run \`docker compose up\` or debug why the network was not created.
|
|
357
|
+
9. **Readiness gate** — the SUT may take several minutes to start, especially compiled frontends (Next.js, Vite, Webpack) that build on first start. Do NOT proceed to test execution until the endpoint is actually serving. Check **both** the base URL and the specific API endpoint under test — a root page returning 200 does not mean the API is ready (downstream services may still be initializing, causing 500s on API routes). Run this readiness loop in a SINGLE Bash command:
|
|
358
|
+
\`\`\`bash
|
|
359
|
+
echo "Waiting for SUT to be ready..."
|
|
360
|
+
# Adjust to the SUT's actual base URL and port if different
|
|
361
|
+
BASE_URL="http://localhost:8080"
|
|
362
|
+
# Replace with the primary API endpoint under test
|
|
363
|
+
API_PATH="/api/<endpoint-under-test>"
|
|
364
|
+
for i in $(seq 1 60); do
|
|
365
|
+
base_status=$(curl -s -o /dev/null -w '%{http_code}' "$BASE_URL/" 2>/dev/null)
|
|
366
|
+
api_status=$(curl -s -o /dev/null -w '%{http_code}' "$BASE_URL$API_PATH" 2>/dev/null)
|
|
367
|
+
if [ "$base_status" -ge 200 ] && [ "$base_status" -lt 500 ] && [ "$api_status" -ne 0 ] && [ "$api_status" -lt 500 ]; then
|
|
368
|
+
echo "SUT ready (base=$base_status, api=$api_status) after $((i*10))s"
|
|
369
|
+
break
|
|
370
|
+
fi
|
|
371
|
+
echo "Attempt $i/60: base=$base_status api=$api_status — waiting 10s..."
|
|
372
|
+
sleep 10
|
|
373
|
+
done
|
|
374
|
+
\`\`\`
|
|
375
|
+
This waits up to 10 minutes (60 attempts x 10s). Replace \`API_PATH\` with the primary endpoint under test. If the API returns 4xx (e.g., 400 missing params, 404 no resource), the SUT is ready — only 5xx and connection failures indicate it is still starting. Do NOT split this into multiple Bash commands — run it as one command to use only one turn. Only proceed to Execute when the loop reports ready.
|
|
376
|
+
|
|
377
|
+
The \`skyramp_execute_test\` tool runs the enriched test in the language runner. Its generated Skyramp client connects to the worker — emitted with \`runtime="docker"\`, \`docker_network\`, and \`docker_skyramp_port\` (from the \`dockerNetwork\` and \`dockerWorkerPort\` passed to generation) — and applies the selected mocks before exercising the service.
|
|
378
|
+
|
|
379
|
+
**Do NOT proceed to Execute until: (a) the service under test responds, (b) the \`skyramp\` worker is running, and (c) all mocked service containers are confirmed stopped.**
|
|
380
|
+
|
|
381
|
+
**Do NOT modify application source code from the diff.** The changed files provide the complete implementation. If a source file appears truncated or incomplete, that is how it was authored — do not "complete" or edit it. Modifying source files triggers unnecessary rebuilds and wastes turns. Only modify generated test and mock files.`)
|
|
382
|
+
.subStep("DEPLOY_GRPC", "Handle gRPC dependencies", (_ctx) => `If the dependency map from Phase 1 includes gRPC services selected for mocking:
|
|
383
|
+
|
|
384
|
+
To regenerate a single missing gRPC mock, call \`skyramp_mock_generation\` with \`protocol: "grpc"\`.
|
|
385
|
+
- Use \`endpointURL\` as the original Docker service target in \`host:port\` form, for example \`profile-service:50051\`.
|
|
386
|
+
- Provide \`protoPath\` for the .proto containing the service and \`grpcServiceName\` (${GRPC_SERVICE_NAME_GUIDANCE}).
|
|
387
|
+
- Provide \`protobufExtraFiles\` and \`protobufExtraImportPath\` when the service proto imports other files.
|
|
388
|
+
- Do NOT replace a gRPC dependency with a REST fallback mock unless the implementation actually calls that REST fallback in the scenario under test.
|
|
389
|
+
- If a gRPC scenario cannot be mocked because required proto/schema details are unavailable, record that exact gap in issuesFound and do not fabricate an HTTP mock.
|
|
390
|
+
|
|
391
|
+
For services like profile-service that expose multiple gRPC RPCs (get_profile, get_preferences, refresh_metrics), each RPC is a separate dependency that may need its own gRPC mock. Document each RPC's mock decision separately.`)
|
|
392
|
+
.subStep("DEPLOY_KAFKA", "Handle Kafka dependencies", (_ctx) => `If the dependency map from Phase 1 includes Kafka producers or consumers selected for mocking:
|
|
393
|
+
|
|
394
|
+
To regenerate a single missing Kafka mock, call \`skyramp_mock_generation\` with \`protocol: "kafka"\`.
|
|
395
|
+
- Use \`endpointURL\` as the original broker target in \`host:port\` form, for example \`broker:9092\`.
|
|
396
|
+
- Provide \`kafkaTopic\` with the topic name.
|
|
397
|
+
- Use \`requestData\` as the producer message schema/shape when validating published messages.
|
|
398
|
+
- Use \`responseData\` as the message payload to inject for consumer scenarios.
|
|
399
|
+
- Use a real Kafka-compatible broker when it is local infrastructure or when the scenario explicitly needs infrastructure behavior that the Kafka mock cannot model, and document that decision in issuesFound. If Kafka is selected for mocking, provide exactly one of \`requestData\` or \`responseData\` and enrich the integration test so the Kafka mock is applied with the rest of the mock set.`)
|
|
400
|
+
.done()
|
|
401
|
+
// ─── Phase 6: Execute ───────────────────────────────────────────────────────
|
|
402
|
+
.addPhase("execute", "Execute & Report", {
|
|
403
|
+
headerLevel: "##",
|
|
404
|
+
stepFormat: "hash",
|
|
405
|
+
})
|
|
406
|
+
.step("EXECUTE_TESTS", "Execute tests via skyramp_execute_test MCP tool", (ctx) => `**You MUST use the \`skyramp_execute_test\` MCP tool for every test file.**
|
|
407
|
+
|
|
408
|
+
**NEVER use any of these alternatives:**
|
|
409
|
+
- \`skyramp execute test\` CLI command
|
|
410
|
+
- Direct language runtimes (\`python3\`, \`pytest\`, \`node\`, \`npx jest\`, etc.)
|
|
411
|
+
- Any shell command to run tests
|
|
412
|
+
|
|
413
|
+
**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."
|
|
414
|
+
|
|
415
|
+
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\`.
|
|
416
|
+
|
|
417
|
+
**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.
|
|
418
|
+
|
|
419
|
+
**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.
|
|
420
|
+
|
|
421
|
+
**Execution — one call per test file:**
|
|
422
|
+
\`\`\`
|
|
423
|
+
skyramp_execute_test({
|
|
424
|
+
testFile: "${ctx.repositoryPath}/tests/skyramp/<scenario>_test.py",
|
|
425
|
+
language: "<from workspace config>",
|
|
426
|
+
testType: "<'integration' or 'contract' — match the scenario table>",
|
|
427
|
+
workspacePath: "${ctx.repositoryPath}",
|
|
428
|
+
token: "",
|
|
429
|
+
})
|
|
430
|
+
\`\`\`
|
|
431
|
+
|
|
432
|
+
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.
|
|
433
|
+
|
|
434
|
+
**On failure:**
|
|
435
|
+
1. Read the error output to diagnose the root cause.
|
|
436
|
+
2. "401 Unauthorized" → auth token missing or wrong. Do not hardcode a token into the test; pass the correct token through \`skyramp_execute_test.token\` or set \`SKYRAMP_TEST_TOKEN\` in the MCP execution environment, then retry once.
|
|
437
|
+
3. "HTTP 500 / Internal Server Error" → the SUT is running but the API endpoint is not ready yet. This commonly happens with frameworks that compile routes lazily (Next.js, Vite) — the root page serves while new API routes added by the diff are still compiling. Do NOT immediately try a different test file — the same issue will affect all tests. Instead, wait 30 seconds, re-run the readiness gate against the API endpoint, and retry the SAME test. If HTTP 500 persists after 2 retries, check \`docker compose <composeFilePrefix> logs <sut-service>\` for startup errors.
|
|
438
|
+
4. "Connection refused" → Skyramp worker not running or network mismatch. Run \`docker compose <composeFilePrefix> ps\` to verify the worker and SUT are running. If a stale port-forward or unrelated process holds the port, kill it and retry.
|
|
439
|
+
5. Syntax/import error → fix the test file and retry **once**.
|
|
440
|
+
6. Mock deployment error / "failed to get containers in network" → the Docker network does not exist or docker-compose did not start correctly. Re-run \`docker compose <composeFilePrefix> up -d\` and check \`docker compose <composeFilePrefix> logs\` for containers that crashed on startup. Do NOT delete mock imports, empty \`MOCK_SERVICES\`, or switch selected mocked downstreams to live Compose stubs just to get a green test. If the retry still fails, record \`status: "Fail"\` or blocked with the exact mock deployment error.
|
|
441
|
+
7. **Timeout / no response** → if \`skyramp_execute_test\` does not return within the tool's timeout window, record \`status: "Timeout"\` with the test file path and do not retry automatically. Report to the user: the test may still be running or the worker may be unresponsive. Do not infer pass or fail from a timeout.
|
|
442
|
+
8. **Application bug** → if the failure is a DB constraint violation, a null reference, a business logic error, or any error clearly originating in the application under test (not in Skyramp tooling or mock wiring), record \`status: "Fail — app bug"\` with the exact error. Do NOT modify the application code. Report it as a real regression caught by the test.
|
|
443
|
+
9. If it still fails after one retry for non-app-bug errors, record \`status: "Fail"\` with details and move on.
|
|
444
|
+
|
|
445
|
+
**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.
|
|
446
|
+
|
|
447
|
+
**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 outbound request correctness and the outbound target is mocked instead of real. Execute every generated test file and report any high-priority scenarios that could not be generated or executed, including the reason.`)
|
|
448
|
+
.step("REPORT_RESULTS", "Report results to user", (_ctx) => `Present a summary to the user covering:
|
|
449
|
+
|
|
450
|
+
- **What changed**: 2-3 sentence summary of the diff
|
|
451
|
+
- **Mock scope**: list every downstream dependency and its disposition — (a) mocked via Skyramp, (b) real (outbound validation) — kept real because the test validates what the SUT sends to it (default behavior when user did not explicitly request mocking), (c) mocked per user request (outbound gap) — user requested mocking but outbound value correctness cannot be validated, (d) real/live local service with reason, or (e) skipped with reason. Flag explicitly if the actual scope differs from what the user requested. For gRPC mocks, report the endpoint host:port and whether the worker alias matched the original host.
|
|
452
|
+
- **Mock deployment**: which services are mocked on which ports
|
|
453
|
+
- **Tests created**: file path, type, scenario description
|
|
454
|
+
- **Full-flow coverage**: which API workflows were covered as integration scenarios, and for frontend changes, which backend APIs were tested instead of UI flows
|
|
455
|
+
- **Results table**: one row per test — test name, scenario, status (PASS / FAIL / Timeout / Blocked), duration, failure detail
|
|
456
|
+
- **Failure classification**: for every non-passing test, distinguish the root cause:
|
|
457
|
+
- **App bug** — failure originates in the application under test (DB constraint, null reference, business logic error). The test did its job; this is a real regression to fix.
|
|
458
|
+
- **Skyramp/mock issue** — failure from mock deployment, worker connectivity, or tool error. The app may be correct; the test infrastructure needs fixing.
|
|
459
|
+
- **Generated test issue** — failure from wrong auth, missing field, or incorrect assertion in the generated test itself.
|
|
460
|
+
- **Timeout** — execution did not complete; pass/fail is unknown.
|
|
461
|
+
- **Outbound coverage gaps**: for any scenario whose value depends on outbound request correctness but whose target was mocked per user request, flag it: "outbound value correctness for <service> was NOT validated because it was mocked; mocks cannot distinguish correct from incorrect request bodies. To validate, re-run with <service> as a real service."
|
|
462
|
+
- **Issues found**: unmockable deps, generation failures, connection errors, gaps
|
|
463
|
+
- **Teardown**: remind the user that, when finished, tearing the stack down with \`docker compose <composeFilePrefix> down -v --remove-orphans\` (the same \`-f\` prefix returned by \`skyramp_setup_local_dev_worker\`) removes the SUT, real dependencies, **and the Skyramp worker** together — so no worker container or network is left behind. A plain \`docker compose down\` *without* the override prefix leaves the worker running (it is defined only in the override, so a base-only \`down\` treats it as an orphan) and the network fails to delete with "resource still in use"; always include the prefix. If the prefix is ever unavailable at teardown, \`docker compose down --remove-orphans\` against the base file still sweeps the worker as an orphan.
|
|
464
|
+
|
|
465
|
+
Do NOT write results to a file — report inline to the user.`)
|
|
466
|
+
.done();
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { type Service } from "../../workspace/workspace.js";
|
|
3
|
+
export declare function getLocalDevPrompt(baseCommit: string, repositoryPath: string, sandboxWorkerUrl?: string, maxRecommendations?: number, maxGenerate?: number, services?: Service[], stateOutputFile?: string, servicesToMock?: string[], realServices?: string[], mockProtocols?: string[], proxyKubeconfig?: string, proxyNamespace?: string): string;
|
|
4
|
+
export declare function registerLocalDevPrompt(server: McpServer): void;
|