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

     1  package tflint
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	"github.com/google/go-cmp/cmp/cmpopts"
    11  	hcl "github.com/hashicorp/hcl/v2"
    12  	"github.com/hashicorp/terraform/terraform"
    13  	"github.com/spf13/afero"
    14  )
    15  
    16  // TestRunner returns a runner for testing.
    17  // Note that this runner ignores a config, annotations, and input variables.
    18  func TestRunner(t *testing.T, files map[string]string) *Runner {
    19  	return TestRunnerWithConfig(t, files, EmptyConfig())
    20  }
    21  
    22  // TestRunnerWithConfig returns a runner with passed config for testing.
    23  func TestRunnerWithConfig(t *testing.T, files map[string]string, config *Config) *Runner {
    24  	fs := afero.Afero{Fs: afero.NewMemMapFs()}
    25  	for name, src := range files {
    26  		err := fs.WriteFile(name, []byte(src), os.ModePerm)
    27  		if err != nil {
    28  			t.Fatal(err)
    29  		}
    30  	}
    31  
    32  	loader, err := NewLoader(fs, config)
    33  	if err != nil {
    34  		t.Fatal(err)
    35  	}
    36  
    37  	dirMap := map[string]*struct{}{}
    38  	for file := range files {
    39  		dirMap[filepath.Dir(file)] = nil
    40  	}
    41  	dirs := make([]string, 0)
    42  	for dir := range dirMap {
    43  		dirs = append(dirs, dir)
    44  	}
    45  
    46  	if len(dirs) > 1 {
    47  		t.Fatalf("All test files must be in the same directory, got %d directories: %v", len(dirs), dirs)
    48  		return nil
    49  	}
    50  
    51  	var dir string
    52  	if len(dirs) == 0 {
    53  		dir = "."
    54  	} else {
    55  		dir = dirs[0]
    56  	}
    57  
    58  	cfg, err := loader.LoadConfig(dir)
    59  	if err != nil {
    60  		t.Fatal(err)
    61  	}
    62  	f, err := loader.Files()
    63  	if err != nil {
    64  		t.Fatal(err)
    65  	}
    66  
    67  	runner, err := NewRunner(config, f, map[string]Annotations{}, cfg, map[string]*terraform.InputValue{})
    68  	if err != nil {
    69  		t.Fatal(err)
    70  	}
    71  
    72  	return runner
    73  }
    74  
    75  // AssertIssues is an assertion helper for comparing issues
    76  func AssertIssues(t *testing.T, expected Issues, actual Issues) {
    77  	opts := []cmp.Option{
    78  		// Byte field will be ignored because it's not important in tests such as positions
    79  		cmpopts.IgnoreFields(hcl.Pos{}, "Byte"),
    80  		ruleComparer(),
    81  	}
    82  	if !cmp.Equal(expected, actual, opts...) {
    83  		t.Fatalf("Expected issues are not matched:\n %s\n", cmp.Diff(expected, actual, opts...))
    84  	}
    85  }
    86  
    87  // AssertIssuesWithoutRange is an assertion helper for comparing issues
    88  func AssertIssuesWithoutRange(t *testing.T, expected Issues, actual Issues) {
    89  	opts := []cmp.Option{
    90  		cmpopts.IgnoreFields(Issue{}, "Range"),
    91  		ruleComparer(),
    92  	}
    93  	if !cmp.Equal(expected, actual, opts...) {
    94  		t.Fatalf("Expected issues are not matched:\n %s\n", cmp.Diff(expected, actual, opts...))
    95  	}
    96  }
    97  
    98  // AssertAppError is an assertion helper for comparing tflint.Error
    99  func AssertAppError(t *testing.T, expected Error, got error) {
   100  	if appErr, ok := got.(*Error); ok {
   101  		if appErr == nil {
   102  			t.Fatalf("expected err is `%s`, but nothing occurred", expected.Error())
   103  		}
   104  		if appErr.Code != expected.Code {
   105  			t.Fatalf("expected error code is `%s`, but get `%s`", expected.Code, appErr.Code)
   106  		}
   107  		if appErr.Level != expected.Level {
   108  			t.Fatalf("expected error level is `%s`, but get `%s`", expected.Level, appErr.Level)
   109  		}
   110  		if appErr.Error() != expected.Error() {
   111  			t.Fatalf("expected error is `%s`, but get `%s`", expected.Error(), appErr.Error())
   112  		}
   113  	} else {
   114  		t.Fatalf("unexpected error occurred: %s", got)
   115  	}
   116  }
   117  
   118  // ruleComparer returns a Comparer func that checks that two rule interfaces
   119  // have the same underlying type. It does not compare struct fields.
   120  func ruleComparer() cmp.Option {
   121  	return cmp.Comparer(func(x, y Rule) bool {
   122  		return reflect.TypeOf(x) == reflect.TypeOf(y)
   123  	})
   124  }