github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/configs/parser_config_test.go (about)

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