mirror of
https://github.com/hashicorp/terraform.git
synced 2026-02-18 18:29:44 -05:00
241 lines
7.3 KiB
Go
241 lines
7.3 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package backendbase
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hcltest"
|
|
"github.com/zclconf/go-cty-debug/ctydebug"
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/hashicorp/terraform/internal/configs/configschema"
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
)
|
|
|
|
func TestBase_coerceError(t *testing.T) {
|
|
// This tests that we return errors if type coersion fails.
|
|
// This doesn't thoroughly test all cases because we're just delegating
|
|
// to the configschema package's coersion function, which is already
|
|
// tested in its own package.
|
|
|
|
b := Base{
|
|
Schema: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {
|
|
Type: cty.String,
|
|
Optional: true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
// This is a fake body just to give us something to correlate the
|
|
// diagnostic attribute paths against so we can test that the
|
|
// errors are properly annotated. In the real implementation
|
|
// the command package logic would evaluate the diagnostics against
|
|
// the real HCL body written by the end-user.
|
|
//
|
|
// Because we're using MockExprLiteral for the expressions here,
|
|
// the source range for each expression is just the fake filename
|
|
// "MockExprLiteral". If the PrepareConfig function fails to properly
|
|
// annotate its diagnostics then the source range won't be populated
|
|
// at all.
|
|
body := hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcl.Attributes{
|
|
"foo": {
|
|
Expr: hcltest.MockExprLiteral(cty.StringVal("")),
|
|
},
|
|
},
|
|
})
|
|
|
|
t.Run("error", func(t *testing.T) {
|
|
_, diags := b.PrepareConfig(cty.ObjectVal(map[string]cty.Value{
|
|
// This is incorrect because the schema wants a string
|
|
"foo": cty.MapValEmpty(cty.String),
|
|
}))
|
|
gotDiags := diags.InConfigBody(body, "")
|
|
var wantDiags tfdiags.Diagnostics
|
|
wantDiags = wantDiags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid backend configuration",
|
|
Detail: "The backend configuration is incorrect: .foo: string required, but have map of string.",
|
|
Subject: &hcl.Range{Filename: "MockExprLiteral"},
|
|
})
|
|
|
|
tfdiags.AssertDiagnosticsMatch(t, gotDiags, wantDiags)
|
|
})
|
|
}
|
|
|
|
func TestBase_deprecatedArg(t *testing.T) {
|
|
b := Base{
|
|
Schema: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"not_deprecated": {
|
|
Type: cty.String,
|
|
Optional: true,
|
|
},
|
|
"deprecated": {
|
|
Type: cty.String,
|
|
Optional: true,
|
|
Deprecated: true,
|
|
},
|
|
},
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
"nested": {
|
|
Nesting: configschema.NestingList,
|
|
Block: configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"deprecated": {
|
|
Type: cty.String,
|
|
Optional: true,
|
|
Deprecated: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
// This is a fake body just to give us something to correlate the
|
|
// diagnostic attribute paths against so we can test that the
|
|
// warnings are properly annotated. In the real implementation
|
|
// the command package logic would evaluate the diagnostics against
|
|
// the real HCL body written by the end-user.
|
|
//
|
|
// Because we're using MockExprLiteral for the expressions here,
|
|
// the source range for each expression is just the fake filename
|
|
// "MockExprLiteral". If the PrepareConfig function fails to properly
|
|
// annotate its diagnostics then the source range won't be populated
|
|
// at all.
|
|
body := hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcl.Attributes{
|
|
"deprecated": {
|
|
Expr: hcltest.MockExprLiteral(cty.StringVal("")),
|
|
},
|
|
},
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "nested",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcl.Attributes{
|
|
"deprecated": {
|
|
Expr: hcltest.MockExprLiteral(cty.StringVal("")),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
{
|
|
Type: "nested",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcl.Attributes{
|
|
"deprecated": {
|
|
Expr: hcltest.MockExprLiteral(cty.StringVal("")),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
})
|
|
|
|
t.Run("nothing deprecated", func(t *testing.T) {
|
|
got, diags := b.PrepareConfig(cty.ObjectVal(map[string]cty.Value{
|
|
"not_deprecated": cty.StringVal("hello"),
|
|
}))
|
|
if len(diags) != 0 {
|
|
t.Errorf("unexpected diagnostics: %s", diags.ErrWithWarnings().Error())
|
|
}
|
|
want := cty.ObjectVal(map[string]cty.Value{
|
|
"deprecated": cty.NullVal(cty.String),
|
|
"not_deprecated": cty.StringVal("hello"),
|
|
"nested": cty.ListValEmpty(cty.Object(map[string]cty.Type{
|
|
"deprecated": cty.String,
|
|
})),
|
|
})
|
|
if diff := cmp.Diff(want, got, ctydebug.CmpOptions); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
})
|
|
t.Run("toplevel deprecated", func(t *testing.T) {
|
|
_, diags := b.PrepareConfig(cty.ObjectVal(map[string]cty.Value{
|
|
"deprecated": cty.StringVal("hello"),
|
|
}))
|
|
|
|
gotDiags := diags.InConfigBody(body, "")
|
|
var wantDiags tfdiags.Diagnostics
|
|
wantDiags = wantDiags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagWarning,
|
|
Summary: "Deprecated provider argument",
|
|
Detail: "The argument .deprecated is deprecated. Refer to the backend documentation for more information.",
|
|
Subject: &hcl.Range{Filename: "MockExprLiteral"},
|
|
})
|
|
tfdiags.AssertDiagnosticsMatch(t, wantDiags, gotDiags)
|
|
})
|
|
t.Run("nested deprecated", func(t *testing.T) {
|
|
_, diags := b.PrepareConfig(cty.ObjectVal(map[string]cty.Value{
|
|
"nested": cty.TupleVal([]cty.Value{
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"deprecated": cty.StringVal("hello"),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"deprecated": cty.StringVal("hello"),
|
|
}),
|
|
}),
|
|
}))
|
|
gotDiags := diags.InConfigBody(body, "")
|
|
var wantDiags tfdiags.Diagnostics
|
|
wantDiags = wantDiags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagWarning,
|
|
Summary: "Deprecated provider argument",
|
|
Detail: "The argument .nested[0].deprecated is deprecated. Refer to the backend documentation for more information.",
|
|
Subject: &hcl.Range{Filename: "MockExprLiteral"},
|
|
})
|
|
wantDiags = wantDiags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagWarning,
|
|
Summary: "Deprecated provider argument",
|
|
Detail: "The argument .nested[1].deprecated is deprecated. Refer to the backend documentation for more information.",
|
|
Subject: &hcl.Range{Filename: "MockExprLiteral"},
|
|
})
|
|
tfdiags.AssertDiagnosticsMatch(t, wantDiags, gotDiags)
|
|
})
|
|
}
|
|
|
|
func TestBase_nullCrash(t *testing.T) {
|
|
// This test ensures that we don't crash while applying defaults to
|
|
// a null value
|
|
|
|
b := Base{
|
|
Schema: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
SDKLikeDefaults: SDKLikeDefaults{
|
|
"foo": {
|
|
Fallback: "fallback",
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("error", func(t *testing.T) {
|
|
// We pass an explicit null value here to simulate an interrupt
|
|
_, gotDiags := b.PrepareConfig(cty.NullVal(cty.Object(map[string]cty.Type{
|
|
"foo": cty.String,
|
|
})))
|
|
var wantDiags tfdiags.Diagnostics
|
|
wantDiags = wantDiags.Append(
|
|
&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid backend configuration",
|
|
Detail: "The backend configuration is incorrect: attribute \"foo\" is required.",
|
|
})
|
|
if diff := cmp.Diff(wantDiags.ForRPC(), gotDiags.ForRPC()); diff != "" {
|
|
t.Errorf("wrong diagnostics\n%s", diff)
|
|
}
|
|
})
|
|
}
|