Skip to content

Commit bee17ac

Browse files
Adding test coverage for changes to ignore data sources in imported state (#1077)
* Adding test coverage for changes in #1074 (#1066) * Skips data sources from both original state and imported state * Adding changelog entry (#1066) Co-authored-by: Graham Davison <[email protected]>
1 parent 443a5c7 commit bee17ac

File tree

3 files changed

+236
-43
lines changed

3 files changed

+236
-43
lines changed

Diff for: .changelog/1077.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
helper/resource: Fixed `TestStep` type `ImportStateVerify` field so that it only matches against resources following a change in behaviour in Terraform 1.3 that imports both resources and their dependent data sources
3+
```

Diff for: helper/resource/testing_new_import_state.go

+17-10
Original file line numberDiff line numberDiff line change
@@ -158,20 +158,27 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
158158
if step.ImportStateVerify {
159159
logging.HelperResourceTrace(ctx, "Using TestStep ImportStateVerify")
160160

161-
newResources := importState.RootModule().Resources
162-
oldResources := state.RootModule().Resources
161+
// Ensure that we do not match against data sources as they
162+
// cannot be imported and are not what we want to verify.
163+
// Mode is not present in ResourceState so we use the
164+
// stringified ResourceStateKey for comparison.
165+
newResources := make(map[string]*terraform.ResourceState)
166+
for k, v := range importState.RootModule().Resources {
167+
if !strings.HasPrefix(k, "data.") {
168+
newResources[k] = v
169+
}
170+
}
171+
oldResources := make(map[string]*terraform.ResourceState)
172+
for k, v := range state.RootModule().Resources {
173+
if !strings.HasPrefix(k, "data.") {
174+
oldResources[k] = v
175+
}
176+
}
163177

164178
for _, r := range newResources {
165179
// Find the existing resource
166180
var oldR *terraform.ResourceState
167-
for r2Key, r2 := range oldResources {
168-
// Ensure that we do not match against data sources as they
169-
// cannot be imported and are not what we want to verify.
170-
// Mode is not present in ResourceState so we use the
171-
// stringified ResourceStateKey for comparison.
172-
if strings.HasPrefix(r2Key, "data.") {
173-
continue
174-
}
181+
for _, r2 := range oldResources {
175182

176183
if r2.Primary != nil && r2.Primary.ID == r.Primary.ID && r2.Type == r.Type && r2.Provider == r.Provider {
177184
oldR = r2

Diff for: helper/resource/teststep_providers_test.go

+216-33
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,7 @@ func TestTest_TestStep_Taint(t *testing.T) {
10761076
}
10771077
}
10781078

1079+
//nolint:unparam
10791080
func extractResourceAttr(resourceName string, attributeName string, attributeValue *string) TestCheckFunc {
10801081
return func(s *terraform.State) error {
10811082
rs, ok := s.RootModule().Resources[resourceName]
@@ -1253,6 +1254,8 @@ func TestTest_TestStep_ProviderFactories_To_ExternalProviders(t *testing.T) {
12531254
}
12541255

12551256
func TestTest_TestStep_ProviderFactories_Import_Inline(t *testing.T) {
1257+
id := "none"
1258+
12561259
t.Parallel()
12571260

12581261
Test(t, TestCase{
@@ -1316,9 +1319,8 @@ func TestTest_TestStep_ProviderFactories_Import_Inline(t *testing.T) {
13161319
ImportStateId: "Z=:cbrJE?Ltg",
13171320
ImportStatePersist: true,
13181321
ImportStateCheck: composeImportStateCheck(
1319-
testCheckResourceAttrInstanceState("id", "none"),
1320-
testCheckResourceAttrInstanceState("result", "Z=:cbrJE?Ltg"),
1321-
testCheckResourceAttrInstanceState("length", "12"),
1322+
testCheckResourceAttrInstanceState(&id, "result", "Z=:cbrJE?Ltg"),
1323+
testCheckResourceAttrInstanceState(&id, "length", "12"),
13221324
),
13231325
},
13241326
},
@@ -1391,7 +1393,7 @@ func TestTest_TestStep_ProviderFactories_Import_Inline_WithPersistMatch(t *testi
13911393
ImportStateId: "Z=:cbrJE?Ltg",
13921394
ImportStatePersist: true,
13931395
ImportStateCheck: composeImportStateCheck(
1394-
testExtractResourceAttrInstanceState("result", &result1),
1396+
testExtractResourceAttrInstanceState("none", "result", &result1),
13951397
),
13961398
},
13971399
{
@@ -1484,6 +1486,8 @@ func TestTest_TestStep_ProviderFactories_Import_Inline_WithoutPersist(t *testing
14841486
}
14851487

14861488
func TestTest_TestStep_ProviderFactories_Import_External(t *testing.T) {
1489+
id := "none"
1490+
14871491
t.Parallel()
14881492

14891493
Test(t, TestCase{
@@ -1500,9 +1504,8 @@ func TestTest_TestStep_ProviderFactories_Import_External(t *testing.T) {
15001504
ImportStateId: "Z=:cbrJE?Ltg",
15011505
ImportStatePersist: true,
15021506
ImportStateCheck: composeImportStateCheck(
1503-
testCheckResourceAttrInstanceState("id", "none"),
1504-
testCheckResourceAttrInstanceState("result", "Z=:cbrJE?Ltg"),
1505-
testCheckResourceAttrInstanceState("length", "12"),
1507+
testCheckResourceAttrInstanceState(&id, "result", "Z=:cbrJE?Ltg"),
1508+
testCheckResourceAttrInstanceState(&id, "length", "12"),
15061509
),
15071510
},
15081511
},
@@ -1528,7 +1531,7 @@ func TestTest_TestStep_ProviderFactories_Import_External_WithPersistMatch(t *tes
15281531
ImportStateId: "Z=:cbrJE?Ltg",
15291532
ImportStatePersist: true,
15301533
ImportStateCheck: composeImportStateCheck(
1531-
testExtractResourceAttrInstanceState("result", &result1),
1534+
testExtractResourceAttrInstanceState("none", "result", &result1),
15321535
),
15331536
},
15341537
{
@@ -1561,7 +1564,7 @@ func TestTest_TestStep_ProviderFactories_Import_External_WithoutPersistNonMatch(
15611564
ImportStateId: "Z=:cbrJE?Ltg",
15621565
ImportStatePersist: false,
15631566
ImportStateCheck: composeImportStateCheck(
1564-
testExtractResourceAttrInstanceState("result", &result1),
1567+
testExtractResourceAttrInstanceState("none", "result", &result1),
15651568
),
15661569
},
15671570
{
@@ -1714,6 +1717,188 @@ func TestTest_TestStep_ProviderFactories_RefreshWithPlanModifier_Inline(t *testi
17141717
})
17151718
}
17161719

1720+
func TestTest_TestStep_ProviderFactories_Import_Inline_With_Data_Source(t *testing.T) {
1721+
var id string
1722+
1723+
t.Parallel()
1724+
1725+
Test(t, TestCase{
1726+
ProviderFactories: map[string]func() (*schema.Provider, error){
1727+
"http": func() (*schema.Provider, error) { //nolint:unparam // required signature
1728+
return &schema.Provider{
1729+
DataSourcesMap: map[string]*schema.Resource{
1730+
"http": {
1731+
ReadContext: func(ctx context.Context, d *schema.ResourceData, i interface{}) (diags diag.Diagnostics) {
1732+
url := d.Get("url").(string)
1733+
1734+
responseHeaders := map[string]string{
1735+
"headerOne": "one",
1736+
"headerTwo": "two",
1737+
"headerThree": "three",
1738+
"headerFour": "four",
1739+
}
1740+
if err := d.Set("response_headers", responseHeaders); err != nil {
1741+
return append(diags, diag.Errorf("Error setting HTTP response headers: %s", err)...)
1742+
}
1743+
1744+
d.SetId(url)
1745+
1746+
return diags
1747+
},
1748+
Schema: map[string]*schema.Schema{
1749+
"url": {
1750+
Type: schema.TypeString,
1751+
Required: true,
1752+
},
1753+
"response_headers": {
1754+
Type: schema.TypeMap,
1755+
Computed: true,
1756+
Elem: &schema.Schema{
1757+
Type: schema.TypeString,
1758+
},
1759+
},
1760+
},
1761+
},
1762+
},
1763+
}, nil
1764+
},
1765+
"random": func() (*schema.Provider, error) { //nolint:unparam // required signature
1766+
return &schema.Provider{
1767+
ResourcesMap: map[string]*schema.Resource{
1768+
"random_string": {
1769+
CreateContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics {
1770+
d.SetId("none")
1771+
err := d.Set("length", 4)
1772+
if err != nil {
1773+
panic(err)
1774+
}
1775+
err = d.Set("result", "none")
1776+
if err != nil {
1777+
panic(err)
1778+
}
1779+
return nil
1780+
},
1781+
DeleteContext: func(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
1782+
return nil
1783+
},
1784+
ReadContext: func(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
1785+
return nil
1786+
},
1787+
Schema: map[string]*schema.Schema{
1788+
"length": {
1789+
Required: true,
1790+
ForceNew: true,
1791+
Type: schema.TypeInt,
1792+
},
1793+
"result": {
1794+
Type: schema.TypeString,
1795+
Computed: true,
1796+
Sensitive: true,
1797+
},
1798+
1799+
"id": {
1800+
Computed: true,
1801+
Type: schema.TypeString,
1802+
},
1803+
},
1804+
Importer: &schema.ResourceImporter{
1805+
StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
1806+
val := d.Id()
1807+
1808+
d.SetId(val)
1809+
1810+
err := d.Set("result", val)
1811+
if err != nil {
1812+
panic(err)
1813+
}
1814+
1815+
err = d.Set("length", len(val))
1816+
if err != nil {
1817+
panic(err)
1818+
}
1819+
1820+
return []*schema.ResourceData{d}, nil
1821+
},
1822+
},
1823+
},
1824+
},
1825+
}, nil
1826+
},
1827+
},
1828+
Steps: []TestStep{
1829+
{
1830+
Config: `data "http" "example" {
1831+
url = "https://checkpoint-api.hashicorp.com/v1/check/terraform"
1832+
}
1833+
1834+
resource "random_string" "example" {
1835+
length = length(data.http.example.response_headers)
1836+
}`,
1837+
Check: extractResourceAttr("random_string.example", "id", &id),
1838+
},
1839+
{
1840+
Config: `data "http" "example" {
1841+
url = "https://checkpoint-api.hashicorp.com/v1/check/terraform"
1842+
}
1843+
1844+
resource "random_string" "example" {
1845+
length = length(data.http.example.response_headers)
1846+
}`,
1847+
ResourceName: "random_string.example",
1848+
ImportState: true,
1849+
ImportStateCheck: composeImportStateCheck(
1850+
testCheckResourceAttrInstanceState(&id, "length", "4"),
1851+
),
1852+
ImportStateVerify: true,
1853+
},
1854+
},
1855+
})
1856+
}
1857+
1858+
func TestTest_TestStep_ProviderFactories_Import_External_With_Data_Source(t *testing.T) {
1859+
var id string
1860+
1861+
t.Parallel()
1862+
1863+
Test(t, TestCase{
1864+
ExternalProviders: map[string]ExternalProvider{
1865+
"http": {
1866+
Source: "registry.terraform.io/hashicorp/http",
1867+
},
1868+
"random": {
1869+
Source: "registry.terraform.io/hashicorp/random",
1870+
},
1871+
},
1872+
Steps: []TestStep{
1873+
{
1874+
Config: `data "http" "example" {
1875+
url = "https://checkpoint-api.hashicorp.com/v1/check/terraform"
1876+
}
1877+
1878+
resource "random_string" "example" {
1879+
length = length(data.http.example.response_headers)
1880+
}`,
1881+
Check: extractResourceAttr("random_string.example", "id", &id),
1882+
},
1883+
{
1884+
Config: `data "http" "example" {
1885+
url = "https://checkpoint-api.hashicorp.com/v1/check/terraform"
1886+
}
1887+
1888+
resource "random_string" "example" {
1889+
length = length(data.http.example.response_headers)
1890+
}`,
1891+
ResourceName: "random_string.example",
1892+
ImportState: true,
1893+
ImportStateCheck: composeImportStateCheck(
1894+
testCheckResourceAttrInstanceState(&id, "length", "12"),
1895+
),
1896+
ImportStateVerify: true,
1897+
},
1898+
},
1899+
})
1900+
}
1901+
17171902
func setTimeForTest(t time.Time) func() {
17181903
return func() {
17191904
getTimeForTest = func() time.Time {
@@ -1738,43 +1923,41 @@ func composeImportStateCheck(fs ...ImportStateCheckFunc) ImportStateCheckFunc {
17381923
}
17391924
}
17401925

1741-
func testExtractResourceAttrInstanceState(attributeName string, attributeValue *string) ImportStateCheckFunc {
1926+
func testExtractResourceAttrInstanceState(id, attributeName string, attributeValue *string) ImportStateCheckFunc {
17421927
return func(is []*terraform.InstanceState) error {
1743-
if len(is) != 1 {
1744-
return fmt.Errorf("unexpected number of instance states: %d", len(is))
1745-
}
1928+
for _, v := range is {
1929+
if v.ID != id {
1930+
continue
1931+
}
17461932

1747-
s := is[0]
1933+
if attrVal, ok := v.Attributes[attributeName]; ok {
1934+
*attributeValue = attrVal
17481935

1749-
attrValue, ok := s.Attributes[attributeName]
1750-
if !ok {
1751-
return fmt.Errorf("attribute %s not found in instance state", attributeName)
1936+
return nil
1937+
}
17521938
}
17531939

1754-
*attributeValue = attrValue
1755-
1756-
return nil
1940+
return fmt.Errorf("attribute %s not found in instance state", attributeName)
17571941
}
17581942
}
17591943

1760-
func testCheckResourceAttrInstanceState(attributeName, attributeValue string) ImportStateCheckFunc {
1944+
func testCheckResourceAttrInstanceState(id *string, attributeName, attributeValue string) ImportStateCheckFunc {
17611945
return func(is []*terraform.InstanceState) error {
1762-
if len(is) != 1 {
1763-
return fmt.Errorf("unexpected number of instance states: %d", len(is))
1764-
}
1765-
1766-
s := is[0]
1946+
for _, v := range is {
1947+
if v.ID != *id {
1948+
continue
1949+
}
17671950

1768-
attrVal, ok := s.Attributes[attributeName]
1769-
if !ok {
1770-
return fmt.Errorf("attribute %s found in instance state", attributeName)
1771-
}
1951+
if attrVal, ok := v.Attributes[attributeName]; ok {
1952+
if attrVal != attributeValue {
1953+
return fmt.Errorf("expected: %s got: %s", attributeValue, attrVal)
1954+
}
17721955

1773-
if attrVal != attributeValue {
1774-
return fmt.Errorf("expected: %s got: %s", attributeValue, attrVal)
1956+
return nil
1957+
}
17751958
}
17761959

1777-
return nil
1960+
return fmt.Errorf("attribute %s not found in instance state", attributeName)
17781961
}
17791962
}
17801963

0 commit comments

Comments
 (0)