Skip to content

Commit

Permalink
terraform test: allow computed/mocked values override during planning (
Browse files Browse the repository at this point in the history
  • Loading branch information
dsa0x authored Jan 8, 2025
1 parent b59439b commit aec7c3c
Show file tree
Hide file tree
Showing 20 changed files with 472 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20250108-113433.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: ENHANCEMENTS
body: '`terraform test`: Test runs now support using mocked or overridden values during unit test runs (e.g., with command = "plan"). When override_during = "plan"'
time: 2025-01-08T11:34:33.709443+01:00
custom:
Issue: "36227"
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ ENHANCEMENTS:

* New command `modules -json`: Displays a full list of all installed modules in a working directory, including whether each module is currently referenced by the working directory's configuration. ([#35884](https://github.com/hashicorp/terraform/issues/35884))


EXPERIMENTS:

Experiments are only enabled in alpha releases of Terraform CLI. The following features are not yet available in stable releases.
Expand Down
24 changes: 19 additions & 5 deletions internal/command/test_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,25 @@ func TestTest_Runs(t *testing.T) {
code: 0,
},
"mocking": {
expectedOut: []string{"6 passed, 0 failed."},
expectedOut: []string{"9 passed, 0 failed."},
code: 0,
},
"mocking-invalid": {
expectedErr: []string{"Invalid outputs attribute"},
initCode: 1,
expectedErr: []string{
"Invalid outputs attribute",
"The override_during attribute must be a value of plan or apply.",
},
initCode: 1,
},
"mocking-error": {
expectedErr: []string{
"Unknown condition value",
"plan_mocked_overridden.tftest.hcl",
"test_resource.primary[0].id",
"plan_mocked_provider.tftest.hcl",
"test_resource.secondary[0].id",
},
code: 1,
},
"dangling_data_block": {
expectedOut: []string{"2 passed, 0 failed."},
Expand Down Expand Up @@ -1748,8 +1761,9 @@ Condition expression could not be evaluated at this time. This means you have
executed a %s block with %s and one of the values your
condition depended on is not known until after the plan has been applied.
Either remove this value from your condition, or execute an %s command
from this %s block.
`, "`run`", "`command = plan`", "`apply`", "`run`"),
from this %s block. Alternatively, if there is an override for this value,
you can make it available during the plan phase by setting %s in the %s block.
`, "`run`", "`command = plan`", "`apply`", "`run`", "`override_during =\nplan`", "`override_`"),
},
"unknown_value_in_vars": {
code: 1,
Expand Down
30 changes: 30 additions & 0 deletions internal/command/testdata/test/mocking-error/child/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
terraform {
required_providers {
test = {
source = "hashicorp/test"
configuration_aliases = [test.primary, test.secondary]
}
}
}

variable "instances" {
type = number
}

resource "test_resource" "primary" {
provider = test.primary
count = var.instances
}

resource "test_resource" "secondary" {
provider = test.secondary
count = var.instances
}

output "primary" {
value = test_resource.primary
}

output "secondary" {
value = test_resource.secondary
}
46 changes: 46 additions & 0 deletions internal/command/testdata/test/mocking-error/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
terraform {
required_providers {
test = {
source = "hashicorp/test"
}
}
}

provider "test" {
alias = "primary"
}

provider "test" {
alias = "secondary"
}

variable "instances" {
type = number
}

variable "child_instances" {
type = number
}

resource "test_resource" "primary" {
provider = test.primary
count = var.instances
}

resource "test_resource" "secondary" {
provider = test.secondary
count = var.instances
}

module "child" {
count = var.instances

source = "./child"

providers = {
test.primary = test.primary
test.secondary = test.secondary
}

instances = var.child_instances
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
mock_provider "test" {
alias = "primary"

mock_resource "test_resource" {
defaults = {
id = "aaaa"
}
}

override_resource {
target = test_resource.primary
values = {
id = "bbbb"
}
}
}

variables {
instances = 1
child_instances = 1
}

// This test will fail because the plan command does not use the
// overridden values for computed properties,
// making the left-hand side of the condition unknown.
run "test" {
command = plan

assert {
condition = test_resource.primary[0].id == "bbbb"
error_message = "plan should not have the overridden value"
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
mock_provider "test" {
alias = "secondary"

mock_resource "test_resource" {
defaults = {
id = "ffff"
}
}
}


variables {
instances = 2
child_instances = 1
}

run "test" {
command = plan

assert {
condition = test_resource.secondary[0].id == "ffff"
error_message = "plan should use the mocked provider value when override_during is plan"
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
mock_provider "test" {
alias = "primary"
override_during = baz // This should either be plan or apply, therefore this test should fail

mock_resource "test_resource" {
defaults = {
id = "aaaa"
}
}

override_resource {
target = test_resource.primary
values = {
id = "bbbb"
}
}
}

variables {
instances = 1
child_instances = 1
}

run "test" {

assert {
condition = test_resource.primary[0].id == "bbbb"
error_message = "mock not applied"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
mock_provider "test" {
alias = "primary"

mock_resource "test_resource" {
defaults = {
id = "aaaa"
}
}

override_resource {
target = test_resource.primary
override_during = plan
values = {
id = "bbbb"
}
}
}

variables {
instances = 1
child_instances = 1
}

run "test" {
command = plan

assert {
condition = test_resource.primary[0].id == "bbbb"
error_message = "plan should override the value when override_during is plan"
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
mock_provider "test" {
alias = "secondary"
override_during = plan

mock_resource "test_resource" {
defaults = {
id = "ffff"
}
}
}


variables {
instances = 2
child_instances = 1
}

run "test" {
command = plan

assert {
condition = test_resource.secondary[0].id == "ffff"
error_message = "plan should use the mocked provider value when override_during is plan"
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
mock_provider "test" {
alias = "primary"
override_during = plan

mock_resource "test_resource" {
defaults = {
id = "aaaa"
}
}

override_resource {
target = test_resource.primary
values = {
id = "bbbb"
}
}

override_resource {
target = test_resource.primary[1]
override_during = apply // this should take precedence over the provider-level override_during
values = {
id = "bbbb"
}
}
}


override_resource {
target = test_resource.secondary[0]
override_during = plan
values = {
id = "ssss"
}
}


variables {
instances = 2
child_instances = 1
}

run "test" {
command = plan

assert {
condition = test_resource.primary[0].id == "bbbb"
error_message = "plan should override the value when override_during is plan"
}

assert {
condition = test_resource.secondary[0].id == "ssss"
error_message = "plan should override the value when override_during is plan"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,16 @@ run "test" {
error_message = "did not apply mocks"
}

assert {
// Override should not affect the other instances
condition = !contains(["aaaa", "cccc"], test_resource.secondary[0].id)
error_message = "override from another instance affected this instance"
}

assert {
// Provider Override should propagate to the child module
condition = module.child[0].primary[0].id == "aaaa"
error_message = "did not apply mocks"
}

}
Loading

0 comments on commit aec7c3c

Please sign in to comment.