github.com/opentofu/opentofu@v1.7.1/internal/legacy/tofu/upgrade_state_v2_test.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package tofu
     7  
     8  import (
     9  	"bytes"
    10  	"strings"
    11  	"testing"
    12  )
    13  
    14  // TestReadUpgradeStateV2toV3 tests the state upgrade process from the V2 state
    15  // to the current version, and needs editing each time. This means it tests the
    16  // entire pipeline of upgrades (which migrate version to version).
    17  func TestReadUpgradeStateV2toV3(t *testing.T) {
    18  	// ReadState should transparently detect the old version but will upgrade
    19  	// it on Write.
    20  	upgraded, err := ReadState(strings.NewReader(testV2State))
    21  	if err != nil {
    22  		t.Fatalf("err: %s", err)
    23  	}
    24  
    25  	buf := new(bytes.Buffer)
    26  	if err := WriteState(upgraded, buf); err != nil {
    27  		t.Fatalf("err: %s", err)
    28  	}
    29  
    30  	if upgraded.Version != 3 {
    31  		t.Fatalf("bad: State version not incremented; is %d", upgraded.Version)
    32  	}
    33  
    34  	// For this test we cannot assert that we match the round trip because an
    35  	// empty map has been removed from state. Instead we make assertions against
    36  	// some of the key fields in the _upgraded_ state.
    37  	instanceState, ok := upgraded.RootModule().Resources["test_resource.main"]
    38  	if !ok {
    39  		t.Fatalf("Instance state for test_resource.main was removed from state during upgrade")
    40  	}
    41  
    42  	primary := instanceState.Primary
    43  	if primary == nil {
    44  		t.Fatalf("Primary instance was removed from state for test_resource.main")
    45  	}
    46  
    47  	// Non-empty computed map is moved from .# to .%
    48  	if _, ok := primary.Attributes["computed_map.#"]; ok {
    49  		t.Fatalf("Count was not upgraded from .# to .%% for computed_map")
    50  	}
    51  	if count, ok := primary.Attributes["computed_map.%"]; !ok || count != "1" {
    52  		t.Fatalf("Count was not in .%% or was not 2 for computed_map")
    53  	}
    54  
    55  	// list_of_map top level retains .#
    56  	if count, ok := primary.Attributes["list_of_map.#"]; !ok || count != "2" {
    57  		t.Fatal("Count for list_of_map was migrated incorrectly")
    58  	}
    59  
    60  	// list_of_map.0 is moved from .# to .%
    61  	if _, ok := primary.Attributes["list_of_map.0.#"]; ok {
    62  		t.Fatalf("Count was not upgraded from .# to .%% for list_of_map.0")
    63  	}
    64  	if count, ok := primary.Attributes["list_of_map.0.%"]; !ok || count != "2" {
    65  		t.Fatalf("Count was not in .%% or was not 2 for list_of_map.0")
    66  	}
    67  
    68  	// list_of_map.1 is moved from .# to .%
    69  	if _, ok := primary.Attributes["list_of_map.1.#"]; ok {
    70  		t.Fatalf("Count was not upgraded from .# to .%% for list_of_map.1")
    71  	}
    72  	if count, ok := primary.Attributes["list_of_map.1.%"]; !ok || count != "2" {
    73  		t.Fatalf("Count was not in .%% or was not 2 for list_of_map.1")
    74  	}
    75  
    76  	// map is moved from .# to .%
    77  	if _, ok := primary.Attributes["map.#"]; ok {
    78  		t.Fatalf("Count was not upgraded from .# to .%% for map")
    79  	}
    80  	if count, ok := primary.Attributes["map.%"]; !ok || count != "2" {
    81  		t.Fatalf("Count was not in .%% or was not 2 for map")
    82  	}
    83  
    84  	// optional_computed_map should be removed from state
    85  	if _, ok := primary.Attributes["optional_computed_map"]; ok {
    86  		t.Fatal("optional_computed_map was not removed from state")
    87  	}
    88  
    89  	// required_map is moved from .# to .%
    90  	if _, ok := primary.Attributes["required_map.#"]; ok {
    91  		t.Fatalf("Count was not upgraded from .# to .%% for required_map")
    92  	}
    93  	if count, ok := primary.Attributes["required_map.%"]; !ok || count != "3" {
    94  		t.Fatalf("Count was not in .%% or was not 3 for map")
    95  	}
    96  
    97  	// computed_list keeps .#
    98  	if count, ok := primary.Attributes["computed_list.#"]; !ok || count != "2" {
    99  		t.Fatal("Count was migrated incorrectly for computed_list")
   100  	}
   101  
   102  	// computed_set keeps .#
   103  	if count, ok := primary.Attributes["computed_set.#"]; !ok || count != "2" {
   104  		t.Fatal("Count was migrated incorrectly for computed_set")
   105  	}
   106  	if val, ok := primary.Attributes["computed_set.2337322984"]; !ok || val != "setval1" {
   107  		t.Fatal("Set item for computed_set.2337322984 changed or moved")
   108  	}
   109  	if val, ok := primary.Attributes["computed_set.307881554"]; !ok || val != "setval2" {
   110  		t.Fatal("Set item for computed_set.307881554 changed or moved")
   111  	}
   112  
   113  	// string properties are unaffected
   114  	if val, ok := primary.Attributes["id"]; !ok || val != "testId" {
   115  		t.Fatal("id was not set correctly after migration")
   116  	}
   117  }
   118  
   119  const testV2State = `{
   120      "version": 2,
   121      "terraform_version": "0.7.0",
   122      "serial": 2,
   123      "modules": [
   124          {
   125              "path": [
   126                  "root"
   127              ],
   128              "outputs": {
   129                  "computed_map": {
   130                      "sensitive": false,
   131                      "type": "map",
   132                      "value": {
   133                          "key1": "value1"
   134                      }
   135                  },
   136                  "computed_set": {
   137                      "sensitive": false,
   138                      "type": "list",
   139                      "value": [
   140                          "setval1",
   141                          "setval2"
   142                      ]
   143                  },
   144                  "map": {
   145                      "sensitive": false,
   146                      "type": "map",
   147                      "value": {
   148                          "key": "test",
   149                          "test": "test"
   150                      }
   151                  },
   152                  "set": {
   153                      "sensitive": false,
   154                      "type": "list",
   155                      "value": [
   156                          "test1",
   157                          "test2"
   158                      ]
   159                  }
   160              },
   161              "resources": {
   162                  "test_resource.main": {
   163                      "type": "test_resource",
   164                      "primary": {
   165                          "id": "testId",
   166                          "attributes": {
   167                              "computed_list.#": "2",
   168                              "computed_list.0": "listval1",
   169                              "computed_list.1": "listval2",
   170                              "computed_map.#": "1",
   171                              "computed_map.key1": "value1",
   172                              "computed_read_only": "value_from_api",
   173                              "computed_read_only_force_new": "value_from_api",
   174                              "computed_set.#": "2",
   175                              "computed_set.2337322984": "setval1",
   176                              "computed_set.307881554": "setval2",
   177                              "id": "testId",
   178                              "list_of_map.#": "2",
   179                              "list_of_map.0.#": "2",
   180                              "list_of_map.0.key1": "value1",
   181                              "list_of_map.0.key2": "value2",
   182                              "list_of_map.1.#": "2",
   183                              "list_of_map.1.key3": "value3",
   184                              "list_of_map.1.key4": "value4",
   185                              "map.#": "2",
   186                              "map.key": "test",
   187                              "map.test": "test",
   188                              "map_that_look_like_set.#": "2",
   189                              "map_that_look_like_set.12352223": "hello",
   190                              "map_that_look_like_set.36234341": "world",
   191                              "optional_computed_map.#": "0",
   192                              "required": "Hello World",
   193                              "required_map.#": "3",
   194                              "required_map.key1": "value1",
   195                              "required_map.key2": "value2",
   196                              "required_map.key3": "value3",
   197                              "set.#": "2",
   198                              "set.2326977762": "test1",
   199                              "set.331058520": "test2"
   200                          }
   201                      }
   202                  }
   203              }
   204          }
   205      ]
   206  }
   207  `