opentofu/internal/configs/hcl2shim/mock_value_composer_test.go
Oleksandr Levchenkov 9d9a7aab06
add mock providers for testing framework (#1772)
Signed-off-by: ollevche <ollevche@gmail.com>
2024-07-09 14:41:52 +03:00

525 lines
14 KiB
Go

package hcl2shim
import (
"testing"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/zclconf/go-cty/cty"
)
// TestComposeMockValueBySchema ensures different configschema.Block values
// processed correctly (lists, maps, objects, etc). Also, it should ensure that
// the resulting values are equal given the same set of inputs (seed, schema, etc).
func TestComposeMockValueBySchema(t *testing.T) {
t.Parallel()
tests := map[string]struct {
schema *configschema.Block
config cty.Value
defaults map[string]cty.Value
wantVal cty.Value
wantWarning bool
wantError bool
}{
"diff-props-in-root-attributes": {
schema: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"required-only": {
Type: cty.String,
Required: true,
Optional: false,
Computed: false,
Sensitive: false,
},
"required-computed": {
Type: cty.String,
Required: true,
Optional: false,
Computed: true,
Sensitive: false,
},
"optional": {
Type: cty.String,
Required: false,
Optional: true,
Computed: false,
Sensitive: false,
},
"optional-computed": {
Type: cty.String,
Required: false,
Optional: true,
Computed: true,
Sensitive: false,
},
"computed-only": {
Type: cty.String,
Required: false,
Optional: false,
Computed: true,
Sensitive: false,
},
"sensitive-optional": {
Type: cty.String,
Required: false,
Optional: true,
Computed: false,
Sensitive: true,
},
"sensitive-required": {
Type: cty.String,
Required: true,
Optional: false,
Computed: false,
Sensitive: true,
},
"sensitive-computed": {
Type: cty.String,
Required: true,
Optional: false,
Computed: true,
Sensitive: true,
},
},
},
config: cty.NilVal,
wantVal: cty.ObjectVal(map[string]cty.Value{
"required-only": cty.NullVal(cty.String),
"required-computed": cty.StringVal("xNmGyAVmNkB4"),
"optional": cty.NullVal(cty.String),
"optional-computed": cty.StringVal("6zQu0"),
"computed-only": cty.StringVal("l3INvNSQT"),
"sensitive-optional": cty.NullVal(cty.String),
"sensitive-required": cty.NullVal(cty.String),
"sensitive-computed": cty.StringVal("ionwj3qrsh4xyC9"),
}),
},
"diff-props-in-single-block-attributes": {
schema: &configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"nested": {
Nesting: configschema.NestingSingle,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"required-only": {
Type: cty.String,
Required: true,
Optional: false,
Computed: false,
Sensitive: false,
},
"required-computed": {
Type: cty.String,
Required: true,
Optional: false,
Computed: true,
Sensitive: false,
},
"optional": {
Type: cty.String,
Required: false,
Optional: true,
Computed: false,
Sensitive: false,
},
"optional-computed": {
Type: cty.String,
Required: false,
Optional: true,
Computed: true,
Sensitive: false,
},
"computed-only": {
Type: cty.String,
Required: false,
Optional: false,
Computed: true,
Sensitive: false,
},
"sensitive-optional": {
Type: cty.String,
Required: false,
Optional: true,
Computed: false,
Sensitive: true,
},
"sensitive-required": {
Type: cty.String,
Required: true,
Optional: false,
Computed: false,
Sensitive: true,
},
"sensitive-computed": {
Type: cty.String,
Required: true,
Optional: false,
Computed: true,
Sensitive: true,
},
},
},
},
},
},
config: cty.ObjectVal(map[string]cty.Value{
"nested": cty.ObjectVal(map[string]cty.Value{}),
}),
wantVal: cty.ObjectVal(map[string]cty.Value{
"nested": cty.ObjectVal(map[string]cty.Value{
"required-only": cty.NullVal(cty.String),
"required-computed": cty.StringVal("xNmGyAVmNkB4"),
"optional": cty.NullVal(cty.String),
"optional-computed": cty.StringVal("6zQu0"),
"computed-only": cty.StringVal("l3INvNSQT"),
"sensitive-optional": cty.NullVal(cty.String),
"sensitive-required": cty.NullVal(cty.String),
"sensitive-computed": cty.StringVal("ionwj3qrsh4xyC9"),
}),
}),
},
"basic-group-block": {
schema: &configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"nested": {
Nesting: configschema.NestingGroup,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"field": {
Type: cty.Number,
Computed: true,
},
},
},
},
},
},
config: cty.ObjectVal(map[string]cty.Value{
"nested": cty.ObjectVal(map[string]cty.Value{}),
}),
wantVal: cty.ObjectVal(map[string]cty.Value{
"nested": cty.ObjectVal(map[string]cty.Value{
"field": cty.NumberIntVal(0),
}),
}),
},
"basic-list-block": {
schema: &configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"nested": {
Nesting: configschema.NestingList,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"num": {
Type: cty.Number,
Computed: true,
},
"str1": {
Type: cty.String,
Computed: true,
},
"str2": {
Type: cty.String,
Computed: true,
},
},
},
},
},
},
config: cty.ObjectVal(map[string]cty.Value{
"nested": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{})}),
}),
wantVal: cty.ObjectVal(map[string]cty.Value{
"nested": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"num": cty.NumberIntVal(0),
"str1": cty.StringVal("l3INvNSQT"),
"str2": cty.StringVal("6zQu0"),
}),
}),
}),
},
"basic-set-block": {
schema: &configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"nested": {
Nesting: configschema.NestingSet,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"num": {
Type: cty.Number,
Computed: true,
},
"str1": {
Type: cty.String,
Computed: true,
},
"str2": {
Type: cty.String,
Computed: true,
},
},
},
},
},
},
config: cty.ObjectVal(map[string]cty.Value{
"nested": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{})}),
}),
wantVal: cty.ObjectVal(map[string]cty.Value{
"nested": cty.SetVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"num": cty.NumberIntVal(0),
"str1": cty.StringVal("l3INvNSQT"),
"str2": cty.StringVal("6zQu0"),
}),
}),
}),
},
"basic-map-block": {
schema: &configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"nested": {
Nesting: configschema.NestingMap,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"num": {
Type: cty.Number,
Computed: true,
},
"str1": {
Type: cty.String,
Computed: true,
},
"str2": {
Type: cty.String,
Computed: true,
},
},
},
},
},
},
config: cty.ObjectVal(map[string]cty.Value{
"nested": cty.MapVal(map[string]cty.Value{
"somelabel": cty.ObjectVal(map[string]cty.Value{}),
}),
}),
wantVal: cty.ObjectVal(map[string]cty.Value{
"nested": cty.MapVal(map[string]cty.Value{
"somelabel": cty.ObjectVal(map[string]cty.Value{
"num": cty.NumberIntVal(0),
"str1": cty.StringVal("l3INvNSQT"),
"str2": cty.StringVal("6zQu0"),
}),
}),
}),
},
"basic-mocked-attributes": {
schema: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"num": {
Type: cty.Number,
Computed: true,
Optional: true,
},
"str": {
Type: cty.String,
Computed: true,
Optional: true,
},
"bool": {
Type: cty.Bool,
Computed: true,
Optional: true,
},
"obj": {
Type: cty.Object(map[string]cty.Type{
"fieldNum": cty.Number,
"fieldStr1": cty.String,
"fieldStr2": cty.String,
}),
Computed: true,
Optional: true,
},
"list": {
Type: cty.List(cty.String),
Computed: true,
Optional: true,
},
},
BlockTypes: map[string]*configschema.NestedBlock{
"nested": {
Nesting: configschema.NestingList,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"num": {
Type: cty.Number,
Computed: true,
Optional: true,
},
"str1": {
Type: cty.String,
Computed: true,
Optional: true,
},
"str2": {
Type: cty.String,
Computed: true,
Optional: true,
},
"bool": {
Type: cty.Bool,
Computed: true,
Optional: true,
},
"obj": {
Type: cty.Object(map[string]cty.Type{
"fieldNum": cty.Number,
"fieldStr": cty.String,
}),
Computed: true,
Optional: true,
},
"list": {
Type: cty.List(cty.String),
Computed: true,
Optional: true,
},
},
},
},
},
},
config: cty.ObjectVal(map[string]cty.Value{
"nested": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{})}),
}),
wantVal: cty.ObjectVal(map[string]cty.Value{
"num": cty.NumberIntVal(0),
"str": cty.StringVal("xNmGyAVmNkB4"),
"bool": cty.False,
"obj": cty.ObjectVal(map[string]cty.Value{
"fieldNum": cty.NumberIntVal(0),
"fieldStr1": cty.StringVal("l3INvNSQT"),
"fieldStr2": cty.StringVal("6zQu0"),
}),
"list": cty.ListValEmpty(cty.String),
"nested": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"num": cty.NumberIntVal(0),
"str1": cty.StringVal("mCp2gObD"),
"str2": cty.StringVal("iOtQNQsLiFD5"),
"bool": cty.False,
"obj": cty.ObjectVal(map[string]cty.Value{
"fieldNum": cty.NumberIntVal(0),
"fieldStr": cty.StringVal("ionwj3qrsh4xyC9"),
}),
"list": cty.ListValEmpty(cty.String),
}),
}),
}),
},
"source-priority": {
schema: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"useConfigValue": {
Type: cty.String,
Computed: true,
Optional: true,
},
"useDefaultsValue": {
Type: cty.String,
Computed: true,
Optional: true,
},
"generateMockValue": {
Type: cty.String,
Computed: true,
Optional: true,
},
},
BlockTypes: map[string]*configschema.NestedBlock{
"nested": {
Nesting: configschema.NestingList,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"useConfigValue": {
Type: cty.String,
Computed: true,
Optional: true,
},
"useDefaultsValue": {
Type: cty.String,
Computed: true,
Optional: true,
},
"generateMockValue": {
Type: cty.String,
Computed: true,
Optional: true,
},
},
},
},
},
},
config: cty.ObjectVal(map[string]cty.Value{
"useConfigValue": cty.StringVal("iAmFromConfig"),
"nested": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"useConfigValue": cty.StringVal("iAmFromConfig"),
}),
}),
}),
defaults: map[string]cty.Value{
"useConfigValue": cty.StringVal("iAmFromDefaults"),
"useDefaultsValue": cty.StringVal("iAmFromDefaults"),
"nested": cty.ObjectVal(map[string]cty.Value{
"useConfigValue": cty.StringVal("iAmFromDefaults"),
"useDefaultsValue": cty.StringVal("iAmFromDefaults"),
}),
},
wantVal: cty.ObjectVal(map[string]cty.Value{
"useConfigValue": cty.StringVal("iAmFromConfig"),
"useDefaultsValue": cty.StringVal("iAmFromDefaults"),
"generateMockValue": cty.StringVal("l3INvNSQT"),
"nested": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"useConfigValue": cty.StringVal("iAmFromConfig"),
"useDefaultsValue": cty.StringVal("iAmFromDefaults"),
"generateMockValue": cty.StringVal("6zQu0"),
}),
}),
}),
wantWarning: true, // ignored value in defaults
},
}
for name, test := range tests {
test := test
t.Run(name, func(t *testing.T) {
t.Parallel()
gotVal, gotDiags := NewMockValueComposer(42).ComposeBySchema(test.schema, test.config, test.defaults)
switch {
case test.wantError && !gotDiags.HasErrors():
t.Fatalf("Expected error in diags, but none returned")
case !test.wantError && gotDiags.HasErrors():
t.Fatalf("Got unexpected error diags: %v", gotDiags.ErrWithWarnings())
case test.wantWarning && len(gotDiags) == 0:
t.Fatalf("Expected warning in diags, but none returned")
case !test.wantWarning && len(gotDiags) != 0:
t.Fatalf("Got unexpected diags: %v", gotDiags.ErrWithWarnings())
case !test.wantVal.RawEquals(gotVal):
t.Fatalf("Got unexpected value: %v", gotVal.GoString())
}
})
}
}