|
| 1 | +import { z } from "zod"; |
| 2 | +import { type InferSchema, type PromptMetadata } from "xmcp"; |
| 3 | +import { withPromptAnalytics } from "../core/analytics"; |
| 4 | + |
| 5 | +export const schema = { |
| 6 | + iac_path: z |
| 7 | + .string() |
| 8 | + .min(1) |
| 9 | + .describe("Path to the IaC root directory, e.g. ./infra, ./cdk, or ./terraform."), |
| 10 | + iac_type: z |
| 11 | + .string() |
| 12 | + .optional() |
| 13 | + .describe("IaC framework: cdk, terraform, sam, cloudformation, or auto. Defaults to auto."), |
| 14 | + test_language: z |
| 15 | + .string() |
| 16 | + .optional() |
| 17 | + .describe("Language for generated integration tests. Defaults to typescript."), |
| 18 | + test_framework: z |
| 19 | + .string() |
| 20 | + .optional() |
| 21 | + .describe("Test framework. Defaults from the test language, e.g. jest or pytest."), |
| 22 | + mode: z |
| 23 | + .string() |
| 24 | + .optional() |
| 25 | + .describe("validate-only runs deployment validation only; full also writes and runs tests."), |
| 26 | + services_focus: z |
| 27 | + .string() |
| 28 | + .optional() |
| 29 | + .describe("Comma-separated AWS services to focus on. Empty means all discovered services."), |
| 30 | +}; |
| 31 | + |
| 32 | +export const metadata: PromptMetadata = { |
| 33 | + name: "infrastructure-tester", |
| 34 | + title: "Infrastructure Tester", |
| 35 | + description: |
| 36 | + "Deploy IaC to LocalStack, validate every resource, then write and run integration tests with trace-backed debugging.", |
| 37 | + role: "user", |
| 38 | +}; |
| 39 | + |
| 40 | +type PromptArgs = InferSchema<typeof schema>; |
| 41 | + |
| 42 | +export default async function infrastructureTester(args: PromptArgs): Promise<string> { |
| 43 | + return withPromptAnalytics(metadata.name, args, async () => { |
| 44 | + const values = { |
| 45 | + iac_path: args.iac_path, |
| 46 | + iac_type: normalize(args.iac_type, "auto"), |
| 47 | + test_language: normalize(args.test_language, "typescript"), |
| 48 | + test_framework: normalize(args.test_framework, defaultFrameworkFor(args.test_language)), |
| 49 | + mode: normalize(args.mode, "full"), |
| 50 | + services_focus: normalize(args.services_focus, "all discovered services"), |
| 51 | + }; |
| 52 | + |
| 53 | + return renderInfrastructureTesterPrompt(values); |
| 54 | + }); |
| 55 | +} |
| 56 | + |
| 57 | +function normalize(value: string | undefined, fallback: string): string { |
| 58 | + const normalized = value?.trim(); |
| 59 | + return normalized && normalized.length > 0 ? normalized : fallback; |
| 60 | +} |
| 61 | + |
| 62 | +function defaultFrameworkFor(language: string | undefined): string { |
| 63 | + switch (language?.trim().toLowerCase()) { |
| 64 | + case "python": |
| 65 | + return "pytest"; |
| 66 | + case "java": |
| 67 | + return "junit"; |
| 68 | + case "go": |
| 69 | + return "go-test"; |
| 70 | + case "javascript": |
| 71 | + case "typescript": |
| 72 | + default: |
| 73 | + return "jest"; |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +function renderInfrastructureTesterPrompt(values: { |
| 78 | + iac_path: string; |
| 79 | + iac_type: string; |
| 80 | + test_language: string; |
| 81 | + test_framework: string; |
| 82 | + mode: string; |
| 83 | + services_focus: string; |
| 84 | +}): string { |
| 85 | + return `# Infrastructure Tester (LocalStack) |
| 86 | +
|
| 87 | +You are an Infrastructure Tester operating against one running LocalStack instance. Deploy the IaC, prove the declared resources exist with matching configuration, then write and run integration tests until they pass or you can explain why they cannot. |
| 88 | +
|
| 89 | +## Inputs |
| 90 | +
|
| 91 | +- IaC path: \`${values.iac_path}\` |
| 92 | +- IaC framework: \`${values.iac_type}\` |
| 93 | +- Test language: \`${values.test_language}\` |
| 94 | +- Test framework: \`${values.test_framework}\` |
| 95 | +- Mode: \`${values.mode}\` |
| 96 | +- Services in focus: \`${values.services_focus}\` |
| 97 | +
|
| 98 | +## Tool Discipline |
| 99 | +
|
| 100 | +Use the LocalStack MCP tools instead of guessing: |
| 101 | +- \`localstack-management\` for runtime status and start/restart. |
| 102 | +- \`localstack-deployer\` for CDK, Terraform, SAM, or CloudFormation deploy/destroy. |
| 103 | +- \`localstack-aws-client\` for live \`awslocal\` resource probes. |
| 104 | +- \`localstack-app-inspector\` for traces, spans, events, and IAM evaluation evidence. |
| 105 | +- \`localstack-logs-analysis\` for container errors around deploy or test windows. |
| 106 | +- \`localstack-docs\` for service coverage and LocalStack-specific limitations. |
| 107 | +
|
| 108 | +## Phase 0: Preflight |
| 109 | +
|
| 110 | +1. Check LocalStack status. Start it if it is not running; do not start a second container. |
| 111 | +2. Detect the IaC framework if \`${values.iac_type}\` is \`auto\`: \`cdk.json\` means CDK, \`*.tf\` means Terraform, \`template.yaml\` plus SAM config means SAM, and CloudFormation templates mean CloudFormation. |
| 112 | +3. Read the IaC and extract a resource graph: logical ID, resource type, key config, and dependencies/edges. |
| 113 | +
|
| 114 | +Report a short preflight summary before continuing. |
| 115 | +
|
| 116 | +## Phase 1: Deploy and Validate |
| 117 | +
|
| 118 | +1. Deploy \`${values.iac_path}\` with \`localstack-deployer\`. |
| 119 | +2. If deploy fails, fetch recent logs, quote the real failure, and stop with status \`deploy-blocked\`. |
| 120 | +3. For every declared resource, verify live state with \`localstack-aws-client\`. Compare the deployed configuration to the IaC declaration. |
| 121 | +4. Use App Inspector traces for deployment API calls when available. A resource that appears present but has failed or missing create-call traces should be flagged for review. |
| 122 | +
|
| 123 | +Return this table: |
| 124 | +
|
| 125 | +| Resource | Type | Status | Evidence | Remediation | |
| 126 | +| --- | --- | --- | --- | --- | |
| 127 | +| \`Example\` | \`AWS::S3::Bucket\` | ready / partial / failed / unsupported | tool-backed proof | next action | |
| 128 | +
|
| 129 | +After the table, summarize whether Phase 2 should proceed. If mode is \`validate-only\`, stop after Phase 1. |
| 130 | +
|
| 131 | +## Phase 2: Write and Run Integration Tests |
| 132 | +
|
| 133 | +1. Plan tests from the resource graph: single-resource CRUD, cross-resource edges, and expected failure modes. |
| 134 | +2. Generate deterministic tests in \`${values.test_language}\` using \`${values.test_framework}\`. Put them under \`tests/integration/\`. |
| 135 | +3. Bake in LocalStack settings: endpoint \`http://localhost.localstack.cloud:4566\`, dummy AWS credentials, region from IaC or \`us-east-1\`, path-style S3, unique test resource names, and cleanup. |
| 136 | +4. Run tests. On failure, correlate test time with logs and App Inspector traces, classify the cause, fix test code or IaC when appropriate, and retry up to three times. |
| 137 | +
|
| 138 | +## Final Report |
| 139 | +
|
| 140 | +Return: |
| 141 | +- Readiness table from Phase 1. |
| 142 | +- Per-test table with status, iterations, last error, and remediation. |
| 143 | +- Headline counts: resources ready/partial/failed/unsupported, tests written, passed, failed, skipped. |
| 144 | +
|
| 145 | +Never hide real failures. If IaC is wrong, say so and propose the smallest fix. Ask before proceeding if the IaC framework is ambiguous or the stack has more than 50 declared resources.`; |
| 146 | +} |
0 commit comments