github.com/jpreese/tflint@v0.19.2-0.20200908152133-b01686250fb6/tflint/runner_walk_test.go (about) 1 package tflint 2 3 import ( 4 "fmt" 5 "github.com/hashicorp/terraform/configs" 6 "testing" 7 8 "github.com/google/go-cmp/cmp" 9 "github.com/google/go-cmp/cmp/cmpopts" 10 hcl "github.com/hashicorp/hcl/v2" 11 ) 12 13 func Test_WalkResourceAttributes(t *testing.T) { 14 cases := []struct { 15 Name string 16 Content string 17 ErrorText string 18 }{ 19 { 20 Name: "Resource not found", 21 Content: ` 22 resource "null_resource" "test" { 23 key = "foo" 24 }`, 25 }, 26 { 27 Name: "Attribute not found", 28 Content: ` 29 resource "aws_instance" "test" { 30 key = "foo" 31 }`, 32 }, 33 { 34 Name: "Block attribute", 35 Content: ` 36 resource "aws_instance" "test" { 37 instance_type { 38 name = "t2.micro" 39 } 40 }`, 41 }, 42 { 43 Name: "walk", 44 Content: ` 45 resource "aws_instance" "test" { 46 instance_type = "t2.micro" 47 }`, 48 ErrorText: "Walk instance_type", 49 }, 50 } 51 52 for _, tc := range cases { 53 runner := TestRunner(t, map[string]string{"main.tf": tc.Content}) 54 55 err := runner.WalkResourceAttributes("aws_instance", "instance_type", func(attribute *hcl.Attribute) error { 56 return fmt.Errorf("Walk %s", attribute.Name) 57 }) 58 if err == nil { 59 if tc.ErrorText != "" { 60 t.Fatalf("Failed `%s` test: expected error is not occurred `%s`", tc.Name, tc.ErrorText) 61 } 62 } else if err.Error() != tc.ErrorText { 63 t.Fatalf("Failed `%s` test: expected error is %s, but get %s", tc.Name, tc.ErrorText, err) 64 } 65 } 66 } 67 68 func Test_WalkResourceBlocks(t *testing.T) { 69 cases := []struct { 70 Name string 71 Content string 72 ErrorText string 73 }{ 74 { 75 Name: "Resource not found", 76 Content: ` 77 resource "null_resource" "test" { 78 key { 79 foo = "bar" 80 } 81 }`, 82 }, 83 { 84 Name: "Block not found", 85 Content: ` 86 resource "aws_instance" "test" { 87 key { 88 foo = "bar" 89 } 90 }`, 91 }, 92 { 93 Name: "Attribute", 94 Content: ` 95 resource "aws_instance" "test" { 96 instance_type = "foo" 97 }`, 98 }, 99 { 100 Name: "walk", 101 Content: ` 102 resource "aws_instance" "test" { 103 instance_type { 104 foo = "bar" 105 } 106 }`, 107 ErrorText: "Walk instance_type", 108 }, 109 { 110 Name: "walk dynamic blocks", 111 Content: ` 112 resource "aws_instance" "test" { 113 dynamic "instance_type" { 114 for_each = ["foo", "bar"] 115 116 content { 117 foo = instance_type.value 118 } 119 } 120 }`, 121 ErrorText: "Walk content", 122 }, 123 { 124 Name: "Another dynamic block", 125 Content: ` 126 resource "aws_instance" "test" { 127 dynamic "key" { 128 for_each = ["foo", "bar"] 129 130 content { 131 foo = key.value 132 } 133 } 134 }`, 135 }, 136 } 137 138 for _, tc := range cases { 139 runner := TestRunner(t, map[string]string{"main.tf": tc.Content}) 140 141 err := runner.WalkResourceBlocks("aws_instance", "instance_type", func(block *hcl.Block) error { 142 return fmt.Errorf("Walk %s", block.Type) 143 }) 144 if err == nil { 145 if tc.ErrorText != "" { 146 t.Fatalf("Failed `%s` test: expected error is not occurred `%s`", tc.Name, tc.ErrorText) 147 } 148 } else if err.Error() != tc.ErrorText { 149 t.Fatalf("Failed `%s` test: expected error is %s, but get %s", tc.Name, tc.ErrorText, err) 150 } 151 } 152 } 153 154 func Test_WalkResources(t *testing.T) { 155 cases := []struct { 156 Name string 157 Content string 158 ErrorText string 159 }{ 160 { 161 Name: "Ignore unspecified resources", 162 Content: ` 163 resource "null_resource" "test" { 164 key { 165 foo = "bar" 166 } 167 }`, 168 }, 169 { 170 Name: "Fail with correct resource", 171 Content: ` 172 resource "aws_instance" "test" { 173 key { 174 foo = "bar" 175 } 176 }`, 177 ErrorText: "Walk aws_instance", 178 }, 179 } 180 181 for _, tc := range cases { 182 t.Run(tc.Name, func(t *testing.T) { 183 runner := TestRunner(t, map[string]string{"main.tf": tc.Content}) 184 185 err := runner.WalkResources("aws_instance", func(block *configs.Resource) error { 186 return fmt.Errorf("Walk %s", block.Type) 187 }) 188 if err == nil { 189 if tc.ErrorText != "" { 190 t.Fatalf("Failed `%s` test: expected error is not occurred `%s`", tc.Name, tc.ErrorText) 191 } 192 } else if err.Error() != tc.ErrorText { 193 t.Fatalf("Failed `%s` test: expected error is %s, but get %s", tc.Name, tc.ErrorText, err) 194 } 195 }) 196 } 197 } 198 199 func Test_WalkExpressions(t *testing.T) { 200 cases := []struct { 201 Name string 202 Content string 203 JSON bool 204 Expressions []hcl.Range 205 ErrorText string 206 }{ 207 { 208 Name: "resource", 209 Content: ` 210 resource "null_resource" "test" { 211 key = "foo" 212 }`, 213 Expressions: []hcl.Range{ 214 { 215 Start: hcl.Pos{ 216 Line: 3, 217 Column: 9, 218 }, 219 End: hcl.Pos{ 220 Line: 3, 221 Column: 14, 222 }, 223 }, 224 }, 225 }, 226 { 227 Name: "data source", 228 Content: ` 229 data "null_dataresource" "test" { 230 key = "foo" 231 }`, 232 Expressions: []hcl.Range{ 233 { 234 Start: hcl.Pos{ 235 Line: 3, 236 Column: 9, 237 }, 238 End: hcl.Pos{ 239 Line: 3, 240 Column: 14, 241 }, 242 }, 243 }, 244 }, 245 { 246 Name: "module call", 247 Content: ` 248 module "m" { 249 source = "." 250 key = "foo" 251 }`, 252 Expressions: []hcl.Range{ 253 { 254 Start: hcl.Pos{ 255 Line: 3, 256 Column: 12, 257 }, 258 End: hcl.Pos{ 259 Line: 3, 260 Column: 15, 261 }, 262 }, 263 { 264 Start: hcl.Pos{ 265 Line: 4, 266 Column: 12, 267 }, 268 End: hcl.Pos{ 269 Line: 4, 270 Column: 17, 271 }, 272 }, 273 }, 274 }, 275 { 276 Name: "provider config", 277 Content: ` 278 provider "p" { 279 key = "foo" 280 }`, 281 Expressions: []hcl.Range{ 282 { 283 Start: hcl.Pos{ 284 Line: 3, 285 Column: 9, 286 }, 287 End: hcl.Pos{ 288 Line: 3, 289 Column: 14, 290 }, 291 }, 292 }, 293 }, 294 { 295 Name: "locals", 296 Content: ` 297 locals { 298 key = "foo" 299 }`, 300 Expressions: []hcl.Range{ 301 { 302 Start: hcl.Pos{ 303 Line: 3, 304 Column: 9, 305 }, 306 End: hcl.Pos{ 307 Line: 3, 308 Column: 14, 309 }, 310 }, 311 }, 312 }, 313 { 314 Name: "output", 315 Content: ` 316 output "o" { 317 value = "foo" 318 }`, 319 Expressions: []hcl.Range{ 320 { 321 Start: hcl.Pos{ 322 Line: 3, 323 Column: 11, 324 }, 325 End: hcl.Pos{ 326 Line: 3, 327 Column: 16, 328 }, 329 }, 330 }, 331 }, 332 { 333 Name: "resource with block", 334 Content: ` 335 resource "null_resource" "test" { 336 key = "foo" 337 338 lifecycle { 339 ignore_changes = [key] 340 } 341 }`, 342 Expressions: []hcl.Range{ 343 { 344 Start: hcl.Pos{ 345 Line: 3, 346 Column: 9, 347 }, 348 End: hcl.Pos{ 349 Line: 3, 350 Column: 14, 351 }, 352 }, 353 { 354 Start: hcl.Pos{ 355 Line: 6, 356 Column: 22, 357 }, 358 End: hcl.Pos{ 359 Line: 6, 360 Column: 27, 361 }, 362 }, 363 }, 364 }, 365 { 366 Name: "resource json", 367 JSON: true, 368 Content: ` 369 { 370 "resource": { 371 "null_resource": { 372 "test": { 373 "key": "foo", 374 "nested": { 375 "key": "foo" 376 }, 377 "list": [{ 378 "key": "foo" 379 }] 380 } 381 } 382 } 383 }`, 384 Expressions: []hcl.Range{ 385 { 386 Start: hcl.Pos{ 387 Line: 6, 388 Column: 16, 389 }, 390 End: hcl.Pos{ 391 Line: 6, 392 Column: 21, 393 }, 394 }, 395 { 396 Start: hcl.Pos{ 397 Line: 7, 398 Column: 19, 399 }, 400 End: hcl.Pos{ 401 Line: 9, 402 Column: 10, 403 }, 404 }, 405 { 406 Start: hcl.Pos{ 407 Line: 10, 408 Column: 17, 409 }, 410 End: hcl.Pos{ 411 Line: 12, 412 Column: 11, 413 }, 414 }, 415 }, 416 }, 417 } 418 419 for _, tc := range cases { 420 filename := "main.tf" 421 if tc.JSON { 422 filename += ".json" 423 } 424 425 runner := TestRunner(t, map[string]string{filename: tc.Content}) 426 expressions := make([]hcl.Range, 0) 427 428 err := runner.WalkExpressions(func(expr hcl.Expression) error { 429 expressions = append(expressions, expr.Range()) 430 return nil 431 }) 432 if err == nil { 433 if tc.ErrorText != "" { 434 t.Fatalf("Failed `%s` test: expected error is not occurred `%s`", tc.Name, tc.ErrorText) 435 } 436 437 opts := cmp.Options{ 438 cmpopts.IgnoreFields(hcl.Range{}, "Filename"), 439 cmpopts.IgnoreFields(hcl.Pos{}, "Byte"), 440 cmpopts.SortSlices(func(x, y hcl.Range) bool { return x.String() > y.String() }), 441 } 442 if !cmp.Equal(expressions, tc.Expressions, opts) { 443 t.Fatalf("Failed `%s` test: diff=%s", tc.Name, cmp.Diff(expressions, tc.Expressions, opts)) 444 } 445 } else if err.Error() != tc.ErrorText { 446 t.Fatalf("Failed `%s` test: expected error is %s, but get %s", tc.Name, tc.ErrorText, err) 447 } 448 } 449 }