github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/states/statefile/version4_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package statefile 5 6 import ( 7 "sort" 8 "strings" 9 "testing" 10 11 "github.com/terramate-io/tf/tfdiags" 12 "github.com/zclconf/go-cty/cty" 13 ) 14 15 // This test verifies that modules are sorted before resources: 16 // https://github.com/terramate-io/tf/issues/21552 17 func TestVersion4_sort(t *testing.T) { 18 resources := sortResourcesV4{ 19 { 20 Module: "module.child", 21 Type: "test_instance", 22 Name: "foo", 23 }, 24 { 25 Type: "test_instance", 26 Name: "foo", 27 }, 28 { 29 Module: "module.kinder", 30 Type: "test_instance", 31 Name: "foo", 32 }, 33 { 34 Module: "module.child.grandchild", 35 Type: "test_instance", 36 Name: "foo", 37 }, 38 } 39 sort.Stable(resources) 40 41 moduleOrder := []string{"", "module.child", "module.child.grandchild", "module.kinder"} 42 43 for i, resource := range resources { 44 if resource.Module != moduleOrder[i] { 45 t.Errorf("wrong sort order: expected %q, got %q\n", moduleOrder[i], resource.Module) 46 } 47 } 48 } 49 50 func TestVersion4_unmarshalPaths(t *testing.T) { 51 testCases := map[string]struct { 52 json string 53 paths []cty.Path 54 diags []string 55 }{ 56 "no paths": { 57 json: `[]`, 58 paths: []cty.Path{}, 59 }, 60 "attribute path": { 61 json: `[ 62 [ 63 { 64 "type": "get_attr", 65 "value": "password" 66 } 67 ] 68 ]`, 69 paths: []cty.Path{cty.GetAttrPath("password")}, 70 }, 71 "attribute and string index": { 72 json: `[ 73 [ 74 { 75 "type": "get_attr", 76 "value": "triggers" 77 }, 78 { 79 "type": "index", 80 "value": { 81 "value": "secret", 82 "type": "string" 83 } 84 } 85 ] 86 ]`, 87 paths: []cty.Path{cty.GetAttrPath("triggers").IndexString("secret")}, 88 }, 89 "attribute, number index, attribute": { 90 json: `[ 91 [ 92 { 93 "type": "get_attr", 94 "value": "identities" 95 }, 96 { 97 "type": "index", 98 "value": { 99 "value": 2, 100 "type": "number" 101 } 102 }, 103 { 104 "type": "get_attr", 105 "value": "private_key" 106 } 107 ] 108 ]`, 109 paths: []cty.Path{cty.GetAttrPath("identities").IndexInt(2).GetAttr("private_key")}, 110 }, 111 "multiple paths": { 112 json: `[ 113 [ 114 { 115 "type": "get_attr", 116 "value": "alpha" 117 } 118 ], 119 [ 120 { 121 "type": "get_attr", 122 "value": "beta" 123 } 124 ], 125 [ 126 { 127 "type": "get_attr", 128 "value": "gamma" 129 } 130 ] 131 ]`, 132 paths: []cty.Path{cty.GetAttrPath("alpha"), cty.GetAttrPath("beta"), cty.GetAttrPath("gamma")}, 133 }, 134 "errors": { 135 json: `[ 136 [ 137 { 138 "type": "get_attr", 139 "value": 5 140 } 141 ], 142 [ 143 { 144 "type": "index", 145 "value": "test" 146 } 147 ], 148 [ 149 { 150 "type": "invalid_type", 151 "value": ["this is invalid too"] 152 } 153 ] 154 ]`, 155 paths: []cty.Path{}, 156 diags: []string{ 157 "Failed to unmarshal get attr step name", 158 "Failed to unmarshal index step key", 159 "Unsupported path step", 160 }, 161 }, 162 "one invalid path, others valid": { 163 json: `[ 164 [ 165 { 166 "type": "get_attr", 167 "value": "alpha" 168 } 169 ], 170 [ 171 { 172 "type": "invalid_type", 173 "value": ["this is invalid too"] 174 } 175 ], 176 [ 177 { 178 "type": "get_attr", 179 "value": "gamma" 180 } 181 ] 182 ]`, 183 paths: []cty.Path{cty.GetAttrPath("alpha"), cty.GetAttrPath("gamma")}, 184 diags: []string{"Unsupported path step"}, 185 }, 186 "invalid structure": { 187 json: `{}`, 188 paths: []cty.Path{}, 189 diags: []string{"Error unmarshaling path steps"}, 190 }, 191 } 192 193 for name, tc := range testCases { 194 t.Run(name, func(t *testing.T) { 195 paths, diags := unmarshalPaths([]byte(tc.json)) 196 197 if len(tc.diags) == 0 { 198 if len(diags) != 0 { 199 t.Errorf("expected no diags, got: %#v", diags) 200 } 201 } else { 202 if got, want := len(diags), len(tc.diags); got != want { 203 t.Fatalf("got %d diags, want %d\n%s", got, want, diags.Err()) 204 } 205 for i := range tc.diags { 206 got := tfdiags.Diagnostics{diags[i]}.Err().Error() 207 if !strings.Contains(got, tc.diags[i]) { 208 t.Errorf("expected diag %d to contain %q, but was:\n%s", i, tc.diags[i], got) 209 } 210 } 211 } 212 213 if len(paths) != len(tc.paths) { 214 t.Fatalf("got %d paths, want %d", len(paths), len(tc.paths)) 215 } 216 for i, path := range paths { 217 if !path.Equals(tc.paths[i]) { 218 t.Errorf("wrong paths\n got: %#v\nwant: %#v", path, tc.paths[i]) 219 } 220 } 221 }) 222 } 223 } 224 225 func TestVersion4_marshalPaths(t *testing.T) { 226 testCases := map[string]struct { 227 paths []cty.Path 228 json string 229 }{ 230 "no paths": { 231 paths: []cty.Path{}, 232 json: `[]`, 233 }, 234 "attribute path": { 235 paths: []cty.Path{cty.GetAttrPath("password")}, 236 json: `[[{"type":"get_attr","value":"password"}]]`, 237 }, 238 "attribute, number index, attribute": { 239 paths: []cty.Path{cty.GetAttrPath("identities").IndexInt(2).GetAttr("private_key")}, 240 json: `[[{"type":"get_attr","value":"identities"},{"type":"index","value":{"value":2,"type":"number"}},{"type":"get_attr","value":"private_key"}]]`, 241 }, 242 "multiple paths": { 243 paths: []cty.Path{cty.GetAttrPath("a"), cty.GetAttrPath("b"), cty.GetAttrPath("c")}, 244 json: `[[{"type":"get_attr","value":"a"}],[{"type":"get_attr","value":"b"}],[{"type":"get_attr","value":"c"}]]`, 245 }, 246 } 247 248 for name, tc := range testCases { 249 t.Run(name, func(t *testing.T) { 250 json, diags := marshalPaths(tc.paths) 251 252 if len(diags) != 0 { 253 t.Fatalf("expected no diags, got: %#v", diags) 254 } 255 256 if got, want := string(json), tc.json; got != want { 257 t.Fatalf("wrong JSON output\n got: %s\nwant: %s\n", got, want) 258 } 259 }) 260 } 261 }