Skip to content

Commit

Permalink
test: also allow mock_data and mock_resource blocks to generate data …
Browse files Browse the repository at this point in the history
…during planning
  • Loading branch information
liamcervante committed Jan 14, 2025
1 parent d074d14 commit 286654d
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 80 deletions.
2 changes: 1 addition & 1 deletion internal/command/test_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ func TestTest_Runs(t *testing.T) {
code: 0,
},
"mocking": {
expectedOut: []string{"9 passed, 0 failed."},
expectedOut: []string{"10 passed, 0 failed."},
code: 0,
},
"mocking-invalid": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mock_resource "test_resource" {
override_during = plan
defaults = {
id = "aaaa"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
mock_provider "test" {
alias = "primary"

source ="./tests/mocks"
}

mock_provider "test" {
alias = "secondary"

mock_resource "test_resource" {
override_during = plan
defaults = {
id = "bbbb"
}
}
}

variables {
instances = 1
child_instances = 1
}


run "test" {
command = plan

assert {
condition = test_resource.primary[0].id == "aaaa"
error_message = "did not apply mocks"
}

assert {
condition = module.child[0].primary[0].id == "aaaa"
error_message = "did not apply mocks"
}

assert {
condition = test_resource.secondary[0].id == "bbbb"
error_message = "wrongly applied mocks"
}

assert {
condition = module.child[0].secondary[0].id == "bbbb"
error_message = "wrongly applied mocks"
}

}
4 changes: 2 additions & 2 deletions internal/configs/config_build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func TestBuildConfig_WithMockDataSources(t *testing.T) {

cfg, diags := BuildConfig(mod, nil, MockDataLoaderFunc(func(provider *Provider) (*MockData, hcl.Diagnostics) {
sourcePath := filepath.Join("testdata/valid-modules/with-mock-sources", provider.MockDataExternalSource)
return parser.LoadMockDataDir(sourcePath, hcl.Range{})
return parser.LoadMockDataDir(sourcePath, provider.MockDataDuringPlan, hcl.Range{})
}))
assertNoDiagnostics(t, diags)
if cfg == nil {
Expand Down Expand Up @@ -339,7 +339,7 @@ func TestBuildConfig_WithMockDataSourcesInline(t *testing.T) {

cfg, diags := BuildConfig(mod, nil, MockDataLoaderFunc(func(provider *Provider) (*MockData, hcl.Diagnostics) {
sourcePath := filepath.Join("testdata/valid-modules/with-mock-sources-inline", provider.MockDataExternalSource)
return parser.LoadMockDataDir(sourcePath, hcl.Range{})
return parser.LoadMockDataDir(sourcePath, provider.MockDataDuringPlan, hcl.Range{})
}))
assertNoDiagnostics(t, diags)
if cfg == nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/configs/configload/loader_load.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (l *Loader) LoadExternalMockData(provider *configs.Provider) (*configs.Mock
}

// Otherwise, just hand this off to the parser to handle.
return l.parser.LoadMockDataDir(provider.MockDataExternalSource, provider.DeclRange)
return l.parser.LoadMockDataDir(provider.MockDataExternalSource, provider.MockDataDuringPlan, provider.DeclRange)
}

// moduleWalkerLoad is a configs.ModuleWalkerFunc for loading modules that
Expand Down
104 changes: 44 additions & 60 deletions internal/configs/mock_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,12 @@ func decodeMockProviderBlock(block *hcl.Block) (*Provider, hcl.Diagnostics) {
}
}

useForPlan, useForPlanDiags := useForPlan(content, false)
diags = append(diags, useForPlanDiags...)
provider.MockDataDuringPlan = useForPlan

var dataDiags hcl.Diagnostics
provider.MockData, dataDiags = decodeMockDataBody(config, MockProviderOverrideSource)
provider.MockData, dataDiags = decodeMockDataBody(config, useForPlan, MockProviderOverrideSource)
diags = append(diags, dataDiags...)

if attr, exists := content.Attributes["source"]; exists {
Expand All @@ -72,23 +76,25 @@ func decodeMockProviderBlock(block *hcl.Block) (*Provider, hcl.Diagnostics) {
return provider, diags
}

func extractOverrideDuring(content *hcl.BodyContent) (*string, hcl.Diagnostics) {
func useForPlan(content *hcl.BodyContent, def bool) (bool, hcl.Diagnostics) {
var diags hcl.Diagnostics

if attr, exists := content.Attributes[overrideDuringCommand]; exists {
overrideComputedStr := hcl.ExprAsKeyword(attr.Expr)
if overrideComputedStr != "plan" && overrideComputedStr != "apply" {
switch hcl.ExprAsKeyword(attr.Expr) {
case "plan":
return true, diags
case "apply":
return false, diags
default:
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Invalid %s value", overrideDuringCommand),
Detail: fmt.Sprintf("The %s attribute must be a value of plan or apply.", overrideDuringCommand),
Subject: attr.Range.Ptr(),
})
return def, diags
}
return &overrideComputedStr, diags
}

return nil, diags
return def, diags
}

// MockData packages up all the available mock and override data available to
Expand All @@ -97,10 +103,6 @@ type MockData struct {
MockResources map[string]*MockResource
MockDataSources map[string]*MockResource
Overrides addrs.Map[addrs.Targetable, *Override]

// UseForPlan returns true if the provider-level setting for overrideComputed
// is true, meaning that computed values can be overridden with the mocked values during planning.
UseForPlan bool
}

// Merge will merge the target MockData object into the current MockData.
Expand Down Expand Up @@ -180,6 +182,10 @@ type MockResource struct {

Defaults cty.Value

// UseForPlan is true if the values should be computed during the planning
// phase.
UseForPlan bool

Range hcl.Range
TypeRange hcl.Range
DefaultsRange hcl.Range
Expand All @@ -202,12 +208,9 @@ type Override struct {
Target *addrs.Target
Values cty.Value

// By default, overridden computed values are ignored during planning,
// and the computed values are set to unknown to simulate the behavior
// of a real plan. This attribute indicates that the computed values
// should be overridden with the values specified in the override block,
// even when planning.
useForPlan *bool
// UseForPlan is true if the values should be computed during the planning
// phase.
UseForPlan bool

// Source tells us where this Override was defined.
Source OverrideSource
Expand All @@ -218,33 +221,22 @@ type Override struct {
ValuesRange hcl.Range
}

// UseForPlan returns true if the computed values in the target
// resource can be overridden with the values specified in the override block.
func (o *Override) UseForPlan() bool {
return o.useForPlan != nil && *o.useForPlan
}

func decodeMockDataBody(body hcl.Body, source OverrideSource) (*MockData, hcl.Diagnostics) {
func decodeMockDataBody(body hcl.Body, useForPlanDefault bool, source OverrideSource) (*MockData, hcl.Diagnostics) {
var diags hcl.Diagnostics

content, contentDiags := body.Content(mockDataSchema)
diags = append(diags, contentDiags...)

// provider-level setting for overrideComputed
providerOverrideComputed, valueDiags := extractOverrideDuring(content)
diags = append(diags, valueDiags...)
useForPlan := providerOverrideComputed != nil && *providerOverrideComputed == "plan"
data := &MockData{
MockResources: make(map[string]*MockResource),
MockDataSources: make(map[string]*MockResource),
Overrides: addrs.MakeMap[addrs.Targetable, *Override](),
UseForPlan: useForPlan,
}

for _, block := range content.Blocks {
switch block.Type {
case "mock_resource", "mock_data":
resource, resourceDiags := decodeMockResourceBlock(block)
resource, resourceDiags := decodeMockResourceBlock(block, useForPlanDefault)
diags = append(diags, resourceDiags...)

if resource != nil {
Expand Down Expand Up @@ -274,7 +266,7 @@ func decodeMockDataBody(body hcl.Body, source OverrideSource) (*MockData, hcl.Di
}
}
case "override_resource":
override, overrideDiags := decodeOverrideResourceBlock(block, source)
override, overrideDiags := decodeOverrideResourceBlock(block, useForPlanDefault, source)
diags = append(diags, overrideDiags...)

if override != nil && override.Target != nil {
Expand All @@ -291,7 +283,7 @@ func decodeMockDataBody(body hcl.Body, source OverrideSource) (*MockData, hcl.Di
data.Overrides.Put(subject, override)
}
case "override_data":
override, overrideDiags := decodeOverrideDataBlock(block, source)
override, overrideDiags := decodeOverrideDataBlock(block, useForPlanDefault, source)
diags = append(diags, overrideDiags...)

if override != nil && override.Target != nil {
Expand All @@ -310,19 +302,10 @@ func decodeMockDataBody(body hcl.Body, source OverrideSource) (*MockData, hcl.Di
}
}

for _, elem := range data.Overrides.Elements() {
// use the provider-level setting if there is none set for this override
useForPlan := providerOverrideComputed != nil && *providerOverrideComputed == "plan"
if elem.Value.useForPlan == nil {
elem.Value.useForPlan = &useForPlan
}
data.Overrides.Put(elem.Key, elem.Value)
}

return data, diags
}

func decodeMockResourceBlock(block *hcl.Block) (*MockResource, hcl.Diagnostics) {
func decodeMockResourceBlock(block *hcl.Block, useForPlanDefault bool) (*MockResource, hcl.Diagnostics) {
var diags hcl.Diagnostics

content, contentDiags := block.Body.Content(mockResourceSchema)
Expand Down Expand Up @@ -352,11 +335,15 @@ func decodeMockResourceBlock(block *hcl.Block) (*MockResource, hcl.Diagnostics)
resource.Defaults = cty.NilVal
}

useForPlan, useForPlanDiags := useForPlan(content, useForPlanDefault)
diags = append(diags, useForPlanDiags...)
resource.UseForPlan = useForPlan

return resource, diags
}

func decodeOverrideModuleBlock(block *hcl.Block, source OverrideSource) (*Override, hcl.Diagnostics) {
override, diags := decodeOverrideBlock(block, "outputs", "override_module", source)
func decodeOverrideModuleBlock(block *hcl.Block, useForPlanDefault bool, source OverrideSource) (*Override, hcl.Diagnostics) {
override, diags := decodeOverrideBlock(block, "outputs", "override_module", useForPlanDefault, source)

if override.Target != nil {
switch override.Target.Subject.AddrType() {
Expand All @@ -376,8 +363,8 @@ func decodeOverrideModuleBlock(block *hcl.Block, source OverrideSource) (*Overri
return override, diags
}

func decodeOverrideResourceBlock(block *hcl.Block, source OverrideSource) (*Override, hcl.Diagnostics) {
override, diags := decodeOverrideBlock(block, "values", "override_resource", source)
func decodeOverrideResourceBlock(block *hcl.Block, useForPlanDefault bool, source OverrideSource) (*Override, hcl.Diagnostics) {
override, diags := decodeOverrideBlock(block, "values", "override_resource", useForPlanDefault, source)

if override.Target != nil {
var mode addrs.ResourceMode
Expand Down Expand Up @@ -413,8 +400,8 @@ func decodeOverrideResourceBlock(block *hcl.Block, source OverrideSource) (*Over
return override, diags
}

func decodeOverrideDataBlock(block *hcl.Block, source OverrideSource) (*Override, hcl.Diagnostics) {
override, diags := decodeOverrideBlock(block, "values", "override_data", source)
func decodeOverrideDataBlock(block *hcl.Block, useForPlanDefault bool, source OverrideSource) (*Override, hcl.Diagnostics) {
override, diags := decodeOverrideBlock(block, "values", "override_data", useForPlanDefault, source)

if override.Target != nil {
var mode addrs.ResourceMode
Expand Down Expand Up @@ -450,7 +437,7 @@ func decodeOverrideDataBlock(block *hcl.Block, source OverrideSource) (*Override
return override, diags
}

func decodeOverrideBlock(block *hcl.Block, attributeName string, blockName string, source OverrideSource) (*Override, hcl.Diagnostics) {
func decodeOverrideBlock(block *hcl.Block, attributeName string, blockName string, useForPlanDefault bool, source OverrideSource) (*Override, hcl.Diagnostics) {
var diags hcl.Diagnostics

content, contentDiags := block.Body.Content(&hcl.BodySchema{
Expand Down Expand Up @@ -498,13 +485,9 @@ func decodeOverrideBlock(block *hcl.Block, attributeName string, blockName strin
override.Values = cty.EmptyObjectVal
}

// Override computed values during planning if override_during is plan.
overrideComputedStr, valueDiags := extractOverrideDuring(content)
diags = append(diags, valueDiags...)
if overrideComputedStr != nil {
useForPlan := *overrideComputedStr == "plan"
override.useForPlan = &useForPlan
}
useForPlan, useForPlanDiags := useForPlan(content, useForPlanDefault)
diags = append(diags, useForPlanDiags...)
override.UseForPlan = useForPlan

if !override.Values.Type().IsObjectType() {

Expand Down Expand Up @@ -535,13 +518,13 @@ var mockProviderSchema = &hcl.BodySchema{
{
Name: "source",
},
{
Name: overrideDuringCommand,
},
},
}

var mockDataSchema = &hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{Name: overrideDuringCommand},
},
Blocks: []hcl.BlockHeaderSchema{
{Type: "mock_resource", LabelNames: []string{"type"}},
{Type: "mock_data", LabelNames: []string{"type"}},
Expand All @@ -553,5 +536,6 @@ var mockDataSchema = &hcl.BodySchema{
var mockResourceSchema = &hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{Name: "defaults"},
{Name: overrideDuringCommand},
},
}
4 changes: 2 additions & 2 deletions internal/configs/parser_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ func (p *Parser) LoadTestFile(path string) (*TestFile, hcl.Diagnostics) {
//
// It references the same LoadHCLFile as LoadConfigFile, so inherits the same
// syntax selection behaviours.
func (p *Parser) LoadMockDataFile(path string) (*MockData, hcl.Diagnostics) {
func (p *Parser) LoadMockDataFile(path string, useForPlanDefault bool) (*MockData, hcl.Diagnostics) {
body, diags := p.LoadHCLFile(path)
if body == nil {
return nil, diags
}

data, dataDiags := decodeMockDataBody(body, MockDataFileOverrideSource)
data, dataDiags := decodeMockDataBody(body, useForPlanDefault, MockDataFileOverrideSource)
diags = append(diags, dataDiags...)
return data, diags
}
Expand Down
4 changes: 2 additions & 2 deletions internal/configs/parser_config_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (p *Parser) LoadConfigDirWithTests(path string, testDirectory string) (*Mod
return mod, diags
}

func (p *Parser) LoadMockDataDir(dir string, source hcl.Range) (*MockData, hcl.Diagnostics) {
func (p *Parser) LoadMockDataDir(dir string, useForPlanDefault bool, source hcl.Range) (*MockData, hcl.Diagnostics) {
var diags hcl.Diagnostics

infos, err := p.fs.ReadDir(dir)
Expand Down Expand Up @@ -113,7 +113,7 @@ func (p *Parser) LoadMockDataDir(dir string, source hcl.Range) (*MockData, hcl.D

var data *MockData
for _, file := range files {
current, currentDiags := p.LoadMockDataFile(file)
current, currentDiags := p.LoadMockDataFile(file, useForPlanDefault)
diags = append(diags, currentDiags...)
if data != nil {
diags = append(diags, data.Merge(current, false)...)
Expand Down
9 changes: 6 additions & 3 deletions internal/configs/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ type Provider struct {

// Mock and MockData declare this provider as a "mock_provider", which means
// it should use the data in MockData instead of actually initialising the
// provider.
Mock bool
MockData *MockData
// provider. MockDataDuringPlan tells the provider that, by default, it
// should generate values during the planning stage instead of waiting for
// the apply stage.
Mock bool
MockDataDuringPlan bool
MockData *MockData

// MockDataExternalSource is a file path pointing to the external data
// file for a mock provider. An empty string indicates all data should be
Expand Down
Loading

0 comments on commit 286654d

Please sign in to comment.