-
Notifications
You must be signed in to change notification settings - Fork 0
chore(tests): add an end to end test that uses real terraform plan output #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3c9fa30
8747342
1af27b4
df0e0ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| { | ||
| "name": "infra-diff", | ||
| "image": "mcr.microsoft.com/devcontainers/typescript-node:22", | ||
| "features": { | ||
| "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}, | ||
| "ghcr.io/devcontainers-contrib/features/terraform-asdf:2": { | ||
| "version": "1.13.4" | ||
| } | ||
| }, | ||
| "customizations": { | ||
| "vscode": { | ||
| "extensions": [ | ||
| "dbaeumer.vscode-eslint", | ||
| "esbenp.prettier-vscode", | ||
| "biomejs.biome", | ||
| "hashicorp.terraform" | ||
| ], | ||
| "settings": { | ||
| "editor.formatOnSave": true, | ||
| "editor.defaultFormatter": "biomejs.biome" | ||
| } | ||
| } | ||
| }, | ||
| "postCreateCommand": "npm install", | ||
| "forwardPorts": [50000], | ||
| "mounts": [ | ||
| "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" | ||
| ], | ||
| "remoteUser": "node" | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -40,9 +40,23 @@ jobs: | |||||
| - run: npm test | ||||||
| - run: npm run test:e2e:file-reading | ||||||
|
|
||||||
| test-terraform-integration: | ||||||
| runs-on: ubuntu-latest | ||||||
| steps: | ||||||
| - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 | ||||||
| - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 | ||||||
| with: | ||||||
| node-version-file: ".nvmrc" | ||||||
| - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 | ||||||
| with: | ||||||
| terraform_version: 1.13.4 | ||||||
|
||||||
| terraform_version: 1.13.4 | |
| terraform_version_file: ".terraform-version" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 1.13.4 |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,15 @@ | ||||
| --- | ||||
| version: '3.8' | ||||
|
||||
| version: '3.8' |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,253 @@ | ||||||
| import { existsSync, rmSync, unlinkSync } from "node:fs"; | ||||||
| import * as fs from "node:fs/promises"; | ||||||
| import * as path from "node:path"; | ||||||
| import { | ||||||
| GenericContainer, | ||||||
| Network, | ||||||
| type StartedNetwork, | ||||||
| type StartedTestContainer, | ||||||
| } from "testcontainers"; | ||||||
| import { afterAll, beforeAll, describe, expect, it } from "vitest"; | ||||||
| import { ParsePlanUseCase } from "../src/domain/usecases/ParsePlanUseCase"; | ||||||
| import { ReadPlanFileUseCase } from "../src/domain/usecases/ReadPlanFileUseCase"; | ||||||
| import { FilesystemAdapter } from "../src/infrastructure/adapters/FilesystemAdapter"; | ||||||
|
|
||||||
| describe("E2E: Terraform Integration with moto", () => { | ||||||
| const terraformDir = path.join(process.cwd(), "e2e", "terraform"); | ||||||
| const planFileRelative = "plan.bin"; | ||||||
| const planFile = path.join(terraformDir, planFileRelative); | ||||||
| const planJsonFileRelative = "plan.json"; | ||||||
| const planJsonFile = path.join(terraformDir, planJsonFileRelative); | ||||||
| let motoContainer: StartedTestContainer; | ||||||
| let terraformContainer: StartedTestContainer; | ||||||
| let network: StartedNetwork; | ||||||
| const motoContainerName = "moto-server"; | ||||||
|
|
||||||
| // Read Terraform version from .terraform-version file | ||||||
| const getTerraformVersion = async (): Promise<string> => { | ||||||
| const versionFilePath = path.join(process.cwd(), ".terraform-version"); | ||||||
| const versionContent = await fs.readFile(versionFilePath, "utf-8"); | ||||||
| return versionContent.trim(); | ||||||
| }; | ||||||
|
|
||||||
| beforeAll(async () => { | ||||||
| try { | ||||||
| // Get Terraform version from .terraform-version file | ||||||
| const terraformVersion = await getTerraformVersion(); | ||||||
| console.log(`Using Terraform version: ${terraformVersion}`); | ||||||
|
|
||||||
| // Create a shared network for containers | ||||||
| console.log("Creating Docker network..."); | ||||||
| network = await new Network().start(); | ||||||
|
|
||||||
| // Start moto server in the network | ||||||
| console.log("Starting moto server..."); | ||||||
| motoContainer = await new GenericContainer("motoserver/moto:5.0.0") | ||||||
| .withNetwork(network) | ||||||
| .withNetworkAliases(motoContainerName) | ||||||
| .withExposedPorts(5000) | ||||||
| .withStartupTimeout(120000) | ||||||
| .start(); | ||||||
|
|
||||||
| const motoPort = motoContainer.getFirstMappedPort(); | ||||||
| console.log(`Moto server is ready on port ${motoPort}`); | ||||||
|
||||||
| console.log(`Moto server is ready on port ${motoPort}`); | |
| console.log(JSON.stringify({ event: "moto_server_ready", port: motoPort })); |
Copilot
AI
Nov 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The version string used in this template literal comes from reading the .terraform-version file, but there's no validation that the version format is correct or that the Docker image tag exists. Consider adding validation or error handling if the version is malformed or the image doesn't exist with that tag.
Copilot
AI
Nov 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using console.log for test output violates the operational convention stated in the coding guidelines: 'ensure all application logs are structured as JSON objects, except when logging to the console in GitHub Actions.' Since this is a test file, consider using the test framework's logging capabilities or a structured logger.
| console.log("Initializing Terraform..."); | |
| console.log(JSON.stringify({ message: "Initializing Terraform..." })); |
Copilot
AI
Nov 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using console.log for test output violates the operational convention stated in the coding guidelines: 'ensure all application logs are structured as JSON objects, except when logging to the console in GitHub Actions.' Since this is a test file, consider using the test framework's logging capabilities or a structured logger.
| console.log("Generating Terraform plan..."); | |
| console.log(JSON.stringify({ event: "Generating Terraform plan" })); |
Copilot
AI
Nov 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using console.log for test output violates the operational convention stated in the coding guidelines: 'ensure all application logs are structured as JSON objects, except when logging to the console in GitHub Actions.' Since this is a test file, consider using the test framework's logging capabilities or a structured logger.
Copilot
AI
Nov 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using console.error violates the operational convention stated in the coding guidelines: 'ensure all application logs are structured as JSON objects, except when logging to the console in GitHub Actions.' Since this is a test file, consider using the test framework's logging capabilities or a structured logger.
Copilot
AI
Nov 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using console.log for test output violates the operational convention stated in the coding guidelines: 'ensure all application logs are structured as JSON objects, except when logging to the console in GitHub Actions.' Since this is a test file, consider using the test framework's logging capabilities or a structured logger.
| console.log("Cleaning up..."); | |
| console.log(JSON.stringify({ event: "cleanup", message: "Cleaning up..." })); |
Copilot
AI
Nov 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using console.error violates the operational convention stated in the coding guidelines: 'ensure all application logs are structured as JSON objects, except when logging to the console in GitHub Actions.' Since this is a test file, consider using the test framework's logging capabilities or a structured logger.
| console.error("Failed to cleanup plan files:", error); | |
| console.log(JSON.stringify({ level: "error", message: "Failed to cleanup plan files", error: error instanceof Error ? error.message : String(error) })); |
Copilot
AI
Nov 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using console.error violates the operational convention stated in the coding guidelines: 'ensure all application logs are structured as JSON objects, except when logging to the console in GitHub Actions.' Since this is a test file, consider using the test framework's logging capabilities or a structured logger.
Copilot
AI
Nov 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The cleanup logic in the afterAll hook has multiple try-catch blocks that swallow errors. While some failures during cleanup are acceptable (as noted in comments), the current approach makes it difficult to distinguish between expected and unexpected failures. Consider consolidating cleanup logic and using more specific error handling to ensure critical cleanup steps don't fail silently.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| terraform { | ||
| backend "local" {} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| provider "aws" { | ||
| region = "us-east-1" | ||
| skip_credentials_validation = true | ||
| skip_metadata_api_check = true | ||
| skip_requesting_account_id = true | ||
|
|
||
| endpoints { | ||
| sts = var.moto_endpoint | ||
| s3 = var.moto_endpoint | ||
| sqs = var.moto_endpoint | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| resource "aws_sqs_queue" "queue" { | ||
| name = "test-queue" | ||
| tags = { | ||
| github = "https://github.com/zpratt/infra-diff.git" | ||
| random = 1234 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| variable "moto_endpoint" { | ||
| type = string | ||
| description = "The endpoint for the moto server" | ||
| default = "http://localhost:5000" | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,16 @@ | ||||||
| # End to End with Terraform | ||||||
|
|
||||||
| Goal: Demonstrate how Infra Diff can be used in an end-to-end workflow with Terraform and moto to provide a fake of the AWS API. The goal is to produce a real binary terraform plan, convert it to json, and then run infra-diff against that json plan. | ||||||
|
||||||
| Goal: Demonstrate how Infra Diff can be used in an end-to-end workflow with Terraform and moto to provide a fake of the AWS API. The goal is to produce a real binary terraform plan, convert it to json, and then run infra-diff against that json plan. | |
| Goal: Demonstrate how Infra Diff can be used in an end-to-end workflow with Terraform and moto to mock the AWS API. The goal is to produce a real binary terraform plan, convert it to json, and then run infra-diff against that json plan. |
Copilot
AI
Nov 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement states "Ensure we're testing with the latest version of terraform," but version 1.13.4 is specified. This creates ambiguity - should the version be pinned (as is done) or should it actually use the latest version? Consider clarifying this requirement to reflect the actual implementation (using a pinned version for consistency) rather than "latest."
| - Ensure we're testing with the latest version of terraform | |
| - Ensure we're testing with a pinned version of terraform (currently 1.13.4) for consistency and reproducibility |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Terraform version is duplicated here. It should be read from the .terraform-version file to maintain consistency. The devcontainer feature supports using a version file reference instead of hardcoding the version.