Skip to content

Commit 37fbad1

Browse files
PF cross-tests for computed in sets
1 parent 27e7ff1 commit 37fbad1

File tree

241 files changed

+10812
-121
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

241 files changed

+10812
-121
lines changed

pkg/pf/tests/diff_set_test.go

Lines changed: 170 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@ import (
44
"context"
55
"testing"
66

7-
"github.com/hashicorp/terraform-plugin-framework/path"
87
"github.com/hashicorp/terraform-plugin-framework/resource"
98
rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
109
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
1110
"github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier"
1211
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
13-
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
1412
"github.com/hashicorp/terraform-plugin-framework/types"
15-
"github.com/hashicorp/terraform-plugin-go/tftypes"
1613
"github.com/hexops/autogold/v2"
1714
"github.com/zclconf/go-cty/cty"
1815

@@ -154,140 +151,182 @@ func TestDetailedDiffSet(t *testing.T) {
154151
},
155152
})
156153

154+
computedCreateFunc := func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
155+
type Nested struct {
156+
Nested types.String `tfsdk:"nested"`
157+
Computed types.String `tfsdk:"computed"`
158+
}
159+
160+
type ObjectModel struct {
161+
ID types.String `tfsdk:"id"`
162+
Keys []Nested `tfsdk:"key"`
163+
}
164+
165+
reqObj := ObjectModel{}
166+
diags := req.Plan.Get(ctx, &reqObj)
167+
contract.Assertf(diags.ErrorsCount() == 0, "failed to get attribute: %v", diags)
168+
169+
respObj := ObjectModel{
170+
ID: types.StringValue("test-id"),
171+
Keys: make([]Nested, len(reqObj.Keys)),
172+
}
173+
174+
for i, key := range reqObj.Keys {
175+
newKey := Nested{}
176+
if key.Computed.IsNull() || key.Computed.IsUnknown() {
177+
nestedVal := ""
178+
if !key.Nested.IsNull() && !key.Nested.IsUnknown() {
179+
nestedVal = key.Nested.ValueString()
180+
}
181+
computedVal := "computed-" + nestedVal
182+
newKey.Nested = types.StringValue(nestedVal)
183+
newKey.Computed = types.StringValue(computedVal)
184+
} else {
185+
newKey.Nested = key.Nested
186+
newKey.Computed = key.Computed
187+
}
188+
respObj.Keys[i] = newKey
189+
}
190+
191+
diags = resp.State.Set(ctx, &respObj)
192+
contract.Assertf(diags.ErrorsCount() == 0, "failed to set attribute: %v", diags)
193+
}
194+
195+
computedUpdateFunc := func(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
196+
createResp := resource.CreateResponse{
197+
State: resp.State,
198+
Diagnostics: resp.Diagnostics,
199+
}
200+
computedCreateFunc(ctx, resource.CreateRequest{
201+
Plan: req.Plan,
202+
Config: req.Config,
203+
ProviderMeta: req.ProviderMeta,
204+
}, &createResp)
205+
206+
resp.State = createResp.State
207+
resp.Diagnostics = createResp.Diagnostics
208+
}
209+
157210
blockSchemaWithComputed := pb.NewResource(pb.NewResourceArgs{
158211
ResourceSchema: rschema.Schema{
159212
Blocks: map[string]rschema.Block{
160213
"key": rschema.SetNestedBlock{
161214
NestedObject: rschema.NestedBlockObject{
162215
Attributes: map[string]rschema.Attribute{
163-
"nested": rschema.StringAttribute{Optional: true},
164-
"computed": rschema.StringAttribute{Computed: true, Optional: true},
216+
"nested": rschema.StringAttribute{Optional: true},
217+
"computed": rschema.StringAttribute{
218+
Computed: true,
219+
Optional: true,
220+
PlanModifiers: []planmodifier.String{
221+
stringplanmodifier.UseStateForUnknown(),
222+
},
223+
},
165224
},
166225
},
167226
},
168227
},
169228
},
170-
CreateFunc: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
171-
resp.State = tfsdk.State(req.Plan)
172-
diags := resp.State.SetAttribute(ctx, path.Root("id"), "test-id")
173-
contract.Assertf(diags.ErrorsCount() == 0, "failed to set attribute: %v", diags)
174-
175-
keys := make([]tftypes.Value, 0)
176-
diags = resp.State.GetAttribute(ctx, path.Root("key"), &keys)
177-
contract.Assertf(diags.ErrorsCount() == 0, "failed to get attribute: %v", diags)
178-
179-
resultKeys := make([]tftypes.Value, len(keys))
180-
for i, key := range keys {
181-
keyMap := make(map[string]tftypes.Value)
182-
if key.As(&keyMap) == nil {
183-
nestedVal := ""
184-
if val, ok := keyMap["nested"]; ok {
185-
nestedVal = val.String()
186-
}
187-
computedVal := ""
188-
if _, ok := keyMap["computed"]; !ok {
189-
computedVal = "computed-" + nestedVal
190-
} else {
191-
computedVal = keyMap["computed"].String()
192-
}
193-
keyMap["computed"] = tftypes.NewValue(tftypes.String, computedVal)
194-
}
195-
resultKeys[i] = tftypes.NewValue(tftypes.Object{
196-
AttributeTypes: map[string]tftypes.Type{
197-
"nested": tftypes.String,
198-
"computed": tftypes.String,
229+
CreateFunc: computedCreateFunc,
230+
UpdateFunc: computedUpdateFunc,
231+
})
232+
233+
blockSchemaWithComputedNoStateForUnknown := pb.NewResource(pb.NewResourceArgs{
234+
ResourceSchema: rschema.Schema{
235+
Blocks: map[string]rschema.Block{
236+
"key": rschema.SetNestedBlock{
237+
NestedObject: rschema.NestedBlockObject{
238+
Attributes: map[string]rschema.Attribute{
239+
"nested": rschema.StringAttribute{Optional: true},
240+
"computed": rschema.StringAttribute{
241+
Computed: true,
242+
Optional: true,
243+
},
244+
},
199245
},
200-
}, keyMap)
201-
}
246+
},
247+
},
248+
},
249+
CreateFunc: computedCreateFunc,
250+
UpdateFunc: computedUpdateFunc,
251+
})
252+
253+
blockSchemaWithComputedReplace := pb.NewResource(pb.NewResourceArgs{
254+
ResourceSchema: rschema.Schema{
255+
Blocks: map[string]rschema.Block{
256+
"key": rschema.SetNestedBlock{
257+
NestedObject: rschema.NestedBlockObject{
258+
Attributes: map[string]rschema.Attribute{
259+
"nested": rschema.StringAttribute{Optional: true},
260+
"computed": rschema.StringAttribute{
261+
Computed: true,
262+
Optional: true,
263+
PlanModifiers: []planmodifier.String{
264+
stringplanmodifier.UseStateForUnknown(),
265+
},
266+
},
267+
},
268+
},
269+
PlanModifiers: []planmodifier.Set{
270+
setplanmodifier.RequiresReplace(),
271+
},
272+
},
273+
},
274+
},
275+
CreateFunc: computedCreateFunc,
276+
UpdateFunc: computedUpdateFunc,
277+
})
202278

203-
diags = resp.State.SetAttribute(ctx, path.Root("key"), resultKeys)
204-
contract.Assertf(diags.ErrorsCount() == 0, "failed to set attribute: %v", diags)
279+
blockSchemaWithComputedNestedReplace := pb.NewResource(pb.NewResourceArgs{
280+
ResourceSchema: rschema.Schema{
281+
Blocks: map[string]rschema.Block{
282+
"key": rschema.SetNestedBlock{
283+
NestedObject: rschema.NestedBlockObject{
284+
Attributes: map[string]rschema.Attribute{
285+
"nested": rschema.StringAttribute{
286+
Optional: true,
287+
PlanModifiers: []planmodifier.String{
288+
stringplanmodifier.RequiresReplace(),
289+
},
290+
},
291+
"computed": rschema.StringAttribute{
292+
Computed: true,
293+
Optional: true,
294+
PlanModifiers: []planmodifier.String{
295+
stringplanmodifier.UseStateForUnknown(),
296+
},
297+
},
298+
},
299+
},
300+
},
301+
},
205302
},
303+
CreateFunc: computedCreateFunc,
304+
UpdateFunc: computedUpdateFunc,
206305
})
207306

208-
// blockSchemaWithComputedAndStateForUnknown := pb.NewResource(pb.NewResourceArgs{
209-
// ResourceSchema: rschema.Schema{
210-
// Blocks: map[string]rschema.Block{
211-
// "key": rschema.SetNestedBlock{
212-
// NestedObject: rschema.NestedBlockObject{
213-
// Attributes: map[string]rschema.Attribute{
214-
// "nested": rschema.StringAttribute{Optional: true},
215-
// "computed": rschema.StringAttribute{
216-
// Computed: true,
217-
// Optional: true,
218-
// PlanModifiers: []planmodifier.String{
219-
// stringplanmodifier.UseStateForUnknown(),
220-
// },
221-
// },
222-
// },
223-
// },
224-
// },
225-
// },
226-
// },
227-
// })
228-
229-
// blockSchemaWithComputedReplace := pb.NewResource(pb.NewResourceArgs{
230-
// ResourceSchema: rschema.Schema{
231-
// Blocks: map[string]rschema.Block{
232-
// "key": rschema.SetNestedBlock{
233-
// NestedObject: rschema.NestedBlockObject{
234-
// Attributes: map[string]rschema.Attribute{
235-
// "nested": rschema.StringAttribute{Optional: true},
236-
// "computed": rschema.StringAttribute{Computed: true, Optional: true},
237-
// },
238-
// },
239-
// PlanModifiers: []planmodifier.Set{
240-
// setplanmodifier.RequiresReplace(),
241-
// },
242-
// },
243-
// },
244-
// },
245-
// })
246-
247-
// blockSchemaWithComputedNestedReplace := pb.NewResource(pb.NewResourceArgs{
248-
// ResourceSchema: rschema.Schema{
249-
// Blocks: map[string]rschema.Block{
250-
// "key": rschema.SetNestedBlock{
251-
// NestedObject: rschema.NestedBlockObject{
252-
// Attributes: map[string]rschema.Attribute{
253-
// "nested": rschema.StringAttribute{
254-
// Optional: true,
255-
// PlanModifiers: []planmodifier.String{
256-
// stringplanmodifier.RequiresReplace(),
257-
// },
258-
// },
259-
// "computed": rschema.StringAttribute{
260-
// Computed: true,
261-
// Optional: true,
262-
// },
263-
// },
264-
// },
265-
// },
266-
// },
267-
// },
268-
// })
269-
270-
// blockSchemaWithComputedAndPlanModifierRequiresReplace := pb.NewResource(pb.NewResourceArgs{
271-
// ResourceSchema: rschema.Schema{
272-
// Blocks: map[string]rschema.Block{
273-
// "key": rschema.SetNestedBlock{
274-
// NestedObject: rschema.NestedBlockObject{
275-
// Attributes: map[string]rschema.Attribute{
276-
// "nested": rschema.StringAttribute{Optional: true},
277-
// "computed": rschema.StringAttribute{
278-
// Computed: true,
279-
// Optional: true,
280-
// PlanModifiers: []planmodifier.String{
281-
// stringplanmodifier.UseStateForUnknown(),
282-
// stringplanmodifier.RequiresReplace(),
283-
// },
284-
// },
285-
// },
286-
// },
287-
// },
288-
// },
289-
// },
290-
// })
307+
blockSchemaWithComputedComputedRequiresReplace := pb.NewResource(pb.NewResourceArgs{
308+
ResourceSchema: rschema.Schema{
309+
Blocks: map[string]rschema.Block{
310+
"key": rschema.SetNestedBlock{
311+
NestedObject: rschema.NestedBlockObject{
312+
Attributes: map[string]rschema.Attribute{
313+
"nested": rschema.StringAttribute{Optional: true},
314+
"computed": rschema.StringAttribute{
315+
Computed: true,
316+
Optional: true,
317+
PlanModifiers: []planmodifier.String{
318+
stringplanmodifier.UseStateForUnknown(),
319+
stringplanmodifier.RequiresReplace(),
320+
},
321+
},
322+
},
323+
},
324+
},
325+
},
326+
},
327+
CreateFunc: computedCreateFunc,
328+
UpdateFunc: computedUpdateFunc,
329+
})
291330

292331
attrList := func(arr *[]string) cty.Value {
293332
if arr == nil {
@@ -354,8 +393,18 @@ func TestDetailedDiffSet(t *testing.T) {
354393
{"block requires replace", blockReplaceSchema, nestedAttrList},
355394
{"block nested requires replace", blockNestedReplaceSchema, nestedAttrList},
356395

396+
// Computed, each state we test both the behaviour when the computed value is specified in the program and when it is not.
357397
{"block with computed no replace computed", blockSchemaWithComputed, nestedAttrList},
358398
{"block with computed no replace computed specified in program", blockSchemaWithComputed, nestedAttrListWithComputedSpecified},
399+
{"block with computed requires replace", blockSchemaWithComputedReplace, nestedAttrList},
400+
{"block with computed requires replace computed specified in program", blockSchemaWithComputedReplace, nestedAttrListWithComputedSpecified},
401+
{"block with computed and nested requires replace", blockSchemaWithComputedNestedReplace, nestedAttrList},
402+
{"block with computed and nested requires replace computed specified in program", blockSchemaWithComputedNestedReplace, nestedAttrListWithComputedSpecified},
403+
{"block with computed and computed requires replace", blockSchemaWithComputedComputedRequiresReplace, nestedAttrList},
404+
{"block with computed and computed requires replace computed specified in program", blockSchemaWithComputedComputedRequiresReplace, nestedAttrListWithComputedSpecified},
405+
// Rarely used, but supported
406+
{"block with computed no state for unknown", blockSchemaWithComputedNoStateForUnknown, nestedAttrList},
407+
{"block with computed no state for unknown computed specified in program", blockSchemaWithComputedNoStateForUnknown, nestedAttrListWithComputedSpecified},
359408
}
360409

361410
scenarios := []struct {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
tfbridgetests.testOutput{
2+
initialValue: &[]string{},
3+
changeValue: &[]string{"value"},
4+
tfOut: `
5+
Terraform used the selected providers to generate the following execution
6+
plan. Resource actions are indicated with the following symbols:
7+
+/- create replacement and then destroy
8+
9+
Terraform will perform the following actions:
10+
11+
# testprovider_test.res must be replaced
12+
+/- resource "testprovider_test" "res" {
13+
~ id = "test-id" -> (known after apply)
14+
15+
+ key { # forces replacement
16+
+ computed = (known after apply)
17+
+ nested = "value"
18+
}
19+
}
20+
21+
Plan: 1 to add, 0 to change, 1 to destroy.
22+
23+
`,
24+
pulumiOut: `Previewing update (test):
25+
pulumi:pulumi:Stack: (same)
26+
[urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test]
27+
+-testprovider:index/test:Test: (replace)
28+
[id=test-id]
29+
[urn=urn:pulumi:test::project::testprovider:index/test:Test::p]
30+
~ id : "test-id" => output<string>
31+
~ keys: [
32+
+ [0]: {
33+
+ computed: output<string>
34+
+ nested : "value"
35+
}
36+
]
37+
Resources:
38+
+-1 to replace
39+
1 unchanged
40+
`,
41+
}

0 commit comments

Comments
 (0)