<commit_before>package cmd

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/fatih/color"
	"github.com/golang/mock/gomock"
	hcl "github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/terraform"
	"github.com/wata727/tflint/rules"
	"github.com/wata727/tflint/tflint"
)

func TestCLIRun__noIssuesFound(t *testing.T) {
	cases := []struct {
		Name    string
		Command string
		LoadErr error
		Status  int
		Stdout  string
		Stderr  string
	}{
		{
			Name:    "print version",
			Command: "./tflint --version",
			Status:  ExitCodeOK,
			Stdout:  fmt.Sprintf("TFLint version %s", tflint.Version),
		},
		{
			Name:    "print help",
			Command: "./tflint --help",
			Status:  ExitCodeOK,
			Stdout:  "Application Options:",
		},
		{
			Name:    "no options",
			Command: "./tflint",
			Status:  ExitCodeOK,
			Stdout:  "",
		},
		{
			Name:    "specify format",
			Command: "./tflint --format json",
			Status:  ExitCodeOK,
			Stdout:  "[]",
		},
		{
			Name:    "`--force` option",
			Command: "./tflint --force",
			Status:  ExitCodeOK,
			Stdout:  "",
		},
		{
			Name:    "loading errors are occurred",
			Command: "./tflint",
			LoadErr: errors.New("Load error occurred"),
			Status:  ExitCodeError,
			Stderr:  "Load error occurred",
		},
		{
			Name:    "removed `debug` options",
			Command: "./tflint --debug",
			Status:  ExitCodeError,
			Stderr:  "`debug` option was removed in v0.8.0. Please set `TFLINT_LOG` environment variables instead",
		},
		{
			Name:    "removed `fast` option",
			Command: "./tflint --fast",
			Status:  ExitCodeError,
			Stderr:  "`fast` option was removed in v0.9.0. The `aws_instance_invalid_ami` rule is already fast enough",
		},
		{
			Name:    "removed `--error-with-issues` option",
			Command: "./tflint --error-with-issues",
			Status:  ExitCodeError,
			Stderr:  "`error-with-issues` option was removed in v0.9.0. The behavior is now default",
		},
		{
			Name:    "removed `--quiet` option",
			Command: "./tflint --quiet",
			Status:  ExitCodeError,
			Stderr:  "`quiet` option was removed in v0.11.0. The behavior is now default",
		},
		{
			Name:    "removed `--ignore-rule` option",
			Command: "./tflint --ignore-rule aws_instance_invalid_type",
			Status:  ExitCodeError,
			Stderr:  "`ignore-rule` option was removed in v0.12.0. Please use `--disable-rule` instead",
		},
		{
			Name:    "invalid options",
			Command: "./tflint --unknown",
			Status:  ExitCodeError,
			Stderr:  "`unknown` is unknown option. Please run `tflint --help`",
		},
		{
			Name:    "invalid format",
			Command: "./tflint --format awesome",
			Status:  ExitCodeError,
			Stderr:  "Invalid value `awesome' for option",
		},
	}

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	for _, tc := range cases {
		outStream, errStream := new(bytes.Buffer), new(bytes.Buffer)
		cli := &CLI{
			outStream: outStream,
			errStream: errStream,
			testMode:  true,
		}

		loader := tflint.NewMockAbstractLoader(ctrl)
		loader.EXPECT().LoadConfig(".").Return(configs.NewEmptyConfig(), tc.LoadErr).AnyTimes()
		loader.EXPECT().LoadAnnotations(".").Return(map[string]tflint.Annotations{}, tc.LoadErr).AnyTimes()
		loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, tc.LoadErr).AnyTimes()
		loader.EXPECT().Sources().Return(map[string][]byte{}).AnyTimes()
		cli.loader = loader

		status := cli.Run(strings.Split(tc.Command, " "))

		if status != tc.Status {
			t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status)
		}
		if !strings.Contains(outStream.String(), tc.Stdout) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String())
		}
		if tc.Stdout == "" && outStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stdout, but get `%s`", tc.Name, outStream.String())
		}
		if !strings.Contains(errStream.String(), tc.Stderr) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String())
		}
		if tc.Stderr == "" && errStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stderr, but get `%s`", tc.Name, errStream.String())
		}
	}
}

type testRule struct {
	dir string
}
type errorRule struct{}

func (r *testRule) Name() string {
	return "test_rule"
}
func (r *errorRule) Name() string {
	return "error_rule"
}

func (r *testRule) Enabled() bool {
	return true
}
func (r *errorRule) Enabled() bool {
	return true
}

func (r *testRule) Severity() string {
	return tflint.ERROR
}
func (r *errorRule) Severity() string {
	return tflint.ERROR
}

func (r *testRule) Link() string {
	return ""
}
func (r *errorRule) Link() string {
	return ""
}

func (r *testRule) Check(runner *tflint.Runner) error {
	filename := "test.tf"
	if r.dir != "" {
		filename = filepath.Join(r.dir, filename)
	}

	runner.EmitIssue(
		r,
		"This is test error",
		hcl.Range{
			Filename: filename,
			Start:    hcl.Pos{Line: 1},
		},
	)
	return nil
}
func (r *errorRule) Check(runner *tflint.Runner) error {
	return errors.New("Check failed")
}

func TestCLIRun__issuesFound(t *testing.T) {
	cases := []struct {
		Name    string
		Command string
		Rule    rules.Rule
		Status  int
		Stdout  string
		Stderr  string
	}{
		{
			Name:    "issues found",
			Command: "./tflint",
			Rule:    &testRule{},
			Status:  ExitCodeIssuesFound,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "`--force` option",
			Command: "./tflint --force",
			Rule:    &testRule{},
			Status:  ExitCodeOK,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "`--no-color` option",
			Command: "./tflint --no-color",
			Rule:    &testRule{},
			Status:  ExitCodeIssuesFound,
			Stdout:  "This is test error (test_rule)",
		},
		{
			Name:    "checking errors are occurred",
			Command: "./tflint",
			Rule:    &errorRule{},
			Status:  ExitCodeError,
			Stderr:  "Check failed",
		},
	}

	ctrl := gomock.NewController(t)
	originalRules := rules.DefaultRules
	defer func() {
		rules.DefaultRules = originalRules
		ctrl.Finish()
	}()

	for _, tc := range cases {
		// Mock rules
		rules.DefaultRules = []rules.Rule{tc.Rule}

		outStream, errStream := new(bytes.Buffer), new(bytes.Buffer)
		cli := &CLI{
			outStream: outStream,
			errStream: errStream,
			testMode:  true,
		}

		loader := tflint.NewMockAbstractLoader(ctrl)
		loader.EXPECT().LoadConfig(".").Return(configs.NewEmptyConfig(), nil).AnyTimes()
		loader.EXPECT().LoadAnnotations(".").Return(map[string]tflint.Annotations{}, nil).AnyTimes()
		loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, nil).AnyTimes()
		loader.EXPECT().Sources().Return(map[string][]byte{}).AnyTimes()
		cli.loader = loader

		status := cli.Run(strings.Split(tc.Command, " "))

		if status != tc.Status {
			t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status)
		}
		if !strings.Contains(outStream.String(), tc.Stdout) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String())
		}
		if tc.Stdout == "" && outStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stdout, but get `%s`", tc.Name, outStream.String())
		}
		if !strings.Contains(errStream.String(), tc.Stderr) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String())
		}
		if tc.Stderr == "" && errStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stderr, but get `%s`", tc.Name, errStream.String())
		}
	}
}

func TestCLIRun__withArguments(t *testing.T) {
	cases := []struct {
		Name    string
		Command string
		Dir     string
		Status  int
		Stdout  string
		Stderr  string
	}{
		{
			Name:    "no arguments",
			Command: "./tflint",
			Dir:     ".",
			Status:  ExitCodeIssuesFound,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "files arguments",
			Command: "./tflint template.tf",
			Dir:     ".",
			Status:  ExitCodeOK,
			Stdout:  "",
		},
		{
			Name:    "file not found",
			Command: "./tflint not_found.tf",
			Dir:     ".",
			Status:  ExitCodeError,
			Stderr:  "Failed to load `not_found.tf`: File not found",
		},
		{
			Name:    "not Terraform configuration",
			Command: "./tflint README",
			Dir:     ".",
			Status:  ExitCodeError,
			Stderr:  "Failed to load `README`: File is not a target of Terraform",
		},
		{
			Name:    "multiple files",
			Command: "./tflint template.tf test.tf",
			Dir:     ".",
			Status:  ExitCodeIssuesFound,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "directory argument",
			Command: "./tflint example",
			Dir:     "example",
			Status:  ExitCodeIssuesFound,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "file under the directory",
			Command: fmt.Sprintf("./tflint %s", filepath.Join("example", "test.tf")),
			Dir:     "example",
			Status:  ExitCodeIssuesFound,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "multiple directories",
			Command: "./tflint example ./",
			Dir:     "example",
			Status:  ExitCodeError,
			Stderr:  "Failed to load `example`: Multiple arguments are not allowed when passing a directory",
		},
		{
			Name:    "file and directory",
			Command: "./tflint template.tf example",
			Dir:     "example",
			Status:  ExitCodeError,
			Stderr:  "Failed to load `example`: Multiple arguments are not allowed when passing a directory",
		},
		{
			Name:    "multiple files in different directories",
			Command: fmt.Sprintf("./tflint test.tf %s", filepath.Join("example", "test.tf")),
			Dir:     "example",
			Status:  ExitCodeError,
			Stderr:  fmt.Sprintf("Failed to load `%s`: Multiple files in different directories are not allowed", filepath.Join("example", "test.tf")),
		},
	}

	currentDir, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	err = os.Chdir(filepath.Join(currentDir, "test-fixtures", "arguments"))
	if err != nil {
		t.Fatal(err)
	}

	ctrl := gomock.NewController(t)
	originalRules := rules.DefaultRules

	defer func() {
		os.Chdir(currentDir)
		rules.DefaultRules = originalRules
		ctrl.Finish()
	}()

	for _, tc := range cases {
		// Mock rules
		rules.DefaultRules = []rules.Rule{&testRule{dir: tc.Dir}}

		outStream, errStream := new(bytes.Buffer), new(bytes.Buffer)
		cli := &CLI{
			outStream: outStream,
			errStream: errStream,
			testMode:  true,
		}

		loader := tflint.NewMockAbstractLoader(ctrl)
		loader.EXPECT().LoadConfig(tc.Dir).Return(configs.NewEmptyConfig(), nil).AnyTimes()
		loader.EXPECT().LoadAnnotations(tc.Dir).Return(map[string]tflint.Annotations{}, nil).AnyTimes()
		loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, nil).AnyTimes()
		loader.EXPECT().Sources().Return(map[string][]byte{}).AnyTimes()
		cli.loader = loader

		status := cli.Run(strings.Split(tc.Command, " "))

		if status != tc.Status {
			t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status)
		}
		if !strings.Contains(outStream.String(), tc.Stdout) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String())
		}
		if tc.Stdout == "" && outStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stdout, but get `%s`", tc.Name, outStream.String())
		}
		if !strings.Contains(errStream.String(), tc.Stderr) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String())
		}
		if tc.Stderr == "" && errStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stderr, but get `%s`", tc.Name, errStream.String())
		}
	}
}
<commit_msg>Add cli test for invalid rule name<commit_after>package cmd

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/fatih/color"
	"github.com/golang/mock/gomock"
	hcl "github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/terraform"
	"github.com/wata727/tflint/rules"
	"github.com/wata727/tflint/tflint"
)

func TestCLIRun__noIssuesFound(t *testing.T) {
	cases := []struct {
		Name    string
		Command string
		LoadErr error
		Status  int
		Stdout  string
		Stderr  string
	}{
		{
			Name:    "print version",
			Command: "./tflint --version",
			Status:  ExitCodeOK,
			Stdout:  fmt.Sprintf("TFLint version %s", tflint.Version),
		},
		{
			Name:    "print help",
			Command: "./tflint --help",
			Status:  ExitCodeOK,
			Stdout:  "Application Options:",
		},
		{
			Name:    "no options",
			Command: "./tflint",
			Status:  ExitCodeOK,
			Stdout:  "",
		},
		{
			Name:    "specify format",
			Command: "./tflint --format json",
			Status:  ExitCodeOK,
			Stdout:  "[]",
		},
		{
			Name:    "`--force` option",
			Command: "./tflint --force",
			Status:  ExitCodeOK,
			Stdout:  "",
		},
		{
			Name:    "loading errors are occurred",
			Command: "./tflint",
			LoadErr: errors.New("Load error occurred"),
			Status:  ExitCodeError,
			Stderr:  "Load error occurred",
		},
		{
			Name:    "removed `debug` options",
			Command: "./tflint --debug",
			Status:  ExitCodeError,
			Stderr:  "`debug` option was removed in v0.8.0. Please set `TFLINT_LOG` environment variables instead",
		},
		{
			Name:    "removed `fast` option",
			Command: "./tflint --fast",
			Status:  ExitCodeError,
			Stderr:  "`fast` option was removed in v0.9.0. The `aws_instance_invalid_ami` rule is already fast enough",
		},
		{
			Name:    "removed `--error-with-issues` option",
			Command: "./tflint --error-with-issues",
			Status:  ExitCodeError,
			Stderr:  "`error-with-issues` option was removed in v0.9.0. The behavior is now default",
		},
		{
			Name:    "removed `--quiet` option",
			Command: "./tflint --quiet",
			Status:  ExitCodeError,
			Stderr:  "`quiet` option was removed in v0.11.0. The behavior is now default",
		},
		{
			Name:    "removed `--ignore-rule` option",
			Command: "./tflint --ignore-rule aws_instance_invalid_type",
			Status:  ExitCodeError,
			Stderr:  "`ignore-rule` option was removed in v0.12.0. Please use `--disable-rule` instead",
		},
		{
			Name:    "invalid options",
			Command: "./tflint --unknown",
			Status:  ExitCodeError,
			Stderr:  "`unknown` is unknown option. Please run `tflint --help`",
		},
		{
			Name:    "invalid format",
			Command: "./tflint --format awesome",
			Status:  ExitCodeError,
			Stderr:  "Invalid value `awesome' for option",
		},
		{
			Name:    "invalid rule name",
			Command: "./tflint --enable-rule nosuchrule",
			Status:  ExitCodeError,
			Stderr:  "Rule not found: nosuchrule",
		},
	}

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	for _, tc := range cases {
		outStream, errStream := new(bytes.Buffer), new(bytes.Buffer)
		cli := &CLI{
			outStream: outStream,
			errStream: errStream,
			testMode:  true,
		}

		loader := tflint.NewMockAbstractLoader(ctrl)
		loader.EXPECT().LoadConfig(".").Return(configs.NewEmptyConfig(), tc.LoadErr).AnyTimes()
		loader.EXPECT().LoadAnnotations(".").Return(map[string]tflint.Annotations{}, tc.LoadErr).AnyTimes()
		loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, tc.LoadErr).AnyTimes()
		loader.EXPECT().Sources().Return(map[string][]byte{}).AnyTimes()
		cli.loader = loader

		status := cli.Run(strings.Split(tc.Command, " "))

		if status != tc.Status {
			t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status)
		}
		if !strings.Contains(outStream.String(), tc.Stdout) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String())
		}
		if tc.Stdout == "" && outStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stdout, but get `%s`", tc.Name, outStream.String())
		}
		if !strings.Contains(errStream.String(), tc.Stderr) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String())
		}
		if tc.Stderr == "" && errStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stderr, but get `%s`", tc.Name, errStream.String())
		}
	}
}

type testRule struct {
	dir string
}
type errorRule struct{}

func (r *testRule) Name() string {
	return "test_rule"
}
func (r *errorRule) Name() string {
	return "error_rule"
}

func (r *testRule) Enabled() bool {
	return true
}
func (r *errorRule) Enabled() bool {
	return true
}

func (r *testRule) Severity() string {
	return tflint.ERROR
}
func (r *errorRule) Severity() string {
	return tflint.ERROR
}

func (r *testRule) Link() string {
	return ""
}
func (r *errorRule) Link() string {
	return ""
}

func (r *testRule) Check(runner *tflint.Runner) error {
	filename := "test.tf"
	if r.dir != "" {
		filename = filepath.Join(r.dir, filename)
	}

	runner.EmitIssue(
		r,
		"This is test error",
		hcl.Range{
			Filename: filename,
			Start:    hcl.Pos{Line: 1},
		},
	)
	return nil
}
func (r *errorRule) Check(runner *tflint.Runner) error {
	return errors.New("Check failed")
}

func TestCLIRun__issuesFound(t *testing.T) {
	cases := []struct {
		Name    string
		Command string
		Rule    rules.Rule
		Status  int
		Stdout  string
		Stderr  string
	}{
		{
			Name:    "issues found",
			Command: "./tflint",
			Rule:    &testRule{},
			Status:  ExitCodeIssuesFound,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "`--force` option",
			Command: "./tflint --force",
			Rule:    &testRule{},
			Status:  ExitCodeOK,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "`--no-color` option",
			Command: "./tflint --no-color",
			Rule:    &testRule{},
			Status:  ExitCodeIssuesFound,
			Stdout:  "This is test error (test_rule)",
		},
		{
			Name:    "checking errors are occurred",
			Command: "./tflint",
			Rule:    &errorRule{},
			Status:  ExitCodeError,
			Stderr:  "Check failed",
		},
	}

	ctrl := gomock.NewController(t)
	originalRules := rules.DefaultRules
	defer func() {
		rules.DefaultRules = originalRules
		ctrl.Finish()
	}()

	for _, tc := range cases {
		// Mock rules
		rules.DefaultRules = []rules.Rule{tc.Rule}

		outStream, errStream := new(bytes.Buffer), new(bytes.Buffer)
		cli := &CLI{
			outStream: outStream,
			errStream: errStream,
			testMode:  true,
		}

		loader := tflint.NewMockAbstractLoader(ctrl)
		loader.EXPECT().LoadConfig(".").Return(configs.NewEmptyConfig(), nil).AnyTimes()
		loader.EXPECT().LoadAnnotations(".").Return(map[string]tflint.Annotations{}, nil).AnyTimes()
		loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, nil).AnyTimes()
		loader.EXPECT().Sources().Return(map[string][]byte{}).AnyTimes()
		cli.loader = loader

		status := cli.Run(strings.Split(tc.Command, " "))

		if status != tc.Status {
			t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status)
		}
		if !strings.Contains(outStream.String(), tc.Stdout) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String())
		}
		if tc.Stdout == "" && outStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stdout, but get `%s`", tc.Name, outStream.String())
		}
		if !strings.Contains(errStream.String(), tc.Stderr) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String())
		}
		if tc.Stderr == "" && errStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stderr, but get `%s`", tc.Name, errStream.String())
		}
	}
}

func TestCLIRun__withArguments(t *testing.T) {
	cases := []struct {
		Name    string
		Command string
		Dir     string
		Status  int
		Stdout  string
		Stderr  string
	}{
		{
			Name:    "no arguments",
			Command: "./tflint",
			Dir:     ".",
			Status:  ExitCodeIssuesFound,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "files arguments",
			Command: "./tflint template.tf",
			Dir:     ".",
			Status:  ExitCodeOK,
			Stdout:  "",
		},
		{
			Name:    "file not found",
			Command: "./tflint not_found.tf",
			Dir:     ".",
			Status:  ExitCodeError,
			Stderr:  "Failed to load `not_found.tf`: File not found",
		},
		{
			Name:    "not Terraform configuration",
			Command: "./tflint README",
			Dir:     ".",
			Status:  ExitCodeError,
			Stderr:  "Failed to load `README`: File is not a target of Terraform",
		},
		{
			Name:    "multiple files",
			Command: "./tflint template.tf test.tf",
			Dir:     ".",
			Status:  ExitCodeIssuesFound,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "directory argument",
			Command: "./tflint example",
			Dir:     "example",
			Status:  ExitCodeIssuesFound,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "file under the directory",
			Command: fmt.Sprintf("./tflint %s", filepath.Join("example", "test.tf")),
			Dir:     "example",
			Status:  ExitCodeIssuesFound,
			Stdout:  fmt.Sprintf("%s (test_rule)", color.New(color.Bold).Sprint("This is test error")),
		},
		{
			Name:    "multiple directories",
			Command: "./tflint example ./",
			Dir:     "example",
			Status:  ExitCodeError,
			Stderr:  "Failed to load `example`: Multiple arguments are not allowed when passing a directory",
		},
		{
			Name:    "file and directory",
			Command: "./tflint template.tf example",
			Dir:     "example",
			Status:  ExitCodeError,
			Stderr:  "Failed to load `example`: Multiple arguments are not allowed when passing a directory",
		},
		{
			Name:    "multiple files in different directories",
			Command: fmt.Sprintf("./tflint test.tf %s", filepath.Join("example", "test.tf")),
			Dir:     "example",
			Status:  ExitCodeError,
			Stderr:  fmt.Sprintf("Failed to load `%s`: Multiple files in different directories are not allowed", filepath.Join("example", "test.tf")),
		},
	}

	currentDir, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	err = os.Chdir(filepath.Join(currentDir, "test-fixtures", "arguments"))
	if err != nil {
		t.Fatal(err)
	}

	ctrl := gomock.NewController(t)
	originalRules := rules.DefaultRules

	defer func() {
		os.Chdir(currentDir)
		rules.DefaultRules = originalRules
		ctrl.Finish()
	}()

	for _, tc := range cases {
		// Mock rules
		rules.DefaultRules = []rules.Rule{&testRule{dir: tc.Dir}}

		outStream, errStream := new(bytes.Buffer), new(bytes.Buffer)
		cli := &CLI{
			outStream: outStream,
			errStream: errStream,
			testMode:  true,
		}

		loader := tflint.NewMockAbstractLoader(ctrl)
		loader.EXPECT().LoadConfig(tc.Dir).Return(configs.NewEmptyConfig(), nil).AnyTimes()
		loader.EXPECT().LoadAnnotations(tc.Dir).Return(map[string]tflint.Annotations{}, nil).AnyTimes()
		loader.EXPECT().LoadValuesFiles().Return([]terraform.InputValues{}, nil).AnyTimes()
		loader.EXPECT().Sources().Return(map[string][]byte{}).AnyTimes()
		cli.loader = loader

		status := cli.Run(strings.Split(tc.Command, " "))

		if status != tc.Status {
			t.Fatalf("Failed `%s`: Expected status is `%d`, but get `%d`", tc.Name, tc.Status, status)
		}
		if !strings.Contains(outStream.String(), tc.Stdout) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stdout, but get `%s`", tc.Name, tc.Stdout, outStream.String())
		}
		if tc.Stdout == "" && outStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stdout, but get `%s`", tc.Name, outStream.String())
		}
		if !strings.Contains(errStream.String(), tc.Stderr) {
			t.Fatalf("Failed `%s`: Expected to contain `%s` in stderr, but get `%s`", tc.Name, tc.Stderr, errStream.String())
		}
		if tc.Stderr == "" && errStream.String() != "" {
			t.Fatalf("Failed `%s`: Expected empty in stderr, but get `%s`", tc.Name, errStream.String())
		}
	}
}

{'fraction_non_alphanumeric': 0.17178395801479399, 'fraction_numerical': 0.0020662010826893672, 'mean_word_length': 3.422514619883041, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6367607', 'n_tokens_mistral': 9701, 'n_tokens_neox': 8761, 'n_words': 2029}