github.com/terraform-linters/tflint-plugin-sdk@v0.22.0/helper/testing.go (about)

     1  package helper
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	"github.com/google/go-cmp/cmp/cmpopts"
    11  	"github.com/hashicorp/hcl/v2"
    12  	"github.com/hashicorp/hcl/v2/gohcl"
    13  	"github.com/hashicorp/hcl/v2/hclparse"
    14  	"github.com/terraform-linters/tflint-plugin-sdk/tflint"
    15  )
    16  
    17  // TestRunner returns a mock Runner for testing.
    18  // You can pass the map of file names and their contents in the second argument.
    19  func TestRunner(t *testing.T, files map[string]string) *Runner {
    20  	t.Helper()
    21  
    22  	runner := newLocalRunner(map[string]*hcl.File{}, Issues{})
    23  	parser := hclparse.NewParser()
    24  
    25  	for name, src := range files {
    26  		var file *hcl.File
    27  		var diags hcl.Diagnostics
    28  		if strings.HasSuffix(name, ".json") {
    29  			file, diags = parser.ParseJSON([]byte(src), name)
    30  		} else {
    31  			file, diags = parser.ParseHCL([]byte(src), name)
    32  		}
    33  		if diags.HasErrors() {
    34  			t.Fatal(diags)
    35  		}
    36  
    37  		if name == ".tflint.hcl" {
    38  			var config Config
    39  			if diags := gohcl.DecodeBody(file.Body, nil, &config); diags.HasErrors() {
    40  				t.Fatal(diags)
    41  			}
    42  			runner.config = config
    43  		} else {
    44  			runner.addLocalFile(name, file)
    45  		}
    46  	}
    47  
    48  	if err := runner.initFromFiles(); err != nil {
    49  		panic(fmt.Sprintf("Failed to initialize runner: %s", err))
    50  	}
    51  	return runner
    52  }
    53  
    54  // AssertIssues is an assertion helper for comparing issues.
    55  func AssertIssues(t *testing.T, want Issues, got Issues) {
    56  	t.Helper()
    57  
    58  	opts := []cmp.Option{
    59  		// Byte field will be ignored because it's not important in tests such as positions
    60  		cmpopts.IgnoreFields(hcl.Pos{}, "Byte"),
    61  		// Issues will be sorted and output in the end, so ignore the order.
    62  		ignoreIssuesOrder(),
    63  		ruleComparer(),
    64  	}
    65  	if diff := cmp.Diff(want, got, opts...); diff != "" {
    66  		t.Fatalf("Expected issues are not matched:\n %s\n", diff)
    67  	}
    68  }
    69  
    70  // AssertIssuesWithoutRange is an assertion helper for comparing issues except for range.
    71  func AssertIssuesWithoutRange(t *testing.T, want Issues, got Issues) {
    72  	t.Helper()
    73  
    74  	opts := []cmp.Option{
    75  		cmpopts.IgnoreFields(Issue{}, "Range"),
    76  		ignoreIssuesOrder(),
    77  		ruleComparer(),
    78  	}
    79  	if diff := cmp.Diff(want, got, opts...); diff != "" {
    80  		t.Fatalf("Expected issues are not matched:\n %s\n", diff)
    81  	}
    82  }
    83  
    84  // AssertChanges is an assertion helper for comparing autofix changes.
    85  func AssertChanges(t *testing.T, want map[string]string, got map[string][]byte) {
    86  	t.Helper()
    87  
    88  	sources := make(map[string]string)
    89  	for name, src := range got {
    90  		sources[name] = string(src)
    91  	}
    92  	if diff := cmp.Diff(want, sources); diff != "" {
    93  		t.Fatalf("Expected changes are not matched:\n %s\n", diff)
    94  	}
    95  }
    96  
    97  // ruleComparer returns a Comparer func that checks that two rule interfaces
    98  // have the same underlying type. It does not compare struct fields.
    99  func ruleComparer() cmp.Option {
   100  	return cmp.Comparer(func(x, y tflint.Rule) bool {
   101  		return reflect.TypeOf(x) == reflect.TypeOf(y)
   102  	})
   103  }
   104  
   105  func ignoreIssuesOrder() cmp.Option {
   106  	return cmpopts.SortSlices(func(i, j *Issue) bool {
   107  		if i.Range.Filename != j.Range.Filename {
   108  			return i.Range.Filename < j.Range.Filename
   109  		}
   110  		if i.Range.Start.Line != j.Range.Start.Line {
   111  			return i.Range.Start.Line < j.Range.Start.Line
   112  		}
   113  		if i.Range.Start.Column != j.Range.Start.Column {
   114  			return i.Range.Start.Column < j.Range.Start.Column
   115  		}
   116  		if i.Range.End.Line != j.Range.End.Line {
   117  			return i.Range.End.Line > j.Range.End.Line
   118  		}
   119  		if i.Range.End.Column != j.Range.End.Column {
   120  			return i.Range.End.Column > j.Range.End.Column
   121  		}
   122  		return i.Message < j.Message
   123  	})
   124  }