github.com/wata727/tflint@v0.12.2-0.20191013070026-96dd0d36f385/cmd/cli_test.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "os" 8 "path/filepath" 9 "strings" 10 "testing" 11 12 "github.com/fatih/color" 13 "github.com/golang/mock/gomock" 14 hcl "github.com/hashicorp/hcl/v2" 15 "github.com/hashicorp/terraform/configs" 16 "github.com/hashicorp/terraform/terraform" 17 "github.com/wata727/tflint/rules" 18 "github.com/wata727/tflint/tflint" 19 ) 20 21 func TestCLIRun__noIssuesFound(t *testing.T) { 22 cases := []struct { 23 Name string 24 Command string 25 LoadErr error 26 Status int 27 Stdout string 28 Stderr string 29 }{ 30 { 31 Name: "print version", 32 Command: "./tflint --version", 33 Status: ExitCodeOK, 34 Stdout: fmt.Sprintf("TFLint version %s", tflint.Version), 35 }, 36 { 37 Name: "print help", 38 Command: "./tflint --help", 39 Status: ExitCodeOK, 40 Stdout: "Application Options:", 41 }, 42 { 43 Name: "no options", 44 Command: "./tflint", 45 Status: ExitCodeOK, 46 Stdout: "", 47 }, 48 { 49 Name: "specify format", 50 Command: "./tflint --format json", 51 Status: ExitCodeOK, 52 Stdout: "[]", 53 }, 54 { 55 Name: "`--force` option", 56 Command: "./tflint --force", 57 Status: ExitCodeOK, 58 Stdout: "", 59 }, 60 { 61 Name: "loading errors are occurred", 62 Command: "./tflint", 63 LoadErr: errors.New("Load error occurred"), 64 Status: ExitCodeError, 65 Stderr: "Load error occurred", 66 }, 67 { 68 Name: "removed `debug` options", 69 Command: "./tflint --debug", 70 Status: ExitCodeError, 71 Stderr: "`debug` option was removed in v0.8.0. Please set `TFLINT_LOG` environment variables instead", 72 }, 73 { 74 Name: "removed `fast` option", 75 Command: "./tflint --fast", 76 Status: ExitCodeError, 77 Stderr: "`fast` option was removed in v0.9.0. The `aws_instance_invalid_ami` rule is already fast enough", 78 }, 79 { 80 Name: "removed `--error-with-issues` option", 81 Command: "./tflint --error-with-issues", 82 Status: ExitCodeError, 83 Stderr: "`error-with-issues` option was removed in v0.9.0. The behavior is now default", 84 }, 85 { 86 Name: "removed `--quiet` option", 87 Command: "./tflint --quiet", 88 Status: ExitCodeError, 89 Stderr: "`quiet` option was removed in v0.11.0. The behavior is now default", 90 }, 91 { 92 Name: "removed `--ignore-rule` option", 93 Command: "./tflint --ignore-rule aws_instance_invalid_type", 94 Status: ExitCodeError, 95 Stderr: "`ignore-rule` option was removed in v0.12.0. Please use `--disable-rule` instead", 96 }, 97 { 98 Name: "invalid options", 99 Command: "./tflint --unknown", 100 Status: ExitCodeError, 101 Stderr: "`unknown` is unknown option. Please run `tflint --help`", 102 }, 103 { 104 Name: "invalid format", 105 Command: "./tflint --format awesome", 106 Status: ExitCodeError, 107 Stderr: "Invalid value `awesome' for option", 108 }, 109 } 110 111 ctrl := gomock.NewController(t) 112 defer ctrl.Finish() 113 114 for _, tc := range cases { 115 outStream, errStream := new(bytes.Buffer), new(bytes.Buffer) 116 cli := &CLI{ 117 outStream: outStream, 118 errStream: errStream, 119 testMode: true, 120 } 121 122 loader := tflint.NewMockAbstractLoader(ctrl) 123 loader.EXPECT().LoadConfig(".").Return(configs.NewEmptyConfig(), tc.LoadErr).AnyTimes() 124 loader.EXPECT().LoadAnnotations(".").Return(map[string]tflint.Annotations{}, tc.LoadErr).AnyTimes() 125 loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, tc.LoadErr).AnyTimes() 126 loader.EXPECT().Sources().Return(map[string][]byte{}).AnyTimes() 127 cli.loader = loader 128 129 status := cli.Run(strings.Split(tc.Command, " ")) 130 131 if status != tc.Status { 132 t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status) 133 } 134 if !strings.Contains(outStream.String(), tc.Stdout) { 135 t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String()) 136 } 137 if tc.Stdout == "" && outStream.String() != "" { 138 t.Fatalf("Failed `%s`: Expected empty in stdout, but get `%s`", tc.Name, outStream.String()) 139 } 140 if !strings.Contains(errStream.String(), tc.Stderr) { 141 t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String()) 142 } 143 if tc.Stderr == "" && errStream.String() != "" { 144 t.Fatalf("Failed `%s`: Expected empty in stderr, but get `%s`", tc.Name, errStream.String()) 145 } 146 } 147 } 148 149 type testRule struct { 150 dir string 151 } 152 type errorRule struct{} 153 154 func (r *testRule) Name() string { 155 return "test_rule" 156 } 157 func (r *errorRule) Name() string { 158 return "error_rule" 159 } 160 161 func (r *testRule) Enabled() bool { 162 return true 163 } 164 func (r *errorRule) Enabled() bool { 165 return true 166 } 167 168 func (r *testRule) Severity() string { 169 return tflint.ERROR 170 } 171 func (r *errorRule) Severity() string { 172 return tflint.ERROR 173 } 174 175 func (r *testRule) Link() string { 176 return "" 177 } 178 func (r *errorRule) Link() string { 179 return "" 180 } 181 182 func (r *testRule) Check(runner *tflint.Runner) error { 183 filename := "test.tf" 184 if r.dir != "" { 185 filename = filepath.Join(r.dir, filename) 186 } 187 188 runner.EmitIssue( 189 r, 190 "This is test error", 191 hcl.Range{ 192 Filename: filename, 193 Start: hcl.Pos{Line: 1}, 194 }, 195 ) 196 return nil 197 } 198 func (r *errorRule) Check(runner *tflint.Runner) error { 199 return errors.New("Check failed") 200 } 201 202 func TestCLIRun__issuesFound(t *testing.T) { 203 cases := []struct { 204 Name string 205 Command string 206 Rule rules.Rule 207 Status int 208 Stdout string 209 Stderr string 210 }{ 211 { 212 Name: "issues found", 213 Command: "./tflint", 214 Rule: &testRule{}, 215 Status: ExitCodeIssuesFound, 216 Stdout: fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")), 217 }, 218 { 219 Name: "`--force` option", 220 Command: "./tflint --force", 221 Rule: &testRule{}, 222 Status: ExitCodeOK, 223 Stdout: fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")), 224 }, 225 { 226 Name: "`--no-color` option", 227 Command: "./tflint --no-color", 228 Rule: &testRule{}, 229 Status: ExitCodeIssuesFound, 230 Stdout: "This is test error (test_rule)", 231 }, 232 { 233 Name: "checking errors are occurred", 234 Command: "./tflint", 235 Rule: &errorRule{}, 236 Status: ExitCodeError, 237 Stderr: "Check failed", 238 }, 239 } 240 241 ctrl := gomock.NewController(t) 242 originalRules := rules.DefaultRules 243 defer func() { 244 rules.DefaultRules = originalRules 245 ctrl.Finish() 246 }() 247 248 for _, tc := range cases { 249 // Mock rules 250 rules.DefaultRules = []rules.Rule{tc.Rule} 251 252 outStream, errStream := new(bytes.Buffer), new(bytes.Buffer) 253 cli := &CLI{ 254 outStream: outStream, 255 errStream: errStream, 256 testMode: true, 257 } 258 259 loader := tflint.NewMockAbstractLoader(ctrl) 260 loader.EXPECT().LoadConfig(".").Return(configs.NewEmptyConfig(), nil).AnyTimes() 261 loader.EXPECT().LoadAnnotations(".").Return(map[string]tflint.Annotations{}, nil).AnyTimes() 262 loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, nil).AnyTimes() 263 loader.EXPECT().Sources().Return(map[string][]byte{}).AnyTimes() 264 cli.loader = loader 265 266 status := cli.Run(strings.Split(tc.Command, " ")) 267 268 if status != tc.Status { 269 t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status) 270 } 271 if !strings.Contains(outStream.String(), tc.Stdout) { 272 t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String()) 273 } 274 if tc.Stdout == "" && outStream.String() != "" { 275 t.Fatalf("Failed `%s`: Expected empty in stdout, but get `%s`", tc.Name, outStream.String()) 276 } 277 if !strings.Contains(errStream.String(), tc.Stderr) { 278 t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String()) 279 } 280 if tc.Stderr == "" && errStream.String() != "" { 281 t.Fatalf("Failed `%s`: Expected empty in stderr, but get `%s`", tc.Name, errStream.String()) 282 } 283 } 284 } 285 286 func TestCLIRun__withArguments(t *testing.T) { 287 cases := []struct { 288 Name string 289 Command string 290 Dir string 291 Status int 292 Stdout string 293 Stderr string 294 }{ 295 { 296 Name: "no arguments", 297 Command: "./tflint", 298 Dir: ".", 299 Status: ExitCodeIssuesFound, 300 Stdout: fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")), 301 }, 302 { 303 Name: "files arguments", 304 Command: "./tflint template.tf", 305 Dir: ".", 306 Status: ExitCodeOK, 307 Stdout: "", 308 }, 309 { 310 Name: "file not found", 311 Command: "./tflint not_found.tf", 312 Dir: ".", 313 Status: ExitCodeError, 314 Stderr: "Failed to load `not_found.tf`: File not found", 315 }, 316 { 317 Name: "not Terraform configuration", 318 Command: "./tflint README", 319 Dir: ".", 320 Status: ExitCodeError, 321 Stderr: "Failed to load `README`: File is not a target of Terraform", 322 }, 323 { 324 Name: "multiple files", 325 Command: "./tflint template.tf test.tf", 326 Dir: ".", 327 Status: ExitCodeIssuesFound, 328 Stdout: fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")), 329 }, 330 { 331 Name: "directory argument", 332 Command: "./tflint example", 333 Dir: "example", 334 Status: ExitCodeIssuesFound, 335 Stdout: fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")), 336 }, 337 { 338 Name: "file under the directory", 339 Command: fmt.Sprintf("./tflint %s", filepath.Join("example", "test.tf")), 340 Dir: "example", 341 Status: ExitCodeIssuesFound, 342 Stdout: fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")), 343 }, 344 { 345 Name: "multiple directories", 346 Command: "./tflint example ./", 347 Dir: "example", 348 Status: ExitCodeError, 349 Stderr: "Failed to load `example`: Multiple arguments are not allowed when passing a directory", 350 }, 351 { 352 Name: "file and directory", 353 Command: "./tflint template.tf example", 354 Dir: "example", 355 Status: ExitCodeError, 356 Stderr: "Failed to load `example`: Multiple arguments are not allowed when passing a directory", 357 }, 358 { 359 Name: "multiple files in different directories", 360 Command: fmt.Sprintf("./tflint test.tf %s", filepath.Join("example", "test.tf")), 361 Dir: "example", 362 Status: ExitCodeError, 363 Stderr: fmt.Sprintf("Failed to load `%s`: Multiple files in different directories are not allowed", filepath.Join("example", "test.tf")), 364 }, 365 } 366 367 currentDir, err := os.Getwd() 368 if err != nil { 369 t.Fatal(err) 370 } 371 err = os.Chdir(filepath.Join(currentDir, "test-fixtures", "arguments")) 372 if err != nil { 373 t.Fatal(err) 374 } 375 376 ctrl := gomock.NewController(t) 377 originalRules := rules.DefaultRules 378 379 defer func() { 380 os.Chdir(currentDir) 381 rules.DefaultRules = originalRules 382 ctrl.Finish() 383 }() 384 385 for _, tc := range cases { 386 // Mock rules 387 rules.DefaultRules = []rules.Rule{&testRule{dir: tc.Dir}} 388 389 outStream, errStream := new(bytes.Buffer), new(bytes.Buffer) 390 cli := &CLI{ 391 outStream: outStream, 392 errStream: errStream, 393 testMode: true, 394 } 395 396 loader := tflint.NewMockAbstractLoader(ctrl) 397 loader.EXPECT().LoadConfig(tc.Dir).Return(configs.NewEmptyConfig(), nil).AnyTimes() 398 loader.EXPECT().LoadAnnotations(tc.Dir).Return(map[string]tflint.Annotations{}, nil).AnyTimes() 399 loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, nil).AnyTimes() 400 loader.EXPECT().Sources().Return(map[string][]byte{}).AnyTimes() 401 cli.loader = loader 402 403 status := cli.Run(strings.Split(tc.Command, " ")) 404 405 if status != tc.Status { 406 t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status) 407 } 408 if !strings.Contains(outStream.String(), tc.Stdout) { 409 t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String()) 410 } 411 if tc.Stdout == "" && outStream.String() != "" { 412 t.Fatalf("Failed `%s`: Expected empty in stdout, but get `%s`", tc.Name, outStream.String()) 413 } 414 if !strings.Contains(errStream.String(), tc.Stderr) { 415 t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String()) 416 } 417 if tc.Stderr == "" && errStream.String() != "" { 418 t.Fatalf("Failed `%s`: Expected empty in stderr, but get `%s`", tc.Name, errStream.String()) 419 } 420 } 421 }