Skip to content

Commit

Permalink
allow mocked values override during planning
Browse files Browse the repository at this point in the history
  • Loading branch information
dsa0x committed Dec 19, 2024
1 parent e42d006 commit 5715278
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 6 deletions.
5 changes: 4 additions & 1 deletion internal/command/test_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ func TestTest_Runs(t *testing.T) {
code: 0,
},
"mocking": {
expectedOut: []string{"8 passed, 0 failed."},
expectedOut: []string{"9 passed, 0 failed."},
code: 0,
},
"mocking-invalid": {
Expand All @@ -232,7 +232,10 @@ func TestTest_Runs(t *testing.T) {
"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,
},
Expand Down
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_computed is true"
}

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

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_computed is true"
}

}
11 changes: 10 additions & 1 deletion internal/configs/mock_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ type MockData struct {
MockResources map[string]*MockResource
MockDataSources map[string]*MockResource
Overrides addrs.Map[addrs.Targetable, *Override]

useForPlan *bool // If true, the mock data can be used for planning.
}

// UseForPlan returns true if the provider-level setting for overrideComputed
// is true, meaning that computed values can be overridden with the mocked values.
func (data *MockData) UseForPlan() bool {
return data.useForPlan != nil && *data.useForPlan
}

// Merge will merge the target MockData object into the current MockData.
Expand Down Expand Up @@ -212,7 +220,7 @@ type Override struct {
}

// UseOverridesForPlan returns true if the computed values in the target
// resource should be overridden with the values specified in the override block.
// resource can be overridden with the values specified in the override block.
func (o *Override) UseOverridesForPlan() bool {
return o.planComputedOverride != nil && *o.planComputedOverride
}
Expand All @@ -231,6 +239,7 @@ func decodeMockDataBody(body hcl.Body, source OverrideSource) (*MockData, hcl.Di
MockResources: make(map[string]*MockResource),
MockDataSources: make(map[string]*MockResource),
Overrides: addrs.MakeMap[addrs.Targetable, *Override](),
useForPlan: providerOverrideComputed,
}

for _, block := range content.Blocks {
Expand Down
15 changes: 11 additions & 4 deletions internal/providers/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,18 @@ func (m *Mock) PlanResourceChange(request PlanResourceChangeRequest) PlanResourc
panic(fmt.Errorf("failed to retrieve schema for resource %s", request.TypeName))
}

// If the provider was overriden via override_* blocks, the mock provider is not called at all,
// so we can continue to make computed values as unknown here (until mock defaults support plan command).
value, diags := mocking.PlanComputedValuesForResource(request.ProposedNewState, &mocking.MockedData{
replacement := &mocking.MockedData{
Value: cty.NilVal, // If we have no data then we use cty.NilVal.
ComputedAsUnknown: true,
}, resource.Block)
}
// if we are allowed to use the mock defaults for plan, we can populate the computed fields with the mock defaults.
if mockedResource, exists := m.Data.MockResources[request.TypeName]; exists && m.Data.UseForPlan() {
replacement.Value = mockedResource.Defaults
replacement.Range = mockedResource.DefaultsRange
replacement.ComputedAsUnknown = false
}

value, diags := mocking.PlanComputedValuesForResource(request.ProposedNewState, replacement, resource.Block)
response.Diagnostics = response.Diagnostics.Append(diags)
response.PlannedState = value
response.PlannedPrivate = []byte("create")
Expand Down

0 comments on commit 5715278

Please sign in to comment.