github.com/terraform-linters/tflint@v0.51.2-0.20240520175844-3750771571b6/terraform/lang/funcs/sensitive_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package funcs
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  
    10  	"github.com/terraform-linters/tflint-plugin-sdk/terraform/lang/marks"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  func TestSensitive(t *testing.T) {
    15  	tests := []struct {
    16  		Input   cty.Value
    17  		WantErr string
    18  	}{
    19  		{
    20  			cty.NumberIntVal(1),
    21  			``,
    22  		},
    23  		{
    24  			// Unknown values stay unknown while becoming sensitive
    25  			cty.UnknownVal(cty.String),
    26  			``,
    27  		},
    28  		{
    29  			// Null values stay unknown while becoming sensitive
    30  			cty.NullVal(cty.String),
    31  			``,
    32  		},
    33  		{
    34  			// DynamicVal can be marked as sensitive
    35  			cty.DynamicVal,
    36  			``,
    37  		},
    38  		{
    39  			// The marking is shallow only
    40  			cty.ListVal([]cty.Value{cty.NumberIntVal(1)}),
    41  			``,
    42  		},
    43  		{
    44  			// A value already marked is allowed and stays marked
    45  			cty.NumberIntVal(1).Mark(marks.Sensitive),
    46  			``,
    47  		},
    48  		{
    49  			// A value with some non-standard mark gets "fixed" to be marked
    50  			// with the standard "sensitive" mark. (This situation occurring
    51  			// would imply an inconsistency/bug elsewhere, so we're just
    52  			// being robust about it here.)
    53  			cty.NumberIntVal(1).Mark("bloop"),
    54  			``,
    55  		},
    56  		{
    57  			// A value deep already marked is allowed and stays marked,
    58  			// _and_ we'll also mark the outer collection as sensitive.
    59  			cty.ListVal([]cty.Value{cty.NumberIntVal(1).Mark(marks.Sensitive)}),
    60  			``,
    61  		},
    62  	}
    63  
    64  	for _, test := range tests {
    65  		t.Run(fmt.Sprintf("sensitive(%#v)", test.Input), func(t *testing.T) {
    66  			got, err := Sensitive(test.Input)
    67  
    68  			if test.WantErr != "" {
    69  				if err == nil {
    70  					t.Fatal("succeeded; want error")
    71  				}
    72  				if got, want := err.Error(), test.WantErr; got != want {
    73  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
    74  				}
    75  				return
    76  			} else if err != nil {
    77  				t.Fatalf("unexpected error: %s", err)
    78  			}
    79  
    80  			if !got.HasMark(marks.Sensitive) {
    81  				t.Errorf("result is not marked sensitive")
    82  			}
    83  
    84  			gotRaw, gotMarks := got.Unmark()
    85  			if len(gotMarks) != 1 {
    86  				// We're only expecting to have the "sensitive" mark we checked
    87  				// above. Any others are an error, even if they happen to
    88  				// appear alongside "sensitive". (We might change this rule
    89  				// if someday we decide to use marks for some additional
    90  				// unrelated thing in Terraform, but currently we assume that
    91  				// _all_ marks imply sensitive, and so returning any other
    92  				// marks would be confusing.)
    93  				t.Errorf("extraneous marks %#v", gotMarks)
    94  			}
    95  
    96  			// Disregarding shallow marks, the result should have the same
    97  			// effective value as the input.
    98  			wantRaw, _ := test.Input.Unmark()
    99  			if !gotRaw.RawEquals(wantRaw) {
   100  				t.Errorf("wrong unmarked result\ngot:  %#v\nwant: %#v", got, wantRaw)
   101  			}
   102  		})
   103  	}
   104  }
   105  
   106  func TestNonsensitive(t *testing.T) {
   107  	tests := []struct {
   108  		Input   cty.Value
   109  		WantErr string
   110  	}{
   111  		{
   112  			cty.NumberIntVal(1).Mark(marks.Sensitive),
   113  			``,
   114  		},
   115  		{
   116  			cty.DynamicVal.Mark(marks.Sensitive),
   117  			``,
   118  		},
   119  		{
   120  			cty.UnknownVal(cty.String).Mark(marks.Sensitive),
   121  			``,
   122  		},
   123  		{
   124  			cty.NullVal(cty.EmptyObject).Mark(marks.Sensitive),
   125  			``,
   126  		},
   127  		{
   128  			// The inner sensitive remains afterwards
   129  			cty.ListVal([]cty.Value{cty.NumberIntVal(1).Mark(marks.Sensitive)}).Mark(marks.Sensitive),
   130  			``,
   131  		},
   132  
   133  		// Passing a value that is already non-sensitive is not an error,
   134  		// as this function may be used with specific to ensure that all
   135  		// values are indeed non-sensitive
   136  		{
   137  			cty.NumberIntVal(1),
   138  			``,
   139  		},
   140  		{
   141  			cty.NullVal(cty.String),
   142  			``,
   143  		},
   144  
   145  		// Unknown values may become sensitive once they are known, so we
   146  		// permit them to be marked nonsensitive.
   147  		{
   148  			cty.DynamicVal,
   149  			``,
   150  		},
   151  		{
   152  			cty.UnknownVal(cty.String),
   153  			``,
   154  		},
   155  	}
   156  
   157  	for _, test := range tests {
   158  		t.Run(fmt.Sprintf("nonsensitive(%#v)", test.Input), func(t *testing.T) {
   159  			got, err := Nonsensitive(test.Input)
   160  
   161  			if test.WantErr != "" {
   162  				if err == nil {
   163  					t.Fatal("succeeded; want error")
   164  				}
   165  				if got, want := err.Error(), test.WantErr; got != want {
   166  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
   167  				}
   168  				return
   169  			} else if err != nil {
   170  				t.Fatalf("unexpected error: %s", err)
   171  			}
   172  
   173  			if got.HasMark(marks.Sensitive) {
   174  				t.Errorf("result is still marked sensitive")
   175  			}
   176  			wantRaw, _ := test.Input.Unmark()
   177  			if !got.RawEquals(wantRaw) {
   178  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Input)
   179  			}
   180  		})
   181  	}
   182  }
   183  
   184  func TestIssensitive(t *testing.T) {
   185  	tests := []struct {
   186  		Input     cty.Value
   187  		Sensitive bool
   188  		WantErr   string
   189  	}{
   190  		{
   191  			cty.NumberIntVal(1).Mark(marks.Sensitive),
   192  			true,
   193  			``,
   194  		},
   195  		{
   196  			cty.NumberIntVal(1),
   197  			false,
   198  			``,
   199  		},
   200  		{
   201  			cty.DynamicVal.Mark(marks.Sensitive),
   202  			true,
   203  			``,
   204  		},
   205  		{
   206  			cty.UnknownVal(cty.String).Mark(marks.Sensitive),
   207  			true,
   208  			``,
   209  		},
   210  		{
   211  			cty.NullVal(cty.EmptyObject).Mark(marks.Sensitive),
   212  			true,
   213  			``,
   214  		},
   215  		{
   216  			cty.NullVal(cty.String),
   217  			false,
   218  			``,
   219  		},
   220  		{
   221  			cty.DynamicVal,
   222  			false,
   223  			``,
   224  		},
   225  		{
   226  			cty.UnknownVal(cty.String),
   227  			false,
   228  			``,
   229  		},
   230  	}
   231  
   232  	for _, test := range tests {
   233  		t.Run(fmt.Sprintf("issensitive(%#v)", test.Input), func(t *testing.T) {
   234  			got, err := Issensitive(test.Input)
   235  
   236  			if test.WantErr != "" {
   237  				if err == nil {
   238  					t.Fatal("succeeded; want error")
   239  				}
   240  				if got, want := err.Error(), test.WantErr; got != want {
   241  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
   242  				}
   243  				return
   244  			} else if err != nil {
   245  				t.Fatalf("unexpected error: %s", err)
   246  			}
   247  
   248  			if (got.True() && !test.Sensitive) || (got.False() && test.Sensitive) {
   249  				t.Errorf("wrong result \ngot:  %#v\nwant: %#v", got, test.Sensitive)
   250  			}
   251  		})
   252  	}
   253  
   254  }