github.com/opentofu/opentofu@v1.7.1/internal/lang/funcs/sensitive_test.go (about)

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