terraform test: parse mocking structures with test files (#34143)

pull/34167/head
Liam Cervante 3 years ago committed by GitHub
parent 1a4eb51007
commit 05f877166d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -66,6 +66,14 @@ func (m Module) Equal(other Module) bool {
return true
}
type moduleKey string
func (m Module) UniqueKey() UniqueKey {
return moduleKey(m.String())
}
func (mk moduleKey) uniqueKeySigil() {}
func (m Module) targetableSigil() {
// Module is targetable
}

@ -6,6 +6,8 @@ package addrs
// Targetable is an interface implemented by all address types that can be
// used as "targets" for selecting sub-graphs of a graph.
type Targetable interface {
UniqueKeyer
targetableSigil()
// TargetContains returns true if the receiver is considered to contain

@ -0,0 +1,383 @@
package configs
import (
"fmt"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/tfdiags"
)
func decodeMockProviderBlock(block *hcl.Block) (*Provider, hcl.Diagnostics) {
var diags hcl.Diagnostics
content, config, moreDiags := block.Body.PartialContent(mockProviderSchema)
diags = append(diags, moreDiags...)
name := block.Labels[0]
nameDiags := checkProviderNameNormalized(name, block.DefRange)
diags = append(diags, nameDiags...)
if nameDiags.HasErrors() {
// If the name is invalid then we mustn't produce a result because
// downstream could try to use it as a provider type and then crash.
return nil, diags
}
provider := &Provider{
Name: name,
NameRange: block.LabelRanges[0],
DeclRange: block.DefRange,
Config: config,
// Mark this provider as being mocked.
Mock: true,
}
if attr, exists := content.Attributes["alias"]; exists {
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &provider.Alias)
diags = append(diags, valDiags...)
provider.AliasRange = attr.Expr.Range().Ptr()
if !hclsyntax.ValidIdentifier(provider.Alias) {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid provider configuration alias",
Detail: fmt.Sprintf("An alias must be a valid name. %s", badIdentifierDetail),
})
}
}
var dataDiags hcl.Diagnostics
provider.MockData, dataDiags = decodeMockDataBody(config)
diags = append(diags, dataDiags...)
// TODO(liamcervante): Add support for the "source" attribute before the
// v1.7 release.
return provider, diags
}
// MockData packages up all the available mock and override data available to
// a mocked provider.
type MockData struct {
MockResources map[string]*MockResource
MockDataSources map[string]*MockResource
Overrides addrs.Map[addrs.Targetable, *Override]
}
// MockResource maps a resource or data source type and name to a set of values
// for that resource.
type MockResource struct {
Mode addrs.ResourceMode
Type string
Defaults cty.Value
Range hcl.Range
TypeRange hcl.Range
DefaultsRange hcl.Range
}
// Override targets a specific module, resource or data source with a set of
// replacement values that should be used in place of whatever the underlying
// provider would normally do.
type Override struct {
Target *addrs.Target
Values cty.Value
Range hcl.Range
TypeRange hcl.Range
TargetRange hcl.Range
ValuesRange hcl.Range
}
func decodeMockDataBody(body hcl.Body) (*MockData, hcl.Diagnostics) {
var diags hcl.Diagnostics
content, contentDiags := body.Content(mockDataSchema)
diags = append(diags, contentDiags...)
data := &MockData{
MockResources: make(map[string]*MockResource),
MockDataSources: make(map[string]*MockResource),
Overrides: addrs.MakeMap[addrs.Targetable, *Override](),
}
for _, block := range content.Blocks {
switch block.Type {
case "mock_resource", "mock_data":
resource, resourceDiags := decodeMockResourceBlock(block)
diags = append(diags, resourceDiags...)
if resource != nil {
switch resource.Mode {
case addrs.ManagedResourceMode:
if previous, ok := data.MockResources[resource.Type]; ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate mock_resource block",
Detail: fmt.Sprintf("A mock_resource block for %s has already been defined at %s.", resource.Type, previous.Range),
Subject: resource.TypeRange.Ptr(),
})
continue
}
data.MockResources[resource.Type] = resource
case addrs.DataResourceMode:
if previous, ok := data.MockDataSources[resource.Type]; ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate mock_data block",
Detail: fmt.Sprintf("A mock_data block for %s has already been defined at %s.", resource.Type, previous.Range),
Subject: resource.TypeRange.Ptr(),
})
continue
}
data.MockDataSources[resource.Type] = resource
}
}
case "override_resource":
override, overrideDiags := decodeOverrideResourceBlock(block)
diags = append(diags, overrideDiags...)
if override != nil && override.Target != nil {
subject := override.Target.Subject
if previous, ok := data.Overrides.GetOk(subject); ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate override_resource block",
Detail: fmt.Sprintf("An override_resource block targeting %s has already been defined at %s.", subject, previous.Range),
Subject: override.Range.Ptr(),
})
continue
}
data.Overrides.Put(subject, override)
}
case "override_data":
override, overrideDiags := decodeOverrideDataBlock(block)
diags = append(diags, overrideDiags...)
if override != nil && override.Target != nil {
subject := override.Target.Subject
if previous, ok := data.Overrides.GetOk(subject); ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate override_data block",
Detail: fmt.Sprintf("An override_data block targeting %s has already been defined at %s.", subject, previous.Range),
Subject: override.Range.Ptr(),
})
continue
}
data.Overrides.Put(subject, override)
}
}
}
return data, diags
}
func decodeMockResourceBlock(block *hcl.Block) (*MockResource, hcl.Diagnostics) {
var diags hcl.Diagnostics
content, contentDiags := block.Body.Content(mockResourceSchema)
diags = append(diags, contentDiags...)
resource := &MockResource{
Type: block.Labels[0],
Range: block.DefRange,
TypeRange: block.LabelRanges[0],
}
switch block.Type {
case "mock_resource":
resource.Mode = addrs.ManagedResourceMode
case "mock_data":
resource.Mode = addrs.DataResourceMode
}
if defaults, exists := content.Attributes["defaults"]; exists {
var defaultDiags hcl.Diagnostics
resource.DefaultsRange = defaults.Range
resource.Defaults, defaultDiags = defaults.Expr.Value(nil)
diags = append(diags, defaultDiags...)
} else {
// It's fine if we don't have any defaults, just means we'll generate
// values for everything ourselves.
resource.Defaults = cty.NilVal
}
return resource, diags
}
func decodeOverrideModuleBlock(block *hcl.Block) (*Override, hcl.Diagnostics) {
override, diags := decodeOverrideBlock(block, "outputs", "override_module")
if override.Target != nil {
switch override.Target.Subject.AddrType() {
case addrs.ModuleAddrType, addrs.ModuleInstanceAddrType:
// Do nothing, we're good here.
default:
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid override target",
Detail: fmt.Sprintf("You can only target modules from override_module blocks, not %s.", override.Target.Subject),
Subject: override.TargetRange.Ptr(),
})
return nil, diags
}
}
return override, diags
}
func decodeOverrideResourceBlock(block *hcl.Block) (*Override, hcl.Diagnostics) {
override, diags := decodeOverrideBlock(block, "values", "override_resource")
if override.Target != nil {
var mode addrs.ResourceMode
switch override.Target.Subject.AddrType() {
case addrs.AbsResourceInstanceAddrType:
subject := override.Target.Subject.(addrs.AbsResourceInstance)
mode = subject.Resource.Resource.Mode
case addrs.AbsResourceAddrType:
subject := override.Target.Subject.(addrs.AbsResource)
mode = subject.Resource.Mode
default:
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid override target",
Detail: fmt.Sprintf("You can only target resources from override_resource blocks, not %s.", override.Target.Subject),
Subject: override.TargetRange.Ptr(),
})
return nil, diags
}
if mode != addrs.ManagedResourceMode {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid override target",
Detail: fmt.Sprintf("You can only target resources from override_resource blocks, not %s.", override.Target.Subject),
Subject: override.TargetRange.Ptr(),
})
return nil, diags
}
}
return override, diags
}
func decodeOverrideDataBlock(block *hcl.Block) (*Override, hcl.Diagnostics) {
override, diags := decodeOverrideBlock(block, "values", "override_data")
if override.Target != nil {
var mode addrs.ResourceMode
switch override.Target.Subject.AddrType() {
case addrs.AbsResourceInstanceAddrType:
subject := override.Target.Subject.(addrs.AbsResourceInstance)
mode = subject.Resource.Resource.Mode
case addrs.AbsResourceAddrType:
subject := override.Target.Subject.(addrs.AbsResource)
mode = subject.Resource.Mode
default:
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid override target",
Detail: fmt.Sprintf("You can only target data sources from override_data blocks, not %s.", override.Target.Subject),
Subject: override.TargetRange.Ptr(),
})
return nil, diags
}
if mode != addrs.DataResourceMode {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid override target",
Detail: fmt.Sprintf("You can only target data sources from override_data blocks, not %s.", override.Target.Subject),
Subject: override.TargetRange.Ptr(),
})
return nil, diags
}
}
return override, diags
}
func decodeOverrideBlock(block *hcl.Block, attributeName string, blockName string) (*Override, hcl.Diagnostics) {
var diags hcl.Diagnostics
content, contentDiags := block.Body.Content(&hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{Name: "target"},
{Name: attributeName},
},
})
diags = append(diags, contentDiags...)
override := &Override{
Range: block.DefRange,
TypeRange: block.TypeRange,
}
if target, exists := content.Attributes["target"]; exists {
override.TargetRange = target.Range
traversal, traversalDiags := hcl.AbsTraversalForExpr(target.Expr)
diags = append(diags, traversalDiags...)
if traversal != nil {
var targetDiags tfdiags.Diagnostics
override.Target, targetDiags = addrs.ParseTarget(traversal)
diags = append(diags, targetDiags.ToHCL()...)
}
} else {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing target attribute",
Detail: fmt.Sprintf("%s blocks must specify a target address.", blockName),
Subject: override.Range.Ptr(),
})
}
if attribute, exists := content.Attributes[attributeName]; exists {
var valueDiags hcl.Diagnostics
override.ValuesRange = attribute.Range
override.Values, valueDiags = attribute.Expr.Value(nil)
diags = append(diags, valueDiags...)
} else {
// It's fine if we don't have any values, just means we'll generate
// values for everything ourselves.
override.Values = cty.NilVal
}
return override, diags
}
var mockProviderSchema = &hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{
Name: "alias",
},
{
Name: "source",
},
},
}
var mockDataSchema = &hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{Type: "mock_resource", LabelNames: []string{"type"}},
{Type: "mock_data", LabelNames: []string{"type"}},
{Type: "override_resource"},
{Type: "override_data"},
},
}
var mockResourceSchema = &hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{Name: "defaults"},
},
}

@ -6,6 +6,7 @@ package configs
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -120,6 +121,7 @@ func TestParserLoadConfigDirWithTests(t *testing.T) {
"testdata/valid-modules/with-tests-nested",
"testdata/valid-modules/with-tests-very-nested",
"testdata/valid-modules/with-tests-json",
"testdata/valid-modules/with-mocks",
}
for _, directory := range directories {
@ -146,6 +148,88 @@ func TestParserLoadConfigDirWithTests(t *testing.T) {
}
}
func TestParserLoadTestFiles_Invalid(t *testing.T) {
tcs := map[string][]string{
"duplicate_data_overrides": {
"duplicate_data_overrides.tftest.hcl:7,3-16: Duplicate override_data block; An override_data block targeting data.aws_instance.test has already been defined at duplicate_data_overrides.tftest.hcl:2,3-16.",
"duplicate_data_overrides.tftest.hcl:18,1-14: Duplicate override_data block; An override_data block targeting data.aws_instance.test has already been defined at duplicate_data_overrides.tftest.hcl:13,1-14.",
"duplicate_data_overrides.tftest.hcl:29,3-16: Duplicate override_data block; An override_data block targeting data.aws_instance.test has already been defined at duplicate_data_overrides.tftest.hcl:24,3-16.",
},
"duplicate_mixed_providers": {
"duplicate_mixed_providers.tftest.hcl:3,1-20: Duplicate provider block; A provider for aws is already defined at duplicate_mixed_providers.tftest.hcl:1,10-15.",
"duplicate_mixed_providers.tftest.hcl:9,1-20: Duplicate provider block; A provider for aws.test is already defined at duplicate_mixed_providers.tftest.hcl:5,10-15.",
},
"duplicate_mock_data_sources": {
"duplicate_mock_data_sources.tftest.hcl:7,13-27: Duplicate mock_data block; A mock_data block for aws_instance has already been defined at duplicate_mock_data_sources.tftest.hcl:3,3-27.",
},
"duplicate_mock_providers": {
"duplicate_mock_providers.tftest.hcl:3,1-20: Duplicate provider block; A provider for aws is already defined at duplicate_mock_providers.tftest.hcl:1,15-20.",
"duplicate_mock_providers.tftest.hcl:9,1-20: Duplicate provider block; A provider for aws.test is already defined at duplicate_mock_providers.tftest.hcl:5,15-20.",
},
"duplicate_mock_resources": {
"duplicate_mock_resources.tftest.hcl:7,17-31: Duplicate mock_resource block; A mock_resource block for aws_instance has already been defined at duplicate_mock_resources.tftest.hcl:3,3-31.",
},
"duplicate_module_overrides": {
"duplicate_module_overrides.tftest.hcl:7,1-16: Duplicate override_module block; An override_module block targeting module.child has already been defined at duplicate_module_overrides.tftest.hcl:2,1-16.",
"duplicate_module_overrides.tftest.hcl:18,3-18: Duplicate override_module block; An override_module block targeting module.child has already been defined at duplicate_module_overrides.tftest.hcl:13,3-18.",
},
"duplicate_providers": {
"duplicate_providers.tftest.hcl:3,1-15: Duplicate provider block; A provider for aws is already defined at duplicate_providers.tftest.hcl:1,10-15.",
"duplicate_providers.tftest.hcl:9,1-15: Duplicate provider block; A provider for aws.test is already defined at duplicate_providers.tftest.hcl:5,10-15.",
},
"duplicate_resource_overrides": {
"duplicate_resource_overrides.tftest.hcl:7,3-20: Duplicate override_resource block; An override_resource block targeting aws_instance.test has already been defined at duplicate_resource_overrides.tftest.hcl:2,3-20.",
"duplicate_resource_overrides.tftest.hcl:18,1-18: Duplicate override_resource block; An override_resource block targeting aws_instance.test has already been defined at duplicate_resource_overrides.tftest.hcl:13,1-18.",
"duplicate_resource_overrides.tftest.hcl:29,3-20: Duplicate override_resource block; An override_resource block targeting aws_instance.test has already been defined at duplicate_resource_overrides.tftest.hcl:24,3-20.",
},
"invalid_data_override": {
"invalid_data_override.tftest.hcl:6,1-14: Missing target attribute; override_data blocks must specify a target address.",
},
"invalid_data_override_target": {
"invalid_data_override_target.tftest.hcl:8,3-24: Invalid override target; You can only target data sources from override_data blocks, not module.child.",
"invalid_data_override_target.tftest.hcl:3,3-31: Invalid override target; You can only target data sources from override_data blocks, not aws_instance.target.",
},
"invalid_mock_data_sources": {
"invalid_mock_data_sources.tftest.hcl:7,13-16: Variables not allowed; Variables may not be used here.",
},
"invalid_mock_resources": {
"invalid_mock_resources.tftest.hcl:7,13-16: Variables not allowed; Variables may not be used here.",
},
"invalid_module_override": {
"invalid_module_override.tftest.hcl:5,1-16: Missing target attribute; override_module blocks must specify a target address.",
"invalid_module_override.tftest.hcl:11,3-9: Unsupported argument; An argument named \"values\" is not expected here.",
},
"invalid_module_override_target": {
"invalid_module_override_target.tftest.hcl:3,3-31: Invalid override target; You can only target modules from override_module blocks, not aws_instance.target.",
"invalid_module_override_target.tftest.hcl:8,3-36: Invalid override target; You can only target modules from override_module blocks, not data.aws_instance.target.",
},
"invalid_resource_override": {
"invalid_resource_override.tftest.hcl:6,1-18: Missing target attribute; override_resource blocks must specify a target address.",
},
"invalid_resource_override_target": {
"invalid_resource_override_target.tftest.hcl:3,3-36: Invalid override target; You can only target resources from override_resource blocks, not data.aws_instance.target.",
"invalid_resource_override_target.tftest.hcl:8,3-24: Invalid override target; You can only target resources from override_resource blocks, not module.child.",
},
}
for name, expected := range tcs {
t.Run(name, func(t *testing.T) {
src, err := os.ReadFile(fmt.Sprintf("testdata/invalid-test-files/%s.tftest.hcl", name))
if err != nil {
t.Fatal(err)
}
parser := testParser(map[string]string{
fmt.Sprintf("%s.tftest.hcl", name): string(src),
})
_, actual := parser.LoadTestFile(fmt.Sprintf("%s.tftest.hcl", name))
assertExactDiagnostics(t, actual, expected)
})
}
}
func TestParserLoadConfigDirWithTests_ReturnsWarnings(t *testing.T) {
parser := NewParser(nil)
mod, diags := parser.LoadConfigDirWithTests("testdata/valid-modules/with-tests", "not_real")

@ -35,6 +35,12 @@ type Provider struct {
// export this so providers don't need to be re-resolved.
// This same field is also added to the ProviderConfigRef struct.
providerType addrs.Provider
// 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
}
func decodeProviderBlock(block *hcl.Block) (*Provider, hcl.Diagnostics) {
@ -60,6 +66,10 @@ func decodeProviderBlock(block *hcl.Block) (*Provider, hcl.Diagnostics) {
NameRange: block.LabelRanges[0],
Config: config,
DeclRange: block.DefRange,
// We'll just explicitly mark real providers as not being mocks even
// though this is the default.
Mock: false,
}
if attr, exists := content.Attributes["alias"]; exists {

@ -51,10 +51,16 @@ type TestFile struct {
// Providers defines a set of providers that are available to run blocks
// within this test file.
//
// Some or all of these providers may be mocked providers.
//
// If empty, tests should use the default providers for the module under
// test.
Providers map[string]*Provider
// Overrides contains any specific overrides that should be applied for this
// test outside any mock providers.
Overrides addrs.Map[addrs.Targetable, *Override]
// Runs defines the sequential list of run blocks that should be executed in
// order.
Runs []*TestRun
@ -88,6 +94,10 @@ type TestRun struct {
// take precedence over the global definition.
Variables map[string]hcl.Expression
// Overrides contains any specific overrides that should be applied for this
// run block only outside any mock providers or overrides from the file.
Overrides addrs.Map[addrs.Targetable, *Override]
// Providers specifies the set of providers that should be loaded into the
// module for this run block.
//
@ -181,7 +191,7 @@ type TestRunOptions struct {
// Refresh is analogous to the -refresh=false Terraform plan option.
Refresh bool
// Replace is analogous to the -refresh=ADDRESS Terraform plan option.
// Replace is analogous to the -replace=ADDRESS Terraform plan option.
Replace []hcl.Traversal
// Target is analogous to the -target=ADDRESS Terraform plan option.
@ -198,6 +208,7 @@ func loadTestFile(body hcl.Body) (*TestFile, hcl.Diagnostics) {
tf := TestFile{
Providers: make(map[string]*Provider),
Overrides: addrs.MakeMap[addrs.Targetable, *Override](),
}
runBlockNames := make(map[string]hcl.Range)
@ -245,7 +256,84 @@ func loadTestFile(body hcl.Body) (*TestFile, hcl.Diagnostics) {
provider, providerDiags := decodeProviderBlock(block)
diags = append(diags, providerDiags...)
if provider != nil {
tf.Providers[provider.moduleUniqueKey()] = provider
key := provider.moduleUniqueKey()
if previous, exists := tf.Providers[key]; exists {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate provider block",
Detail: fmt.Sprintf("A provider for %s is already defined at %s.", key, previous.NameRange),
Subject: provider.DeclRange.Ptr(),
})
continue
}
tf.Providers[key] = provider
}
case "mock_provider":
provider, providerDiags := decodeMockProviderBlock(block)
diags = append(diags, providerDiags...)
if provider != nil {
key := provider.moduleUniqueKey()
if previous, exists := tf.Providers[key]; exists {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate provider block",
Detail: fmt.Sprintf("A provider for %s is already defined at %s.", key, previous.NameRange),
Subject: provider.DeclRange.Ptr(),
})
continue
}
tf.Providers[key] = provider
}
case "override_resource":
override, overrideDiags := decodeOverrideResourceBlock(block)
diags = append(diags, overrideDiags...)
if override != nil && override.Target != nil {
subject := override.Target.Subject
if previous, ok := tf.Overrides.GetOk(subject); ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate override_resource block",
Detail: fmt.Sprintf("An override_resource block targeting %s has already been defined at %s.", subject, previous.Range),
Subject: override.Range.Ptr(),
})
continue
}
tf.Overrides.Put(subject, override)
}
case "override_data":
override, overrideDiags := decodeOverrideDataBlock(block)
diags = append(diags, overrideDiags...)
if override != nil && override.Target != nil {
subject := override.Target.Subject
if previous, ok := tf.Overrides.GetOk(subject); ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate override_data block",
Detail: fmt.Sprintf("An override_data block targeting %s has already been defined at %s.", subject, previous.Range),
Subject: override.Range.Ptr(),
})
continue
}
tf.Overrides.Put(subject, override)
}
case "override_module":
override, overrideDiags := decodeOverrideModuleBlock(block)
diags = append(diags, overrideDiags...)
if override != nil && override.Target != nil {
subject := override.Target.Subject
if previous, ok := tf.Overrides.GetOk(subject); ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate override_module block",
Detail: fmt.Sprintf("An override_module block targeting %s has already been defined at %s.", subject, previous.Range),
Subject: override.Range.Ptr(),
})
continue
}
tf.Overrides.Put(subject, override)
}
}
}
@ -260,6 +348,8 @@ func decodeTestRunBlock(block *hcl.Block) (*TestRun, hcl.Diagnostics) {
diags = append(diags, contentDiags...)
r := TestRun{
Overrides: addrs.MakeMap[addrs.Targetable, *Override](),
Name: block.Labels[0],
NameDeclRange: block.LabelRanges[0],
DeclRange: block.DefRange,
@ -322,6 +412,57 @@ func decodeTestRunBlock(block *hcl.Block) (*TestRun, hcl.Diagnostics) {
if !moduleDiags.HasErrors() {
r.Module = module
}
case "override_resource":
override, overrideDiags := decodeOverrideResourceBlock(block)
diags = append(diags, overrideDiags...)
if override != nil && override.Target != nil {
subject := override.Target.Subject
if previous, ok := r.Overrides.GetOk(subject); ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate override_resource block",
Detail: fmt.Sprintf("An override_resource block targeting %s has already been defined at %s.", subject, previous.Range),
Subject: override.Range.Ptr(),
})
continue
}
r.Overrides.Put(subject, override)
}
case "override_data":
override, overrideDiags := decodeOverrideDataBlock(block)
diags = append(diags, overrideDiags...)
if override != nil && override.Target != nil {
subject := override.Target.Subject
if previous, ok := r.Overrides.GetOk(subject); ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate override_data block",
Detail: fmt.Sprintf("An override_data block targeting %s has already been defined at %s.", subject, previous.Range),
Subject: override.Range.Ptr(),
})
continue
}
r.Overrides.Put(subject, override)
}
case "override_module":
override, overrideDiags := decodeOverrideModuleBlock(block)
diags = append(diags, overrideDiags...)
if override != nil && override.Target != nil {
subject := override.Target.Subject
if previous, ok := r.Overrides.GetOk(subject); ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate override_module block",
Detail: fmt.Sprintf("An override_module block targeting %s has already been defined at %s.", subject, previous.Range),
Subject: override.Range.Ptr(),
})
continue
}
r.Overrides.Put(subject, override)
}
}
}
@ -548,9 +689,22 @@ var testFileSchema = &hcl.BodySchema{
Type: "provider",
LabelNames: []string{"name"},
},
{
Type: "mock_provider",
LabelNames: []string{"name"},
},
{
Type: "variables",
},
{
Type: "override_resource",
},
{
Type: "override_data",
},
{
Type: "override_module",
},
},
}
@ -573,6 +727,15 @@ var testRunBlockSchema = &hcl.BodySchema{
{
Type: "module",
},
{
Type: "override_resource",
},
{
Type: "override_data",
},
{
Type: "override_module",
},
},
}

@ -0,0 +1,33 @@
mock_provider "aws" {
override_data {
target = data.aws_instance.test
values = {}
}
override_data {
target = data.aws_instance.test
values = {}
}
}
override_data {
target = data.aws_instance.test
values = {}
}
override_data {
target = data.aws_instance.test
values = {}
}
run "test" {
override_data {
target = data.aws_instance.test
values = {}
}
override_data {
target = data.aws_instance.test
values = {}
}
}

@ -0,0 +1,13 @@
provider "aws" {}
mock_provider "aws" {}
provider "aws" {
alias = "test"
}
mock_provider "aws" {
alias = "test"
}
run "test" {}

@ -0,0 +1,13 @@
mock_provider "aws" {
mock_data "aws_instance" {
defaults = {}
}
mock_data "aws_instance" {
defaults = {}
}
}
run "test" {}

@ -0,0 +1,13 @@
mock_provider "aws" {}
mock_provider "aws" {}
mock_provider "aws" {
alias = "test"
}
mock_provider "aws" {
alias = "test"
}
run "test" {}

@ -0,0 +1,13 @@
mock_provider "aws" {
mock_resource "aws_instance" {
defaults = {}
}
mock_resource "aws_instance" {
defaults = {}
}
}
run "test" {}

@ -0,0 +1,22 @@
override_module {
target = module.child
outputs = {}
}
override_module {
target = module.child
outputs = {}
}
run "test" {
override_module {
target = module.child
outputs = {}
}
override_module {
target = module.child
outputs = {}
}
}

@ -0,0 +1,13 @@
provider "aws" {}
provider "aws" {}
provider "aws" {
alias = "test"
}
provider "aws" {
alias = "test"
}
run "test" {}

@ -0,0 +1,33 @@
mock_provider "aws" {
override_resource {
target = aws_instance.test
values = {}
}
override_resource {
target = aws_instance.test
values = {}
}
}
override_resource {
target = aws_instance.test
values = {}
}
override_resource {
target = aws_instance.test
values = {}
}
run "test" {
override_resource {
target = aws_instance.test
values = {}
}
override_resource {
target = aws_instance.test
values = {}
}
}

@ -0,0 +1,10 @@
override_data {
target = data.aws_instance.target
}
override_data {
values = {}
}
run "test" {}

@ -0,0 +1,12 @@
override_data {
target = aws_instance.target
values = {}
}
override_data {
target = module.child
values = {}
}
run "test" {}

@ -0,0 +1,13 @@
mock_provider "aws" {
mock_data "aws_instance" {}
mock_data "aws_ami_instance" {
defaults = {
ami = var.ami
}
}
}
run "test" {}

@ -0,0 +1,13 @@
mock_provider "aws" {
mock_resource "aws_instance" {}
mock_resource "aws_ami_instance" {
defaults = {
ami = var.ami
}
}
}
run "test" {}

@ -0,0 +1,14 @@
override_module {
target = module.child
}
override_module {
outputs = {}
}
override_module {
target = module.other
values = {}
}
run "test" {}

@ -0,0 +1,12 @@
override_module {
target = aws_instance.target
outputs = {}
}
override_module {
target = data.aws_instance.target
outputs = {}
}
run "test" {}

@ -0,0 +1,10 @@
override_resource {
target = aws_instance.target
}
override_resource {
values = {}
}
run "test" {}

@ -0,0 +1,12 @@
override_resource {
target = data.aws_instance.target
values = {}
}
override_resource {
target = module.child
values = {}
}
run "test" {}

@ -0,0 +1,8 @@
output "string" {
value = "Hello, world!"
}
output "number" {
value = 0
}

@ -0,0 +1,19 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
resource "aws_instance" "first" {}
resource "aws_instance" "second" {}
resource "aws_instance" "third" {}
data "aws_secretsmanager_secret" "creds" {}
module "child" {
source = "./child"
}

@ -0,0 +1,48 @@
mock_provider "aws" {
mock_resource "aws_instance" {
defaults = {
arn = "aws:instance"
}
}
mock_data "aws_secretsmanager_secret" {}
override_resource {
target = aws_instance.second
values = {}
}
override_data {
target = data.aws_secretsmanager_secret.creds
values = {
arn = "aws:secretsmanager"
}
}
}
override_module {
target = module.child
outputs = {
string = "testfile"
number = -1
}
}
run "test" {
override_resource {
target = aws_instance.first
values = {
arn = "aws:instance:first"
}
}
override_module {
target = module.child
outputs = {
string = "testrun"
number = -1
}
}
}

@ -0,0 +1,25 @@
provider "aws" {}
override_data {
target = data.aws_secretsmanager_secret.creds
values = {
arn = "aws:secretsmanager"
}
}
run "test" {
override_resource {
target = aws_instance.first
values = {
arn = "aws:instance:first"
}
}
override_data {
target = data.aws_secretsmanager_secret.creds
values = {
arn = "aws:secretsmanager"
}
}
}
Loading…
Cancel
Save