mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
implement override resources for mock providers (#2168)
Signed-off-by: ollevche <ollevche@gmail.com>
This commit is contained in:
parent
cb866bf503
commit
6c8bfa2794
@ -42,6 +42,7 @@ ENHANCEMENTS:
|
|||||||
* Improved performance for large graphs with many submodules. ([#1809](https://github.com/opentofu/opentofu/pull/1809))
|
* Improved performance for large graphs with many submodules. ([#1809](https://github.com/opentofu/opentofu/pull/1809))
|
||||||
* Extended trace logging for HTTP backend, including request and response bodies. ([#2120](https://github.com/opentofu/opentofu/pull/2120))
|
* Extended trace logging for HTTP backend, including request and response bodies. ([#2120](https://github.com/opentofu/opentofu/pull/2120))
|
||||||
* `tofu test` now throws errors instead of warnings for invalid override and mock fields. ([#2220](https://github.com/opentofu/opentofu/pull/2220))
|
* `tofu test` now throws errors instead of warnings for invalid override and mock fields. ([#2220](https://github.com/opentofu/opentofu/pull/2220))
|
||||||
|
* `tofu test` now supports `override_resource` and `override_data` blocks in the scope of a single `mock_provider`. ([#2168](https://github.com/opentofu/opentofu/pull/2168))
|
||||||
|
|
||||||
BUG FIXES:
|
BUG FIXES:
|
||||||
* `templatefile` no longer crashes if the given filename is derived from a sensitive value. ([#1801](https://github.com/opentofu/opentofu/issues/1801))
|
* `templatefile` no longer crashes if the given filename is derived from a sensitive value. ([#1801](https://github.com/opentofu/opentofu/issues/1801))
|
||||||
|
@ -76,7 +76,7 @@ func TestMocksAndOverrides(t *testing.T) {
|
|||||||
if stderr != "" {
|
if stderr != "" {
|
||||||
t.Errorf("unexpected stderr output on 'test':\n%s", stderr)
|
t.Errorf("unexpected stderr output on 'test':\n%s", stderr)
|
||||||
}
|
}
|
||||||
if !strings.Contains(stdout, "13 passed, 0 failed") {
|
if !strings.Contains(stdout, "15 passed, 0 failed") {
|
||||||
t.Errorf("output doesn't have expected success string:\n%s", stdout)
|
t.Errorf("output doesn't have expected success string:\n%s", stdout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,12 +58,17 @@ module "rand_count" {
|
|||||||
source = "./rand"
|
source = "./rand"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket" "test" {
|
data "http" "first" {
|
||||||
bucket = "must not be used anyway"
|
url = "must not be used anyway"
|
||||||
}
|
}
|
||||||
|
|
||||||
data "aws_s3_bucket" "test" {
|
provider http {
|
||||||
bucket = "must not be used anyway"
|
alias = "aliased"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "http" "second" {
|
||||||
|
provider = http.aliased
|
||||||
|
url = "must not be used anyway"
|
||||||
}
|
}
|
||||||
|
|
||||||
provider "local" {
|
provider "local" {
|
||||||
|
@ -4,10 +4,16 @@ override_module {
|
|||||||
|
|
||||||
override_resource {
|
override_resource {
|
||||||
target = local_file.dont_create_me
|
target = local_file.dont_create_me
|
||||||
|
values = {
|
||||||
|
file_permission = "000"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override_resource {
|
override_resource {
|
||||||
target = module.first.local_file.dont_create_me
|
target = module.first.local_file.dont_create_me
|
||||||
|
values = {
|
||||||
|
file_permission = "000"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run "check_root_overridden_res" {
|
run "check_root_overridden_res" {
|
||||||
@ -223,16 +229,10 @@ run "check_for_each_n_count_overridden" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ensures non-aliased provider is mocked by default
|
# ensures non-aliased provider is mocked by default
|
||||||
mock_provider "aws" {
|
mock_provider "http" {
|
||||||
mock_resource "aws_s3_bucket" {
|
mock_data "http" {
|
||||||
defaults = {
|
defaults = {
|
||||||
arn = "arn:aws:s3:::mocked"
|
response_body = "I am mocked!"
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mock_data "aws_s3_bucket" {
|
|
||||||
defaults = {
|
|
||||||
bucket_domain_name = "mocked.com"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,6 +241,12 @@ mock_provider "aws" {
|
|||||||
# and aliased one is mocked
|
# and aliased one is mocked
|
||||||
mock_provider "local" {
|
mock_provider "local" {
|
||||||
alias = "aliased"
|
alias = "aliased"
|
||||||
|
|
||||||
|
mock_resource "local_file" {
|
||||||
|
defaults = {
|
||||||
|
file_permission = "000"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# ensures we can use this provider in run's providers block
|
# ensures we can use this provider in run's providers block
|
||||||
@ -267,13 +273,8 @@ mock_provider "random" {
|
|||||||
|
|
||||||
run "check_mock_providers" {
|
run "check_mock_providers" {
|
||||||
assert {
|
assert {
|
||||||
condition = resource.aws_s3_bucket.test.arn == "arn:aws:s3:::mocked"
|
condition = data.http.first.response_body == "I am mocked!"
|
||||||
error_message = "aws s3 bucket resource doesn't have mocked values"
|
error_message = "http data doesn't have mocked values"
|
||||||
}
|
|
||||||
|
|
||||||
assert {
|
|
||||||
condition = data.aws_s3_bucket.test.bucket_domain_name == "mocked.com"
|
|
||||||
error_message = "aws s3 bucket data doesn't have mocked values"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
@ -294,7 +295,8 @@ run "check_mock_providers" {
|
|||||||
|
|
||||||
run "check_providers_block" {
|
run "check_providers_block" {
|
||||||
providers = {
|
providers = {
|
||||||
aws = aws
|
http = http
|
||||||
|
http.aliased = http.aliased
|
||||||
local.aliased = local.aliased
|
local.aliased = local.aliased
|
||||||
random = random.for_pets
|
random = random.for_pets
|
||||||
}
|
}
|
||||||
@ -309,3 +311,76 @@ run "check_providers_block" {
|
|||||||
error_message = "random integer should not be mocked if providers block present"
|
error_message = "random integer should not be mocked if providers block present"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# http.aliased provider is not used directly,
|
||||||
|
# but we don't want http provider to make any
|
||||||
|
# requests.
|
||||||
|
mock_provider "http" {
|
||||||
|
alias = "aliased"
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_provider "http" {
|
||||||
|
alias = "with_mock_and_override"
|
||||||
|
|
||||||
|
mock_data "http" {
|
||||||
|
defaults = {
|
||||||
|
response_body = "mocked"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override_data {
|
||||||
|
target = data.http.first
|
||||||
|
values = {
|
||||||
|
response_body = "overridden [first]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override_data {
|
||||||
|
target = data.http.second
|
||||||
|
values = {
|
||||||
|
response_body = "overridden [second]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# This test ensures override_data works
|
||||||
|
# inside a provider block and priority
|
||||||
|
# between mocks and overrides is correct.
|
||||||
|
run "check_mock_provider_override" {
|
||||||
|
providers = {
|
||||||
|
http = http.with_mock_and_override
|
||||||
|
http.aliased = http.with_mock_and_override
|
||||||
|
local.aliased = local.aliased
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
condition = data.http.first.response_body == "overridden [first]"
|
||||||
|
error_message = "HTTP response body is not mocked"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
condition = data.http.second.response_body == "overridden [second]"
|
||||||
|
error_message = "HTTP response body is not overridden"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# This test ensures override inside a mock_provider
|
||||||
|
# doesn't apply for resources created via a different
|
||||||
|
# provider.
|
||||||
|
run "check_multiple_mock_provider_override" {
|
||||||
|
providers = {
|
||||||
|
http = http.with_mock_and_override
|
||||||
|
http.aliased = http
|
||||||
|
local.aliased = local.aliased
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
condition = data.http.first.response_body == "overridden [first]"
|
||||||
|
error_message = "HTTP response body is not mocked"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {
|
||||||
|
condition = data.http.second.response_body == "I am mocked!"
|
||||||
|
error_message = "HTTP response body is not overridden"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -975,6 +975,7 @@ func (c *Config) transformProviderConfigsForTest(run *TestRun, file *TestFile) (
|
|||||||
DeclRange: testProvider.DeclRange,
|
DeclRange: testProvider.DeclRange,
|
||||||
IsMocked: testProvider.IsMocked,
|
IsMocked: testProvider.IsMocked,
|
||||||
MockResources: testProvider.MockResources,
|
MockResources: testProvider.MockResources,
|
||||||
|
OverrideResources: testProvider.OverrideResources,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -993,6 +994,7 @@ func (c *Config) transformProviderConfigsForTest(run *TestRun, file *TestFile) (
|
|||||||
DeclRange: mp.DeclRange,
|
DeclRange: mp.DeclRange,
|
||||||
IsMocked: true,
|
IsMocked: true,
|
||||||
MockResources: mp.MockResources,
|
MockResources: mp.MockResources,
|
||||||
|
OverrideResources: mp.OverrideResources,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ type Provider struct {
|
|||||||
// testing framework to instantiate test provider wrapper.
|
// testing framework to instantiate test provider wrapper.
|
||||||
IsMocked bool
|
IsMocked bool
|
||||||
MockResources []*MockResource
|
MockResources []*MockResource
|
||||||
|
OverrideResources []*OverrideResource
|
||||||
|
|
||||||
ForEach hcl.Expression
|
ForEach hcl.Expression
|
||||||
Instances map[addrs.InstanceKey]instances.RepetitionData
|
Instances map[addrs.InstanceKey]instances.RepetitionData
|
||||||
|
@ -107,6 +107,7 @@ func (file *TestFile) getTestProviderOrMock(addr string) (*Provider, bool) {
|
|||||||
DeclRange: mockProvider.DeclRange,
|
DeclRange: mockProvider.DeclRange,
|
||||||
IsMocked: true,
|
IsMocked: true,
|
||||||
MockResources: mockProvider.MockResources,
|
MockResources: mockProvider.MockResources,
|
||||||
|
OverrideResources: mockProvider.OverrideResources,
|
||||||
}
|
}
|
||||||
|
|
||||||
return p, true
|
return p, true
|
||||||
@ -319,6 +320,7 @@ type MockProvider struct {
|
|||||||
// Fields below are specific to configs.MockProvider:
|
// Fields below are specific to configs.MockProvider:
|
||||||
|
|
||||||
MockResources []*MockResource
|
MockResources []*MockResource
|
||||||
|
OverrideResources []*OverrideResource
|
||||||
}
|
}
|
||||||
|
|
||||||
// moduleUniqueKey is copied from Provider.moduleUniqueKey
|
// moduleUniqueKey is copied from Provider.moduleUniqueKey
|
||||||
@ -329,6 +331,58 @@ func (p *MockProvider) moduleUniqueKey() string {
|
|||||||
return p.Name
|
return p.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *MockProvider) validateMockResources() hcl.Diagnostics {
|
||||||
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
|
managedResources := make(map[string]struct{})
|
||||||
|
dataResources := make(map[string]struct{})
|
||||||
|
|
||||||
|
for _, res := range p.MockResources {
|
||||||
|
resources := managedResources
|
||||||
|
if res.Mode == addrs.DataResourceMode {
|
||||||
|
resources = dataResources
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := resources[res.Type]; ok {
|
||||||
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: fmt.Sprintf("Duplicated `%v` block", res.getBlockName()),
|
||||||
|
Detail: fmt.Sprintf("`%v.%v` is already defined in `mock_provider` block.", res.getBlockName(), res.Type),
|
||||||
|
Subject: p.DeclRange.Ptr(),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resources[res.Type] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MockProvider) validateOverrideResources() hcl.Diagnostics {
|
||||||
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
|
resources := make(map[string]struct{})
|
||||||
|
|
||||||
|
for _, res := range p.OverrideResources {
|
||||||
|
k := res.TargetParsed.String()
|
||||||
|
|
||||||
|
if _, ok := resources[k]; ok {
|
||||||
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: fmt.Sprintf("Duplicated `%v` block", res.getBlockName()),
|
||||||
|
Detail: fmt.Sprintf("`%v` with target `%v` is already defined in `mock_provider` block.", res.getBlockName(), k),
|
||||||
|
Subject: p.DeclRange.Ptr(),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resources[k] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
blockNameMockResource = "mock_resource"
|
blockNameMockResource = "mock_resource"
|
||||||
blockNameMockData = "mock_data"
|
blockNameMockData = "mock_data"
|
||||||
@ -402,20 +456,13 @@ func loadTestFile(body hcl.Body) (*TestFile, hcl.Diagnostics) {
|
|||||||
tf.Providers[provider.moduleUniqueKey()] = provider
|
tf.Providers[provider.moduleUniqueKey()] = provider
|
||||||
}
|
}
|
||||||
|
|
||||||
case blockNameOverrideResource:
|
case blockNameOverrideResource, blockNameOverrideData:
|
||||||
overrideRes, overrideResDiags := decodeOverrideResourceBlock(block, addrs.ManagedResourceMode)
|
overrideRes, overrideResDiags := decodeOverrideResourceBlock(block)
|
||||||
diags = append(diags, overrideResDiags...)
|
diags = append(diags, overrideResDiags...)
|
||||||
if !overrideResDiags.HasErrors() {
|
if !overrideResDiags.HasErrors() {
|
||||||
tf.OverrideResources = append(tf.OverrideResources, overrideRes)
|
tf.OverrideResources = append(tf.OverrideResources, overrideRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
case blockNameOverrideData:
|
|
||||||
overrideData, overrideDataDiags := decodeOverrideResourceBlock(block, addrs.DataResourceMode)
|
|
||||||
diags = append(diags, overrideDataDiags...)
|
|
||||||
if !overrideDataDiags.HasErrors() {
|
|
||||||
tf.OverrideResources = append(tf.OverrideResources, overrideData)
|
|
||||||
}
|
|
||||||
|
|
||||||
case blockNameOverrideModule:
|
case blockNameOverrideModule:
|
||||||
overrideMod, overrideModDiags := decodeOverrideModuleBlock(block)
|
overrideMod, overrideModDiags := decodeOverrideModuleBlock(block)
|
||||||
diags = append(diags, overrideModDiags...)
|
diags = append(diags, overrideModDiags...)
|
||||||
@ -527,20 +574,13 @@ func decodeTestRunBlock(block *hcl.Block) (*TestRun, hcl.Diagnostics) {
|
|||||||
r.Module = module
|
r.Module = module
|
||||||
}
|
}
|
||||||
|
|
||||||
case blockNameOverrideResource:
|
case blockNameOverrideResource, blockNameOverrideData:
|
||||||
overrideRes, overrideResDiags := decodeOverrideResourceBlock(block, addrs.ManagedResourceMode)
|
overrideRes, overrideResDiags := decodeOverrideResourceBlock(block)
|
||||||
diags = append(diags, overrideResDiags...)
|
diags = append(diags, overrideResDiags...)
|
||||||
if !overrideResDiags.HasErrors() {
|
if !overrideResDiags.HasErrors() {
|
||||||
r.OverrideResources = append(r.OverrideResources, overrideRes)
|
r.OverrideResources = append(r.OverrideResources, overrideRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
case blockNameOverrideData:
|
|
||||||
overrideData, overrideDataDiags := decodeOverrideResourceBlock(block, addrs.DataResourceMode)
|
|
||||||
diags = append(diags, overrideDataDiags...)
|
|
||||||
if !overrideDataDiags.HasErrors() {
|
|
||||||
r.OverrideResources = append(r.OverrideResources, overrideData)
|
|
||||||
}
|
|
||||||
|
|
||||||
case blockNameOverrideModule:
|
case blockNameOverrideModule:
|
||||||
overrideMod, overrideModDiags := decodeOverrideModuleBlock(block)
|
overrideMod, overrideModDiags := decodeOverrideModuleBlock(block)
|
||||||
diags = append(diags, overrideModDiags...)
|
diags = append(diags, overrideModDiags...)
|
||||||
@ -763,7 +803,7 @@ func decodeTestRunOptionsBlock(block *hcl.Block) (*TestRunOptions, hcl.Diagnosti
|
|||||||
return &opts, diags
|
return &opts, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeOverrideResourceBlock(block *hcl.Block, mode addrs.ResourceMode) (*OverrideResource, hcl.Diagnostics) {
|
func decodeOverrideResourceBlock(block *hcl.Block) (*OverrideResource, hcl.Diagnostics) {
|
||||||
parseTarget := func(attr *hcl.Attribute) (hcl.Traversal, *addrs.ConfigResource, hcl.Diagnostics) {
|
parseTarget := func(attr *hcl.Attribute) (hcl.Traversal, *addrs.ConfigResource, hcl.Diagnostics) {
|
||||||
traversal, traversalDiags := hcl.AbsTraversalForExpr(attr.Expr)
|
traversal, traversalDiags := hcl.AbsTraversalForExpr(attr.Expr)
|
||||||
diags := traversalDiags
|
diags := traversalDiags
|
||||||
@ -780,8 +820,15 @@ func decodeOverrideResourceBlock(block *hcl.Block, mode addrs.ResourceMode) (*Ov
|
|||||||
return traversal, &configRes, diags
|
return traversal, &configRes, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
res := &OverrideResource{
|
res := &OverrideResource{}
|
||||||
Mode: mode,
|
|
||||||
|
switch block.Type {
|
||||||
|
case blockNameOverrideResource:
|
||||||
|
res.Mode = addrs.ManagedResourceMode
|
||||||
|
case blockNameOverrideData:
|
||||||
|
res.Mode = addrs.DataResourceMode
|
||||||
|
default:
|
||||||
|
panic("BUG: unsupported block type for override resource: " + block.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
content, diags := block.Body.Content(overrideResourceBlockSchema)
|
content, diags := block.Body.Content(overrideResourceBlockSchema)
|
||||||
@ -875,37 +922,25 @@ func decodeMockProviderBlock(block *hcl.Block) (*MockProvider, hcl.Diagnostics)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
managedResources = make(map[string]struct{})
|
|
||||||
dataResources = make(map[string]struct{})
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, block := range content.Blocks {
|
for _, block := range content.Blocks {
|
||||||
|
switch block.Type {
|
||||||
|
case blockNameMockData, blockNameMockResource:
|
||||||
res, resDiags := decodeMockResourceBlock(block)
|
res, resDiags := decodeMockResourceBlock(block)
|
||||||
diags = append(diags, resDiags...)
|
diags = append(diags, resDiags...)
|
||||||
if resDiags.HasErrors() {
|
if !resDiags.HasErrors() {
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
resources := managedResources
|
|
||||||
if res.Mode == addrs.DataResourceMode {
|
|
||||||
resources = dataResources
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := resources[res.Type]; ok {
|
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
|
||||||
Severity: hcl.DiagError,
|
|
||||||
Summary: fmt.Sprintf("Duplicated `%v` block", res.getBlockName()),
|
|
||||||
Detail: fmt.Sprintf("`%v.%v` is already defined in `mock_provider` block.", res.getBlockName(), res.Type),
|
|
||||||
Subject: provider.DeclRange.Ptr(),
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
resources[res.Type] = struct{}{}
|
|
||||||
|
|
||||||
provider.MockResources = append(provider.MockResources, res)
|
provider.MockResources = append(provider.MockResources, res)
|
||||||
}
|
}
|
||||||
|
case blockNameOverrideData, blockNameOverrideResource:
|
||||||
|
res, resDiags := decodeOverrideResourceBlock(block)
|
||||||
|
diags = append(diags, resDiags...)
|
||||||
|
if !resDiags.HasErrors() {
|
||||||
|
provider.OverrideResources = append(provider.OverrideResources, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diags = append(diags, provider.validateMockResources()...)
|
||||||
|
diags = append(diags, provider.validateOverrideResources()...)
|
||||||
|
|
||||||
return provider, diags
|
return provider, diags
|
||||||
}
|
}
|
||||||
@ -1146,6 +1181,12 @@ var mockProviderBlockSchema = &hcl.BodySchema{
|
|||||||
Type: blockNameMockData,
|
Type: blockNameMockData,
|
||||||
LabelNames: []string{"type"},
|
LabelNames: []string{"type"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Type: blockNameOverrideResource,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: blockNameOverrideData,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,10 +153,14 @@ func (ctx *BuiltinEvalContext) InitProvider(addr addrs.AbsProviderConfig, provid
|
|||||||
// We cannot wrap providers.Factory itself, because factories don't support aliases.
|
// We cannot wrap providers.Factory itself, because factories don't support aliases.
|
||||||
pc, ok := ctx.Evaluator.Config.Module.GetProviderConfig(addr.Provider.Type, addr.Alias)
|
pc, ok := ctx.Evaluator.Config.Module.GetProviderConfig(addr.Provider.Type, addr.Alias)
|
||||||
if ok && pc.IsMocked {
|
if ok && pc.IsMocked {
|
||||||
p, err = newProviderForTest(p, pc.MockResources)
|
testP, err := newProviderForTestWithSchema(p, p.GetProviderSchema())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p = testP.
|
||||||
|
withMockResources(pc.MockResources).
|
||||||
|
withOverrideResources(pc.OverrideResources)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2704,12 +2704,21 @@ func (n *NodeAbstractResourceInstance) getProvider(ctx EvalContext) (providers.I
|
|||||||
}
|
}
|
||||||
|
|
||||||
if n.Config == nil || !n.Config.IsOverridden {
|
if n.Config == nil || !n.Config.IsOverridden {
|
||||||
|
if p, ok := underlyingProvider.(providerForTest); ok {
|
||||||
|
underlyingProvider = p.linkWithCurrentResource(n.Addr.ConfigResource())
|
||||||
|
}
|
||||||
|
|
||||||
return underlyingProvider, schema, nil
|
return underlyingProvider, schema, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
providerForTest := newProviderForTestWithSchema(underlyingProvider, schema)
|
provider, err := newProviderForTestWithSchema(underlyingProvider, schema)
|
||||||
|
if err != nil {
|
||||||
providerForTest.setSingleResource(n.Addr.Resource.Resource, n.Config.OverrideValues)
|
return nil, providers.ProviderSchema{}, err
|
||||||
|
}
|
||||||
return providerForTest, schema, nil
|
|
||||||
|
provider = provider.
|
||||||
|
withOverrideResource(n.Addr.ConfigResource(), n.Config.OverrideValues).
|
||||||
|
linkWithCurrentResource(n.Addr.ConfigResource())
|
||||||
|
|
||||||
|
return provider, schema, nil
|
||||||
}
|
}
|
||||||
|
@ -26,46 +26,52 @@ type providerForTest struct {
|
|||||||
internal providers.Interface
|
internal providers.Interface
|
||||||
schema providers.ProviderSchema
|
schema providers.ProviderSchema
|
||||||
|
|
||||||
managedResources resourceForTestByType
|
mockResources mockResourcesForTest
|
||||||
dataResources resourceForTestByType
|
overrideResources overrideResourcesForTest
|
||||||
|
|
||||||
|
currentResourceAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newProviderForTestWithSchema(internal providers.Interface, schema providers.ProviderSchema) *providerForTest {
|
func newProviderForTestWithSchema(internal providers.Interface, schema providers.ProviderSchema) (providerForTest, error) {
|
||||||
return &providerForTest{
|
if p, ok := internal.(providerForTest); ok {
|
||||||
|
// We can create a proper deep copy here, however currently
|
||||||
|
// it is only relevant for override resources, since we extend
|
||||||
|
// the override resource map in NodeAbstractResourceInstance.
|
||||||
|
return p.withCopiedOverrideResources(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema.Diagnostics.HasErrors() {
|
||||||
|
return providerForTest{}, fmt.Errorf("invalid provider schema for test wrapper: %w", schema.Diagnostics.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
return providerForTest{
|
||||||
internal: internal,
|
internal: internal,
|
||||||
schema: schema,
|
schema: schema,
|
||||||
managedResources: make(resourceForTestByType),
|
mockResources: mockResourcesForTest{
|
||||||
dataResources: make(resourceForTestByType),
|
managed: make(map[string]resourceForTest),
|
||||||
}
|
data: make(map[string]resourceForTest),
|
||||||
|
},
|
||||||
|
overrideResources: overrideResourcesForTest{
|
||||||
|
managed: make(map[string]resourceForTest),
|
||||||
|
data: make(map[string]resourceForTest),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newProviderForTest(internal providers.Interface, res []*configs.MockResource) (providers.Interface, error) {
|
func (p providerForTest) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse {
|
||||||
schema := internal.GetProviderSchema()
|
|
||||||
if schema.Diagnostics.HasErrors() {
|
|
||||||
return nil, fmt.Errorf("getting provider schema for test wrapper: %w", schema.Diagnostics.Err())
|
|
||||||
}
|
|
||||||
|
|
||||||
p := newProviderForTestWithSchema(internal, schema)
|
|
||||||
|
|
||||||
p.addMockResources(res)
|
|
||||||
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *providerForTest) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse {
|
|
||||||
var resp providers.ReadResourceResponse
|
|
||||||
|
|
||||||
resSchema, _ := p.schema.SchemaForResourceType(addrs.ManagedResourceMode, r.TypeName)
|
resSchema, _ := p.schema.SchemaForResourceType(addrs.ManagedResourceMode, r.TypeName)
|
||||||
|
|
||||||
overrideValues := p.managedResources.getOverrideValues(r.TypeName)
|
mockValues := p.getMockValuesForManagedResource(r.TypeName)
|
||||||
|
|
||||||
|
var resp providers.ReadResourceResponse
|
||||||
|
|
||||||
resp.NewState, resp.Diagnostics = newMockValueComposer(r.TypeName).
|
resp.NewState, resp.Diagnostics = newMockValueComposer(r.TypeName).
|
||||||
ComposeBySchema(resSchema, r.ProviderMeta, overrideValues)
|
ComposeBySchema(resSchema, r.ProviderMeta, mockValues)
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
func (p providerForTest) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
||||||
if r.Config.IsNull() {
|
if r.Config.IsNull() {
|
||||||
return providers.PlanResourceChangeResponse{
|
return providers.PlanResourceChangeResponse{
|
||||||
PlannedState: r.ProposedNewState, // null
|
PlannedState: r.ProposedNewState, // null
|
||||||
@ -74,37 +80,37 @@ func (p *providerForTest) PlanResourceChange(r providers.PlanResourceChangeReque
|
|||||||
|
|
||||||
resSchema, _ := p.schema.SchemaForResourceType(addrs.ManagedResourceMode, r.TypeName)
|
resSchema, _ := p.schema.SchemaForResourceType(addrs.ManagedResourceMode, r.TypeName)
|
||||||
|
|
||||||
overrideValues := p.managedResources.getOverrideValues(r.TypeName)
|
mockValues := p.getMockValuesForManagedResource(r.TypeName)
|
||||||
|
|
||||||
var resp providers.PlanResourceChangeResponse
|
var resp providers.PlanResourceChangeResponse
|
||||||
|
|
||||||
resp.PlannedState, resp.Diagnostics = newMockValueComposer(r.TypeName).
|
resp.PlannedState, resp.Diagnostics = newMockValueComposer(r.TypeName).
|
||||||
ComposeBySchema(resSchema, r.Config, overrideValues)
|
ComposeBySchema(resSchema, r.Config, mockValues)
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
|
func (p providerForTest) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
|
||||||
return providers.ApplyResourceChangeResponse{
|
return providers.ApplyResourceChangeResponse{
|
||||||
NewState: r.PlannedState,
|
NewState: r.PlannedState,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
|
func (p providerForTest) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
|
||||||
resSchema, _ := p.schema.SchemaForResourceType(addrs.DataResourceMode, r.TypeName)
|
resSchema, _ := p.schema.SchemaForResourceType(addrs.DataResourceMode, r.TypeName)
|
||||||
|
|
||||||
var resp providers.ReadDataSourceResponse
|
var resp providers.ReadDataSourceResponse
|
||||||
|
|
||||||
overrideValues := p.dataResources.getOverrideValues(r.TypeName)
|
mockValues := p.getMockValuesForDataResource(r.TypeName)
|
||||||
|
|
||||||
resp.State, resp.Diagnostics = newMockValueComposer(r.TypeName).
|
resp.State, resp.Diagnostics = newMockValueComposer(r.TypeName).
|
||||||
ComposeBySchema(resSchema, r.Config, overrideValues)
|
ComposeBySchema(resSchema, r.Config, mockValues)
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateProviderConfig is irrelevant when provider is mocked or overridden.
|
// ValidateProviderConfig is irrelevant when provider is mocked or overridden.
|
||||||
func (p *providerForTest) ValidateProviderConfig(_ providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
|
func (p providerForTest) ValidateProviderConfig(_ providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
|
||||||
return providers.ValidateProviderConfigResponse{}
|
return providers.ValidateProviderConfigResponse{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +120,7 @@ func (p *providerForTest) ValidateProviderConfig(_ providers.ValidateProviderCon
|
|||||||
// is being transformed for testing framework and original provider configuration is not
|
// is being transformed for testing framework and original provider configuration is not
|
||||||
// accessible so it is safe to wipe metadata as well. See Config.transformProviderConfigsForTest
|
// accessible so it is safe to wipe metadata as well. See Config.transformProviderConfigsForTest
|
||||||
// for more details.
|
// for more details.
|
||||||
func (p *providerForTest) GetProviderSchema() providers.GetProviderSchemaResponse {
|
func (p providerForTest) GetProviderSchema() providers.GetProviderSchemaResponse {
|
||||||
providerSchema := p.internal.GetProviderSchema()
|
providerSchema := p.internal.GetProviderSchema()
|
||||||
providerSchema.Provider = providers.Schema{}
|
providerSchema.Provider = providers.Schema{}
|
||||||
providerSchema.ProviderMeta = providers.Schema{}
|
providerSchema.ProviderMeta = providers.Schema{}
|
||||||
@ -122,92 +128,163 @@ func (p *providerForTest) GetProviderSchema() providers.GetProviderSchemaRespons
|
|||||||
}
|
}
|
||||||
|
|
||||||
// providerForTest doesn't configure its internal provider because it is mocked.
|
// providerForTest doesn't configure its internal provider because it is mocked.
|
||||||
func (p *providerForTest) ConfigureProvider(_ providers.ConfigureProviderRequest) providers.ConfigureProviderResponse {
|
func (p providerForTest) ConfigureProvider(_ providers.ConfigureProviderRequest) providers.ConfigureProviderResponse {
|
||||||
return providers.ConfigureProviderResponse{}
|
return providers.ConfigureProviderResponse{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) ImportResourceState(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
|
func (p providerForTest) ImportResourceState(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
|
||||||
panic("Importing is not supported in testing context. providerForTest must not be used to call ImportResourceState")
|
panic("Importing is not supported in testing context. providerForTest must not be used to call ImportResourceState")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) setSingleResource(addr addrs.Resource, overrides map[string]cty.Value) {
|
|
||||||
res := resourceForTest{
|
|
||||||
overrideValues: overrides,
|
|
||||||
}
|
|
||||||
|
|
||||||
switch addr.Mode {
|
|
||||||
case addrs.ManagedResourceMode:
|
|
||||||
p.managedResources[addr.Type] = res
|
|
||||||
case addrs.DataResourceMode:
|
|
||||||
p.dataResources[addr.Type] = res
|
|
||||||
case addrs.InvalidResourceMode:
|
|
||||||
panic("BUG: invalid mock resource mode")
|
|
||||||
default:
|
|
||||||
panic("BUG: unsupported resource mode: " + addr.Mode.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *providerForTest) addMockResources(mockResources []*configs.MockResource) {
|
|
||||||
for _, mockRes := range mockResources {
|
|
||||||
var resources resourceForTestByType
|
|
||||||
|
|
||||||
switch mockRes.Mode {
|
|
||||||
case addrs.ManagedResourceMode:
|
|
||||||
resources = p.managedResources
|
|
||||||
case addrs.DataResourceMode:
|
|
||||||
resources = p.dataResources
|
|
||||||
case addrs.InvalidResourceMode:
|
|
||||||
panic("BUG: invalid mock resource mode")
|
|
||||||
default:
|
|
||||||
panic("BUG: unsupported mock resource mode: " + mockRes.Mode.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
resources[mockRes.Type] = resourceForTest{
|
|
||||||
overrideValues: mockRes.Defaults,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calling the internal provider ensures providerForTest has the same behaviour as if
|
// Calling the internal provider ensures providerForTest has the same behaviour as if
|
||||||
// it wasn't overridden or mocked. The only exception is ImportResourceState, which panics
|
// it wasn't overridden or mocked. The only exception is ImportResourceState, which panics
|
||||||
// if called via providerForTest because importing is not supported in testing framework.
|
// if called via providerForTest because importing is not supported in testing framework.
|
||||||
|
|
||||||
func (p *providerForTest) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse {
|
func (p providerForTest) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse {
|
||||||
return p.internal.ValidateResourceConfig(r)
|
return p.internal.ValidateResourceConfig(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse {
|
func (p providerForTest) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse {
|
||||||
return p.internal.ValidateDataResourceConfig(r)
|
return p.internal.ValidateDataResourceConfig(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse {
|
func (p providerForTest) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse {
|
||||||
return p.internal.UpgradeResourceState(r)
|
return p.internal.UpgradeResourceState(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) Stop() error {
|
func (p providerForTest) Stop() error {
|
||||||
return p.internal.Stop()
|
return p.internal.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) GetFunctions() providers.GetFunctionsResponse {
|
func (p providerForTest) GetFunctions() providers.GetFunctionsResponse {
|
||||||
return p.internal.GetFunctions()
|
return p.internal.GetFunctions()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) CallFunction(r providers.CallFunctionRequest) providers.CallFunctionResponse {
|
func (p providerForTest) CallFunction(r providers.CallFunctionRequest) providers.CallFunctionResponse {
|
||||||
return p.internal.CallFunction(r)
|
return p.internal.CallFunction(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providerForTest) Close() error {
|
func (p providerForTest) Close() error {
|
||||||
return p.internal.Close()
|
return p.internal.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
type resourceForTest struct {
|
func (p providerForTest) withMockResources(mockResources []*configs.MockResource) providerForTest {
|
||||||
overrideValues map[string]cty.Value
|
for _, res := range mockResources {
|
||||||
|
var resources map[mockResourceType]resourceForTest
|
||||||
|
|
||||||
|
switch res.Mode {
|
||||||
|
case addrs.ManagedResourceMode:
|
||||||
|
resources = p.mockResources.managed
|
||||||
|
case addrs.DataResourceMode:
|
||||||
|
resources = p.mockResources.data
|
||||||
|
case addrs.InvalidResourceMode:
|
||||||
|
panic("BUG: invalid mock resource mode")
|
||||||
|
default:
|
||||||
|
panic("BUG: unsupported mock resource mode: " + res.Mode.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
type resourceForTestByType map[string]resourceForTest
|
resources[res.Type] = resourceForTest{
|
||||||
|
values: res.Defaults,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m resourceForTestByType) getOverrideValues(typeName string) map[string]cty.Value {
|
return p
|
||||||
return m[typeName].overrideValues
|
}
|
||||||
|
|
||||||
|
func (p providerForTest) withCopiedOverrideResources() providerForTest {
|
||||||
|
p.overrideResources = p.overrideResources.copy()
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p providerForTest) withOverrideResources(overrideResources []*configs.OverrideResource) providerForTest {
|
||||||
|
for _, res := range overrideResources {
|
||||||
|
p = p.withOverrideResource(*res.TargetParsed, res.Values)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p providerForTest) withOverrideResource(addr addrs.ConfigResource, overrides map[string]cty.Value) providerForTest {
|
||||||
|
var resources map[string]resourceForTest
|
||||||
|
|
||||||
|
switch addr.Resource.Mode {
|
||||||
|
case addrs.ManagedResourceMode:
|
||||||
|
resources = p.overrideResources.managed
|
||||||
|
case addrs.DataResourceMode:
|
||||||
|
resources = p.overrideResources.data
|
||||||
|
case addrs.InvalidResourceMode:
|
||||||
|
panic("BUG: invalid override resource mode")
|
||||||
|
default:
|
||||||
|
panic("BUG: unsupported override resource mode: " + addr.Resource.Mode.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
resources[addr.String()] = resourceForTest{
|
||||||
|
values: overrides,
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p providerForTest) linkWithCurrentResource(addr addrs.ConfigResource) providerForTest {
|
||||||
|
p.currentResourceAddress = addr.String()
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
type resourceForTest struct {
|
||||||
|
values map[string]cty.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockResourceType = string
|
||||||
|
|
||||||
|
type mockResourcesForTest struct {
|
||||||
|
managed map[mockResourceType]resourceForTest
|
||||||
|
data map[mockResourceType]resourceForTest
|
||||||
|
}
|
||||||
|
|
||||||
|
type overrideResourceAddress = string
|
||||||
|
|
||||||
|
type overrideResourcesForTest struct {
|
||||||
|
managed map[overrideResourceAddress]resourceForTest
|
||||||
|
data map[overrideResourceAddress]resourceForTest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res overrideResourcesForTest) copy() overrideResourcesForTest {
|
||||||
|
resCopy := overrideResourcesForTest{
|
||||||
|
managed: make(map[overrideResourceAddress]resourceForTest, len(res.managed)),
|
||||||
|
data: make(map[overrideResourceAddress]resourceForTest, len(res.data)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range res.managed {
|
||||||
|
resCopy.managed[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range res.data {
|
||||||
|
resCopy.data[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return resCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p providerForTest) getMockValuesForManagedResource(typeName string) map[string]cty.Value {
|
||||||
|
if p.currentResourceAddress != "" {
|
||||||
|
res, ok := p.overrideResources.managed[p.currentResourceAddress]
|
||||||
|
if ok {
|
||||||
|
return res.values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.mockResources.managed[typeName].values
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p providerForTest) getMockValuesForDataResource(typeName string) map[string]cty.Value {
|
||||||
|
if p.currentResourceAddress != "" {
|
||||||
|
res, ok := p.overrideResources.data[p.currentResourceAddress]
|
||||||
|
if ok {
|
||||||
|
return res.values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.mockResources.data[typeName].values
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMockValueComposer(typeName string) hcl2shim.MockValueComposer {
|
func newMockValueComposer(typeName string) hcl2shim.MockValueComposer {
|
||||||
|
@ -158,7 +158,7 @@ A test file consists of:
|
|||||||
* A **[`variables` block](#the-variables-and-runvariables-blocks)** (optional): define variables for all tests in the
|
* A **[`variables` block](#the-variables-and-runvariables-blocks)** (optional): define variables for all tests in the
|
||||||
current file.
|
current file.
|
||||||
* The **[`provider` blocks](#the-providers-block)** (optional): define the providers to be used for the tests.
|
* The **[`provider` blocks](#the-providers-block)** (optional): define the providers to be used for the tests.
|
||||||
* The **[`mock_provider` blocks](#the-mock_providers-blocks)** (optional): define the providers to be mocked.
|
* The **[`mock_provider` blocks](#the-mock_provider-blocks)** (optional): define the providers to be mocked.
|
||||||
* The **[`override_resource` blocks](#the-override_resource-and-override_data-blocks)** (optional): define the resources to be overridden.
|
* The **[`override_resource` blocks](#the-override_resource-and-override_data-blocks)** (optional): define the resources to be overridden.
|
||||||
* The **[`override_data` blocks](#the-override_resource-and-override_data-blocks)** (optional): define the data sources to be overridden.
|
* The **[`override_data` blocks](#the-override_resource-and-override_data-blocks)** (optional): define the data sources to be overridden.
|
||||||
* The **[`override_module` blocks](#the-override_module-block)** (optional): define the module calls to be overridden.
|
* The **[`override_module` blocks](#the-override_module-block)** (optional): define the module calls to be overridden.
|
||||||
@ -429,6 +429,9 @@ Mock providers also support `alias` field as well as `mock_resource` and `mock_d
|
|||||||
In some cases, you may want to use default values instead of automatically generated ones by passing them
|
In some cases, you may want to use default values instead of automatically generated ones by passing them
|
||||||
inside `defaults` field of `mock_resource` or `mock_data` blocks.
|
inside `defaults` field of `mock_resource` or `mock_data` blocks.
|
||||||
|
|
||||||
|
Additionally, you can use `override_resource` and `override_data` blocks to override resources or data
|
||||||
|
sources in the scope of a single provider. Read more about overriding in [the next section](#the-override_resource-and-override_data-blocks).
|
||||||
|
|
||||||
In the example below, we test if the bucket name is correctly passed to the resource
|
In the example below, we test if the bucket name is correctly passed to the resource
|
||||||
without actually creating it:
|
without actually creating it:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user