github.com/tetrafolium/tflint@v0.8.0/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/golang/mock/gomock"
    13  	"github.com/hashicorp/hcl2/hcl"
    14  	"github.com/hashicorp/terraform/configs"
    15  	"github.com/hashicorp/terraform/terraform"
    16  	"github.com/wata727/tflint/issue"
    17  	"github.com/wata727/tflint/mock"
    18  	"github.com/wata727/tflint/project"
    19  	"github.com/wata727/tflint/rules"
    20  	"github.com/wata727/tflint/tflint"
    21  )
    22  
    23  func TestCLIRun__noIssuesFound(t *testing.T) {
    24  	cases := []struct {
    25  		Name    string
    26  		Command string
    27  		LoadErr error
    28  		Status  int
    29  		Stdout  string
    30  		Stderr  string
    31  	}{
    32  		{
    33  			Name:    "print version",
    34  			Command: "./tflint --version",
    35  			Status:  ExitCodeOK,
    36  			Stdout:  fmt.Sprintf("TFLint version %s", project.Version),
    37  		},
    38  		{
    39  			Name:    "print help",
    40  			Command: "./tflint --help",
    41  			Status:  ExitCodeOK,
    42  			Stdout:  "Application Options:",
    43  		},
    44  		{
    45  			Name:    "no options",
    46  			Command: "./tflint",
    47  			Status:  ExitCodeOK,
    48  			Stdout:  "Awesome! Your code is following the best practices :)",
    49  		},
    50  		{
    51  			Name:    "specify format",
    52  			Command: "./tflint --format json",
    53  			Status:  ExitCodeOK,
    54  			Stdout:  "[]",
    55  		},
    56  		{
    57  			Name:    "`--error-with-issues` option",
    58  			Command: "./tflint --error-with-issues",
    59  			Status:  ExitCodeOK,
    60  			Stdout:  "Awesome! Your code is following the best practices :)",
    61  		},
    62  		{
    63  			Name:    "`--quiet` option",
    64  			Command: "./tflint --quiet",
    65  			Status:  ExitCodeOK,
    66  			Stdout:  "",
    67  		},
    68  		{
    69  			Name:    "loading errors are occurred",
    70  			Command: "./tflint",
    71  			LoadErr: errors.New("Load error occurred"),
    72  			Status:  ExitCodeError,
    73  			Stderr:  "Load error occurred",
    74  		},
    75  		{
    76  			Name:    "deprecated options",
    77  			Command: "./tflint --debug",
    78  			Status:  ExitCodeError,
    79  			Stderr:  "`debug` option was removed in v0.8.0. Please set `TFLINT_LOG` environment variables instead",
    80  		},
    81  		{
    82  			Name:    "invalid options",
    83  			Command: "./tflint --unknown",
    84  			Status:  ExitCodeError,
    85  			Stderr:  "`unknown` is unknown option. Please run `tflint --help`",
    86  		},
    87  		{
    88  			Name:    "invalid format",
    89  			Command: "./tflint --format awesome",
    90  			Status:  ExitCodeError,
    91  			Stderr:  "Invalid value `awesome' for option",
    92  		},
    93  	}
    94  
    95  	ctrl := gomock.NewController(t)
    96  	defer ctrl.Finish()
    97  
    98  	for _, tc := range cases {
    99  		outStream, errStream := new(bytes.Buffer), new(bytes.Buffer)
   100  		cli := &CLI{
   101  			outStream: outStream,
   102  			errStream: errStream,
   103  			testMode:  true,
   104  		}
   105  
   106  		loader := mock.NewMockAbstractLoader(ctrl)
   107  		loader.EXPECT().LoadConfig().Return(configs.NewEmptyConfig(), tc.LoadErr).AnyTimes()
   108  		loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, tc.LoadErr).AnyTimes()
   109  		cli.loader = loader
   110  
   111  		status := cli.Run(strings.Split(tc.Command, " "))
   112  
   113  		if status != tc.Status {
   114  			t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status)
   115  		}
   116  		if !strings.Contains(outStream.String(), tc.Stdout) {
   117  			t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String())
   118  		}
   119  		if !strings.Contains(errStream.String(), tc.Stderr) {
   120  			t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String())
   121  		}
   122  	}
   123  }
   124  
   125  type testRule struct{}
   126  type errorRule struct{}
   127  
   128  func (r *testRule) Name() string {
   129  	return "test_rule"
   130  }
   131  func (r *errorRule) Name() string {
   132  	return "error_rule"
   133  }
   134  
   135  func (r *testRule) Enabled() bool {
   136  	return true
   137  }
   138  func (r *errorRule) Enabled() bool {
   139  	return true
   140  }
   141  
   142  func (r *testRule) Type() string {
   143  	return issue.ERROR
   144  }
   145  func (r *errorRule) Type() string {
   146  	return issue.ERROR
   147  }
   148  
   149  func (r *testRule) Link() string {
   150  	return ""
   151  }
   152  func (r *errorRule) Link() string {
   153  	return ""
   154  }
   155  
   156  func (r *testRule) Check(runner *tflint.Runner) error {
   157  	runner.EmitIssue(
   158  		r,
   159  		"This is test error",
   160  		hcl.Range{
   161  			Filename: "test.tf",
   162  			Start:    hcl.Pos{Line: 1},
   163  		},
   164  	)
   165  	return nil
   166  }
   167  func (r *errorRule) Check(runner *tflint.Runner) error {
   168  	return errors.New("Check failed")
   169  }
   170  
   171  func TestCLIRun__issuesFound(t *testing.T) {
   172  	cases := []struct {
   173  		Name    string
   174  		Command string
   175  		Rule    rules.Rule
   176  		Status  int
   177  		Stdout  string
   178  		Stderr  string
   179  	}{
   180  		{
   181  			Name:    "issues found",
   182  			Command: "./tflint",
   183  			Rule:    &testRule{},
   184  			Status:  ExitCodeOK,
   185  			Stdout:  "This is test error (test_rule)",
   186  		},
   187  		{
   188  			Name:    "`--error-with-issues` option",
   189  			Command: "./tflint --error-with-issues",
   190  			Rule:    &testRule{},
   191  			Status:  ExitCodeIssuesFound,
   192  			Stdout:  "This is test error (test_rule)",
   193  		},
   194  		{
   195  			Name:    "`--quiet` option",
   196  			Command: "./tflint --quiet",
   197  			Rule:    &testRule{},
   198  			Status:  ExitCodeOK,
   199  			Stdout:  "This is test error (test_rule)",
   200  		},
   201  		{
   202  			Name:    "checking errors are occurred",
   203  			Command: "./tflint",
   204  			Rule:    &errorRule{},
   205  			Status:  ExitCodeError,
   206  			Stderr:  "Check failed",
   207  		},
   208  	}
   209  
   210  	ctrl := gomock.NewController(t)
   211  	originalRules := rules.DefaultRules
   212  	defer func() {
   213  		rules.DefaultRules = originalRules
   214  		ctrl.Finish()
   215  	}()
   216  
   217  	for _, tc := range cases {
   218  		// Mock rules
   219  		rules.DefaultRules = []rules.Rule{tc.Rule}
   220  
   221  		outStream, errStream := new(bytes.Buffer), new(bytes.Buffer)
   222  		cli := &CLI{
   223  			outStream: outStream,
   224  			errStream: errStream,
   225  			testMode:  true,
   226  		}
   227  
   228  		loader := mock.NewMockAbstractLoader(ctrl)
   229  		loader.EXPECT().LoadConfig().Return(configs.NewEmptyConfig(), nil).AnyTimes()
   230  		loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, nil).AnyTimes()
   231  		cli.loader = loader
   232  
   233  		status := cli.Run(strings.Split(tc.Command, " "))
   234  
   235  		if status != tc.Status {
   236  			t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status)
   237  		}
   238  		if !strings.Contains(outStream.String(), tc.Stdout) {
   239  			t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String())
   240  		}
   241  		if !strings.Contains(errStream.String(), tc.Stderr) {
   242  			t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String())
   243  		}
   244  	}
   245  }
   246  
   247  func TestCLIRun__withArguments(t *testing.T) {
   248  	cases := []struct {
   249  		Name         string
   250  		Command      string
   251  		IsConfigFile bool
   252  		Status       int
   253  		Stdout       string
   254  		Stderr       string
   255  	}{
   256  		{
   257  			Name:    "no arguments",
   258  			Command: "./tflint",
   259  			Status:  ExitCodeOK,
   260  			Stdout:  "This is test error (test_rule)",
   261  		},
   262  		{
   263  			Name:         "files arguments",
   264  			Command:      "./tflint template.tf",
   265  			IsConfigFile: true,
   266  			Status:       ExitCodeOK,
   267  			Stdout:       "Awesome! Your code is following the best practices :)",
   268  		},
   269  		{
   270  			Name:         "directories arguments",
   271  			Command:      "./tflint ./",
   272  			IsConfigFile: true,
   273  			Status:       ExitCodeError,
   274  			Stderr:       "Failed to load `./`: TFLint doesn't accept directories as arguments",
   275  		},
   276  		{
   277  			Name:         "file not found",
   278  			Command:      "./tflint not_found.tf",
   279  			IsConfigFile: true,
   280  			Status:       ExitCodeError,
   281  			Stderr:       "Failed to load `not_found.tf`: File not found",
   282  		},
   283  		{
   284  			Name:         "not Terraform configuration",
   285  			Command:      "./tflint README",
   286  			IsConfigFile: false,
   287  			Status:       ExitCodeError,
   288  			Stderr:       "Failed to load `README`: File is not a target of Terraform",
   289  		},
   290  	}
   291  
   292  	currentDir, err := os.Getwd()
   293  	if err != nil {
   294  		t.Fatal(err)
   295  	}
   296  	err = os.Chdir(filepath.Join(currentDir, "test-fixtures", "arguments"))
   297  	if err != nil {
   298  		t.Fatal(err)
   299  	}
   300  
   301  	ctrl := gomock.NewController(t)
   302  	originalRules := rules.DefaultRules
   303  
   304  	defer func() {
   305  		os.Chdir(currentDir)
   306  		rules.DefaultRules = originalRules
   307  		ctrl.Finish()
   308  	}()
   309  
   310  	for _, tc := range cases {
   311  		// Mock rules
   312  		rules.DefaultRules = []rules.Rule{&testRule{}}
   313  
   314  		outStream, errStream := new(bytes.Buffer), new(bytes.Buffer)
   315  		cli := &CLI{
   316  			outStream: outStream,
   317  			errStream: errStream,
   318  			testMode:  true,
   319  		}
   320  
   321  		loader := mock.NewMockAbstractLoader(ctrl)
   322  		loader.EXPECT().IsConfigFile(gomock.Any()).Return(tc.IsConfigFile).AnyTimes()
   323  		loader.EXPECT().LoadConfig().Return(configs.NewEmptyConfig(), nil).AnyTimes()
   324  		loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, nil).AnyTimes()
   325  		cli.loader = loader
   326  
   327  		status := cli.Run(strings.Split(tc.Command, " "))
   328  
   329  		if status != tc.Status {
   330  			t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status)
   331  		}
   332  		if !strings.Contains(outStream.String(), tc.Stdout) {
   333  			t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String())
   334  		}
   335  		if !strings.Contains(errStream.String(), tc.Stderr) {
   336  			t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String())
   337  		}
   338  	}
   339  }