Skip to content

Commit

Permalink
Add extract schema inputs tests for sdkv2 (#2224)
Browse files Browse the repository at this point in the history
This adds test cases for how TF types are handled by
`ExtractInputsFromOutputs` in combination with `Default` and `Computed`
and `MaxItems: 1`
`ExtractInputsFromOutputs` is the function in `schema.go` responsible
for generating the inputs from resource outputs when a resource is
imported. Its output is what the engine then uses for generating the
code for the import.

Notably, nested computed values are wrong for both max items one and
non-max items one.

In #2180 we
found a regression there, so this fills in some missing coverage.
Extracting the tests to make the changes more apparent and to make sure
we don't regress in
#2181
  • Loading branch information
VenelinMartinov authored Jul 23, 2024
1 parent 6796105 commit 7ccdd03
Showing 1 changed file with 306 additions and 0 deletions.
306 changes: 306 additions & 0 deletions pkg/tfbridge/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3436,3 +3436,309 @@ func Test_makeTerraformInputsNoDefaults(t *testing.T) {
})
}
}

func TestExtractInputsFromOutputsSdkv2(t *testing.T) {
t.Parallel()

type testCase struct {
name string
props resource.PropertyMap
schemaMap map[string]*schemav2.Schema
expected autogold.Value
}

testCases := []testCase{
{
name: "string attribute extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{"foo": "bar"}),
schemaMap: map[string]*schemav2.Schema{
"foo": {Type: schemav2.TypeString, Optional: true},
},
expected: autogold.Expect(resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("foo"): resource.PropertyValue{V: "bar"},
}),
},
{
name: "string attribute with defaults not extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{"foo": "baz"}),
schemaMap: map[string]*schemav2.Schema{
"foo": {Type: schemav2.TypeString, Optional: true, Default: "baz"},
},
expected: autogold.Expect(resource.PropertyMap{resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
}}),
},
{
name: "string attribute with empty value not extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{"foo": ""}),
schemaMap: map[string]*schemav2.Schema{
"foo": {Type: schemav2.TypeString, Optional: true},
},
expected: autogold.Expect(resource.PropertyMap{resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
}}),
},
{
name: "string attribute with computed not extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": resource.Computed{Element: resource.NewStringProperty("bar")},
}),
schemaMap: map[string]*schemav2.Schema{
"foo": {Type: schemav2.TypeString, Computed: true},
},
expected: autogold.Expect(resource.PropertyMap{resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
}}),
},
{
name: "map attribute extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
},
}),
schemaMap: map[string]*schemav2.Schema{
"foo": {
Type: schemav2.TypeMap,
Optional: true,
Elem: &schemav2.Schema{
Type: schemav2.TypeString,
},
},
},
expected: autogold.Expect(resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("foo"): resource.PropertyValue{V: resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("bar"): resource.PropertyValue{V: "baz"},
}},
}),
},
{
name: "map attribute with computed not extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": resource.Computed{Element: resource.NewStringProperty("bar")},
}),
schemaMap: map[string]*schemav2.Schema{
"foo": {
Type: schemav2.TypeMap,
Computed: true,
Elem: &schemav2.Schema{
Type: schemav2.TypeString,
},
},
},
expected: autogold.Expect(resource.PropertyMap{resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
}}),
},
{
name: "list attribute extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": []interface{}{"bar"},
}),
schemaMap: map[string]*schemav2.Schema{
"foo": {
Type: schemav2.TypeList,
Optional: true,
Elem: &schemav2.Schema{
Type: schemav2.TypeString,
},
},
},
expected: autogold.Expect(resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("foo"): resource.PropertyValue{V: []resource.PropertyValue{{
V: "bar",
}}},
}),
},
{
name: "list block extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": []interface{}{map[string]string{"bar": "baz"}},
}),
schemaMap: map[string]*schemav2.Schema{
"foo": {
Type: schemav2.TypeList,
Optional: true,
Elem: &schemav2.Resource{
Schema: map[string]*schemav2.Schema{
"bar": {Type: schemav2.TypeString, Optional: true},
},
},
},
},
expected: autogold.Expect(resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("foo"): resource.PropertyValue{V: []resource.PropertyValue{{
V: resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("bar"): resource.PropertyValue{V: "baz"},
},
}}},
}),
},
{
name: "list block with computed not extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": []interface{}{map[string]string{"bar": "baz"}},
}),
schemaMap: map[string]*schemav2.Schema{
"foo": {
Type: schemav2.TypeList,
Computed: true,
Elem: &schemav2.Resource{
Schema: map[string]*schemav2.Schema{
"bar": {Type: schemav2.TypeString, Optional: true},
},
},
},
},
expected: autogold.Expect(resource.PropertyMap{resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
}}),
},
{
name: "list block max items one extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
},
}),
schemaMap: map[string]*schemav2.Schema{
"foo": {
Type: schemav2.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schemav2.Resource{
Schema: map[string]*schemav2.Schema{
"bar": {Type: schemav2.TypeString, Optional: true},
},
},
},
},
expected: autogold.Expect(resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("foo"): resource.PropertyValue{V: resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("bar"): resource.PropertyValue{V: "baz"},
}},
}),
},
// This case is invalid since MaxItemsOne only works on settable fields.
// {
// name: "list block max items one computed",
// props: resource.NewPropertyMapFromMap(map[string]interface{}{
// "foo": map[string]interface{}{
// "bar": "baz",
// },
// }),
// schemaMap: map[string]*schemav2.Schema{
// "foo": {
// Type: schemav2.TypeList,
// Computed: true,
// MaxItems: 1,
// Elem: &schemav2.Resource{
// Schema: map[string]*schemav2.Schema{
// "bar": {Type: schemav2.TypeString, Optional: true},
// },
// },
// },
// },
// expected: autogold.Expect(),
// },
// TODO[pulumi/pulumi-terraform-bridge#2180]: This is wrong as an input should not be produced for computed values.
{
name: "list block with computed element not extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": []interface{}{map[string]string{"bar": "baz"}},
}),
schemaMap: map[string]*schemav2.Schema{
"foo": {
Type: schemav2.TypeList,
Optional: true,
Elem: &schemav2.Resource{
Schema: map[string]*schemav2.Schema{
"bar": {Type: schemav2.TypeString, Computed: true},
},
},
},
},
expected: autogold.Expect(resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("foo"): resource.PropertyValue{V: []resource.PropertyValue{{
V: resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("bar"): resource.PropertyValue{V: "baz"},
},
}}},
}),
},
// TODO[pulumi/pulumi-terraform-bridge#2180]: This is wrong as an input should not be produced for computed values.
{
name: "list block max items one with computed element not extracted",
props: resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": []interface{}{map[string]string{"bar": "baz"}},
}),
schemaMap: map[string]*schemav2.Schema{
"foo": {
Type: schemav2.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schemav2.Resource{
Schema: map[string]*schemav2.Schema{
"bar": {Type: schemav2.TypeString, Computed: true},
},
},
},
},
expected: autogold.Expect(resource.PropertyMap{
resource.PropertyKey("__defaults"): resource.PropertyValue{
V: []resource.PropertyValue{},
},
resource.PropertyKey("foo"): resource.PropertyValue{V: []resource.PropertyValue{{
V: resource.PropertyMap{resource.PropertyKey("bar"): resource.PropertyValue{
V: "baz",
}},
}}},
}),
},
}

for _, tc := range testCases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
sm := shimv2.NewSchemaMap(tc.schemaMap)
err := sm.Validate()
require.NoErrorf(t, err, "Invalid test case schema, please fix the testCase")

result, err := ExtractInputsFromOutputs(nil, tc.props, sm, nil, false)
require.NoError(t, err)
tc.expected.Equal(t, result)
})

}
}

0 comments on commit 7ccdd03

Please sign in to comment.