Skip to content

Commit 7f1830a

Browse files
SDKv2 Diff cross tests for replacement of computed properties
1 parent d7eacfb commit 7f1830a

File tree

10 files changed

+432
-1
lines changed

10 files changed

+432
-1
lines changed

pkg/internal/tests/cross-tests/diff_check.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
"github.com/hashicorp/terraform-plugin-go/tftypes"
2222
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
23+
"github.com/pulumi/pulumi/sdk/v3/go/auto/optpreview"
2324
"github.com/stretchr/testify/require"
2425

2526
crosstestsimpl "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/cross-tests/impl"
@@ -79,7 +80,7 @@ func runDiffCheck(t T, tc diffTestCase) crosstestsimpl.DiffResult {
7980
err := os.WriteFile(filepath.Join(pt.CurrentStack().Workspace().WorkDir(), "Pulumi.yaml"), yamlProgram, 0o600)
8081
require.NoErrorf(t, err, "writing Pulumi.yaml")
8182

82-
previewRes := pt.Preview(t)
83+
previewRes := pt.Preview(t, optpreview.Diff())
8384
diffResponse := crosstestsimpl.GetPulumiDiffResponse(t, pt.GrpcLog(t).Entries)
8485
x := pt.Up(t)
8586

pkg/internal/tests/cross-tests/diff_cross_test.go

+112
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ import (
2626
"github.com/hashicorp/terraform-plugin-go/tftypes"
2727
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
2828
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
29+
"github.com/hexops/autogold/v2"
30+
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
2931
"github.com/stretchr/testify/require"
32+
"github.com/zclconf/go-cty/cty"
3033
)
3134

3235
func TestUnchangedBasicObject(t *testing.T) {
@@ -1621,3 +1624,112 @@ func TestBlockCollectionElementForceNew(t *testing.T) {
16211624
})
16221625
})
16231626
}
1627+
1628+
func TestDetailedDiffReplacementComputedProperty(t *testing.T) {
1629+
t.Parallel()
1630+
// TODO[pulumi/pulumi-terraform-bridge#2660]: We fail to re-compute computed properties when the resource is being replaced.
1631+
res := &schema.Resource{
1632+
Schema: map[string]*schema.Schema{
1633+
"computed": {
1634+
Type: schema.TypeString,
1635+
Computed: true,
1636+
},
1637+
"other": {
1638+
Type: schema.TypeString,
1639+
Optional: true,
1640+
ForceNew: true,
1641+
},
1642+
},
1643+
CreateContext: func(ctx context.Context, rd *schema.ResourceData, meta interface{}) diag.Diagnostics {
1644+
rd.SetId("r1")
1645+
1646+
err := rd.Set("computed", "computed_value")
1647+
contract.AssertNoErrorf(err, "setting computed")
1648+
return nil
1649+
},
1650+
}
1651+
1652+
type testOutput struct {
1653+
initialValue cty.Value
1654+
changeValue cty.Value
1655+
tfOut string
1656+
pulumiOut string
1657+
detailedDiff map[string]any
1658+
}
1659+
1660+
t.Run("no change", func(t *testing.T) {
1661+
t.Parallel()
1662+
initialValue := cty.ObjectVal(map[string]cty.Value{})
1663+
changeValue := cty.ObjectVal(map[string]cty.Value{})
1664+
diff := runDiffCheck(t, diffTestCase{
1665+
Resource: res,
1666+
Config1: initialValue,
1667+
Config2: changeValue,
1668+
})
1669+
1670+
autogold.ExpectFile(t, testOutput{
1671+
initialValue: initialValue,
1672+
changeValue: changeValue,
1673+
tfOut: diff.TFOut,
1674+
pulumiOut: diff.PulumiOut,
1675+
detailedDiff: diff.PulumiDiff.DetailedDiff,
1676+
})
1677+
})
1678+
1679+
t.Run("non-computed added", func(t *testing.T) {
1680+
t.Parallel()
1681+
initialValue := cty.ObjectVal(map[string]cty.Value{})
1682+
changeValue := cty.ObjectVal(map[string]cty.Value{"other": cty.StringVal("other_value")})
1683+
diff := runDiffCheck(t, diffTestCase{
1684+
Resource: res,
1685+
Config1: initialValue,
1686+
Config2: changeValue,
1687+
})
1688+
1689+
autogold.ExpectFile(t, testOutput{
1690+
initialValue: initialValue,
1691+
changeValue: changeValue,
1692+
tfOut: diff.TFOut,
1693+
pulumiOut: diff.PulumiOut,
1694+
detailedDiff: diff.PulumiDiff.DetailedDiff,
1695+
})
1696+
})
1697+
1698+
t.Run("non-computed removed", func(t *testing.T) {
1699+
t.Parallel()
1700+
initialValue := cty.ObjectVal(map[string]cty.Value{"other": cty.StringVal("other_value")})
1701+
changeValue := cty.ObjectVal(map[string]cty.Value{})
1702+
diff := runDiffCheck(t, diffTestCase{
1703+
Resource: res,
1704+
Config1: initialValue,
1705+
Config2: changeValue,
1706+
})
1707+
1708+
autogold.ExpectFile(t, testOutput{
1709+
initialValue: initialValue,
1710+
changeValue: changeValue,
1711+
tfOut: diff.TFOut,
1712+
pulumiOut: diff.PulumiOut,
1713+
detailedDiff: diff.PulumiDiff.DetailedDiff,
1714+
})
1715+
})
1716+
1717+
t.Run("non-computed changed", func(t *testing.T) {
1718+
t.Parallel()
1719+
initialValue := cty.ObjectVal(map[string]cty.Value{"other": cty.StringVal("other_value")})
1720+
changeValue := cty.ObjectVal(map[string]cty.Value{"other": cty.StringVal("other_value_2")})
1721+
diff := runDiffCheck(t, diffTestCase{
1722+
Resource: res,
1723+
Config1: initialValue,
1724+
Config2: changeValue,
1725+
})
1726+
1727+
autogold.ExpectFile(t, testOutput{
1728+
initialValue: initialValue,
1729+
changeValue: changeValue,
1730+
tfOut: diff.TFOut,
1731+
pulumiOut: diff.PulumiOut,
1732+
detailedDiff: diff.PulumiDiff.DetailedDiff,
1733+
})
1734+
})
1735+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
crosstests.testOutput{
2+
initialValue: cty.Value{
3+
ty: cty.Type{typeImpl: cty.typeObject{
4+
AttrTypes: map[string]cty.Type{},
5+
}},
6+
v: map[string]interface{}{},
7+
},
8+
changeValue: cty.Value{
9+
ty: cty.Type{typeImpl: cty.typeObject{AttrTypes: map[string]cty.Type{}}},
10+
v: map[string]interface{}{},
11+
},
12+
tfOut: `
13+
No changes. Your infrastructure matches the configuration.
14+
15+
Terraform has compared your real infrastructure against your configuration
16+
and found no differences, so no changes are needed.
17+
`,
18+
pulumiOut: `Previewing update (test):
19+
pulumi:pulumi:Stack: (same)
20+
[urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test]
21+
Resources:
22+
2 unchanged
23+
`,
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
crosstests.testOutput{
2+
initialValue: cty.Value{
3+
ty: cty.Type{typeImpl: cty.typeObject{
4+
AttrTypes: map[string]cty.Type{},
5+
}},
6+
v: map[string]interface{}{},
7+
},
8+
changeValue: cty.Value{
9+
ty: cty.Type{typeImpl: cty.typeObject{AttrTypes: map[string]cty.Type{
10+
"other": {typeImpl: cty.primitiveType{
11+
Kind: cty.primitiveTypeKind(83),
12+
}},
13+
}}},
14+
v: map[string]interface{}{"other": "other_value"},
15+
},
16+
tfOut: `
17+
Terraform used the selected providers to generate the following execution
18+
plan. Resource actions are indicated with the following symbols:
19+
+/- create replacement and then destroy
20+
21+
Terraform will perform the following actions:
22+
23+
# crossprovider_test_res.example must be replaced
24+
+/- resource "crossprovider_test_res" "example" {
25+
~ computed = "computed_value" -> (known after apply)
26+
~ id = "r1" -> (known after apply)
27+
+ other = "other_value" # forces replacement
28+
}
29+
30+
Plan: 1 to add, 0 to change, 1 to destroy.
31+
32+
`,
33+
pulumiOut: `Previewing update (test):
34+
pulumi:pulumi:Stack: (same)
35+
[urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test]
36+
+-crossprovider:index/testRes:TestRes: (replace)
37+
[id=r1]
38+
[urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example]
39+
+ other: "other_value"
40+
Resources:
41+
+-1 to replace
42+
1 unchanged
43+
`,
44+
detailedDiff: map[string]interface{}{"other": map[string]interface{}{"kind": "ADD_REPLACE"}},
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
crosstests.testOutput{
2+
initialValue: cty.Value{
3+
ty: cty.Type{typeImpl: cty.typeObject{
4+
AttrTypes: map[string]cty.Type{"other": {
5+
typeImpl: cty.primitiveType{Kind: cty.primitiveTypeKind(83)},
6+
}},
7+
}},
8+
v: map[string]interface{}{"other": "other_value"},
9+
},
10+
changeValue: cty.Value{
11+
ty: cty.Type{typeImpl: cty.typeObject{AttrTypes: map[string]cty.Type{
12+
"other": {typeImpl: cty.primitiveType{
13+
Kind: cty.primitiveTypeKind(83),
14+
}},
15+
}}},
16+
v: map[string]interface{}{"other": "other_value_2"},
17+
},
18+
tfOut: `
19+
Terraform used the selected providers to generate the following execution
20+
plan. Resource actions are indicated with the following symbols:
21+
+/- create replacement and then destroy
22+
23+
Terraform will perform the following actions:
24+
25+
# crossprovider_test_res.example must be replaced
26+
+/- resource "crossprovider_test_res" "example" {
27+
~ computed = "computed_value" -> (known after apply)
28+
~ id = "r1" -> (known after apply)
29+
~ other = "other_value" -> "other_value_2" # forces replacement
30+
}
31+
32+
Plan: 1 to add, 0 to change, 1 to destroy.
33+
34+
`,
35+
pulumiOut: `Previewing update (test):
36+
pulumi:pulumi:Stack: (same)
37+
[urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test]
38+
+-crossprovider:index/testRes:TestRes: (replace)
39+
[id=r1]
40+
[urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example]
41+
~ other: "other_value" => "other_value_2"
42+
Resources:
43+
+-1 to replace
44+
1 unchanged
45+
`,
46+
detailedDiff: map[string]interface{}{"other": map[string]interface{}{"kind": "UPDATE_REPLACE"}},
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
crosstests.testOutput{
2+
initialValue: cty.Value{
3+
ty: cty.Type{typeImpl: cty.typeObject{
4+
AttrTypes: map[string]cty.Type{"other": {
5+
typeImpl: cty.primitiveType{Kind: cty.primitiveTypeKind(83)},
6+
}},
7+
}},
8+
v: map[string]interface{}{"other": "other_value"},
9+
},
10+
changeValue: cty.Value{
11+
ty: cty.Type{typeImpl: cty.typeObject{AttrTypes: map[string]cty.Type{}}},
12+
v: map[string]interface{}{},
13+
},
14+
tfOut: `
15+
Terraform used the selected providers to generate the following execution
16+
plan. Resource actions are indicated with the following symbols:
17+
+/- create replacement and then destroy
18+
19+
Terraform will perform the following actions:
20+
21+
# crossprovider_test_res.example must be replaced
22+
+/- resource "crossprovider_test_res" "example" {
23+
~ computed = "computed_value" -> (known after apply)
24+
~ id = "r1" -> (known after apply)
25+
- other = "other_value" -> null # forces replacement
26+
}
27+
28+
Plan: 1 to add, 0 to change, 1 to destroy.
29+
30+
`,
31+
pulumiOut: `Previewing update (test):
32+
pulumi:pulumi:Stack: (same)
33+
[urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test]
34+
+-crossprovider:index/testRes:TestRes: (replace)
35+
[id=r1]
36+
[urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example]
37+
- other: "other_value"
38+
Resources:
39+
+-1 to replace
40+
1 unchanged
41+
`,
42+
detailedDiff: map[string]interface{}{"other": map[string]interface{}{"kind": "DELETE_REPLACE"}},
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
crosstests.testOutput{
2+
initialValue: cty.Value{
3+
ty: cty.Type{typeImpl: cty.typeObject{
4+
AttrTypes: map[string]cty.Type{},
5+
}},
6+
v: map[string]interface{}{},
7+
},
8+
changeValue: cty.Value{
9+
ty: cty.Type{typeImpl: cty.typeObject{AttrTypes: map[string]cty.Type{}}},
10+
v: map[string]interface{}{},
11+
},
12+
tfOut: `
13+
No changes. Your infrastructure matches the configuration.
14+
15+
Terraform has compared your real infrastructure against your configuration
16+
and found no differences, so no changes are needed.
17+
`,
18+
pulumiOut: `Previewing update (test):
19+
pulumi:pulumi:Stack: (same)
20+
[urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test]
21+
Resources:
22+
2 unchanged
23+
`,
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
crosstests.testOutput{
2+
initialValue: cty.Value{
3+
ty: cty.Type{typeImpl: cty.typeObject{
4+
AttrTypes: map[string]cty.Type{},
5+
}},
6+
v: map[string]interface{}{},
7+
},
8+
changeValue: cty.Value{
9+
ty: cty.Type{typeImpl: cty.typeObject{AttrTypes: map[string]cty.Type{
10+
"other": {typeImpl: cty.primitiveType{
11+
Kind: cty.primitiveTypeKind(83),
12+
}},
13+
}}},
14+
v: map[string]interface{}{"other": "other_value"},
15+
},
16+
tfOut: `
17+
Terraform used the selected providers to generate the following execution
18+
plan. Resource actions are indicated with the following symbols:
19+
+/- create replacement and then destroy
20+
21+
Terraform will perform the following actions:
22+
23+
# crossprovider_test_res.example must be replaced
24+
+/- resource "crossprovider_test_res" "example" {
25+
~ computed = "computed_value" -> (known after apply)
26+
~ id = "r1" -> (known after apply)
27+
+ other = "other_value" # forces replacement
28+
}
29+
30+
Plan: 1 to add, 0 to change, 1 to destroy.
31+
32+
`,
33+
pulumiOut: `Previewing update (test):
34+
pulumi:pulumi:Stack: (same)
35+
[urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test]
36+
+-crossprovider:index/testRes:TestRes: (replace)
37+
[id=r1]
38+
[urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example]
39+
+ other: "other_value"
40+
Resources:
41+
+-1 to replace
42+
1 unchanged
43+
`,
44+
detailedDiff: map[string]interface{}{"other": map[string]interface{}{"kind": "ADD_REPLACE"}},
45+
}

0 commit comments

Comments
 (0)