Skip to content

Commit e5b632d

Browse files
committed
adjust server implementation to always keep prior state
1 parent bf1f1f7 commit e5b632d

File tree

3 files changed

+72
-41
lines changed

3 files changed

+72
-41
lines changed

echoprovider/server.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,10 @@ func (e *echoProviderServer) PlanResourceChange(ctx context.Context, req *tfprot
265265
}, nil
266266
}
267267

268-
// If we're updating and the provider config data hasn't changed, return prior state to indicate no diff
269-
if priorState.Equal(tftypes.NewValue(echoTestSchema.ValueType(), map[string]tftypes.Value{"data": e.providerConfigData})) {
268+
// If the echo resource has prior state, don't plan anything new as it's valid for the ephemeral data to change
269+
// between operations and we don't want to produce constant diffs. This resource is only for testing data, which a
270+
// single plan/apply should suffice.
271+
if !priorState.IsNull() {
270272
return &tfprotov6.PlanResourceChangeResponse{
271273
PlannedState: req.PriorState,
272274
}, nil

echoprovider/server_test.go

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -49,44 +49,31 @@ func TestEchoProviderServer_primitive(t *testing.T) {
4949
provider "echo" {
5050
data = 200
5151
}
52-
resource "echo" "test_one" {}
52+
resource "echo" "test_two" {}
5353
`,
5454
ConfigPlanChecks: resource.ConfigPlanChecks{
5555
PreApply: []plancheck.PlanCheck{
56-
plancheck.ExpectUnknownValue("echo.test_one", tfjsonpath.New("data")),
56+
plancheck.ExpectUnknownValue("echo.test_two", tfjsonpath.New("data")),
5757
},
5858
},
5959
ConfigStateChecks: []statecheck.StateCheck{
60-
statecheck.ExpectKnownValue("echo.test_one", tfjsonpath.New("data"), knownvalue.Int64Exact(200)),
60+
statecheck.ExpectKnownValue("echo.test_two", tfjsonpath.New("data"), knownvalue.Int64Exact(200)),
6161
},
6262
},
6363
{
6464
Config: `
6565
provider "echo" {
6666
data = true
6767
}
68-
resource "echo" "test_one" {}
68+
resource "echo" "test_three" {}
6969
`,
7070
ConfigPlanChecks: resource.ConfigPlanChecks{
7171
PreApply: []plancheck.PlanCheck{
72-
plancheck.ExpectUnknownValue("echo.test_one", tfjsonpath.New("data")),
72+
plancheck.ExpectUnknownValue("echo.test_three", tfjsonpath.New("data")),
7373
},
7474
},
7575
ConfigStateChecks: []statecheck.StateCheck{
76-
statecheck.ExpectKnownValue("echo.test_one", tfjsonpath.New("data"), knownvalue.Bool(true)),
77-
},
78-
},
79-
{
80-
Config: `
81-
provider "echo" {
82-
data = true
83-
}
84-
resource "echo" "test_one" {}
85-
`,
86-
ConfigPlanChecks: resource.ConfigPlanChecks{
87-
PreApply: []plancheck.PlanCheck{
88-
plancheck.ExpectEmptyPlan(),
89-
},
76+
statecheck.ExpectKnownValue("echo.test_three", tfjsonpath.New("data"), knownvalue.Bool(true)),
9077
},
9178
},
9279
},
@@ -130,15 +117,15 @@ func TestEchoProviderServer_complex(t *testing.T) {
130117
provider "echo" {
131118
data = tomap({"key1": "hello", "key2": "world"})
132119
}
133-
resource "echo" "test_one" {}
120+
resource "echo" "test_two" {}
134121
`,
135122
ConfigPlanChecks: resource.ConfigPlanChecks{
136123
PreApply: []plancheck.PlanCheck{
137-
plancheck.ExpectUnknownValue("echo.test_one", tfjsonpath.New("data")),
124+
plancheck.ExpectUnknownValue("echo.test_two", tfjsonpath.New("data")),
138125
},
139126
},
140127
ConfigStateChecks: []statecheck.StateCheck{
141-
statecheck.ExpectKnownValue("echo.test_one", tfjsonpath.New("data"),
128+
statecheck.ExpectKnownValue("echo.test_two", tfjsonpath.New("data"),
142129
knownvalue.MapExact(map[string]knownvalue.Check{
143130
"key1": knownvalue.StringExact("hello"),
144131
"key2": knownvalue.StringExact("world"),
@@ -151,7 +138,7 @@ func TestEchoProviderServer_complex(t *testing.T) {
151138
provider "echo" {
152139
data = tomap({"key1": "hello", "key2": "world"})
153140
}
154-
resource "echo" "test_one" {}
141+
resource "echo" "test_two" {}
155142
`,
156143
ConfigPlanChecks: resource.ConfigPlanChecks{
157144
PreApply: []plancheck.PlanCheck{
@@ -188,19 +175,6 @@ func TestEchoProviderServer_null(t *testing.T) {
188175
statecheck.ExpectKnownValue("echo.test_one", tfjsonpath.New("data"), knownvalue.Null()),
189176
},
190177
},
191-
{
192-
Config: `
193-
provider "echo" {
194-
data = null
195-
}
196-
resource "echo" "test_one" {}
197-
`,
198-
ConfigPlanChecks: resource.ConfigPlanChecks{
199-
PreApply: []plancheck.PlanCheck{
200-
plancheck.ExpectEmptyPlan(),
201-
},
202-
},
203-
},
204178
},
205179
})
206180
}

website/docs/plugin/testing/acceptance-tests/ephemeral-resources.mdx

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ After including both providers, our test step `Config` references the ephemeral
135135
```go
136136
func TestExampleCloudSecret(t *testing.T) {
137137
resource.UnitTest(t, resource.TestCase{
138-
// .. test case setup from previous step
138+
// .. test case setup
139139

140140
Steps: []resource.TestStep{
141141
{
@@ -161,7 +161,7 @@ The `echo.test_krb` managed resource has a single computed `data` attribute, whi
161161
```go
162162
func TestExampleCloudSecret(t *testing.T) {
163163
resource.UnitTest(t, resource.TestCase{
164-
// .. test case setup from previous step
164+
// .. test case setup
165165

166166
Steps: []resource.TestStep{
167167
{
@@ -194,7 +194,7 @@ You can also reference the entire ephemeral resource instance for assertions, ra
194194
```go
195195
func TestExampleCloudSecret(t *testing.T) {
196196
resource.UnitTest(t, resource.TestCase{
197-
// .. test case setup from previous step
197+
// .. test case setup
198198

199199
Steps: []resource.TestStep{
200200
{
@@ -217,3 +217,58 @@ func TestExampleCloudSecret(t *testing.T) {
217217
})
218218
}
219219
```
220+
221+
### Caveats with `echo` provider
222+
223+
Since data produced by an ephemeral resource is allowed to change between plan/apply operations, the `echo` resource has special handling to allow this data to be used in the `terraform-plugin-testing` Go module without producing confusing error messages:
224+
225+
* During plan, if the `echo` resource is being created, the `data` attribute will always be marked as unknown.
226+
* During plan, if the `echo` resource already exists and is not being destroyed, prior state will always be fully preserved regardless of changes to the provider configuration. This essentially means an instance of the `echo` resource is immutable.
227+
* During refresh, the prior state of the `echo` resource is always returned, regardless of changes to the provider configuration.
228+
229+
Due to this special handling, if multiple test steps are required for testing data, provider developers should create new instances of `echo` for each new test step, for example:
230+
231+
```go
232+
func TestExampleCloudSecret(t *testing.T) {
233+
resource.UnitTest(t, resource.TestCase{
234+
// .. test case setup
235+
236+
Steps: []resource.TestStep{
237+
{
238+
Config: `
239+
ephemeral "examplecloud_secret" "krb" {
240+
name = "user_one"
241+
}
242+
243+
provider "echo" {
244+
data = ephemeral.examplecloud_secret.krb
245+
}
246+
247+
# First test object -> 1
248+
resource "echo" "test_krb_one" {}
249+
`,
250+
ConfigStateChecks: []statecheck.StateCheck{
251+
statecheck.ExpectKnownValue("echo.test_krb_one", tfjsonpath.New("data").AtMapKey("name"), knownvalue.StringExact("user_one")),
252+
},
253+
},
254+
{
255+
Config: `
256+
ephemeral "examplecloud_secret" "krb" {
257+
name = "user_two"
258+
}
259+
260+
provider "echo" {
261+
data = ephemeral.examplecloud_secret.krb
262+
}
263+
264+
# New test object -> 2
265+
resource "echo" "test_krb_two" {}
266+
`,
267+
ConfigStateChecks: []statecheck.StateCheck{
268+
statecheck.ExpectKnownValue("echo.test_krb_two", tfjsonpath.New("data").AtMapKey("name"), knownvalue.StringExact("user_two")),
269+
},
270+
},
271+
},
272+
})
273+
}
274+
```

0 commit comments

Comments
 (0)