github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/backend/remote/backend_context_test.go (about)

     1  package remote
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	tfe "github.com/hashicorp/go-tfe"
     8  	"github.com/hashicorp/terraform/backend"
     9  	"github.com/hashicorp/terraform/configs"
    10  	"github.com/hashicorp/terraform/internal/initwd"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  func TestRemoteStoredVariableValue(t *testing.T) {
    15  	tests := map[string]struct {
    16  		Def       *tfe.Variable
    17  		Want      cty.Value
    18  		WantError string
    19  	}{
    20  		"string literal": {
    21  			&tfe.Variable{
    22  				Key:       "test",
    23  				Value:     "foo",
    24  				HCL:       false,
    25  				Sensitive: false,
    26  			},
    27  			cty.StringVal("foo"),
    28  			``,
    29  		},
    30  		"string HCL": {
    31  			&tfe.Variable{
    32  				Key:       "test",
    33  				Value:     `"foo"`,
    34  				HCL:       true,
    35  				Sensitive: false,
    36  			},
    37  			cty.StringVal("foo"),
    38  			``,
    39  		},
    40  		"list HCL": {
    41  			&tfe.Variable{
    42  				Key:       "test",
    43  				Value:     `[]`,
    44  				HCL:       true,
    45  				Sensitive: false,
    46  			},
    47  			cty.EmptyTupleVal,
    48  			``,
    49  		},
    50  		"null HCL": {
    51  			&tfe.Variable{
    52  				Key:       "test",
    53  				Value:     `null`,
    54  				HCL:       true,
    55  				Sensitive: false,
    56  			},
    57  			cty.NullVal(cty.DynamicPseudoType),
    58  			``,
    59  		},
    60  		"literal sensitive": {
    61  			&tfe.Variable{
    62  				Key:       "test",
    63  				HCL:       false,
    64  				Sensitive: true,
    65  			},
    66  			cty.UnknownVal(cty.String),
    67  			``,
    68  		},
    69  		"HCL sensitive": {
    70  			&tfe.Variable{
    71  				Key:       "test",
    72  				HCL:       true,
    73  				Sensitive: true,
    74  			},
    75  			cty.DynamicVal,
    76  			``,
    77  		},
    78  		"HCL computation": {
    79  			// This (stored expressions containing computation) is not a case
    80  			// we intentionally supported, but it became possible for remote
    81  			// operations in Terraform 0.12 (due to Terraform Cloud/Enterprise
    82  			// just writing the HCL verbatim into generated `.tfvars` files).
    83  			// We support it here for consistency, and we continue to support
    84  			// it in both places for backward-compatibility. In practice,
    85  			// there's little reason to do computation in a stored variable
    86  			// value because references are not supported.
    87  			&tfe.Variable{
    88  				Key:       "test",
    89  				Value:     `[for v in ["a"] : v]`,
    90  				HCL:       true,
    91  				Sensitive: false,
    92  			},
    93  			cty.TupleVal([]cty.Value{cty.StringVal("a")}),
    94  			``,
    95  		},
    96  		"HCL syntax error": {
    97  			&tfe.Variable{
    98  				Key:       "test",
    99  				Value:     `[`,
   100  				HCL:       true,
   101  				Sensitive: false,
   102  			},
   103  			cty.DynamicVal,
   104  			`Invalid expression for var.test: The value of variable "test" is marked in the remote workspace as being specified in HCL syntax, but the given value is not valid HCL. Stored variable values must be valid literal expressions and may not contain references to other variables or calls to functions.`,
   105  		},
   106  		"HCL with references": {
   107  			&tfe.Variable{
   108  				Key:       "test",
   109  				Value:     `foo.bar`,
   110  				HCL:       true,
   111  				Sensitive: false,
   112  			},
   113  			cty.DynamicVal,
   114  			`Invalid expression for var.test: The value of variable "test" is marked in the remote workspace as being specified in HCL syntax, but the given value is not valid HCL. Stored variable values must be valid literal expressions and may not contain references to other variables or calls to functions.`,
   115  		},
   116  	}
   117  
   118  	for name, test := range tests {
   119  		t.Run(name, func(t *testing.T) {
   120  			v := &remoteStoredVariableValue{
   121  				definition: test.Def,
   122  			}
   123  			// This ParseVariableValue implementation ignores the parsing mode,
   124  			// so we'll just always parse literal here. (The parsing mode is
   125  			// selected by the remote server, not by our local configuration.)
   126  			gotIV, diags := v.ParseVariableValue(configs.VariableParseLiteral)
   127  			if test.WantError != "" {
   128  				if !diags.HasErrors() {
   129  					t.Fatalf("missing expected error\ngot:  <no error>\nwant: %s", test.WantError)
   130  				}
   131  				errStr := diags.Err().Error()
   132  				if errStr != test.WantError {
   133  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", errStr, test.WantError)
   134  				}
   135  			} else {
   136  				if diags.HasErrors() {
   137  					t.Fatalf("unexpected error\ngot:  %s\nwant: <no error>", diags.Err().Error())
   138  				}
   139  				got := gotIV.Value
   140  				if !test.Want.RawEquals(got) {
   141  					t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   142  				}
   143  			}
   144  		})
   145  	}
   146  }
   147  
   148  func TestRemoteContextWithVars(t *testing.T) {
   149  	catTerraform := tfe.CategoryTerraform
   150  	catEnv := tfe.CategoryEnv
   151  
   152  	tests := map[string]struct {
   153  		Opts      *tfe.VariableCreateOptions
   154  		WantError string
   155  	}{
   156  		"Terraform variable": {
   157  			&tfe.VariableCreateOptions{
   158  				Category: &catTerraform,
   159  			},
   160  			`Value for undeclared variable: A variable named "key" was assigned a value, but the root module does not declare a variable of that name. To use this value, add a "variable" block to the configuration.`,
   161  		},
   162  		"environment variable": {
   163  			&tfe.VariableCreateOptions{
   164  				Category: &catEnv,
   165  			},
   166  			``,
   167  		},
   168  	}
   169  
   170  	for name, test := range tests {
   171  		t.Run(name, func(t *testing.T) {
   172  			configDir := "./testdata/empty"
   173  
   174  			b, bCleanup := testBackendDefault(t)
   175  			defer bCleanup()
   176  
   177  			_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir)
   178  			defer configCleanup()
   179  
   180  			workspaceID, err := b.getRemoteWorkspaceID(context.Background(), backend.DefaultStateName)
   181  			if err != nil {
   182  				t.Fatal(err)
   183  			}
   184  
   185  			op := &backend.Operation{
   186  				ConfigDir:    configDir,
   187  				ConfigLoader: configLoader,
   188  				Workspace:    backend.DefaultStateName,
   189  			}
   190  
   191  			v := test.Opts
   192  			if v.Key == nil {
   193  				key := "key"
   194  				v.Key = &key
   195  			}
   196  			b.client.Variables.Create(nil, workspaceID, *v)
   197  
   198  			_, _, diags := b.Context(op)
   199  
   200  			if test.WantError != "" {
   201  				if !diags.HasErrors() {
   202  					t.Fatalf("missing expected error\ngot:  <no error>\nwant: %s", test.WantError)
   203  				}
   204  				errStr := diags.Err().Error()
   205  				if errStr != test.WantError {
   206  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", errStr, test.WantError)
   207  				}
   208  			} else {
   209  				if diags.HasErrors() {
   210  					t.Fatalf("unexpected error\ngot:  %s\nwant: <no error>", diags.Err().Error())
   211  				}
   212  			}
   213  		})
   214  	}
   215  }