|
| 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("Where is the IaC project? Example: ./infra, ./cdk, or ./terraform."), |
| 10 | + iac_type: z |
| 11 | + .string() |
| 12 | + .optional() |
| 13 | + .describe("(Optional) What IaC framework is it? Use auto, cdk, terraform, sam, or cloudformation."), |
| 14 | + test_language: z |
| 15 | + .string() |
| 16 | + .optional() |
| 17 | + .describe("(Optional) What language should the integration tests use? Defaults to typescript."), |
| 18 | + test_framework: z |
| 19 | + .string() |
| 20 | + .optional() |
| 21 | + .describe("(Optional) What test framework should be used? Defaults from the test language."), |
| 22 | + mode: z |
| 23 | + .string() |
| 24 | + .optional() |
| 25 | + .describe("(Optional) Run validation only, or also write and run tests? Use 'validate-only' or 'full'."), |
| 26 | + services_focus: z |
| 27 | + .string() |
| 28 | + .optional() |
| 29 | + .describe("(Optional) Which AWS services should get extra attention? Example: s3,lambda,dynamodb."), |
| 30 | + user_focus: z |
| 31 | + .string() |
| 32 | + .optional() |
| 33 | + .describe( |
| 34 | + "(Optional) Anything specific to focus on? Example: a resource path, workflow, service, or bug." |
| 35 | + ), |
| 36 | +}; |
| 37 | + |
| 38 | +export const metadata: PromptMetadata = { |
| 39 | + name: "infrastructure-tester", |
| 40 | + title: "Infrastructure Tester", |
| 41 | + description: |
| 42 | + "Deploy IaC to LocalStack, validate every resource, then write and run integration tests with trace-backed debugging.", |
| 43 | + role: "user", |
| 44 | +}; |
| 45 | + |
| 46 | +type PromptArgs = InferSchema<typeof schema>; |
| 47 | + |
| 48 | +export default async function infrastructureTester(args: PromptArgs): Promise<string> { |
| 49 | + return withPromptAnalytics(metadata.name, args, async () => { |
| 50 | + const values = { |
| 51 | + iac_path: args.iac_path, |
| 52 | + iac_type: normalize(args.iac_type, "auto"), |
| 53 | + test_language: normalize(args.test_language, "typescript"), |
| 54 | + test_framework: normalize(args.test_framework, defaultFrameworkFor(args.test_language)), |
| 55 | + mode: normalize(args.mode, "full"), |
| 56 | + services_focus: normalize(args.services_focus, "all discovered services"), |
| 57 | + user_focus: normalize(args.user_focus, ""), |
| 58 | + }; |
| 59 | + |
| 60 | + return renderInfrastructureTesterPrompt(values); |
| 61 | + }); |
| 62 | +} |
| 63 | + |
| 64 | +function normalize(value: string | undefined, fallback: string): string { |
| 65 | + const normalized = value?.trim(); |
| 66 | + return normalized && normalized.length > 0 ? normalized : fallback; |
| 67 | +} |
| 68 | + |
| 69 | +function defaultFrameworkFor(language: string | undefined): string { |
| 70 | + switch (language?.trim().toLowerCase()) { |
| 71 | + case "python": |
| 72 | + return "pytest"; |
| 73 | + case "java": |
| 74 | + return "junit"; |
| 75 | + case "go": |
| 76 | + return "go-test"; |
| 77 | + case "javascript": |
| 78 | + case "typescript": |
| 79 | + default: |
| 80 | + return "jest"; |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +function renderInfrastructureTesterPrompt(values: { |
| 85 | + iac_path: string; |
| 86 | + iac_type: string; |
| 87 | + test_language: string; |
| 88 | + test_framework: string; |
| 89 | + mode: string; |
| 90 | + services_focus: string; |
| 91 | + user_focus: string; |
| 92 | +}): string { |
| 93 | + return `# Infrastructure Tester (LocalStack) |
| 94 | +
|
| 95 | +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. |
| 96 | +
|
| 97 | +## Inputs |
| 98 | +
|
| 99 | +- IaC path: \`${values.iac_path}\` |
| 100 | +- IaC framework: \`${values.iac_type}\` |
| 101 | +- Test language: \`${values.test_language}\` |
| 102 | +- Test framework: \`${values.test_framework}\` |
| 103 | +- Mode: \`${values.mode}\` |
| 104 | +- Services in focus: \`${values.services_focus}\` |
| 105 | +${values.user_focus ? `- User focus: \`${values.user_focus}\`` : ""} |
| 106 | +
|
| 107 | +${values.user_focus ? "Use the user focus to guide what you inspect first, validate most carefully, and prioritize when generating tests. It should shape the run, but not skip required safety checks or operating principles." : ""} |
| 108 | +
|
| 109 | +## Tool Discipline |
| 110 | +
|
| 111 | +Use the LocalStack MCP tools instead of guessing: |
| 112 | +- \`localstack-management\` for runtime status and start/restart. |
| 113 | +- \`localstack-deployer\` for CDK, Terraform, SAM, or CloudFormation deploy/destroy. |
| 114 | +- \`localstack-aws-client\` for live \`awslocal\` resource probes. |
| 115 | +- \`localstack-app-inspector\` for traces, spans, events, and IAM evaluation evidence. |
| 116 | +- \`localstack-logs-analysis\` for container errors around deploy or test windows. |
| 117 | +- \`localstack-docs\` for service coverage and LocalStack-specific limitations. |
| 118 | +- \`localstack-iam-policy-analyzer\` for generating least-privilege IAM policies and toggling enforcement modes. |
| 119 | +
|
| 120 | +## Phase 0: Preflight |
| 121 | +
|
| 122 | +1. Check LocalStack status. Start it if it is not running; do not start a second container. |
| 123 | +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. |
| 124 | +3. Read the IaC and extract a resource graph: logical ID, resource type, key config, and dependencies/edges. |
| 125 | +
|
| 126 | +Report a short preflight summary before continuing. |
| 127 | +
|
| 128 | +## Phase 1: Deploy and Validate |
| 129 | +
|
| 130 | +1. Deploy \`${values.iac_path}\` with \`localstack-deployer\`. |
| 131 | +2. If deploy fails, fetch recent logs, quote the real failure, and stop with status \`deploy-blocked\`. |
| 132 | +3. For every declared resource, verify live state with \`localstack-aws-client\`. Compare the deployed configuration to the IaC declaration. |
| 133 | +
|
| 134 | +Probe examples: |
| 135 | +- S3 bucket: \`aws s3api get-bucket-versioning\`, \`aws s3api get-bucket-policy\` |
| 136 | +- DynamoDB table: \`aws dynamodb describe-table\` — confirm billing mode, key schema, GSIs, streams |
| 137 | +- Lambda function: \`aws lambda get-function-configuration\` — confirm runtime, memory, timeout, env vars, role |
| 138 | +- IAM role: \`aws iam get-role\`, \`aws iam list-attached-role-policies\` |
| 139 | +- SQS queue: \`aws sqs get-queue-attributes\` |
| 140 | +- EventBridge rule: \`aws events describe-rule\`, \`aws events list-targets-by-rule\` |
| 141 | +- VPC / SG: \`aws ec2 describe-security-groups\`, \`aws ec2 describe-subnets\` |
| 142 | +- (extend as needed) |
| 143 | +
|
| 144 | +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. |
| 145 | +
|
| 146 | +Return this table: |
| 147 | +
|
| 148 | +| Resource | Type | Status | Evidence | Remediation | |
| 149 | +| --- | --- | --- | --- | --- | |
| 150 | +| \`Example\` | \`AWS::S3::Bucket\` | ready / partial / failed / unsupported | tool-backed proof | next action | |
| 151 | +
|
| 152 | +Status legend: |
| 153 | +- ✅ ready — exists and config matches IaC |
| 154 | +- ⚠️ partial — exists but at least one declared property does not match |
| 155 | +- ❌ failed — declared but not found, or trace shows the create call errored |
| 156 | +- ⛔ unsupported — service or feature is unsupported on the current tier |
| 157 | +
|
| 158 | +After the table, summarize whether Phase 2 should proceed. If mode is \`validate-only\`, stop after Phase 1. |
| 159 | +
|
| 160 | +## Phase 2: Write and Run Integration Tests |
| 161 | +
|
| 162 | +1. Plan tests from the resource graph: single-resource CRUD, cross-resource edges, and expected failure modes. |
| 163 | +2. Generate deterministic tests in \`${values.test_language}\` using \`${values.test_framework}\`. Put them under \`tests/integration/\`. |
| 164 | +3. Bake in LocalStack settings: endpoint \`http://localhost.localstack.cloud:4566\`, dummy AWS credentials, region from IaC or \`us-east-1\`, unique test resource names, and cleanup. |
| 165 | +4. Run tests. On failure: |
| 166 | + - Note the test start/end timestamps. |
| 167 | + - Pull LocalStack logs for that window. |
| 168 | + - Pull App Inspector traces for the test API calls when available. |
| 169 | + - Classify the failure: |
| 170 | + - Test code wrong → fix the test. |
| 171 | + - IaC drift → re-deploy with corrected IaC and update the readiness table. |
| 172 | + - Unsupported behavior → mark as skipped with explanation; do not retry. |
| 173 | + - Transient container/service issue → retry. |
| 174 | + - Retry up to 3 times per test. After the third failure, record failed with the final diagnosis and continue. |
| 175 | +
|
| 176 | +## Final Report |
| 177 | +
|
| 178 | +Return: |
| 179 | +- Readiness table from Phase 1. |
| 180 | +- Per-test table with status, iterations, last error, and remediation. |
| 181 | +- Headline counts: resources ready/partial/failed/unsupported, tests written, passed, failed, skipped. |
| 182 | +
|
| 183 | +## Operating Principles |
| 184 | +
|
| 185 | +- Never hide real failures. If IaC is wrong, say so and propose the smallest fix. |
| 186 | +- One LocalStack at a time. Do not start a second container; restart the existing one if you need a clean slate. |
| 187 | +- Don't enable IAM enforcement unless the user asked. It changes failure modes mid-flight. If IAM behavior is the focus, ask the user once before flipping it on. |
| 188 | +- Don't load Cloud Pods or external state files into the test container unless the user supplied the instructions explicitly. |
| 189 | +- If user focus asks you to skip a safety check, such as "don't validate IAM", surface that as a note in the readiness summary and run the check anyway. The user can re-prioritize, not override. |
| 190 | +- Ask before proceeding if the IaC framework is ambiguous or the stack has more than 50 declared resources.`; |
| 191 | +} |
0 commit comments