github.com/pulumi/terraform@v1.4.0/pkg/command/validate_test.go (about)

     1  package command
     2  
     3  import (
     4  	"encoding/json"
     5  	"io/ioutil"
     6  	"os"
     7  	"path"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  	"github.com/zclconf/go-cty/cty"
    13  
    14  	"github.com/pulumi/terraform/pkg/configs/configschema"
    15  	"github.com/pulumi/terraform/pkg/providers"
    16  	"github.com/pulumi/terraform/pkg/terminal"
    17  )
    18  
    19  func setupTest(t *testing.T, fixturepath string, args ...string) (*terminal.TestOutput, int) {
    20  	view, done := testView(t)
    21  	p := testProvider()
    22  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
    23  		ResourceTypes: map[string]providers.Schema{
    24  			"test_instance": {
    25  				Block: &configschema.Block{
    26  					Attributes: map[string]*configschema.Attribute{
    27  						"ami": {Type: cty.String, Optional: true},
    28  					},
    29  					BlockTypes: map[string]*configschema.NestedBlock{
    30  						"network_interface": {
    31  							Nesting: configschema.NestingList,
    32  							Block: configschema.Block{
    33  								Attributes: map[string]*configschema.Attribute{
    34  									"device_index": {Type: cty.String, Optional: true},
    35  									"description":  {Type: cty.String, Optional: true},
    36  									"name":         {Type: cty.String, Optional: true},
    37  								},
    38  							},
    39  						},
    40  					},
    41  				},
    42  			},
    43  		},
    44  	}
    45  	c := &ValidateCommand{
    46  		Meta: Meta{
    47  			testingOverrides: metaOverridesForProvider(p),
    48  			View:             view,
    49  		},
    50  	}
    51  
    52  	args = append(args, "-no-color")
    53  	args = append(args, testFixturePath(fixturepath))
    54  
    55  	code := c.Run(args)
    56  	return done(t), code
    57  }
    58  
    59  func TestValidateCommand(t *testing.T) {
    60  	if output, code := setupTest(t, "validate-valid"); code != 0 {
    61  		t.Fatalf("unexpected non-successful exit code %d\n\n%s", code, output.Stderr())
    62  	}
    63  }
    64  
    65  func TestValidateCommandWithTfvarsFile(t *testing.T) {
    66  	// Create a temporary working directory that is empty because this test
    67  	// requires scanning the current working directory by validate command.
    68  	td := t.TempDir()
    69  	testCopyDir(t, testFixturePath("validate-valid/with-tfvars-file"), td)
    70  	defer testChdir(t, td)()
    71  
    72  	view, done := testView(t)
    73  	c := &ValidateCommand{
    74  		Meta: Meta{
    75  			testingOverrides: metaOverridesForProvider(testProvider()),
    76  			View:             view,
    77  		},
    78  	}
    79  
    80  	args := []string{}
    81  	code := c.Run(args)
    82  	output := done(t)
    83  	if code != 0 {
    84  		t.Fatalf("bad %d\n\n%s", code, output.Stderr())
    85  	}
    86  }
    87  
    88  func TestValidateFailingCommand(t *testing.T) {
    89  	if output, code := setupTest(t, "validate-invalid"); code != 1 {
    90  		t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr())
    91  	}
    92  }
    93  
    94  func TestValidateFailingCommandMissingQuote(t *testing.T) {
    95  	output, code := setupTest(t, "validate-invalid/missing_quote")
    96  
    97  	if code != 1 {
    98  		t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr())
    99  	}
   100  	wantError := "Error: Invalid reference"
   101  	if !strings.Contains(output.Stderr(), wantError) {
   102  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   103  	}
   104  }
   105  
   106  func TestValidateFailingCommandMissingVariable(t *testing.T) {
   107  	output, code := setupTest(t, "validate-invalid/missing_var")
   108  	if code != 1 {
   109  		t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr())
   110  	}
   111  	wantError := "Error: Reference to undeclared input variable"
   112  	if !strings.Contains(output.Stderr(), wantError) {
   113  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   114  	}
   115  }
   116  
   117  func TestSameProviderMutipleTimesShouldFail(t *testing.T) {
   118  	output, code := setupTest(t, "validate-invalid/multiple_providers")
   119  	if code != 1 {
   120  		t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr())
   121  	}
   122  	wantError := "Error: Duplicate provider configuration"
   123  	if !strings.Contains(output.Stderr(), wantError) {
   124  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   125  	}
   126  }
   127  
   128  func TestSameModuleMultipleTimesShouldFail(t *testing.T) {
   129  	output, code := setupTest(t, "validate-invalid/multiple_modules")
   130  	if code != 1 {
   131  		t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr())
   132  	}
   133  	wantError := "Error: Duplicate module call"
   134  	if !strings.Contains(output.Stderr(), wantError) {
   135  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   136  	}
   137  }
   138  
   139  func TestSameResourceMultipleTimesShouldFail(t *testing.T) {
   140  	output, code := setupTest(t, "validate-invalid/multiple_resources")
   141  	if code != 1 {
   142  		t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr())
   143  	}
   144  	wantError := `Error: Duplicate resource "aws_instance" configuration`
   145  	if !strings.Contains(output.Stderr(), wantError) {
   146  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   147  	}
   148  }
   149  
   150  func TestOutputWithoutValueShouldFail(t *testing.T) {
   151  	output, code := setupTest(t, "validate-invalid/outputs")
   152  	if code != 1 {
   153  		t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr())
   154  	}
   155  	wantError := `The argument "value" is required, but no definition was found.`
   156  	if !strings.Contains(output.Stderr(), wantError) {
   157  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   158  	}
   159  	wantError = `An argument named "values" is not expected here. Did you mean "value"?`
   160  	if !strings.Contains(output.Stderr(), wantError) {
   161  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   162  	}
   163  }
   164  
   165  func TestModuleWithIncorrectNameShouldFail(t *testing.T) {
   166  	output, code := setupTest(t, "validate-invalid/incorrectmodulename")
   167  	if code != 1 {
   168  		t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr())
   169  	}
   170  
   171  	wantError := `Error: Invalid module instance name`
   172  	if !strings.Contains(output.Stderr(), wantError) {
   173  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   174  	}
   175  	wantError = `Error: Variables not allowed`
   176  	if !strings.Contains(output.Stderr(), wantError) {
   177  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   178  	}
   179  }
   180  
   181  func TestWronglyUsedInterpolationShouldFail(t *testing.T) {
   182  	output, code := setupTest(t, "validate-invalid/interpolation")
   183  	if code != 1 {
   184  		t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr())
   185  	}
   186  
   187  	wantError := `Error: Variables not allowed`
   188  	if !strings.Contains(output.Stderr(), wantError) {
   189  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   190  	}
   191  	wantError = `A single static variable reference is required`
   192  	if !strings.Contains(output.Stderr(), wantError) {
   193  		t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr())
   194  	}
   195  }
   196  
   197  func TestMissingDefinedVar(t *testing.T) {
   198  	output, code := setupTest(t, "validate-invalid/missing_defined_var")
   199  	// This is allowed because validate tests only that variables are referenced
   200  	// correctly, not that they all have defined values.
   201  	if code != 0 {
   202  		t.Fatalf("Should have passed: %d\n\n%s", code, output.Stderr())
   203  	}
   204  }
   205  
   206  func TestValidate_json(t *testing.T) {
   207  	tests := []struct {
   208  		path  string
   209  		valid bool
   210  	}{
   211  		{"validate-valid", true},
   212  		{"validate-invalid", false},
   213  		{"validate-invalid/missing_quote", false},
   214  		{"validate-invalid/missing_var", false},
   215  		{"validate-invalid/multiple_providers", false},
   216  		{"validate-invalid/multiple_modules", false},
   217  		{"validate-invalid/multiple_resources", false},
   218  		{"validate-invalid/outputs", false},
   219  		{"validate-invalid/incorrectmodulename", false},
   220  		{"validate-invalid/interpolation", false},
   221  		{"validate-invalid/missing_defined_var", true},
   222  	}
   223  
   224  	for _, tc := range tests {
   225  		t.Run(tc.path, func(t *testing.T) {
   226  			var want, got map[string]interface{}
   227  
   228  			wantFile, err := os.Open(path.Join(testFixturePath(tc.path), "output.json"))
   229  			if err != nil {
   230  				t.Fatalf("failed to open output file: %s", err)
   231  			}
   232  			defer wantFile.Close()
   233  			wantBytes, err := ioutil.ReadAll(wantFile)
   234  			if err != nil {
   235  				t.Fatalf("failed to read output file: %s", err)
   236  			}
   237  			err = json.Unmarshal([]byte(wantBytes), &want)
   238  			if err != nil {
   239  				t.Fatalf("failed to unmarshal expected JSON: %s", err)
   240  			}
   241  
   242  			output, code := setupTest(t, tc.path, "-json")
   243  
   244  			gotString := output.Stdout()
   245  			err = json.Unmarshal([]byte(gotString), &got)
   246  			if err != nil {
   247  				t.Fatalf("failed to unmarshal actual JSON: %s", err)
   248  			}
   249  
   250  			if !cmp.Equal(got, want) {
   251  				t.Errorf("wrong output:\n %v\n", cmp.Diff(got, want))
   252  				t.Errorf("raw output:\n%s\n", gotString)
   253  			}
   254  
   255  			if tc.valid && code != 0 {
   256  				t.Errorf("wrong exit code: want 0, got %d", code)
   257  			} else if !tc.valid && code != 1 {
   258  				t.Errorf("wrong exit code: want 1, got %d", code)
   259  			}
   260  
   261  			if errorOutput := output.Stderr(); errorOutput != "" {
   262  				t.Errorf("unexpected error output:\n%s", errorOutput)
   263  			}
   264  		})
   265  	}
   266  }