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  }