github.com/pulumi/terraform@v1.4.0/pkg/configs/parser_config_test.go (about)

     1  package configs
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"io/ioutil"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  
    13  	"github.com/hashicorp/hcl/v2"
    14  )
    15  
    16  // TestParseLoadConfigFileSuccess is a simple test that just verifies that
    17  // a number of test configuration files (in testdata/valid-files) can
    18  // be parsed without raising any diagnostics.
    19  //
    20  // This test does not verify that reading these files produces the correct
    21  // file element contents. More detailed assertions may be made on some subset
    22  // of these configuration files in other tests.
    23  func TestParserLoadConfigFileSuccess(t *testing.T) {
    24  	files, err := ioutil.ReadDir("testdata/valid-files")
    25  	if err != nil {
    26  		t.Fatal(err)
    27  	}
    28  
    29  	for _, info := range files {
    30  		name := info.Name()
    31  		t.Run(name, func(t *testing.T) {
    32  			src, err := ioutil.ReadFile(filepath.Join("testdata/valid-files", name))
    33  			if err != nil {
    34  				t.Fatal(err)
    35  			}
    36  
    37  			parser := testParser(map[string]string{
    38  				name: string(src),
    39  			})
    40  
    41  			_, diags := parser.LoadConfigFile(name)
    42  			if len(diags) != 0 {
    43  				t.Errorf("unexpected diagnostics")
    44  				for _, diag := range diags {
    45  					t.Logf("- %s", diag)
    46  				}
    47  			}
    48  		})
    49  	}
    50  }
    51  
    52  // TestParseLoadConfigFileFailure is a simple test that just verifies that
    53  // a number of test configuration files (in testdata/invalid-files)
    54  // produce errors as expected.
    55  //
    56  // This test does not verify specific error messages, so more detailed
    57  // assertions should be made on some subset of these configuration files in
    58  // other tests.
    59  func TestParserLoadConfigFileFailure(t *testing.T) {
    60  	files, err := ioutil.ReadDir("testdata/invalid-files")
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  
    65  	for _, info := range files {
    66  		name := info.Name()
    67  		t.Run(name, func(t *testing.T) {
    68  			src, err := ioutil.ReadFile(filepath.Join("testdata/invalid-files", name))
    69  			if err != nil {
    70  				t.Fatal(err)
    71  			}
    72  
    73  			parser := testParser(map[string]string{
    74  				name: string(src),
    75  			})
    76  
    77  			_, diags := parser.LoadConfigFile(name)
    78  			if !diags.HasErrors() {
    79  				t.Errorf("LoadConfigFile succeeded; want errors")
    80  			}
    81  			for _, diag := range diags {
    82  				t.Logf("- %s", diag)
    83  			}
    84  		})
    85  	}
    86  }
    87  
    88  // This test uses a subset of the same fixture files as
    89  // TestParserLoadConfigFileFailure, but additionally verifies that each
    90  // file produces the expected diagnostic summary.
    91  func TestParserLoadConfigFileFailureMessages(t *testing.T) {
    92  	tests := []struct {
    93  		Filename     string
    94  		WantSeverity hcl.DiagnosticSeverity
    95  		WantDiag     string
    96  	}{
    97  		{
    98  			"invalid-files/data-resource-lifecycle.tf",
    99  			hcl.DiagError,
   100  			"Invalid data resource lifecycle argument",
   101  		},
   102  		{
   103  			"invalid-files/variable-type-unknown.tf",
   104  			hcl.DiagError,
   105  			"Invalid type specification",
   106  		},
   107  		{
   108  			"invalid-files/unexpected-attr.tf",
   109  			hcl.DiagError,
   110  			"Unsupported argument",
   111  		},
   112  		{
   113  			"invalid-files/unexpected-block.tf",
   114  			hcl.DiagError,
   115  			"Unsupported block type",
   116  		},
   117  		{
   118  			"invalid-files/resource-count-and-for_each.tf",
   119  			hcl.DiagError,
   120  			`Invalid combination of "count" and "for_each"`,
   121  		},
   122  		{
   123  			"invalid-files/data-count-and-for_each.tf",
   124  			hcl.DiagError,
   125  			`Invalid combination of "count" and "for_each"`,
   126  		},
   127  		{
   128  			"invalid-files/resource-lifecycle-badbool.tf",
   129  			hcl.DiagError,
   130  			"Unsuitable value type",
   131  		},
   132  	}
   133  
   134  	for _, test := range tests {
   135  		t.Run(test.Filename, func(t *testing.T) {
   136  			src, err := ioutil.ReadFile(filepath.Join("testdata", test.Filename))
   137  			if err != nil {
   138  				t.Fatal(err)
   139  			}
   140  
   141  			parser := testParser(map[string]string{
   142  				test.Filename: string(src),
   143  			})
   144  
   145  			_, diags := parser.LoadConfigFile(test.Filename)
   146  			if len(diags) != 1 {
   147  				t.Errorf("Wrong number of diagnostics %d; want 1", len(diags))
   148  				for _, diag := range diags {
   149  					t.Logf("- %s", diag)
   150  				}
   151  				return
   152  			}
   153  			if diags[0].Severity != test.WantSeverity {
   154  				t.Errorf("Wrong diagnostic severity %#v; want %#v", diags[0].Severity, test.WantSeverity)
   155  			}
   156  			if diags[0].Summary != test.WantDiag {
   157  				t.Errorf("Wrong diagnostic summary\ngot:  %s\nwant: %s", diags[0].Summary, test.WantDiag)
   158  			}
   159  		})
   160  	}
   161  }
   162  
   163  // TestParseLoadConfigFileWarning is a test that verifies files from
   164  // testdata/warning-files produce particular warnings.
   165  //
   166  // This test does not verify that reading these files produces the correct
   167  // file element contents in spite of those warnings. More detailed assertions
   168  // may be made on some subset of these configuration files in other tests.
   169  func TestParserLoadConfigFileWarning(t *testing.T) {
   170  	files, err := ioutil.ReadDir("testdata/warning-files")
   171  	if err != nil {
   172  		t.Fatal(err)
   173  	}
   174  
   175  	for _, info := range files {
   176  		name := info.Name()
   177  		t.Run(name, func(t *testing.T) {
   178  			src, err := ioutil.ReadFile(filepath.Join("testdata/warning-files", name))
   179  			if err != nil {
   180  				t.Fatal(err)
   181  			}
   182  
   183  			// First we'll scan the file to see what warnings are expected.
   184  			// That's declared inside the files themselves by using the
   185  			// string "WARNING: " somewhere on each line that is expected
   186  			// to produce a warning, followed by the expected warning summary
   187  			// text. A single-line comment (with #) is the main way to do that.
   188  			const marker = "WARNING: "
   189  			sc := bufio.NewScanner(bytes.NewReader(src))
   190  			wantWarnings := make(map[int]string)
   191  			lineNum := 1
   192  			for sc.Scan() {
   193  				lineText := sc.Text()
   194  				if idx := strings.Index(lineText, marker); idx != -1 {
   195  					summaryText := lineText[idx+len(marker):]
   196  					wantWarnings[lineNum] = summaryText
   197  				}
   198  				lineNum++
   199  			}
   200  
   201  			parser := testParser(map[string]string{
   202  				name: string(src),
   203  			})
   204  
   205  			_, diags := parser.LoadConfigFile(name)
   206  			if diags.HasErrors() {
   207  				t.Errorf("unexpected error diagnostics")
   208  				for _, diag := range diags {
   209  					t.Logf("- %s", diag)
   210  				}
   211  			}
   212  
   213  			gotWarnings := make(map[int]string)
   214  			for _, diag := range diags {
   215  				if diag.Severity != hcl.DiagWarning || diag.Subject == nil {
   216  					continue
   217  				}
   218  				gotWarnings[diag.Subject.Start.Line] = diag.Summary
   219  			}
   220  
   221  			if diff := cmp.Diff(wantWarnings, gotWarnings); diff != "" {
   222  				t.Errorf("wrong warnings\n%s", diff)
   223  			}
   224  		})
   225  	}
   226  }
   227  
   228  // TestParseLoadConfigFileError is a test that verifies files from
   229  // testdata/warning-files produce particular errors.
   230  //
   231  // This test does not verify that reading these files produces the correct
   232  // file element contents in spite of those errors. More detailed assertions
   233  // may be made on some subset of these configuration files in other tests.
   234  func TestParserLoadConfigFileError(t *testing.T) {
   235  	files, err := ioutil.ReadDir("testdata/error-files")
   236  	if err != nil {
   237  		t.Fatal(err)
   238  	}
   239  
   240  	for _, info := range files {
   241  		name := info.Name()
   242  		t.Run(name, func(t *testing.T) {
   243  			src, err := ioutil.ReadFile(filepath.Join("testdata/error-files", name))
   244  			if err != nil {
   245  				t.Fatal(err)
   246  			}
   247  
   248  			// First we'll scan the file to see what warnings are expected.
   249  			// That's declared inside the files themselves by using the
   250  			// string "ERROR: " somewhere on each line that is expected
   251  			// to produce a warning, followed by the expected warning summary
   252  			// text. A single-line comment (with #) is the main way to do that.
   253  			const marker = "ERROR: "
   254  			sc := bufio.NewScanner(bytes.NewReader(src))
   255  			wantErrors := make(map[int]string)
   256  			lineNum := 1
   257  			for sc.Scan() {
   258  				lineText := sc.Text()
   259  				if idx := strings.Index(lineText, marker); idx != -1 {
   260  					summaryText := lineText[idx+len(marker):]
   261  					wantErrors[lineNum] = summaryText
   262  				}
   263  				lineNum++
   264  			}
   265  
   266  			parser := testParser(map[string]string{
   267  				name: string(src),
   268  			})
   269  
   270  			_, diags := parser.LoadConfigFile(name)
   271  
   272  			gotErrors := make(map[int]string)
   273  			for _, diag := range diags {
   274  				if diag.Severity != hcl.DiagError || diag.Subject == nil {
   275  					continue
   276  				}
   277  				gotErrors[diag.Subject.Start.Line] = diag.Summary
   278  			}
   279  
   280  			if diff := cmp.Diff(wantErrors, gotErrors); diff != "" {
   281  				t.Errorf("wrong errors\n%s", diff)
   282  			}
   283  		})
   284  	}
   285  }