github.com/jpreese/tflint@v0.19.2-0.20200908152133-b01686250fb6/rules/terraformrules/terraform_naming_convention_test.go (about)

     1  package terraformrules
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"testing"
     8  
     9  	hcl "github.com/hashicorp/hcl/v2"
    10  	"github.com/terraform-linters/tflint/tflint"
    11  )
    12  
    13  // Data blocks
    14  func Test_TerraformNamingConventionRule_Data_DefaultEmpty(t *testing.T) {
    15  	testDataSnakeCase(t, "default config", "format: snake_case", `
    16  rule "terraform_naming_convention" {
    17    enabled = true
    18  }`)
    19  }
    20  
    21  func Test_TerraformNamingConventionRule_Data_DefaultFormat(t *testing.T) {
    22  	testDataMixedSnakeCase(t, `default config (format="mixed_snake_case")`, `
    23  rule "terraform_naming_convention" {
    24    enabled = true
    25    format  = "mixed_snake_case"
    26  }`)
    27  }
    28  
    29  func Test_TerraformNamingConventionRule_Data_DefaultCustom(t *testing.T) {
    30  	testDataSnakeCase(t, `default config (custom="^[a-z_]+$")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
    31  rule "terraform_naming_convention" {
    32    enabled = true
    33    custom  = "^[a-z][a-z]*(_[a-z]+)*$"
    34  }`)
    35  }
    36  
    37  func Test_TerraformNamingConventionRule_Data_DefaultDisabled(t *testing.T) {
    38  	testDataDisabled(t, `default config (format=null)`, `
    39  rule "terraform_naming_convention" {
    40    enabled = true
    41    format  = "none"
    42  }`)
    43  }
    44  
    45  func Test_TerraformNamingConventionRule_Data_DefaultFormat_OverrideFormat(t *testing.T) {
    46  	testDataSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
    47  rule "terraform_naming_convention" {
    48    enabled = true
    49    format  = "mixed_snake_case"
    50  
    51    data {
    52      format = "snake_case"
    53    }
    54  }`)
    55  }
    56  
    57  func Test_TerraformNamingConventionRule_Data_DefaultFormat_OverrideCustom(t *testing.T) {
    58  	testDataSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
    59  rule "terraform_naming_convention" {
    60    enabled = true
    61    format  = "mixed_snake_case"
    62  
    63    data {
    64      custom = "^[a-z][a-z]*(_[a-z]+)*$"
    65    }
    66  }`)
    67  }
    68  
    69  func Test_TerraformNamingConventionRule_Data_DefaultCustom_OverrideFormat(t *testing.T) {
    70  	testDataSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
    71  rule "terraform_naming_convention" {
    72    enabled = true
    73    custom  = "^ignored$"
    74  
    75    data {
    76      format = "snake_case"
    77    }
    78  }`)
    79  }
    80  
    81  func Test_TerraformNamingConventionRule_Data_DefaultCustom_OverrideCustom(t *testing.T) {
    82  	testDataSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
    83  rule "terraform_naming_convention" {
    84    enabled = true
    85    custom  = "^ignored$"
    86  
    87    data {
    88      custom = "^[a-z][a-z]*(_[a-z]+)*$"
    89    }
    90  }`)
    91  }
    92  
    93  func Test_TerraformNamingConventionRule_Data_DefaultDisabled_OverrideFormat(t *testing.T) {
    94  	testDataSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
    95  rule "terraform_naming_convention" {
    96    enabled = true
    97    format  = "none"
    98  
    99    data {
   100      format = "snake_case"
   101    }
   102  }`)
   103  }
   104  
   105  func Test_TerraformNamingConventionRule_Data_DefaultDisabled_OverrideCustom(t *testing.T) {
   106  	testDataSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
   107  rule "terraform_naming_convention" {
   108    enabled = true
   109    format  = "none"
   110  
   111    data {
   112      custom = "^[a-z][a-z]*(_[a-z]+)*$"
   113    }
   114  }`)
   115  }
   116  
   117  func Test_TerraformNamingConventionRule_Data_DefaultEmpty_OverrideDisabled(t *testing.T) {
   118  	testDataDisabled(t, `overridden config (format=null)`, `
   119  rule "terraform_naming_convention" {
   120    enabled = true
   121  
   122    data {
   123      format = "none"
   124    }
   125  }`)
   126  }
   127  
   128  func Test_TerraformNamingConventionRule_Data_DefaultFormat_OverrideDisabled(t *testing.T) {
   129  	testDataDisabled(t, `overridden config (format=null)`, `
   130  rule "terraform_naming_convention" {
   131    enabled = true
   132    format  = "snake_case"
   133  
   134    data {
   135      format = "none"
   136    }
   137  }`)
   138  }
   139  
   140  func Test_TerraformNamingConventionRule_Data_CustomFormats(t *testing.T) {
   141  	testDataSnakeCase(t, `default config (custom_format="custom_snake_case")`, "format: Custom Snake Case", `
   142  rule "terraform_naming_convention" {
   143    enabled = true
   144    format = "custom_snake_case"
   145  
   146    custom_formats = {
   147      custom_snake_case = {
   148        description = "Custom Snake Case"
   149        regex       = "^[a-z][a-z0-9]*(_[a-z0-9]+)*$"
   150      }
   151    }
   152  }`)
   153  }
   154  
   155  func Test_TerraformNamingConventionRule_Data_CustomFormats_OverridePredefined(t *testing.T) {
   156  	testDataSnakeCase(t, `default config (custom_format="snake_case")`, "format: Custom Snake Case", `
   157  rule "terraform_naming_convention" {
   158    enabled = true
   159    format = "snake_case"
   160  
   161    custom_formats = {
   162      snake_case = {
   163        description = "Custom Snake Case"
   164        regex       = "^[a-z][a-z0-9]*(_[a-z0-9]+)*$"
   165      }
   166    }
   167  }`)
   168  }
   169  
   170  func testDataSnakeCase(t *testing.T, testType string, formatName string, config string) {
   171  	rule := NewTerraformNamingConventionRule()
   172  
   173  	cases := []struct {
   174  		Name     string
   175  		Content  string
   176  		Config   string
   177  		Expected tflint.Issues
   178  	}{
   179  		{
   180  			Name: fmt.Sprintf("data: %s - Invalid snake_case with dash", testType),
   181  			Content: `
   182  data "aws_eip" "dash-name" {
   183  }`,
   184  			Config: config,
   185  			Expected: tflint.Issues{
   186  				{
   187  					Rule:    rule,
   188  					Message: fmt.Sprintf("data name `dash-name` must match the following %s", formatName),
   189  					Range: hcl.Range{
   190  						Filename: "tests.tf",
   191  						Start:    hcl.Pos{Line: 2, Column: 1},
   192  						End:      hcl.Pos{Line: 2, Column: 27},
   193  					},
   194  				},
   195  			},
   196  		},
   197  		{
   198  			Name: fmt.Sprintf("data: %s - Invalid snake_case with camelCase", testType),
   199  			Content: `
   200  data "aws_eip" "camelCased" {
   201  }`,
   202  			Config: config,
   203  			Expected: tflint.Issues{
   204  				{
   205  					Rule:    rule,
   206  					Message: fmt.Sprintf("data name `camelCased` must match the following %s", formatName),
   207  					Range: hcl.Range{
   208  						Filename: "tests.tf",
   209  						Start:    hcl.Pos{Line: 2, Column: 1},
   210  						End:      hcl.Pos{Line: 2, Column: 28},
   211  					},
   212  				},
   213  			},
   214  		},
   215  		{
   216  			Name: fmt.Sprintf("data: %s - Invalid snake_case with double underscore", testType),
   217  			Content: `
   218  data "aws_eip" "foo__bar" {
   219  }`,
   220  			Config: config,
   221  			Expected: tflint.Issues{
   222  				{
   223  					Rule:    rule,
   224  					Message: fmt.Sprintf("data name `foo__bar` must match the following %s", formatName),
   225  					Range: hcl.Range{
   226  						Filename: "tests.tf",
   227  						Start:    hcl.Pos{Line: 2, Column: 1},
   228  						End:      hcl.Pos{Line: 2, Column: 26},
   229  					},
   230  				},
   231  			},
   232  		},
   233  		{
   234  			Name: fmt.Sprintf("data: %s - Invalid snake_case with underscore tail", testType),
   235  			Content: `
   236  data "aws_eip" "foo_bar_" {
   237  }`,
   238  			Config: config,
   239  			Expected: tflint.Issues{
   240  				{
   241  					Rule:    rule,
   242  					Message: fmt.Sprintf("data name `foo_bar_` must match the following %s", formatName),
   243  					Range: hcl.Range{
   244  						Filename: "tests.tf",
   245  						Start:    hcl.Pos{Line: 2, Column: 1},
   246  						End:      hcl.Pos{Line: 2, Column: 26},
   247  					},
   248  				},
   249  			},
   250  		},
   251  		{
   252  			Name: fmt.Sprintf("data: %s - Invalid snake_case with Mixed_Snake_Case", testType),
   253  			Content: `
   254  data "aws_eip" "Foo_Bar" {
   255  }`,
   256  			Config: config,
   257  			Expected: tflint.Issues{
   258  				{
   259  					Rule:    rule,
   260  					Message: fmt.Sprintf("data name `Foo_Bar` must match the following %s", formatName),
   261  					Range: hcl.Range{
   262  						Filename: "tests.tf",
   263  						Start:    hcl.Pos{Line: 2, Column: 1},
   264  						End:      hcl.Pos{Line: 2, Column: 25},
   265  					},
   266  				},
   267  			},
   268  		},
   269  		{
   270  			Name: fmt.Sprintf("data: %s - Valid snake_case", testType),
   271  			Content: `
   272  data "aws_eip" "foo_bar" {
   273  }`,
   274  			Config:   config,
   275  			Expected: tflint.Issues{},
   276  		},
   277  		{
   278  			Name: fmt.Sprintf("data: %s - Valid single word", testType),
   279  			Content: `
   280  data "aws_eip" "foo" {
   281  }`,
   282  			Config:   config,
   283  			Expected: tflint.Issues{},
   284  		},
   285  	}
   286  
   287  	for _, tc := range cases {
   288  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
   289  
   290  		if err := rule.Check(runner); err != nil {
   291  			t.Fatalf("Unexpected error occurred: %s", err)
   292  		}
   293  
   294  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
   295  	}
   296  }
   297  
   298  func testDataMixedSnakeCase(t *testing.T, testType string, config string) {
   299  	rule := NewTerraformNamingConventionRule()
   300  
   301  	cases := []struct {
   302  		Name     string
   303  		Content  string
   304  		Config   string
   305  		Expected tflint.Issues
   306  	}{
   307  		{
   308  			Name: fmt.Sprintf("data: %s - Invalid mixed_snake_case with dash", testType),
   309  			Content: `
   310  data "aws_eip" "dash-name" {
   311  }`,
   312  			Config: config,
   313  			Expected: tflint.Issues{
   314  				{
   315  					Rule:    rule,
   316  					Message: "data name `dash-name` must match the following format: mixed_snake_case",
   317  					Range: hcl.Range{
   318  						Filename: "tests.tf",
   319  						Start:    hcl.Pos{Line: 2, Column: 1},
   320  						End:      hcl.Pos{Line: 2, Column: 27},
   321  					},
   322  				},
   323  			},
   324  		},
   325  		{
   326  			Name: fmt.Sprintf("data: %s - Invalid mixed_snake_case with double underscore", testType),
   327  			Content: `
   328  data "aws_eip" "Foo__Bar" {
   329  }`,
   330  			Config: config,
   331  			Expected: tflint.Issues{
   332  				{
   333  					Rule:    rule,
   334  					Message: "data name `Foo__Bar` must match the following format: mixed_snake_case",
   335  					Range: hcl.Range{
   336  						Filename: "tests.tf",
   337  						Start:    hcl.Pos{Line: 2, Column: 1},
   338  						End:      hcl.Pos{Line: 2, Column: 26},
   339  					},
   340  				},
   341  			},
   342  		},
   343  		{
   344  			Name: fmt.Sprintf("data: %s - Invalid mixed_snake_case with underscore tail", testType),
   345  			Content: `
   346  data "aws_eip" "Foo_Bar_" {
   347  }`,
   348  			Config: config,
   349  			Expected: tflint.Issues{
   350  				{
   351  					Rule:    rule,
   352  					Message: "data name `Foo_Bar_` must match the following format: mixed_snake_case",
   353  					Range: hcl.Range{
   354  						Filename: "tests.tf",
   355  						Start:    hcl.Pos{Line: 2, Column: 1},
   356  						End:      hcl.Pos{Line: 2, Column: 26},
   357  					},
   358  				},
   359  			},
   360  		},
   361  		{
   362  			Name: fmt.Sprintf("data: %s - Valid snake_case", testType),
   363  			Content: `
   364  data "aws_eip" "foo_bar" {
   365  }`,
   366  			Config:   config,
   367  			Expected: tflint.Issues{},
   368  		},
   369  		{
   370  			Name: fmt.Sprintf("data: %s - Valid single word", testType),
   371  			Content: `
   372  data "aws_eip" "foo" {
   373  }`,
   374  			Config:   config,
   375  			Expected: tflint.Issues{},
   376  		},
   377  		{
   378  			Name: fmt.Sprintf("data: %s - Valid Mixed_Snake_Case", testType),
   379  			Content: `
   380  data "aws_eip" "Foo_Bar" {
   381  }`,
   382  			Config:   config,
   383  			Expected: tflint.Issues{},
   384  		},
   385  		{
   386  			Name: fmt.Sprintf("data: %s - Valid single word with upper characters", testType),
   387  			Content: `
   388  data "aws_eip" "foo" {
   389  }`,
   390  			Config:   config,
   391  			Expected: tflint.Issues{},
   392  		},
   393  		{
   394  			Name: fmt.Sprintf("data: %s - Valid PascalCase", testType),
   395  			Content: `
   396  data "aws_eip" "PascalCase" {
   397  }`,
   398  			Config:   config,
   399  			Expected: tflint.Issues{},
   400  		},
   401  		{
   402  			Name: fmt.Sprintf("data: %s - Valid camelCase", testType),
   403  			Content: `
   404  data "aws_eip" "camelCase" {
   405  }`,
   406  			Config:   config,
   407  			Expected: tflint.Issues{},
   408  		},
   409  	}
   410  
   411  	for _, tc := range cases {
   412  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
   413  
   414  		if err := rule.Check(runner); err != nil {
   415  			t.Fatalf("Unexpected error occurred: %s", err)
   416  		}
   417  
   418  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
   419  	}
   420  }
   421  
   422  func testDataDisabled(t *testing.T, testType string, config string) {
   423  	rule := NewTerraformNamingConventionRule()
   424  
   425  	cases := []struct {
   426  		Name     string
   427  		Content  string
   428  		Config   string
   429  		Expected tflint.Issues
   430  	}{
   431  		{
   432  			Name: fmt.Sprintf("data: %s - Valid mixed_snake_case with dash", testType),
   433  			Content: `
   434  data "aws_eip" "dash-name" {
   435  }`,
   436  			Config:   config,
   437  			Expected: tflint.Issues{},
   438  		},
   439  		{
   440  			Name: fmt.Sprintf("data: %s - Valid snake_case", testType),
   441  			Content: `
   442  data "aws_eip" "foo_bar" {
   443  }`,
   444  			Config:   config,
   445  			Expected: tflint.Issues{},
   446  		},
   447  		{
   448  			Name: fmt.Sprintf("data: %s - Valid single word", testType),
   449  			Content: `
   450  data "aws_eip" "foo" {
   451  }`,
   452  			Config:   config,
   453  			Expected: tflint.Issues{},
   454  		},
   455  		{
   456  			Name: fmt.Sprintf("data: %s - Valid Mixed_Snake_Case", testType),
   457  			Content: `
   458  data "aws_eip" "Foo_Bar" {
   459  }`,
   460  			Config:   config,
   461  			Expected: tflint.Issues{},
   462  		},
   463  		{
   464  			Name: fmt.Sprintf("data: %s - Valid single word upper characters", testType),
   465  			Content: `
   466  data "aws_eip" "Foo" {
   467  }`,
   468  			Config:   config,
   469  			Expected: tflint.Issues{},
   470  		},
   471  		{
   472  			Name: fmt.Sprintf("data: %s - Valid PascalCase", testType),
   473  			Content: `
   474  data "aws_eip" "PascalCase" {
   475  }`,
   476  			Config:   config,
   477  			Expected: tflint.Issues{},
   478  		},
   479  		{
   480  			Name: fmt.Sprintf("data: %s - Valid camelCase", testType),
   481  			Content: `
   482  data "aws_eip" "camelCase" {
   483  }`,
   484  			Config:   config,
   485  			Expected: tflint.Issues{},
   486  		},
   487  	}
   488  
   489  	for _, tc := range cases {
   490  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
   491  
   492  		if err := rule.Check(runner); err != nil {
   493  			t.Fatalf("Unexpected error occurred: %s", err)
   494  		}
   495  
   496  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
   497  	}
   498  }
   499  
   500  // Local values
   501  func Test_TerraformNamingConventionRule_Locals_DefaultEmpty(t *testing.T) {
   502  	testLocalsSnakeCase(t, "default config", "format: snake_case", `
   503  rule "terraform_naming_convention" {
   504    enabled = true
   505  }`)
   506  }
   507  
   508  func Test_TerraformNamingConventionRule_Locals_DefaultFormat(t *testing.T) {
   509  	testLocalsMixedSnakeCase(t, `default config (format="mixed_snake_case")`, `
   510  rule "terraform_naming_convention" {
   511    enabled = true
   512    format  = "mixed_snake_case"
   513  }`)
   514  }
   515  
   516  func Test_TerraformNamingConventionRule_Locals_DefaultCustom(t *testing.T) {
   517  	testLocalsSnakeCase(t, `default config (custom="^[a-z_]+$")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
   518  rule "terraform_naming_convention" {
   519    enabled = true
   520    custom  = "^[a-z][a-z]*(_[a-z]+)*$"
   521  }`)
   522  }
   523  
   524  func Test_TerraformNamingConventionRule_Locals_DefaultDisabled(t *testing.T) {
   525  	testLocalsDisabled(t, `default config (format=null)`, `
   526  rule "terraform_naming_convention" {
   527    enabled = true
   528    format  = "none"
   529  }`)
   530  }
   531  
   532  func Test_TerraformNamingConventionRule_Locals_DefaultFormat_OverrideFormat(t *testing.T) {
   533  	testLocalsSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
   534  rule "terraform_naming_convention" {
   535    enabled = true
   536    format  = "mixed_snake_case"
   537  
   538    locals {
   539      format = "snake_case"
   540    }
   541  }`)
   542  }
   543  
   544  func Test_TerraformNamingConventionRule_Locals_DefaultFormat_OverrideCustom(t *testing.T) {
   545  	testLocalsSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
   546  rule "terraform_naming_convention" {
   547    enabled = true
   548    format  = "mixed_snake_case"
   549  
   550    locals {
   551      custom = "^[a-z][a-z]*(_[a-z]+)*$"
   552    }
   553  }`)
   554  }
   555  
   556  func Test_TerraformNamingConventionRule_Locals_DefaultCustom_OverrideFormat(t *testing.T) {
   557  	testLocalsSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
   558  rule "terraform_naming_convention" {
   559    enabled = true
   560    custom  = "^ignored$"
   561  
   562    locals {
   563      format = "snake_case"
   564    }
   565  }`)
   566  }
   567  
   568  func Test_TerraformNamingConventionRule_Locals_DefaultCustom_OverrideCustom(t *testing.T) {
   569  	testLocalsSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
   570  rule "terraform_naming_convention" {
   571    enabled = true
   572    custom  = "^ignored$"
   573  
   574    locals {
   575      custom = "^[a-z][a-z]*(_[a-z]+)*$"
   576    }
   577  }`)
   578  }
   579  
   580  func Test_TerraformNamingConventionRule_Locals_DefaultDisabled_OverrideFormat(t *testing.T) {
   581  	testLocalsSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
   582  rule "terraform_naming_convention" {
   583    enabled = true
   584    format  = "none"
   585  
   586    locals {
   587      format = "snake_case"
   588    }
   589  }`)
   590  }
   591  
   592  func Test_TerraformNamingConventionRule_Locals_DefaultDisabled_OverrideCustom(t *testing.T) {
   593  	testLocalsSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
   594  rule "terraform_naming_convention" {
   595    enabled = true
   596    format  = "none"
   597  
   598    locals {
   599      custom = "^[a-z][a-z]*(_[a-z]+)*$"
   600    }
   601  }`)
   602  }
   603  
   604  func Test_TerraformNamingConventionRule_Locals_DefaultEmpty_OverrideDisabled(t *testing.T) {
   605  	testLocalsDisabled(t, `overridden config (format=null)`, `
   606  rule "terraform_naming_convention" {
   607    enabled = true
   608  
   609    locals {
   610      format = "none"
   611    }
   612  }`)
   613  }
   614  
   615  func Test_TerraformNamingConventionRule_Locals_DefaultFormat_OverrideDisabled(t *testing.T) {
   616  	testLocalsDisabled(t, `overridden config (format=null)`, `
   617  rule "terraform_naming_convention" {
   618    enabled = true
   619    format  = "snake_case"
   620  
   621    locals {
   622      format = "none"
   623    }
   624  }`)
   625  }
   626  
   627  func testLocalsSnakeCase(t *testing.T, testType string, formatName string, config string) {
   628  	rule := NewTerraformNamingConventionRule()
   629  
   630  	cases := []struct {
   631  		Name     string
   632  		Content  string
   633  		Config   string
   634  		Expected tflint.Issues
   635  	}{
   636  		{
   637  			Name: fmt.Sprintf("locals: %s - Invalid snake_case with dash", testType),
   638  			Content: `
   639  locals {
   640    dash-name = "invalid"
   641  }`,
   642  			Config: config,
   643  			Expected: tflint.Issues{
   644  				{
   645  					Rule:    rule,
   646  					Message: fmt.Sprintf("local value name `dash-name` must match the following %s", formatName),
   647  					Range: hcl.Range{
   648  						Filename: "tests.tf",
   649  						Start:    hcl.Pos{Line: 3, Column: 3},
   650  						End:      hcl.Pos{Line: 3, Column: 24},
   651  					},
   652  				},
   653  			},
   654  		},
   655  		{
   656  			Name: fmt.Sprintf("locals: %s - Invalid snake_case with camelCase", testType),
   657  			Content: `
   658  locals {
   659    camelCased = "invalid"
   660  }`,
   661  			Config: config,
   662  			Expected: tflint.Issues{
   663  				{
   664  					Rule:    rule,
   665  					Message: fmt.Sprintf("local value name `camelCased` must match the following %s", formatName),
   666  					Range: hcl.Range{
   667  						Filename: "tests.tf",
   668  						Start:    hcl.Pos{Line: 3, Column: 3},
   669  						End:      hcl.Pos{Line: 3, Column: 25},
   670  					},
   671  				},
   672  			},
   673  		},
   674  		{
   675  			Name: fmt.Sprintf("locals: %s - Invalid snake_case with double underscore", testType),
   676  			Content: `
   677  locals {
   678    foo__bar = "invalid"
   679  }`,
   680  			Config: config,
   681  			Expected: tflint.Issues{
   682  				{
   683  					Rule:    rule,
   684  					Message: fmt.Sprintf("local value name `foo__bar` must match the following %s", formatName),
   685  					Range: hcl.Range{
   686  						Filename: "tests.tf",
   687  						Start:    hcl.Pos{Line: 3, Column: 3},
   688  						End:      hcl.Pos{Line: 3, Column: 23},
   689  					},
   690  				},
   691  			},
   692  		},
   693  		{
   694  			Name: fmt.Sprintf("locals: %s - Invalid snake_case with underscore tail", testType),
   695  			Content: `
   696  locals {
   697    foo_bar_ = "invalid"
   698  }`,
   699  			Config: config,
   700  			Expected: tflint.Issues{
   701  				{
   702  					Rule:    rule,
   703  					Message: fmt.Sprintf("local value name `foo_bar_` must match the following %s", formatName),
   704  					Range: hcl.Range{
   705  						Filename: "tests.tf",
   706  						Start:    hcl.Pos{Line: 3, Column: 3},
   707  						End:      hcl.Pos{Line: 3, Column: 23},
   708  					},
   709  				},
   710  			},
   711  		},
   712  		{
   713  			Name: fmt.Sprintf("locals: %s - Invalid snake_case with Mixed_Snake_Case", testType),
   714  			Content: `
   715  locals {
   716    Foo_Bar = "invalid"
   717  }`,
   718  			Config: config,
   719  			Expected: tflint.Issues{
   720  				{
   721  					Rule:    rule,
   722  					Message: fmt.Sprintf("local value name `Foo_Bar` must match the following %s", formatName),
   723  					Range: hcl.Range{
   724  						Filename: "tests.tf",
   725  						Start:    hcl.Pos{Line: 3, Column: 3},
   726  						End:      hcl.Pos{Line: 3, Column: 22},
   727  					},
   728  				},
   729  			},
   730  		},
   731  		{
   732  			Name: fmt.Sprintf("locals: %s - Valid snake_case", testType),
   733  			Content: `
   734  locals {
   735    foo_bar = "valid"
   736  }`,
   737  			Config:   config,
   738  			Expected: tflint.Issues{},
   739  		},
   740  		{
   741  			Name: fmt.Sprintf("locals: %s - Valid single word", testType),
   742  			Content: `
   743  locals {
   744    foo = "valid"
   745  }`,
   746  			Config:   config,
   747  			Expected: tflint.Issues{},
   748  		},
   749  	}
   750  
   751  	for _, tc := range cases {
   752  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
   753  
   754  		if err := rule.Check(runner); err != nil {
   755  			t.Fatalf("Unexpected error occurred: %s", err)
   756  		}
   757  
   758  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
   759  	}
   760  }
   761  
   762  func testLocalsMixedSnakeCase(t *testing.T, testType string, config string) {
   763  	rule := NewTerraformNamingConventionRule()
   764  
   765  	cases := []struct {
   766  		Name     string
   767  		Content  string
   768  		Config   string
   769  		Expected tflint.Issues
   770  	}{
   771  		{
   772  			Name: fmt.Sprintf("locals: %s - Invalid mixed_snake_case with dash", testType),
   773  			Content: `
   774  locals {
   775    dash-name = "invalid"
   776  }`,
   777  			Config: config,
   778  			Expected: tflint.Issues{
   779  				{
   780  					Rule:    rule,
   781  					Message: "local value name `dash-name` must match the following format: mixed_snake_case",
   782  					Range: hcl.Range{
   783  						Filename: "tests.tf",
   784  						Start:    hcl.Pos{Line: 3, Column: 3},
   785  						End:      hcl.Pos{Line: 3, Column: 24},
   786  					},
   787  				},
   788  			},
   789  		},
   790  		{
   791  			Name: fmt.Sprintf("locals: %s - Invalid mixed_snake_case with double underscore", testType),
   792  			Content: `
   793  locals {
   794    Foo__Bar = "invalid"
   795  }`,
   796  			Config: config,
   797  			Expected: tflint.Issues{
   798  				{
   799  					Rule:    rule,
   800  					Message: "local value name `Foo__Bar` must match the following format: mixed_snake_case",
   801  					Range: hcl.Range{
   802  						Filename: "tests.tf",
   803  						Start:    hcl.Pos{Line: 3, Column: 3},
   804  						End:      hcl.Pos{Line: 3, Column: 23},
   805  					},
   806  				},
   807  			},
   808  		},
   809  		{
   810  			Name: fmt.Sprintf("locals: %s - Invalid mixed_snake_case with underscore tail", testType),
   811  			Content: `
   812  locals {
   813    Foo_Bar_ = "invalid"
   814  }`,
   815  			Config: config,
   816  			Expected: tflint.Issues{
   817  				{
   818  					Rule:    rule,
   819  					Message: "local value name `Foo_Bar_` must match the following format: mixed_snake_case",
   820  					Range: hcl.Range{
   821  						Filename: "tests.tf",
   822  						Start:    hcl.Pos{Line: 3, Column: 3},
   823  						End:      hcl.Pos{Line: 3, Column: 23},
   824  					},
   825  				},
   826  			},
   827  		},
   828  		{
   829  			Name: fmt.Sprintf("locals: %s - Valid snake_case", testType),
   830  			Content: `
   831  locals {
   832    foo_bar = "valid"
   833  }`,
   834  			Config:   config,
   835  			Expected: tflint.Issues{},
   836  		},
   837  		{
   838  			Name: fmt.Sprintf("locals: %s - Valid single word", testType),
   839  			Content: `
   840  locals {
   841    foo = "valid"
   842  }`,
   843  			Config:   config,
   844  			Expected: tflint.Issues{},
   845  		},
   846  		{
   847  			Name: fmt.Sprintf("locals: %s - Valid Mixed_Snake_Case", testType),
   848  			Content: `
   849  locals {
   850    Foo_Bar = "valid"
   851  }`,
   852  			Config:   config,
   853  			Expected: tflint.Issues{},
   854  		},
   855  		{
   856  			Name: fmt.Sprintf("locals: %s - Valid single word with upper characters", testType),
   857  			Content: `
   858  locals {
   859    Foo = "valid"
   860  }`,
   861  			Config:   config,
   862  			Expected: tflint.Issues{},
   863  		},
   864  		{
   865  			Name: fmt.Sprintf("locals: %s - Valid PascalCase", testType),
   866  			Content: `
   867  locals {
   868    PascalCase = "valid"
   869  }`,
   870  			Config:   config,
   871  			Expected: tflint.Issues{},
   872  		},
   873  		{
   874  			Name: fmt.Sprintf("locals: %s - Valid camelCase", testType),
   875  			Content: `
   876  locals {
   877    camelCase = "valid"
   878  }`,
   879  			Config:   config,
   880  			Expected: tflint.Issues{},
   881  		},
   882  	}
   883  
   884  	for _, tc := range cases {
   885  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
   886  
   887  		if err := rule.Check(runner); err != nil {
   888  			t.Fatalf("Unexpected error occurred: %s", err)
   889  		}
   890  
   891  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
   892  	}
   893  }
   894  
   895  func testLocalsDisabled(t *testing.T, testType string, config string) {
   896  	rule := NewTerraformNamingConventionRule()
   897  
   898  	cases := []struct {
   899  		Name     string
   900  		Content  string
   901  		Config   string
   902  		Expected tflint.Issues
   903  	}{
   904  		{
   905  			Name: fmt.Sprintf("locals: %s - Valid mixed_snake_case with dash", testType),
   906  			Content: `
   907  locals {
   908    dash-name = "valid"
   909  }`,
   910  			Config:   config,
   911  			Expected: tflint.Issues{},
   912  		},
   913  		{
   914  			Name: fmt.Sprintf("locals: %s - Valid snake_case", testType),
   915  			Content: `
   916  locals {
   917    foo_bar = "valid"
   918  }`,
   919  			Config:   config,
   920  			Expected: tflint.Issues{},
   921  		},
   922  		{
   923  			Name: fmt.Sprintf("locals: %s - Valid single word", testType),
   924  			Content: `
   925  locals {
   926    foo = "valid"
   927  }`,
   928  			Config:   config,
   929  			Expected: tflint.Issues{},
   930  		},
   931  		{
   932  			Name: fmt.Sprintf("locals: %s - Valid Mixed_Snake_Case", testType),
   933  			Content: `
   934  locals {
   935    Foo_Bar = "valid"
   936  }`,
   937  			Config:   config,
   938  			Expected: tflint.Issues{},
   939  		},
   940  		{
   941  			Name: fmt.Sprintf("locals: %s - Valid single word with upper characters", testType),
   942  			Content: `
   943  locals {
   944    Foo = "valid"
   945  }`,
   946  			Config:   config,
   947  			Expected: tflint.Issues{},
   948  		},
   949  		{
   950  			Name: fmt.Sprintf("locals: %s - Valid PascalCase", testType),
   951  			Content: `
   952  locals {
   953    PascalCase = "valid"
   954  }`,
   955  			Config:   config,
   956  			Expected: tflint.Issues{},
   957  		},
   958  		{
   959  			Name: fmt.Sprintf("locals: %s - Valid camelCase", testType),
   960  			Content: `
   961  locals {
   962    camelCase = "valid"
   963  }`,
   964  			Config:   config,
   965  			Expected: tflint.Issues{},
   966  		},
   967  	}
   968  
   969  	for _, tc := range cases {
   970  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
   971  
   972  		if err := rule.Check(runner); err != nil {
   973  			t.Fatalf("Unexpected error occurred: %s", err)
   974  		}
   975  
   976  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
   977  	}
   978  }
   979  
   980  // Module blocks
   981  func Test_TerraformNamingConventionRule_Module_DefaultEmpty(t *testing.T) {
   982  	testModuleSnakeCase(t, "default config", "format: snake_case", `
   983  rule "terraform_naming_convention" {
   984    enabled = true
   985  }`)
   986  }
   987  
   988  func Test_TerraformNamingConventionRule_Module_DefaultFormat(t *testing.T) {
   989  	testModuleMixedSnakeCase(t, `default config (format="mixed_snake_case")`, `
   990  rule "terraform_naming_convention" {
   991    enabled = true
   992    format  = "mixed_snake_case"
   993  }`)
   994  }
   995  
   996  func Test_TerraformNamingConventionRule_Module_DefaultCustom(t *testing.T) {
   997  	testModuleSnakeCase(t, `default config (custom="^[a-z_]+$")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
   998  rule "terraform_naming_convention" {
   999    enabled = true
  1000    custom  = "^[a-z][a-z]*(_[a-z]+)*$"
  1001  }`)
  1002  }
  1003  
  1004  func Test_TerraformNamingConventionRule_Module_DefaultDisabled(t *testing.T) {
  1005  	testModuleDisabled(t, `default config (format=null)`, `
  1006  rule "terraform_naming_convention" {
  1007    enabled = true
  1008    format  = "none"
  1009  }`)
  1010  }
  1011  
  1012  func Test_TerraformNamingConventionRule_Module_DefaultFormat_OverrideFormat(t *testing.T) {
  1013  	testModuleSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  1014  rule "terraform_naming_convention" {
  1015    enabled = true
  1016    format  = "mixed_snake_case"
  1017  
  1018    module {
  1019      format = "snake_case"
  1020    }
  1021  }`)
  1022  }
  1023  
  1024  func Test_TerraformNamingConventionRule_Module_DefaultFormat_OverrideCustom(t *testing.T) {
  1025  	testModuleSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  1026  rule "terraform_naming_convention" {
  1027    enabled = true
  1028    format  = "mixed_snake_case"
  1029  
  1030    module {
  1031      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  1032    }
  1033  }`)
  1034  }
  1035  
  1036  func Test_TerraformNamingConventionRule_Module_DefaultCustom_OverrideFormat(t *testing.T) {
  1037  	testModuleSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  1038  rule "terraform_naming_convention" {
  1039    enabled = true
  1040    custom  = "^ignored$"
  1041  
  1042    module {
  1043      format = "snake_case"
  1044    }
  1045  }`)
  1046  }
  1047  
  1048  func Test_TerraformNamingConventionRule_Module_DefaultCustom_OverrideCustom(t *testing.T) {
  1049  	testModuleSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  1050  rule "terraform_naming_convention" {
  1051    enabled = true
  1052    custom  = "^ignored$"
  1053  
  1054    module {
  1055      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  1056    }
  1057  }`)
  1058  }
  1059  
  1060  func Test_TerraformNamingConventionRule_Module_DefaultDisabled_OverrideFormat(t *testing.T) {
  1061  	testModuleSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  1062  rule "terraform_naming_convention" {
  1063    enabled = true
  1064    format  = "none"
  1065  
  1066    module {
  1067      format = "snake_case"
  1068    }
  1069  }`)
  1070  }
  1071  
  1072  func Test_TerraformNamingConventionRule_Module_DefaultDisabled_OverrideCustom(t *testing.T) {
  1073  	testModuleSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  1074  rule "terraform_naming_convention" {
  1075    enabled = true
  1076    format  = "none"
  1077  
  1078    module {
  1079      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  1080    }
  1081  }`)
  1082  }
  1083  
  1084  func Test_TerraformNamingConventionRule_Module_DefaultEmpty_OverrideDisabled(t *testing.T) {
  1085  	testModuleDisabled(t, `overridden config (format=null)`, `
  1086  rule "terraform_naming_convention" {
  1087    enabled = true
  1088  
  1089    module {
  1090      format = "none"
  1091    }
  1092  }`)
  1093  }
  1094  
  1095  func Test_TerraformNamingConventionRule_Module_DefaultFormat_OverrideDisabled(t *testing.T) {
  1096  	testModuleDisabled(t, `overridden config (format=null)`, `
  1097  rule "terraform_naming_convention" {
  1098    enabled = true
  1099    format  = "snake_case"
  1100  
  1101    module {
  1102      format = "none"
  1103    }
  1104  }`)
  1105  }
  1106  
  1107  func testModuleSnakeCase(t *testing.T, testType string, formatName string, config string) {
  1108  	rule := NewTerraformNamingConventionRule()
  1109  
  1110  	cases := []struct {
  1111  		Name     string
  1112  		Content  string
  1113  		Config   string
  1114  		Expected tflint.Issues
  1115  	}{
  1116  		{
  1117  			Name: fmt.Sprintf("module: %s - Invalid snake_case with dash", testType),
  1118  			Content: `
  1119  module "dash-name" {
  1120    source = ""
  1121  }`,
  1122  			Config: config,
  1123  			Expected: tflint.Issues{
  1124  				{
  1125  					Rule:    rule,
  1126  					Message: fmt.Sprintf("module name `dash-name` must match the following %s", formatName),
  1127  					Range: hcl.Range{
  1128  						Filename: "tests.tf",
  1129  						Start:    hcl.Pos{Line: 2, Column: 1},
  1130  						End:      hcl.Pos{Line: 2, Column: 19},
  1131  					},
  1132  				},
  1133  			},
  1134  		},
  1135  		{
  1136  			Name: fmt.Sprintf("module: %s - Invalid snake_case with camelCase", testType),
  1137  			Content: `
  1138  module "camelCased" {
  1139    source = ""
  1140  }`,
  1141  			Config: config,
  1142  			Expected: tflint.Issues{
  1143  				{
  1144  					Rule:    rule,
  1145  					Message: fmt.Sprintf("module name `camelCased` must match the following %s", formatName),
  1146  					Range: hcl.Range{
  1147  						Filename: "tests.tf",
  1148  						Start:    hcl.Pos{Line: 2, Column: 1},
  1149  						End:      hcl.Pos{Line: 2, Column: 20},
  1150  					},
  1151  				},
  1152  			},
  1153  		},
  1154  		{
  1155  			Name: fmt.Sprintf("module: %s - Invalid snake_case with double underscore", testType),
  1156  			Content: `
  1157  module "foo__bar" {
  1158    source = ""
  1159  }`,
  1160  			Config: config,
  1161  			Expected: tflint.Issues{
  1162  				{
  1163  					Rule:    rule,
  1164  					Message: fmt.Sprintf("module name `foo__bar` must match the following %s", formatName),
  1165  					Range: hcl.Range{
  1166  						Filename: "tests.tf",
  1167  						Start:    hcl.Pos{Line: 2, Column: 1},
  1168  						End:      hcl.Pos{Line: 2, Column: 18},
  1169  					},
  1170  				},
  1171  			},
  1172  		},
  1173  		{
  1174  			Name: fmt.Sprintf("module: %s - Invalid snake_case with underscore tail", testType),
  1175  			Content: `
  1176  module "foo_bar_" {
  1177    source = ""
  1178  }`,
  1179  			Config: config,
  1180  			Expected: tflint.Issues{
  1181  				{
  1182  					Rule:    rule,
  1183  					Message: fmt.Sprintf("module name `foo_bar_` must match the following %s", formatName),
  1184  					Range: hcl.Range{
  1185  						Filename: "tests.tf",
  1186  						Start:    hcl.Pos{Line: 2, Column: 1},
  1187  						End:      hcl.Pos{Line: 2, Column: 18},
  1188  					},
  1189  				},
  1190  			},
  1191  		},
  1192  		{
  1193  			Name: fmt.Sprintf("module: %s - Invalid snake_case with Mixed_Snake_Case", testType),
  1194  			Content: `
  1195  module "Foo_Bar" {
  1196    source = ""
  1197  }`,
  1198  			Config: config,
  1199  			Expected: tflint.Issues{
  1200  				{
  1201  					Rule:    rule,
  1202  					Message: fmt.Sprintf("module name `Foo_Bar` must match the following %s", formatName),
  1203  					Range: hcl.Range{
  1204  						Filename: "tests.tf",
  1205  						Start:    hcl.Pos{Line: 2, Column: 1},
  1206  						End:      hcl.Pos{Line: 2, Column: 17},
  1207  					},
  1208  				},
  1209  			},
  1210  		},
  1211  		{
  1212  			Name: fmt.Sprintf("module: %s - Valid snake_case", testType),
  1213  			Content: `
  1214  module "foo_bar" {
  1215    source = ""
  1216  }`,
  1217  			Config:   config,
  1218  			Expected: tflint.Issues{},
  1219  		},
  1220  		{
  1221  			Name: fmt.Sprintf("module: %s - Valid single word", testType),
  1222  			Content: `
  1223  module "foo" {
  1224    source = ""
  1225  }`,
  1226  			Config:   config,
  1227  			Expected: tflint.Issues{},
  1228  		},
  1229  	}
  1230  
  1231  	for _, tc := range cases {
  1232  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  1233  
  1234  		if err := rule.Check(runner); err != nil {
  1235  			t.Fatalf("Unexpected error occurred: %s", err)
  1236  		}
  1237  
  1238  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  1239  	}
  1240  }
  1241  
  1242  func testModuleMixedSnakeCase(t *testing.T, testType string, config string) {
  1243  	rule := NewTerraformNamingConventionRule()
  1244  
  1245  	cases := []struct {
  1246  		Name     string
  1247  		Content  string
  1248  		Config   string
  1249  		Expected tflint.Issues
  1250  	}{
  1251  		{
  1252  			Name: fmt.Sprintf("module: %s - Invalid mixed_snake_case with dash", testType),
  1253  			Content: `
  1254  module "dash-name" {
  1255    source = ""
  1256  }`,
  1257  			Config: config,
  1258  			Expected: tflint.Issues{
  1259  				{
  1260  					Rule:    rule,
  1261  					Message: "module name `dash-name` must match the following format: mixed_snake_case",
  1262  					Range: hcl.Range{
  1263  						Filename: "tests.tf",
  1264  						Start:    hcl.Pos{Line: 2, Column: 1},
  1265  						End:      hcl.Pos{Line: 2, Column: 19},
  1266  					},
  1267  				},
  1268  			},
  1269  		},
  1270  		{
  1271  			Name: fmt.Sprintf("module: %s - Invalid mixed_snake_case with double underscore", testType),
  1272  			Content: `
  1273  module "Foo__Bar" {
  1274    source = ""
  1275  }`,
  1276  			Config: config,
  1277  			Expected: tflint.Issues{
  1278  				{
  1279  					Rule:    rule,
  1280  					Message: "module name `Foo__Bar` must match the following format: mixed_snake_case",
  1281  					Range: hcl.Range{
  1282  						Filename: "tests.tf",
  1283  						Start:    hcl.Pos{Line: 2, Column: 1},
  1284  						End:      hcl.Pos{Line: 2, Column: 18},
  1285  					},
  1286  				},
  1287  			},
  1288  		},
  1289  		{
  1290  			Name: fmt.Sprintf("module: %s - Invalid mixed_snake_case with underscore tail", testType),
  1291  			Content: `
  1292  module "Foo_Bar_" {
  1293    source = ""
  1294  }`,
  1295  			Config: config,
  1296  			Expected: tflint.Issues{
  1297  				{
  1298  					Rule:    rule,
  1299  					Message: "module name `Foo_Bar_` must match the following format: mixed_snake_case",
  1300  					Range: hcl.Range{
  1301  						Filename: "tests.tf",
  1302  						Start:    hcl.Pos{Line: 2, Column: 1},
  1303  						End:      hcl.Pos{Line: 2, Column: 18},
  1304  					},
  1305  				},
  1306  			},
  1307  		},
  1308  		{
  1309  			Name: fmt.Sprintf("module: %s - Valid snake_case", testType),
  1310  			Content: `
  1311  module "foo_bar" {
  1312    source = ""
  1313  }`,
  1314  			Config:   config,
  1315  			Expected: tflint.Issues{},
  1316  		},
  1317  		{
  1318  			Name: fmt.Sprintf("module: %s - Valid single word", testType),
  1319  			Content: `
  1320  module "foo" {
  1321    source = ""
  1322  }`,
  1323  			Config:   config,
  1324  			Expected: tflint.Issues{},
  1325  		},
  1326  		{
  1327  			Name: fmt.Sprintf("module: %s - Valid Mixed_Snake_Case", testType),
  1328  			Content: `
  1329  module "Foo_Bar" {
  1330    source = ""
  1331  }`,
  1332  			Config:   config,
  1333  			Expected: tflint.Issues{},
  1334  		},
  1335  		{
  1336  			Name: fmt.Sprintf("module: %s - Valid single word with upper characters", testType),
  1337  			Content: `
  1338  module "foo" {
  1339    source = ""
  1340  }`,
  1341  			Config:   config,
  1342  			Expected: tflint.Issues{},
  1343  		},
  1344  		{
  1345  			Name: fmt.Sprintf("module: %s - Valid PascalCase", testType),
  1346  			Content: `
  1347  module "PascalCase" {
  1348    source = ""
  1349  }`,
  1350  			Config:   config,
  1351  			Expected: tflint.Issues{},
  1352  		},
  1353  		{
  1354  			Name: fmt.Sprintf("module: %s - Valid camelCase", testType),
  1355  			Content: `
  1356  module "camelCase" {
  1357    source = ""
  1358  }`,
  1359  			Config:   config,
  1360  			Expected: tflint.Issues{},
  1361  		},
  1362  	}
  1363  
  1364  	for _, tc := range cases {
  1365  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  1366  
  1367  		if err := rule.Check(runner); err != nil {
  1368  			t.Fatalf("Unexpected error occurred: %s", err)
  1369  		}
  1370  
  1371  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  1372  	}
  1373  }
  1374  
  1375  func testModuleDisabled(t *testing.T, testType string, config string) {
  1376  	rule := NewTerraformNamingConventionRule()
  1377  
  1378  	cases := []struct {
  1379  		Name     string
  1380  		Content  string
  1381  		Config   string
  1382  		Expected tflint.Issues
  1383  	}{
  1384  		{
  1385  			Name: fmt.Sprintf("module: %s - Valid mixed_snake_case with dash", testType),
  1386  			Content: `
  1387  module "dash-name" {
  1388    source = ""
  1389  }`,
  1390  			Config:   config,
  1391  			Expected: tflint.Issues{},
  1392  		},
  1393  		{
  1394  			Name: fmt.Sprintf("module: %s - Valid snake_case", testType),
  1395  			Content: `
  1396  module "foo_bar" {
  1397    source = ""
  1398  }`,
  1399  			Config:   config,
  1400  			Expected: tflint.Issues{},
  1401  		},
  1402  		{
  1403  			Name: fmt.Sprintf("module: %s - Valid single word", testType),
  1404  			Content: `
  1405  module "foo" {
  1406    source = ""
  1407  }`,
  1408  			Config:   config,
  1409  			Expected: tflint.Issues{},
  1410  		},
  1411  		{
  1412  			Name: fmt.Sprintf("module: %s - Valid Mixed_Snake_Case", testType),
  1413  			Content: `
  1414  module "Foo_Bar" {
  1415    source = ""
  1416  }`,
  1417  			Config:   config,
  1418  			Expected: tflint.Issues{},
  1419  		},
  1420  		{
  1421  			Name: fmt.Sprintf("module: %s - Valid single word upper characters", testType),
  1422  			Content: `
  1423  module "Foo" {
  1424    source = ""
  1425  }`,
  1426  			Config:   config,
  1427  			Expected: tflint.Issues{},
  1428  		},
  1429  		{
  1430  			Name: fmt.Sprintf("module: %s - Valid PascalCase", testType),
  1431  			Content: `
  1432  module "PascalCase" {
  1433    source = ""
  1434  }`,
  1435  			Config:   config,
  1436  			Expected: tflint.Issues{},
  1437  		},
  1438  		{
  1439  			Name: fmt.Sprintf("module: %s - Valid camelCase", testType),
  1440  			Content: `
  1441  module "camelCase" {
  1442    source = ""
  1443  }`,
  1444  			Config:   config,
  1445  			Expected: tflint.Issues{},
  1446  		},
  1447  	}
  1448  
  1449  	for _, tc := range cases {
  1450  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  1451  
  1452  		if err := rule.Check(runner); err != nil {
  1453  			t.Fatalf("Unexpected error occurred: %s", err)
  1454  		}
  1455  
  1456  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  1457  	}
  1458  }
  1459  
  1460  // Output blocks
  1461  func Test_TerraformNamingConventionRule_Output_DefaultEmpty(t *testing.T) {
  1462  	testOutputSnakeCase(t, "default config", "format: snake_case", `
  1463  rule "terraform_naming_convention" {
  1464    enabled = true
  1465  }`)
  1466  }
  1467  
  1468  func Test_TerraformNamingConventionRule_Output_DefaultFormat(t *testing.T) {
  1469  	testOutputMixedSnakeCase(t, `default config (format="mixed_snake_case")`, `
  1470  rule "terraform_naming_convention" {
  1471    enabled = true
  1472    format  = "mixed_snake_case"
  1473  }`)
  1474  }
  1475  
  1476  func Test_TerraformNamingConventionRule_Output_DefaultCustom(t *testing.T) {
  1477  	testOutputSnakeCase(t, `default config (custom="^[a-z_]+$")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  1478  rule "terraform_naming_convention" {
  1479    enabled = true
  1480    custom  = "^[a-z][a-z]*(_[a-z]+)*$"
  1481  }`)
  1482  }
  1483  
  1484  func Test_TerraformNamingConventionRule_Output_DefaultDisabled(t *testing.T) {
  1485  	testOutputDisabled(t, `default config (format=null)`, `
  1486  rule "terraform_naming_convention" {
  1487    enabled = true
  1488    format  = "none"
  1489  }`)
  1490  }
  1491  
  1492  func Test_TerraformNamingConventionRule_Output_DefaultFormat_OverrideFormat(t *testing.T) {
  1493  	testOutputSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  1494  rule "terraform_naming_convention" {
  1495    enabled = true
  1496    format  = "mixed_snake_case"
  1497  
  1498    output {
  1499      format = "snake_case"
  1500    }
  1501  }`)
  1502  }
  1503  
  1504  func Test_TerraformNamingConventionRule_Output_DefaultFormat_OverrideCustom(t *testing.T) {
  1505  	testOutputSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  1506  rule "terraform_naming_convention" {
  1507    enabled = true
  1508    format  = "mixed_snake_case"
  1509  
  1510    output {
  1511      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  1512    }
  1513  }`)
  1514  }
  1515  
  1516  func Test_TerraformNamingConventionRule_Output_DefaultCustom_OverrideFormat(t *testing.T) {
  1517  	testOutputSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  1518  rule "terraform_naming_convention" {
  1519    enabled = true
  1520    custom  = "^ignored$"
  1521  
  1522    output {
  1523      format = "snake_case"
  1524    }
  1525  }`)
  1526  }
  1527  
  1528  func Test_TerraformNamingConventionRule_Output_DefaultCustom_OverrideCustom(t *testing.T) {
  1529  	testOutputSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  1530  rule "terraform_naming_convention" {
  1531    enabled = true
  1532    custom  = "^ignored$"
  1533  
  1534    output {
  1535      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  1536    }
  1537  }`)
  1538  }
  1539  
  1540  func Test_TerraformNamingConventionRule_Output_DefaultDisabled_OverrideFormat(t *testing.T) {
  1541  	testOutputSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  1542  rule "terraform_naming_convention" {
  1543    enabled = true
  1544    format  = "none"
  1545  
  1546    output {
  1547      format = "snake_case"
  1548    }
  1549  }`)
  1550  }
  1551  
  1552  func Test_TerraformNamingConventionRule_Output_DefaultDisabled_OverrideCustom(t *testing.T) {
  1553  	testOutputSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  1554  rule "terraform_naming_convention" {
  1555    enabled = true
  1556    format  = "none"
  1557  
  1558    output {
  1559      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  1560    }
  1561  }`)
  1562  }
  1563  
  1564  func Test_TerraformNamingConventionRule_Output_DefaultEmpty_OverrideDisabled(t *testing.T) {
  1565  	testOutputDisabled(t, `overridden config (format=null)`, `
  1566  rule "terraform_naming_convention" {
  1567    enabled = true
  1568  
  1569    output {
  1570      format = "none"
  1571    }
  1572  }`)
  1573  }
  1574  
  1575  func Test_TerraformNamingConventionRule_Output_DefaultFormat_OverrideDisabled(t *testing.T) {
  1576  	testOutputDisabled(t, `overridden config (format=null)`, `
  1577  rule "terraform_naming_convention" {
  1578    enabled = true
  1579    format  = "snake_case"
  1580  
  1581    output {
  1582      format = "none"
  1583    }
  1584  }`)
  1585  }
  1586  
  1587  func testOutputSnakeCase(t *testing.T, testType string, formatName string, config string) {
  1588  	rule := NewTerraformNamingConventionRule()
  1589  
  1590  	cases := []struct {
  1591  		Name     string
  1592  		Content  string
  1593  		Config   string
  1594  		Expected tflint.Issues
  1595  	}{
  1596  		{
  1597  			Name: fmt.Sprintf("output: %s - Invalid snake_case with dash", testType),
  1598  			Content: `
  1599  output "dash-name" {
  1600    value = "invalid"
  1601  }`,
  1602  			Config: config,
  1603  			Expected: tflint.Issues{
  1604  				{
  1605  					Rule:    rule,
  1606  					Message: fmt.Sprintf("output name `dash-name` must match the following %s", formatName),
  1607  					Range: hcl.Range{
  1608  						Filename: "tests.tf",
  1609  						Start:    hcl.Pos{Line: 2, Column: 1},
  1610  						End:      hcl.Pos{Line: 2, Column: 19},
  1611  					},
  1612  				},
  1613  			},
  1614  		},
  1615  		{
  1616  			Name: fmt.Sprintf("output: %s - Invalid snake_case with camelCase", testType),
  1617  			Content: `
  1618  output "camelCased" {
  1619    value = "invalid"
  1620  }`,
  1621  			Config: config,
  1622  			Expected: tflint.Issues{
  1623  				{
  1624  					Rule:    rule,
  1625  					Message: fmt.Sprintf("output name `camelCased` must match the following %s", formatName),
  1626  					Range: hcl.Range{
  1627  						Filename: "tests.tf",
  1628  						Start:    hcl.Pos{Line: 2, Column: 1},
  1629  						End:      hcl.Pos{Line: 2, Column: 20},
  1630  					},
  1631  				},
  1632  			},
  1633  		},
  1634  		{
  1635  			Name: fmt.Sprintf("output: %s - Invalid snake_case with double underscore", testType),
  1636  			Content: `
  1637  output "foo__bar" {
  1638    value = "invalid"
  1639  }`,
  1640  			Config: config,
  1641  			Expected: tflint.Issues{
  1642  				{
  1643  					Rule:    rule,
  1644  					Message: fmt.Sprintf("output name `foo__bar` must match the following %s", formatName),
  1645  					Range: hcl.Range{
  1646  						Filename: "tests.tf",
  1647  						Start:    hcl.Pos{Line: 2, Column: 1},
  1648  						End:      hcl.Pos{Line: 2, Column: 18},
  1649  					},
  1650  				},
  1651  			},
  1652  		},
  1653  		{
  1654  			Name: fmt.Sprintf("output: %s - Invalid snake_case with underscore tail", testType),
  1655  			Content: `
  1656  output "foo_bar_" {
  1657    value = "invalid"
  1658  }`,
  1659  			Config: config,
  1660  			Expected: tflint.Issues{
  1661  				{
  1662  					Rule:    rule,
  1663  					Message: fmt.Sprintf("output name `foo_bar_` must match the following %s", formatName),
  1664  					Range: hcl.Range{
  1665  						Filename: "tests.tf",
  1666  						Start:    hcl.Pos{Line: 2, Column: 1},
  1667  						End:      hcl.Pos{Line: 2, Column: 18},
  1668  					},
  1669  				},
  1670  			},
  1671  		},
  1672  		{
  1673  			Name: fmt.Sprintf("output: %s - Invalid snake_case with Mixed_Snake_Case", testType),
  1674  			Content: `
  1675  output "Foo_Bar" {
  1676    value = "invalid"
  1677  }`,
  1678  			Config: config,
  1679  			Expected: tflint.Issues{
  1680  				{
  1681  					Rule:    rule,
  1682  					Message: fmt.Sprintf("output name `Foo_Bar` must match the following %s", formatName),
  1683  					Range: hcl.Range{
  1684  						Filename: "tests.tf",
  1685  						Start:    hcl.Pos{Line: 2, Column: 1},
  1686  						End:      hcl.Pos{Line: 2, Column: 17},
  1687  					},
  1688  				},
  1689  			},
  1690  		},
  1691  		{
  1692  			Name: fmt.Sprintf("output: %s - Valid snake_case", testType),
  1693  			Content: `
  1694  output "foo_bar" {
  1695    value = "valid"
  1696  }`,
  1697  			Config:   config,
  1698  			Expected: tflint.Issues{},
  1699  		},
  1700  		{
  1701  			Name: fmt.Sprintf("output: %s - Valid single word", testType),
  1702  			Content: `
  1703  output "foo" {
  1704    value = "valid"
  1705  }`,
  1706  			Config:   config,
  1707  			Expected: tflint.Issues{},
  1708  		},
  1709  	}
  1710  
  1711  	for _, tc := range cases {
  1712  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  1713  
  1714  		if err := rule.Check(runner); err != nil {
  1715  			t.Fatalf("Unexpected error occurred: %s", err)
  1716  		}
  1717  
  1718  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  1719  	}
  1720  }
  1721  
  1722  func testOutputMixedSnakeCase(t *testing.T, testType string, config string) {
  1723  	rule := NewTerraformNamingConventionRule()
  1724  
  1725  	cases := []struct {
  1726  		Name     string
  1727  		Content  string
  1728  		Config   string
  1729  		Expected tflint.Issues
  1730  	}{
  1731  		{
  1732  			Name: fmt.Sprintf("output: %s - Invalid mixed_snake_case with dash", testType),
  1733  			Content: `
  1734  output "dash-name" {
  1735    value = "invalid"
  1736  }`,
  1737  			Config: config,
  1738  			Expected: tflint.Issues{
  1739  				{
  1740  					Rule:    rule,
  1741  					Message: "output name `dash-name` must match the following format: mixed_snake_case",
  1742  					Range: hcl.Range{
  1743  						Filename: "tests.tf",
  1744  						Start:    hcl.Pos{Line: 2, Column: 1},
  1745  						End:      hcl.Pos{Line: 2, Column: 19},
  1746  					},
  1747  				},
  1748  			},
  1749  		},
  1750  		{
  1751  			Name: fmt.Sprintf("output: %s - Invalid mixed_snake_case with double underscore", testType),
  1752  			Content: `
  1753  output "Foo__Bar" {
  1754    value = "invalid"
  1755  }`,
  1756  			Config: config,
  1757  			Expected: tflint.Issues{
  1758  				{
  1759  					Rule:    rule,
  1760  					Message: "output name `Foo__Bar` must match the following format: mixed_snake_case",
  1761  					Range: hcl.Range{
  1762  						Filename: "tests.tf",
  1763  						Start:    hcl.Pos{Line: 2, Column: 1},
  1764  						End:      hcl.Pos{Line: 2, Column: 18},
  1765  					},
  1766  				},
  1767  			},
  1768  		},
  1769  		{
  1770  			Name: fmt.Sprintf("output: %s - Invalid mixed_snake_case with underscore tail", testType),
  1771  			Content: `
  1772  output "Foo_Bar_" {
  1773    value = "invalid"
  1774  }`,
  1775  			Config: config,
  1776  			Expected: tflint.Issues{
  1777  				{
  1778  					Rule:    rule,
  1779  					Message: "output name `Foo_Bar_` must match the following format: mixed_snake_case",
  1780  					Range: hcl.Range{
  1781  						Filename: "tests.tf",
  1782  						Start:    hcl.Pos{Line: 2, Column: 1},
  1783  						End:      hcl.Pos{Line: 2, Column: 18},
  1784  					},
  1785  				},
  1786  			},
  1787  		},
  1788  		{
  1789  			Name: fmt.Sprintf("output: %s - Valid snake_case", testType),
  1790  			Content: `
  1791  output "foo_bar" {
  1792    value = "valid"
  1793  }`,
  1794  			Config:   config,
  1795  			Expected: tflint.Issues{},
  1796  		},
  1797  		{
  1798  			Name: fmt.Sprintf("output: %s - Valid single word", testType),
  1799  			Content: `
  1800  output "foo" {
  1801    value = "valid"
  1802  }`,
  1803  			Config:   config,
  1804  			Expected: tflint.Issues{},
  1805  		},
  1806  		{
  1807  			Name: fmt.Sprintf("output: %s - Valid Mixed_Snake_Case", testType),
  1808  			Content: `
  1809  output "Foo_Bar" {
  1810    value = "valid"
  1811  }`,
  1812  			Config:   config,
  1813  			Expected: tflint.Issues{},
  1814  		},
  1815  		{
  1816  			Name: fmt.Sprintf("output: %s - Valid single word with upper characters", testType),
  1817  			Content: `
  1818  output "foo" {
  1819    value = "valid"
  1820  }`,
  1821  			Config:   config,
  1822  			Expected: tflint.Issues{},
  1823  		},
  1824  		{
  1825  			Name: fmt.Sprintf("output: %s - Valid PascalCase", testType),
  1826  			Content: `
  1827  output "PascalCase" {
  1828    value = "valid"
  1829  }`,
  1830  			Config:   config,
  1831  			Expected: tflint.Issues{},
  1832  		},
  1833  		{
  1834  			Name: fmt.Sprintf("output: %s - Valid camelCase", testType),
  1835  			Content: `
  1836  output "camelCase" {
  1837    value = "valid"
  1838  }`,
  1839  			Config:   config,
  1840  			Expected: tflint.Issues{},
  1841  		},
  1842  	}
  1843  
  1844  	for _, tc := range cases {
  1845  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  1846  
  1847  		if err := rule.Check(runner); err != nil {
  1848  			t.Fatalf("Unexpected error occurred: %s", err)
  1849  		}
  1850  
  1851  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  1852  	}
  1853  }
  1854  
  1855  func testOutputDisabled(t *testing.T, testType string, config string) {
  1856  	rule := NewTerraformNamingConventionRule()
  1857  
  1858  	cases := []struct {
  1859  		Name     string
  1860  		Content  string
  1861  		Config   string
  1862  		Expected tflint.Issues
  1863  	}{
  1864  		{
  1865  			Name: fmt.Sprintf("output: %s - Valid mixed_snake_case with dash", testType),
  1866  			Content: `
  1867  output "dash-name" {
  1868    value = "valid"
  1869  }`,
  1870  			Config:   config,
  1871  			Expected: tflint.Issues{},
  1872  		},
  1873  		{
  1874  			Name: fmt.Sprintf("output: %s - Valid snake_case", testType),
  1875  			Content: `
  1876  output "foo_bar" {
  1877    value = "valid"
  1878  }`,
  1879  			Config:   config,
  1880  			Expected: tflint.Issues{},
  1881  		},
  1882  		{
  1883  			Name: fmt.Sprintf("output: %s - Valid single word", testType),
  1884  			Content: `
  1885  output "foo" {
  1886    value = "valid"
  1887  }`,
  1888  			Config:   config,
  1889  			Expected: tflint.Issues{},
  1890  		},
  1891  		{
  1892  			Name: fmt.Sprintf("output: %s - Valid Mixed_Snake_Case", testType),
  1893  			Content: `
  1894  output "Foo_Bar" {
  1895    value = "valid"
  1896  }`,
  1897  			Config:   config,
  1898  			Expected: tflint.Issues{},
  1899  		},
  1900  		{
  1901  			Name: fmt.Sprintf("output: %s - Valid single word upper characters", testType),
  1902  			Content: `
  1903  output "Foo" {
  1904    value = "valid"
  1905  }`,
  1906  			Config:   config,
  1907  			Expected: tflint.Issues{},
  1908  		},
  1909  		{
  1910  			Name: fmt.Sprintf("output: %s - Valid PascalCase", testType),
  1911  			Content: `
  1912  output "PascalCase" {
  1913    value = "valid"
  1914  }`,
  1915  			Config:   config,
  1916  			Expected: tflint.Issues{},
  1917  		},
  1918  		{
  1919  			Name: fmt.Sprintf("output: %s - Valid camelCase", testType),
  1920  			Content: `
  1921  output "camelCase" {
  1922    value = "valid"
  1923  }`,
  1924  			Config:   config,
  1925  			Expected: tflint.Issues{},
  1926  		},
  1927  	}
  1928  
  1929  	for _, tc := range cases {
  1930  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  1931  
  1932  		if err := rule.Check(runner); err != nil {
  1933  			t.Fatalf("Unexpected error occurred: %s", err)
  1934  		}
  1935  
  1936  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  1937  	}
  1938  }
  1939  
  1940  // Resource blocks
  1941  func Test_TerraformNamingConventionRule_Resource_DefaultEmpty(t *testing.T) {
  1942  	testResourceSnakeCase(t, "default config", "format: snake_case", `
  1943  rule "terraform_naming_convention" {
  1944    enabled = true
  1945  }`)
  1946  }
  1947  
  1948  func Test_TerraformNamingConventionRule_Resource_DefaultFormat(t *testing.T) {
  1949  	testResourceMixedSnakeCase(t, `default config (format="mixed_snake_case")`, `
  1950  rule "terraform_naming_convention" {
  1951    enabled = true
  1952    format  = "mixed_snake_case"
  1953  }`)
  1954  }
  1955  
  1956  func Test_TerraformNamingConventionRule_Resource_DefaultCustom(t *testing.T) {
  1957  	testResourceSnakeCase(t, `default config (custom="^[a-z_]+$")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  1958  rule "terraform_naming_convention" {
  1959    enabled = true
  1960    custom  = "^[a-z][a-z]*(_[a-z]+)*$"
  1961  }`)
  1962  }
  1963  
  1964  func Test_TerraformNamingConventionRule_Resource_DefaultDisabled(t *testing.T) {
  1965  	testResourceDisabled(t, `default config (format=null)`, `
  1966  rule "terraform_naming_convention" {
  1967    enabled = true
  1968    format  = "none"
  1969  }`)
  1970  }
  1971  
  1972  func Test_TerraformNamingConventionRule_Resource_DefaultFormat_OverrideFormat(t *testing.T) {
  1973  	testResourceSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  1974  rule "terraform_naming_convention" {
  1975    enabled = true
  1976    format  = "mixed_snake_case"
  1977  
  1978    resource {
  1979      format = "snake_case"
  1980    }
  1981  }`)
  1982  }
  1983  
  1984  func Test_TerraformNamingConventionRule_Resource_DefaultFormat_OverrideCustom(t *testing.T) {
  1985  	testResourceSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  1986  rule "terraform_naming_convention" {
  1987    enabled = true
  1988    format  = "mixed_snake_case"
  1989  
  1990    resource {
  1991      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  1992    }
  1993  }`)
  1994  }
  1995  
  1996  func Test_TerraformNamingConventionRule_Resource_DefaultCustom_OverrideFormat(t *testing.T) {
  1997  	testResourceSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  1998  rule "terraform_naming_convention" {
  1999    enabled = true
  2000    custom  = "^ignored$"
  2001  
  2002    resource {
  2003      format = "snake_case"
  2004    }
  2005  }`)
  2006  }
  2007  
  2008  func Test_TerraformNamingConventionRule_Resource_DefaultCustom_OverrideCustom(t *testing.T) {
  2009  	testResourceSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  2010  rule "terraform_naming_convention" {
  2011    enabled = true
  2012    custom  = "^ignored$"
  2013  
  2014    resource {
  2015      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  2016    }
  2017  }`)
  2018  }
  2019  
  2020  func Test_TerraformNamingConventionRule_Resource_DefaultDisabled_OverrideFormat(t *testing.T) {
  2021  	testResourceSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  2022  rule "terraform_naming_convention" {
  2023    enabled = true
  2024    format  = "none"
  2025  
  2026    resource {
  2027      format = "snake_case"
  2028    }
  2029  }`)
  2030  }
  2031  
  2032  func Test_TerraformNamingConventionRule_Resource_DefaultDisabled_OverrideCustom(t *testing.T) {
  2033  	testResourceSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  2034  rule "terraform_naming_convention" {
  2035    enabled = true
  2036    format  = "none"
  2037  
  2038    resource {
  2039      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  2040    }
  2041  }`)
  2042  }
  2043  
  2044  func Test_TerraformNamingConventionRule_Resource_DefaultEmpty_OverrideDisabled(t *testing.T) {
  2045  	testResourceDisabled(t, `overridden config (format=null)`, `
  2046  rule "terraform_naming_convention" {
  2047    enabled = true
  2048  
  2049    resource {
  2050      format = "none"
  2051    }
  2052  }`)
  2053  }
  2054  
  2055  func Test_TerraformNamingConventionRule_Resource_DefaultFormat_OverrideDisabled(t *testing.T) {
  2056  	testResourceDisabled(t, `overridden config (format=null)`, `
  2057  rule "terraform_naming_convention" {
  2058    enabled = true
  2059    format  = "snake_case"
  2060  
  2061    resource {
  2062      format = "none"
  2063    }
  2064  }`)
  2065  }
  2066  
  2067  func testResourceSnakeCase(t *testing.T, testType string, formatName string, config string) {
  2068  	rule := NewTerraformNamingConventionRule()
  2069  
  2070  	cases := []struct {
  2071  		Name     string
  2072  		Content  string
  2073  		Config   string
  2074  		Expected tflint.Issues
  2075  	}{
  2076  		{
  2077  			Name: fmt.Sprintf("resource: %s - Invalid snake_case with dash", testType),
  2078  			Content: `
  2079  resource "aws_eip" "dash-name" {
  2080  }`,
  2081  			Config: config,
  2082  			Expected: tflint.Issues{
  2083  				{
  2084  					Rule:    rule,
  2085  					Message: fmt.Sprintf("resource name `dash-name` must match the following %s", formatName),
  2086  					Range: hcl.Range{
  2087  						Filename: "tests.tf",
  2088  						Start:    hcl.Pos{Line: 2, Column: 1},
  2089  						End:      hcl.Pos{Line: 2, Column: 31},
  2090  					},
  2091  				},
  2092  			},
  2093  		},
  2094  		{
  2095  			Name: fmt.Sprintf("resource: %s - Invalid snake_case with camelCase", testType),
  2096  			Content: `
  2097  resource "aws_eip" "camelCased" {
  2098  }`,
  2099  			Config: config,
  2100  			Expected: tflint.Issues{
  2101  				{
  2102  					Rule:    rule,
  2103  					Message: fmt.Sprintf("resource name `camelCased` must match the following %s", formatName),
  2104  					Range: hcl.Range{
  2105  						Filename: "tests.tf",
  2106  						Start:    hcl.Pos{Line: 2, Column: 1},
  2107  						End:      hcl.Pos{Line: 2, Column: 32},
  2108  					},
  2109  				},
  2110  			},
  2111  		},
  2112  		{
  2113  			Name: fmt.Sprintf("resource: %s - Invalid snake_case with double underscore", testType),
  2114  			Content: `
  2115  resource "aws_eip" "foo__bar" {
  2116  }`,
  2117  			Config: config,
  2118  			Expected: tflint.Issues{
  2119  				{
  2120  					Rule:    rule,
  2121  					Message: fmt.Sprintf("resource name `foo__bar` must match the following %s", formatName),
  2122  					Range: hcl.Range{
  2123  						Filename: "tests.tf",
  2124  						Start:    hcl.Pos{Line: 2, Column: 1},
  2125  						End:      hcl.Pos{Line: 2, Column: 30},
  2126  					},
  2127  				},
  2128  			},
  2129  		},
  2130  		{
  2131  			Name: fmt.Sprintf("resource: %s - Invalid snake_case with underscore tail", testType),
  2132  			Content: `
  2133  resource "aws_eip" "foo_bar_" {
  2134  }`,
  2135  			Config: config,
  2136  			Expected: tflint.Issues{
  2137  				{
  2138  					Rule:    rule,
  2139  					Message: fmt.Sprintf("resource name `foo_bar_` must match the following %s", formatName),
  2140  					Range: hcl.Range{
  2141  						Filename: "tests.tf",
  2142  						Start:    hcl.Pos{Line: 2, Column: 1},
  2143  						End:      hcl.Pos{Line: 2, Column: 30},
  2144  					},
  2145  				},
  2146  			},
  2147  		},
  2148  		{
  2149  			Name: fmt.Sprintf("resource: %s - Invalid snake_case with Mixed_Snake_Case", testType),
  2150  			Content: `
  2151  resource "aws_eip" "Foo_Bar" {
  2152  }`,
  2153  			Config: config,
  2154  			Expected: tflint.Issues{
  2155  				{
  2156  					Rule:    rule,
  2157  					Message: fmt.Sprintf("resource name `Foo_Bar` must match the following %s", formatName),
  2158  					Range: hcl.Range{
  2159  						Filename: "tests.tf",
  2160  						Start:    hcl.Pos{Line: 2, Column: 1},
  2161  						End:      hcl.Pos{Line: 2, Column: 29},
  2162  					},
  2163  				},
  2164  			},
  2165  		},
  2166  		{
  2167  			Name: fmt.Sprintf("resource: %s - Valid snake_case", testType),
  2168  			Content: `
  2169  resource "aws_eip" "foo_bar" {
  2170  }`,
  2171  			Config:   config,
  2172  			Expected: tflint.Issues{},
  2173  		},
  2174  		{
  2175  			Name: fmt.Sprintf("resource: %s - Valid single word", testType),
  2176  			Content: `
  2177  resource "aws_eip" "foo" {
  2178  }`,
  2179  			Config:   config,
  2180  			Expected: tflint.Issues{},
  2181  		},
  2182  	}
  2183  
  2184  	for _, tc := range cases {
  2185  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  2186  
  2187  		if err := rule.Check(runner); err != nil {
  2188  			t.Fatalf("Unexpected error occurred: %s", err)
  2189  		}
  2190  
  2191  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  2192  	}
  2193  }
  2194  
  2195  func testResourceMixedSnakeCase(t *testing.T, testType string, config string) {
  2196  	rule := NewTerraformNamingConventionRule()
  2197  
  2198  	cases := []struct {
  2199  		Name     string
  2200  		Content  string
  2201  		Config   string
  2202  		Expected tflint.Issues
  2203  	}{
  2204  		{
  2205  			Name: fmt.Sprintf("resource: %s - Invalid mixed_snake_case with dash", testType),
  2206  			Content: `
  2207  resource "aws_eip" "dash-name" {
  2208  }`,
  2209  			Config: config,
  2210  			Expected: tflint.Issues{
  2211  				{
  2212  					Rule:    rule,
  2213  					Message: "resource name `dash-name` must match the following format: mixed_snake_case",
  2214  					Range: hcl.Range{
  2215  						Filename: "tests.tf",
  2216  						Start:    hcl.Pos{Line: 2, Column: 1},
  2217  						End:      hcl.Pos{Line: 2, Column: 31},
  2218  					},
  2219  				},
  2220  			},
  2221  		},
  2222  		{
  2223  			Name: fmt.Sprintf("resource: %s - Invalid mixed_snake_case with double underscore", testType),
  2224  			Content: `
  2225  resource "aws_eip" "Foo__Bar" {
  2226  }`,
  2227  			Config: config,
  2228  			Expected: tflint.Issues{
  2229  				{
  2230  					Rule:    rule,
  2231  					Message: "resource name `Foo__Bar` must match the following format: mixed_snake_case",
  2232  					Range: hcl.Range{
  2233  						Filename: "tests.tf",
  2234  						Start:    hcl.Pos{Line: 2, Column: 1},
  2235  						End:      hcl.Pos{Line: 2, Column: 30},
  2236  					},
  2237  				},
  2238  			},
  2239  		},
  2240  		{
  2241  			Name: fmt.Sprintf("resource: %s - Invalid mixed_snake_case with underscore tail", testType),
  2242  			Content: `
  2243  resource "aws_eip" "Foo_Bar_" {
  2244  }`,
  2245  			Config: config,
  2246  			Expected: tflint.Issues{
  2247  				{
  2248  					Rule:    rule,
  2249  					Message: "resource name `Foo_Bar_` must match the following format: mixed_snake_case",
  2250  					Range: hcl.Range{
  2251  						Filename: "tests.tf",
  2252  						Start:    hcl.Pos{Line: 2, Column: 1},
  2253  						End:      hcl.Pos{Line: 2, Column: 30},
  2254  					},
  2255  				},
  2256  			},
  2257  		},
  2258  		{
  2259  			Name: fmt.Sprintf("resource: %s - Valid snake_case", testType),
  2260  			Content: `
  2261  resource "aws_eip" "foo_bar" {
  2262  }`,
  2263  			Config:   config,
  2264  			Expected: tflint.Issues{},
  2265  		},
  2266  		{
  2267  			Name: fmt.Sprintf("resource: %s - Valid single word", testType),
  2268  			Content: `
  2269  resource "aws_eip" "foo" {
  2270  }`,
  2271  			Config:   config,
  2272  			Expected: tflint.Issues{},
  2273  		},
  2274  		{
  2275  			Name: fmt.Sprintf("resource: %s - Valid Mixed_Snake_Case", testType),
  2276  			Content: `
  2277  resource "aws_eip" "Foo_Bar" {
  2278  }`,
  2279  			Config:   config,
  2280  			Expected: tflint.Issues{},
  2281  		},
  2282  		{
  2283  			Name: fmt.Sprintf("resource: %s - Valid single word with upper characters", testType),
  2284  			Content: `
  2285  resource "aws_eip" "foo" {
  2286  }`,
  2287  			Config:   config,
  2288  			Expected: tflint.Issues{},
  2289  		},
  2290  		{
  2291  			Name: fmt.Sprintf("resource: %s - Valid PascalCase", testType),
  2292  			Content: `
  2293  resource "aws_eip" "PascalCase" {
  2294  }`,
  2295  			Config:   config,
  2296  			Expected: tflint.Issues{},
  2297  		},
  2298  		{
  2299  			Name: fmt.Sprintf("resource: %s - Valid camelCase", testType),
  2300  			Content: `
  2301  resource "aws_eip" "camelCase" {
  2302  }`,
  2303  			Config:   config,
  2304  			Expected: tflint.Issues{},
  2305  		},
  2306  	}
  2307  
  2308  	for _, tc := range cases {
  2309  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  2310  
  2311  		if err := rule.Check(runner); err != nil {
  2312  			t.Fatalf("Unexpected error occurred: %s", err)
  2313  		}
  2314  
  2315  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  2316  	}
  2317  }
  2318  
  2319  func testResourceDisabled(t *testing.T, testType string, config string) {
  2320  	rule := NewTerraformNamingConventionRule()
  2321  
  2322  	cases := []struct {
  2323  		Name     string
  2324  		Content  string
  2325  		Config   string
  2326  		Expected tflint.Issues
  2327  	}{
  2328  		{
  2329  			Name: fmt.Sprintf("resource: %s - Valid mixed_snake_case with dash", testType),
  2330  			Content: `
  2331  resource "aws_eip" "dash-name" {
  2332  }`,
  2333  			Config:   config,
  2334  			Expected: tflint.Issues{},
  2335  		},
  2336  		{
  2337  			Name: fmt.Sprintf("resource: %s - Valid snake_case", testType),
  2338  			Content: `
  2339  resource "aws_eip" "foo_bar" {
  2340  }`,
  2341  			Config:   config,
  2342  			Expected: tflint.Issues{},
  2343  		},
  2344  		{
  2345  			Name: fmt.Sprintf("resource: %s - Valid single word", testType),
  2346  			Content: `
  2347  resource "aws_eip" "foo" {
  2348  }`,
  2349  			Config:   config,
  2350  			Expected: tflint.Issues{},
  2351  		},
  2352  		{
  2353  			Name: fmt.Sprintf("resource: %s - Valid Mixed_Snake_Case", testType),
  2354  			Content: `
  2355  resource "aws_eip" "Foo_Bar" {
  2356  }`,
  2357  			Config:   config,
  2358  			Expected: tflint.Issues{},
  2359  		},
  2360  		{
  2361  			Name: fmt.Sprintf("resource: %s - Valid single word upper characters", testType),
  2362  			Content: `
  2363  resource "aws_eip" "Foo" {
  2364  }`,
  2365  			Config:   config,
  2366  			Expected: tflint.Issues{},
  2367  		},
  2368  		{
  2369  			Name: fmt.Sprintf("resource: %s - Valid PascalCase", testType),
  2370  			Content: `
  2371  resource "aws_eip" "PascalCase" {
  2372  }`,
  2373  			Config:   config,
  2374  			Expected: tflint.Issues{},
  2375  		},
  2376  		{
  2377  			Name: fmt.Sprintf("resource: %s - Valid camelCase", testType),
  2378  			Content: `
  2379  resource "aws_eip" "camelCase" {
  2380  }`,
  2381  			Config:   config,
  2382  			Expected: tflint.Issues{},
  2383  		},
  2384  	}
  2385  
  2386  	for _, tc := range cases {
  2387  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  2388  
  2389  		if err := rule.Check(runner); err != nil {
  2390  			t.Fatalf("Unexpected error occurred: %s", err)
  2391  		}
  2392  
  2393  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  2394  	}
  2395  }
  2396  
  2397  // Variable blocks
  2398  func Test_TerraformNamingConventionRule_Variable_DefaultEmpty(t *testing.T) {
  2399  	testVariableSnakeCase(t, "default config", "format: snake_case", `
  2400  rule "terraform_naming_convention" {
  2401    enabled = true
  2402  }`)
  2403  }
  2404  
  2405  func Test_TerraformNamingConventionRule_Variable_DefaultFormat(t *testing.T) {
  2406  	testVariableMixedSnakeCase(t, `default config (format="mixed_snake_case")`, `
  2407  rule "terraform_naming_convention" {
  2408    enabled = true
  2409    format  = "mixed_snake_case"
  2410  }`)
  2411  }
  2412  
  2413  func Test_TerraformNamingConventionRule_Variable_DefaultCustom(t *testing.T) {
  2414  	testVariableSnakeCase(t, `default config (custom="^[a-z_]+$")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  2415  rule "terraform_naming_convention" {
  2416    enabled = true
  2417    custom  = "^[a-z][a-z]*(_[a-z]+)*$"
  2418  }`)
  2419  }
  2420  
  2421  func Test_TerraformNamingConventionRule_Variable_DefaultDisabled(t *testing.T) {
  2422  	testVariableDisabled(t, `default config (format=null)`, `
  2423  rule "terraform_naming_convention" {
  2424    enabled = true
  2425    format  = "none"
  2426  }`)
  2427  }
  2428  
  2429  func Test_TerraformNamingConventionRule_Variable_DefaultFormat_OverrideFormat(t *testing.T) {
  2430  	testVariableSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  2431  rule "terraform_naming_convention" {
  2432    enabled = true
  2433    format  = "mixed_snake_case"
  2434  
  2435    variable {
  2436      format = "snake_case"
  2437    }
  2438  }`)
  2439  }
  2440  
  2441  func Test_TerraformNamingConventionRule_Variable_DefaultFormat_OverrideCustom(t *testing.T) {
  2442  	testVariableSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  2443  rule "terraform_naming_convention" {
  2444    enabled = true
  2445    format  = "mixed_snake_case"
  2446  
  2447    variable {
  2448      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  2449    }
  2450  }`)
  2451  }
  2452  
  2453  func Test_TerraformNamingConventionRule_Variable_DefaultCustom_OverrideFormat(t *testing.T) {
  2454  	testVariableSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  2455  rule "terraform_naming_convention" {
  2456    enabled = true
  2457    custom  = "^ignored$"
  2458  
  2459    variable {
  2460      format = "snake_case"
  2461    }
  2462  }`)
  2463  }
  2464  
  2465  func Test_TerraformNamingConventionRule_Variable_DefaultCustom_OverrideCustom(t *testing.T) {
  2466  	testVariableSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  2467  rule "terraform_naming_convention" {
  2468    enabled = true
  2469    custom  = "^ignored$"
  2470  
  2471    variable {
  2472      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  2473    }
  2474  }`)
  2475  }
  2476  
  2477  func Test_TerraformNamingConventionRule_Variable_DefaultDisabled_OverrideFormat(t *testing.T) {
  2478  	testVariableSnakeCase(t, `overridden config (format="snake_case")`, "format: snake_case", `
  2479  rule "terraform_naming_convention" {
  2480    enabled = true
  2481    format  = "none"
  2482  
  2483    variable {
  2484      format = "snake_case"
  2485    }
  2486  }`)
  2487  }
  2488  
  2489  func Test_TerraformNamingConventionRule_Variable_DefaultDisabled_OverrideCustom(t *testing.T) {
  2490  	testVariableSnakeCase(t, `overridden config (format="snake_case")`, "RegExp: ^[a-z][a-z]*(_[a-z]+)*$", `
  2491  rule "terraform_naming_convention" {
  2492    enabled = true
  2493    format  = "none"
  2494  
  2495    variable {
  2496      custom = "^[a-z][a-z]*(_[a-z]+)*$"
  2497    }
  2498  }`)
  2499  }
  2500  
  2501  func Test_TerraformNamingConventionRule_Variable_DefaultEmpty_OverrideDisabled(t *testing.T) {
  2502  	testVariableDisabled(t, `overridden config (format=null)`, `
  2503  rule "terraform_naming_convention" {
  2504    enabled = true
  2505  
  2506    variable {
  2507      format = "none"
  2508    }
  2509  }`)
  2510  }
  2511  
  2512  func Test_TerraformNamingConventionRule_Variable_DefaultFormat_OverrideDisabled(t *testing.T) {
  2513  	testVariableDisabled(t, `overridden config (format=null)`, `
  2514  rule "terraform_naming_convention" {
  2515    enabled = true
  2516    format  = "snake_case"
  2517  
  2518    variable {
  2519      format = "none"
  2520    }
  2521  }`)
  2522  }
  2523  
  2524  func testVariableSnakeCase(t *testing.T, testType string, formatName string, config string) {
  2525  	rule := NewTerraformNamingConventionRule()
  2526  
  2527  	cases := []struct {
  2528  		Name     string
  2529  		Content  string
  2530  		Config   string
  2531  		Expected tflint.Issues
  2532  	}{
  2533  		{
  2534  			Name: fmt.Sprintf("variable: %s - Invalid snake_case with dash", testType),
  2535  			Content: `
  2536  variable "dash-name" {
  2537    description = "invalid"
  2538  }`,
  2539  			Config: config,
  2540  			Expected: tflint.Issues{
  2541  				{
  2542  					Rule:    rule,
  2543  					Message: fmt.Sprintf("variable name `dash-name` must match the following %s", formatName),
  2544  					Range: hcl.Range{
  2545  						Filename: "tests.tf",
  2546  						Start:    hcl.Pos{Line: 2, Column: 1},
  2547  						End:      hcl.Pos{Line: 2, Column: 21},
  2548  					},
  2549  				},
  2550  			},
  2551  		},
  2552  		{
  2553  			Name: fmt.Sprintf("variable: %s - Invalid snake_case with camelCase", testType),
  2554  			Content: `
  2555  variable "camelCased" {
  2556    description = "invalid"
  2557  }`,
  2558  			Config: config,
  2559  			Expected: tflint.Issues{
  2560  				{
  2561  					Rule:    rule,
  2562  					Message: fmt.Sprintf("variable name `camelCased` must match the following %s", formatName),
  2563  					Range: hcl.Range{
  2564  						Filename: "tests.tf",
  2565  						Start:    hcl.Pos{Line: 2, Column: 1},
  2566  						End:      hcl.Pos{Line: 2, Column: 22},
  2567  					},
  2568  				},
  2569  			},
  2570  		},
  2571  		{
  2572  			Name: fmt.Sprintf("variable: %s - Invalid snake_case with double underscore", testType),
  2573  			Content: `
  2574  variable "foo__bar" {
  2575    description = "invalid"
  2576  }`,
  2577  			Config: config,
  2578  			Expected: tflint.Issues{
  2579  				{
  2580  					Rule:    rule,
  2581  					Message: fmt.Sprintf("variable name `foo__bar` must match the following %s", formatName),
  2582  					Range: hcl.Range{
  2583  						Filename: "tests.tf",
  2584  						Start:    hcl.Pos{Line: 2, Column: 1},
  2585  						End:      hcl.Pos{Line: 2, Column: 20},
  2586  					},
  2587  				},
  2588  			},
  2589  		},
  2590  		{
  2591  			Name: fmt.Sprintf("variable: %s - Invalid snake_case with underscore tail", testType),
  2592  			Content: `
  2593  variable "foo_bar_" {
  2594    description = "invalid"
  2595  }`,
  2596  			Config: config,
  2597  			Expected: tflint.Issues{
  2598  				{
  2599  					Rule:    rule,
  2600  					Message: fmt.Sprintf("variable name `foo_bar_` must match the following %s", formatName),
  2601  					Range: hcl.Range{
  2602  						Filename: "tests.tf",
  2603  						Start:    hcl.Pos{Line: 2, Column: 1},
  2604  						End:      hcl.Pos{Line: 2, Column: 20},
  2605  					},
  2606  				},
  2607  			},
  2608  		},
  2609  		{
  2610  			Name: fmt.Sprintf("variable: %s - Invalid snake_case with Mixed_Snake_Case", testType),
  2611  			Content: `
  2612  variable "Foo_Bar" {
  2613    description = "invalid"
  2614  }`,
  2615  			Config: config,
  2616  			Expected: tflint.Issues{
  2617  				{
  2618  					Rule:    rule,
  2619  					Message: fmt.Sprintf("variable name `Foo_Bar` must match the following %s", formatName),
  2620  					Range: hcl.Range{
  2621  						Filename: "tests.tf",
  2622  						Start:    hcl.Pos{Line: 2, Column: 1},
  2623  						End:      hcl.Pos{Line: 2, Column: 19},
  2624  					},
  2625  				},
  2626  			},
  2627  		},
  2628  		{
  2629  			Name: fmt.Sprintf("variable: %s - Valid snake_case", testType),
  2630  			Content: `
  2631  variable "foo_bar" {
  2632    description = "valid"
  2633  }`,
  2634  			Config:   config,
  2635  			Expected: tflint.Issues{},
  2636  		},
  2637  		{
  2638  			Name: fmt.Sprintf("variable: %s - Valid single word", testType),
  2639  			Content: `
  2640  variable "foo" {
  2641    description = "valid"
  2642  }`,
  2643  			Config:   config,
  2644  			Expected: tflint.Issues{},
  2645  		},
  2646  	}
  2647  
  2648  	for _, tc := range cases {
  2649  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  2650  
  2651  		if err := rule.Check(runner); err != nil {
  2652  			t.Fatalf("Unexpected error occurred: %s", err)
  2653  		}
  2654  
  2655  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  2656  	}
  2657  }
  2658  
  2659  func testVariableMixedSnakeCase(t *testing.T, testType string, config string) {
  2660  	rule := NewTerraformNamingConventionRule()
  2661  
  2662  	cases := []struct {
  2663  		Name     string
  2664  		Content  string
  2665  		Config   string
  2666  		Expected tflint.Issues
  2667  	}{
  2668  		{
  2669  			Name: fmt.Sprintf("variable: %s - Invalid mixed_snake_case with dash", testType),
  2670  			Content: `
  2671  variable "dash-name" {
  2672    description = "invalid"
  2673  }`,
  2674  			Config: config,
  2675  			Expected: tflint.Issues{
  2676  				{
  2677  					Rule:    rule,
  2678  					Message: "variable name `dash-name` must match the following format: mixed_snake_case",
  2679  					Range: hcl.Range{
  2680  						Filename: "tests.tf",
  2681  						Start:    hcl.Pos{Line: 2, Column: 1},
  2682  						End:      hcl.Pos{Line: 2, Column: 21},
  2683  					},
  2684  				},
  2685  			},
  2686  		},
  2687  		{
  2688  			Name: fmt.Sprintf("variable: %s - Invalid mixed_snake_case with double underscore", testType),
  2689  			Content: `
  2690  variable "Foo__Bar" {
  2691    description = "invalid"
  2692  }`,
  2693  			Config: config,
  2694  			Expected: tflint.Issues{
  2695  				{
  2696  					Rule:    rule,
  2697  					Message: "variable name `Foo__Bar` must match the following format: mixed_snake_case",
  2698  					Range: hcl.Range{
  2699  						Filename: "tests.tf",
  2700  						Start:    hcl.Pos{Line: 2, Column: 1},
  2701  						End:      hcl.Pos{Line: 2, Column: 20},
  2702  					},
  2703  				},
  2704  			},
  2705  		},
  2706  		{
  2707  			Name: fmt.Sprintf("variable: %s - Invalid mixed_snake_case with underscore tail", testType),
  2708  			Content: `
  2709  variable "Foo_Bar_" {
  2710    description = "invalid"
  2711  }`,
  2712  			Config: config,
  2713  			Expected: tflint.Issues{
  2714  				{
  2715  					Rule:    rule,
  2716  					Message: "variable name `Foo_Bar_` must match the following format: mixed_snake_case",
  2717  					Range: hcl.Range{
  2718  						Filename: "tests.tf",
  2719  						Start:    hcl.Pos{Line: 2, Column: 1},
  2720  						End:      hcl.Pos{Line: 2, Column: 20},
  2721  					},
  2722  				},
  2723  			},
  2724  		},
  2725  		{
  2726  			Name: fmt.Sprintf("variable: %s - Valid snake_case", testType),
  2727  			Content: `
  2728  variable "foo_bar" {
  2729    description = "valid"
  2730  }`,
  2731  			Config:   config,
  2732  			Expected: tflint.Issues{},
  2733  		},
  2734  		{
  2735  			Name: fmt.Sprintf("variable: %s - Valid single word", testType),
  2736  			Content: `
  2737  variable "foo" {
  2738    description = "valid"
  2739  }`,
  2740  			Config:   config,
  2741  			Expected: tflint.Issues{},
  2742  		},
  2743  		{
  2744  			Name: fmt.Sprintf("variable: %s - Valid Mixed_Snake_Case", testType),
  2745  			Content: `
  2746  variable "Foo_Bar" {
  2747    description = "valid"
  2748  }`,
  2749  			Config:   config,
  2750  			Expected: tflint.Issues{},
  2751  		},
  2752  		{
  2753  			Name: fmt.Sprintf("variable: %s - Valid single word with upper characters", testType),
  2754  			Content: `
  2755  variable "foo" {
  2756    description = "valid"
  2757  }`,
  2758  			Config:   config,
  2759  			Expected: tflint.Issues{},
  2760  		},
  2761  		{
  2762  			Name: fmt.Sprintf("variable: %s - Valid PascalCase", testType),
  2763  			Content: `
  2764  variable "PascalCase" {
  2765    description = "valid"
  2766  }`,
  2767  			Config:   config,
  2768  			Expected: tflint.Issues{},
  2769  		},
  2770  		{
  2771  			Name: fmt.Sprintf("variable: %s - Valid camelCase", testType),
  2772  			Content: `
  2773  variable "camelCase" {
  2774    description = "valid"
  2775  }`,
  2776  			Config:   config,
  2777  			Expected: tflint.Issues{},
  2778  		},
  2779  	}
  2780  
  2781  	for _, tc := range cases {
  2782  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  2783  
  2784  		if err := rule.Check(runner); err != nil {
  2785  			t.Fatalf("Unexpected error occurred: %s", err)
  2786  		}
  2787  
  2788  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  2789  	}
  2790  }
  2791  
  2792  func testVariableDisabled(t *testing.T, testType string, config string) {
  2793  	rule := NewTerraformNamingConventionRule()
  2794  
  2795  	cases := []struct {
  2796  		Name     string
  2797  		Content  string
  2798  		Config   string
  2799  		Expected tflint.Issues
  2800  	}{
  2801  		{
  2802  			Name: fmt.Sprintf("variable: %s - Valid mixed_snake_case with dash", testType),
  2803  			Content: `
  2804  variable "dash-name" {
  2805    description = "valid"
  2806  }`,
  2807  			Config:   config,
  2808  			Expected: tflint.Issues{},
  2809  		},
  2810  		{
  2811  			Name: fmt.Sprintf("variable: %s - Valid snake_case", testType),
  2812  			Content: `
  2813  variable "foo_bar" {
  2814    description = "valid"
  2815  }`,
  2816  			Config:   config,
  2817  			Expected: tflint.Issues{},
  2818  		},
  2819  		{
  2820  			Name: fmt.Sprintf("variable: %s - Valid single word", testType),
  2821  			Content: `
  2822  variable "foo" {
  2823    description = "valid"
  2824  }`,
  2825  			Config:   config,
  2826  			Expected: tflint.Issues{},
  2827  		},
  2828  		{
  2829  			Name: fmt.Sprintf("variable: %s - Valid Mixed_Snake_Case", testType),
  2830  			Content: `
  2831  variable "Foo_Bar" {
  2832    description = "valid"
  2833  }`,
  2834  			Config:   config,
  2835  			Expected: tflint.Issues{},
  2836  		},
  2837  		{
  2838  			Name: fmt.Sprintf("variable: %s - Valid single word upper characters", testType),
  2839  			Content: `
  2840  variable "Foo" {
  2841    description = "valid"
  2842  }`,
  2843  			Config:   config,
  2844  			Expected: tflint.Issues{},
  2845  		},
  2846  		{
  2847  			Name: fmt.Sprintf("variable: %s - Valid PascalCase", testType),
  2848  			Content: `
  2849  variable "PascalCase" {
  2850    description = "valid"
  2851  }`,
  2852  			Config:   config,
  2853  			Expected: tflint.Issues{},
  2854  		},
  2855  		{
  2856  			Name: fmt.Sprintf("variable: %s - Valid camelCase", testType),
  2857  			Content: `
  2858  variable "camelCase" {
  2859    description = "valid"
  2860  }`,
  2861  			Config:   config,
  2862  			Expected: tflint.Issues{},
  2863  		},
  2864  	}
  2865  
  2866  	for _, tc := range cases {
  2867  		runner := tflint.TestRunnerWithConfig(t, map[string]string{"tests.tf": tc.Content}, loadConfigFromNamingConventionTempFile(t, tc.Config))
  2868  
  2869  		if err := rule.Check(runner); err != nil {
  2870  			t.Fatalf("Unexpected error occurred: %s", err)
  2871  		}
  2872  
  2873  		tflint.AssertIssues(t, tc.Expected, runner.Issues)
  2874  	}
  2875  }
  2876  
  2877  // TODO: Replace with TestRunner
  2878  func loadConfigFromNamingConventionTempFile(t *testing.T, content string) *tflint.Config {
  2879  	if content == "" {
  2880  		return tflint.EmptyConfig()
  2881  	}
  2882  
  2883  	tmpfile, err := ioutil.TempFile("", "terraform_naming_convention")
  2884  	if err != nil {
  2885  		t.Fatal(err)
  2886  	}
  2887  	defer os.Remove(tmpfile.Name())
  2888  
  2889  	if _, err := tmpfile.Write([]byte(content)); err != nil {
  2890  		t.Fatal(err)
  2891  	}
  2892  	config, err := tflint.LoadConfig(tmpfile.Name())
  2893  	if err != nil {
  2894  		t.Fatal(err)
  2895  	}
  2896  	return config
  2897  }