Skip to content

Commit 66d465a

Browse files
authored
framework: Add semantic equality tests for set bug fix (#297)
* new tests (showing the bug w/ test failure) * show test failures * bump with new framework fix * update dep * update go module
1 parent 1d1bcb9 commit 66d465a

File tree

8 files changed

+528
-0
lines changed

8 files changed

+528
-0
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ require (
66
github.com/hashicorp/go-memdb v1.3.4
77
github.com/hashicorp/terraform-json v0.24.0
88
github.com/hashicorp/terraform-plugin-framework v1.14.0
9+
github.com/hashicorp/terraform-plugin-framework-nettypes v0.2.0
910
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0
1011
github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0
12+
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0
1113
github.com/hashicorp/terraform-plugin-go v0.26.0
1214
github.com/hashicorp/terraform-plugin-mux v0.18.0
1315
github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,14 @@ github.com/hashicorp/terraform-json v0.24.0 h1:rUiyF+x1kYawXeRth6fKFm/MdfBS6+lW4
8989
github.com/hashicorp/terraform-json v0.24.0/go.mod h1:Nfj5ubo9xbu9uiAoZVBsNOjvNKB66Oyrvtit74kC7ow=
9090
github.com/hashicorp/terraform-plugin-framework v1.14.0 h1:lsmTJqBlZ4GUabnDxj8Lsa5bmbuUKiUO3Zm9iIKSDf0=
9191
github.com/hashicorp/terraform-plugin-framework v1.14.0/go.mod h1:xNUKmvTs6ldbwTuId5euAtg37dTxuyj3LHS3uj7BHQ4=
92+
github.com/hashicorp/terraform-plugin-framework-nettypes v0.2.0 h1:Zap24rkky7SvNGGNYHMKFhAriP6+6riI21BMYOYgLRE=
93+
github.com/hashicorp/terraform-plugin-framework-nettypes v0.2.0/go.mod h1:CYPq+I5bWsmI8021VJY85hAyOeiEEQpdGW+NapdQn7A=
9294
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0 h1:I/N0g/eLZ1ZkLZXUQ0oRSXa8YG/EF0CEuQP1wXdrzKw=
9395
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0/go.mod h1:t339KhmxnaF4SzdpxmqW8HnQBHVGYazwtfxU0qCs4eE=
9496
github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0 h1:v3DapR8gsp3EM8fKMh6up9cJUFQ2iRaFsYLP8UJnCco=
9597
github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0/go.mod h1:c3PnGE9pHBDfdEVG9t1S1C9ia5LW+gkFR0CygXlM8ak=
98+
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0 h1:O9QqGoYDzQT7lwTXUsZEtgabeWW96zUBh47Smn2lkFA=
99+
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0/go.mod h1:Bh89/hNmqsEWug4/XWKYBwtnw3tbz5BAy1L1OgvbIaY=
96100
github.com/hashicorp/terraform-plugin-go v0.26.0 h1:cuIzCv4qwigug3OS7iKhpGAbZTiypAfFQmw8aE65O2M=
97101
github.com/hashicorp/terraform-plugin-go v0.26.0/go.mod h1:+CXjuLDiFgqR+GcrM5a2E2Kal5t5q2jb0E3D57tTdNY=
98102
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=

internal/framework5provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ func (p *testProvider) Resources(_ context.Context) []func() resource.Resource {
9191
NewTFSDKReflectionResource,
9292
NewMoveStateResource,
9393
NewSetNestedBlockWithDefaultsResource,
94+
NewSetSemanticEqualityResource,
9495
}
9596
}
9697

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package framework
5+
6+
import (
7+
"context"
8+
9+
"github.com/hashicorp/terraform-plugin-framework-nettypes/iptypes"
10+
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
11+
"github.com/hashicorp/terraform-plugin-framework/attr"
12+
"github.com/hashicorp/terraform-plugin-framework/resource"
13+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
14+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
15+
"github.com/hashicorp/terraform-plugin-framework/types"
16+
)
17+
18+
var _ resource.Resource = SetSemanticEqualityResource{}
19+
20+
func NewSetSemanticEqualityResource() resource.Resource {
21+
return &SetSemanticEqualityResource{}
22+
}
23+
24+
// This resource tests that semantic equality for elements inside of a set are correctly executed
25+
// Original bug: https://github.com/hashicorp/terraform-plugin-framework/issues/1061
26+
type SetSemanticEqualityResource struct{}
27+
28+
func (r SetSemanticEqualityResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
29+
resp.TypeName = req.ProviderTypeName + "_set_semantic_equality"
30+
}
31+
32+
func (r SetSemanticEqualityResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
33+
resp.Schema = schema.Schema{
34+
Attributes: map[string]schema.Attribute{
35+
"set_of_ipv6": schema.SetAttribute{
36+
ElementType: iptypes.IPv6AddressType{},
37+
Required: true,
38+
},
39+
},
40+
Blocks: map[string]schema.Block{
41+
"set_nested_block": schema.SetNestedBlock{
42+
Validators: []validator.Set{
43+
setvalidator.IsRequired(),
44+
},
45+
NestedObject: schema.NestedBlockObject{
46+
Attributes: map[string]schema.Attribute{
47+
"ipv6": schema.StringAttribute{
48+
CustomType: iptypes.IPv6AddressType{},
49+
Required: true,
50+
},
51+
},
52+
},
53+
},
54+
},
55+
}
56+
}
57+
58+
func (r SetSemanticEqualityResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
59+
var data SetSemanticEqualityResourceModel
60+
61+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
62+
63+
if resp.Diagnostics.HasError() {
64+
return
65+
}
66+
67+
// Simulate remote API returning semantically equivalent IPv6 addresses
68+
data.shiftAndShorten()
69+
70+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
71+
}
72+
73+
func (r SetSemanticEqualityResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
74+
var data SetSemanticEqualityResourceModel
75+
76+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
77+
78+
if resp.Diagnostics.HasError() {
79+
return
80+
}
81+
82+
// Simulate remote API returning semantically equivalent IPv6 addresses
83+
data.shiftAndShorten()
84+
85+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
86+
}
87+
88+
func (r SetSemanticEqualityResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
89+
var data SetSemanticEqualityResourceModel
90+
91+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
92+
93+
if resp.Diagnostics.HasError() {
94+
return
95+
}
96+
97+
// Simulate remote API returning semantically equivalent IPv6 addresses
98+
data.shiftAndShorten()
99+
100+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
101+
}
102+
103+
func (r SetSemanticEqualityResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
104+
}
105+
106+
type SetSemanticEqualityResourceModel struct {
107+
SetOfIPv6 types.Set `tfsdk:"set_of_ipv6"`
108+
SetNestedBlock types.Set `tfsdk:"set_nested_block"`
109+
}
110+
111+
var setObjectWithIPv6 = types.ObjectType{
112+
AttrTypes: map[string]attr.Type{
113+
"ipv6": iptypes.IPv6AddressType{},
114+
},
115+
}
116+
117+
// Shifts + switches data to shortened IPv6 addresses, but is semantically equal to test config
118+
func (m *SetSemanticEqualityResourceModel) shiftAndShorten() {
119+
m.SetOfIPv6 = types.SetValueMust(iptypes.IPv6AddressType{}, []attr.Value{
120+
iptypes.NewIPv6AddressValue("2001:DB8::8:800:200C:417A"),
121+
iptypes.NewIPv6AddressValue("::FFFF:192.168.255.255"),
122+
iptypes.NewIPv6AddressValue("::"),
123+
iptypes.NewIPv6AddressValue("::101"),
124+
})
125+
126+
m.SetNestedBlock = types.SetValueMust(setObjectWithIPv6, []attr.Value{
127+
types.ObjectValueMust(setObjectWithIPv6.AttributeTypes(), map[string]attr.Value{
128+
"ipv6": iptypes.NewIPv6AddressValue("::FFFF:192.168.255.255"),
129+
}),
130+
types.ObjectValueMust(setObjectWithIPv6.AttributeTypes(), map[string]attr.Value{
131+
"ipv6": iptypes.NewIPv6AddressValue("FF01::"),
132+
}),
133+
types.ObjectValueMust(setObjectWithIPv6.AttributeTypes(), map[string]attr.Value{
134+
"ipv6": iptypes.NewIPv6AddressValue("2001:DB8::8:800:200C:417A"),
135+
}),
136+
})
137+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package framework
5+
6+
import (
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-framework/providerserver"
10+
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
11+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
12+
"github.com/hashicorp/terraform-plugin-testing/plancheck"
13+
)
14+
15+
// This resource tests that semantic equality for elements inside of a set are correctly executed
16+
// Original bug: https://github.com/hashicorp/terraform-plugin-framework/issues/1061
17+
func TestSetSemanticEqualityResource(t *testing.T) {
18+
resource.UnitTest(t, resource.TestCase{
19+
ProtoV5ProviderFactories: map[string]func() (tfprotov5.ProviderServer, error){
20+
"framework": providerserver.NewProtocol5WithError(New()),
21+
},
22+
Steps: []resource.TestStep{
23+
{
24+
// The resource Create/Read will return semantically equal data that will cause a diff if returned to Terraform.
25+
// The semantic equality logic in iptypes.IPv6Address allows this configuration to successfully apply.
26+
Config: `resource "framework_set_semantic_equality" "test" {
27+
set_of_ipv6 = [
28+
"0:0:0:0:0:0:0:0",
29+
"2001:0DB8:0000:0000:0008:0800:200C:417A",
30+
"0:0:0:0:0:0:0:101",
31+
"0:0:0:0:0:FFFF:192.168.255.255",
32+
]
33+
34+
set_nested_block {
35+
ipv6 = "FF01:0:0:0:0:0:0:0"
36+
}
37+
set_nested_block {
38+
ipv6 = "2001:db8::8:800:200c:417a"
39+
}
40+
set_nested_block {
41+
ipv6 = "0:0:0:0:0:FFFF:192.168.255.255"
42+
}
43+
}`,
44+
},
45+
{
46+
// Re-ordering the set doesn't produce a diff with semantically equal hardcoded data
47+
Config: `resource "framework_set_semantic_equality" "test" {
48+
set_of_ipv6 = [
49+
"0:0:0:0:0:FFFF:192.168.255.255",
50+
"0:0:0:0:0:0:0:0",
51+
"2001:0DB8:0000:0000:0008:0800:200C:417A",
52+
"0:0:0:0:0:0:0:101",
53+
]
54+
55+
set_nested_block {
56+
ipv6 = "0:0:0:0:0:FFFF:192.168.255.255"
57+
}
58+
set_nested_block {
59+
ipv6 = "FF01:0:0:0:0:0:0:0"
60+
}
61+
set_nested_block {
62+
ipv6 = "2001:db8::8:800:200c:417a"
63+
}
64+
}`,
65+
ConfigPlanChecks: resource.ConfigPlanChecks{
66+
PreApply: []plancheck.PlanCheck{
67+
plancheck.ExpectResourceAction("framework_set_semantic_equality.test", plancheck.ResourceActionNoop),
68+
},
69+
},
70+
},
71+
{
72+
// User config changes will still produce a diff, but the apply will be successful with semantically equal data
73+
Config: `resource "framework_set_semantic_equality" "test" {
74+
set_of_ipv6 = [
75+
"0:0:0:0:0:FFFF:192.168.255.255",
76+
"::", # <----------- This update will cause a diff
77+
"2001:0DB8:0000:0000:0008:0800:200C:417A",
78+
"0:0:0:0:0:0:0:101",
79+
]
80+
81+
set_nested_block {
82+
ipv6 = "0:0:0:0:0:FFFF:192.168.255.255"
83+
}
84+
set_nested_block {
85+
ipv6 = "FF01::" # <----------- This update will cause a diff
86+
}
87+
set_nested_block {
88+
ipv6 = "2001:db8::8:800:200c:417a"
89+
}
90+
}`,
91+
ConfigPlanChecks: resource.ConfigPlanChecks{
92+
PreApply: []plancheck.PlanCheck{
93+
plancheck.ExpectResourceAction("framework_set_semantic_equality.test", plancheck.ResourceActionUpdate),
94+
},
95+
},
96+
},
97+
},
98+
})
99+
}

internal/framework6provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ func (p *testProvider) Resources(_ context.Context) []func() resource.Resource {
9191
NewMoveStateResource,
9292
NewSetNestedBlockWithDefaultsResource,
9393
NewSetNestedAttributeWithDefaultsResource,
94+
NewSetSemanticEqualityResource,
9495
}
9596
}
9697

0 commit comments

Comments
 (0)