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

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package funcs
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  
    10  	"github.com/zclconf/go-cty/cty"
    11  )
    12  
    13  func TestReplace(t *testing.T) {
    14  	tests := []struct {
    15  		String  cty.Value
    16  		Substr  cty.Value
    17  		Replace cty.Value
    18  		Want    cty.Value
    19  		Err     bool
    20  	}{
    21  		{ // Regular search and replace
    22  			cty.StringVal("hello"),
    23  			cty.StringVal("hel"),
    24  			cty.StringVal("bel"),
    25  			cty.StringVal("bello"),
    26  			false,
    27  		},
    28  		{ // Search string doesn't match
    29  			cty.StringVal("hello"),
    30  			cty.StringVal("nope"),
    31  			cty.StringVal("bel"),
    32  			cty.StringVal("hello"),
    33  			false,
    34  		},
    35  		{ // Regular expression
    36  			cty.StringVal("hello"),
    37  			cty.StringVal("/l/"),
    38  			cty.StringVal("L"),
    39  			cty.StringVal("heLLo"),
    40  			false,
    41  		},
    42  		{
    43  			cty.StringVal("helo"),
    44  			cty.StringVal("/(l)/"),
    45  			cty.StringVal("$1$1"),
    46  			cty.StringVal("hello"),
    47  			false,
    48  		},
    49  		{ // Bad regexp
    50  			cty.StringVal("hello"),
    51  			cty.StringVal("/(l/"),
    52  			cty.StringVal("$1$1"),
    53  			cty.UnknownVal(cty.String),
    54  			true,
    55  		},
    56  	}
    57  
    58  	for _, test := range tests {
    59  		t.Run(fmt.Sprintf("replace(%#v, %#v, %#v)", test.String, test.Substr, test.Replace), func(t *testing.T) {
    60  			got, err := Replace(test.String, test.Substr, test.Replace)
    61  
    62  			if test.Err {
    63  				if err == nil {
    64  					t.Fatal("succeeded; want error")
    65  				}
    66  				return
    67  			} else if err != nil {
    68  				t.Fatalf("unexpected error: %s", err)
    69  			}
    70  
    71  			if !got.RawEquals(test.Want) {
    72  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
    73  			}
    74  		})
    75  	}
    76  }
    77  
    78  func TestStrContains(t *testing.T) {
    79  	tests := []struct {
    80  		String cty.Value
    81  		Substr cty.Value
    82  		Want   cty.Value
    83  		Err    bool
    84  	}{
    85  		{
    86  			cty.StringVal("hello"),
    87  			cty.StringVal("hel"),
    88  			cty.BoolVal(true),
    89  			false,
    90  		},
    91  		{
    92  			cty.StringVal("hello"),
    93  			cty.StringVal("lo"),
    94  			cty.BoolVal(true),
    95  			false,
    96  		},
    97  		{
    98  			cty.StringVal("hello1"),
    99  			cty.StringVal("1"),
   100  			cty.BoolVal(true),
   101  			false,
   102  		},
   103  		{
   104  			cty.StringVal("hello1"),
   105  			cty.StringVal("heo"),
   106  			cty.BoolVal(false),
   107  			false,
   108  		},
   109  		{
   110  			cty.StringVal("hello1"),
   111  			cty.NumberIntVal(1),
   112  			cty.UnknownVal(cty.Bool),
   113  			true,
   114  		},
   115  	}
   116  
   117  	for _, test := range tests {
   118  		t.Run(fmt.Sprintf("includes(%#v, %#v)", test.String, test.Substr), func(t *testing.T) {
   119  			got, err := StrContains(test.String, test.Substr)
   120  
   121  			if test.Err {
   122  				if err == nil {
   123  					t.Fatal("succeeded; want error")
   124  				}
   125  				return
   126  			} else if err != nil {
   127  				t.Fatalf("unexpected error: %s", err)
   128  			}
   129  
   130  			if !got.RawEquals(test.Want) {
   131  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   132  			}
   133  		})
   134  	}
   135  }
   136  
   137  func TestStartsWith(t *testing.T) {
   138  	tests := []struct {
   139  		String, Prefix cty.Value
   140  		Want           cty.Value
   141  		WantError      string
   142  	}{
   143  		{
   144  			cty.StringVal("hello world"),
   145  			cty.StringVal("hello"),
   146  			cty.True,
   147  			``,
   148  		},
   149  		{
   150  			cty.StringVal("hey world"),
   151  			cty.StringVal("hello"),
   152  			cty.False,
   153  			``,
   154  		},
   155  		{
   156  			cty.StringVal(""),
   157  			cty.StringVal(""),
   158  			cty.True,
   159  			``,
   160  		},
   161  		{
   162  			cty.StringVal("a"),
   163  			cty.StringVal(""),
   164  			cty.True,
   165  			``,
   166  		},
   167  		{
   168  			cty.StringVal(""),
   169  			cty.StringVal("a"),
   170  			cty.False,
   171  			``,
   172  		},
   173  		{
   174  			cty.UnknownVal(cty.String),
   175  			cty.StringVal("a"),
   176  			cty.UnknownVal(cty.Bool).RefineNotNull(),
   177  			``,
   178  		},
   179  		{
   180  			cty.UnknownVal(cty.String),
   181  			cty.StringVal(""),
   182  			cty.True,
   183  			``,
   184  		},
   185  		{
   186  			cty.UnknownVal(cty.String).Refine().StringPrefix("https:").NewValue(),
   187  			cty.StringVal(""),
   188  			cty.True,
   189  			``,
   190  		},
   191  		{
   192  			cty.UnknownVal(cty.String).Refine().StringPrefix("https:").NewValue(),
   193  			cty.StringVal("a"),
   194  			cty.False,
   195  			``,
   196  		},
   197  		{
   198  			cty.UnknownVal(cty.String).Refine().StringPrefix("https:").NewValue(),
   199  			cty.StringVal("ht"),
   200  			cty.True,
   201  			``,
   202  		},
   203  		{
   204  			cty.UnknownVal(cty.String).Refine().StringPrefix("https:").NewValue(),
   205  			cty.StringVal("https:"),
   206  			cty.True,
   207  			``,
   208  		},
   209  		{
   210  			cty.UnknownVal(cty.String).Refine().StringPrefix("https:").NewValue(),
   211  			cty.StringVal("https-"),
   212  			cty.False,
   213  			``,
   214  		},
   215  		{
   216  			cty.UnknownVal(cty.String).Refine().StringPrefix("https:").NewValue(),
   217  			cty.StringVal("https://"),
   218  			cty.UnknownVal(cty.Bool).RefineNotNull(),
   219  			``,
   220  		},
   221  		{
   222  			// Unicode combining characters edge-case: we match the prefix
   223  			// in terms of unicode code units rather than grapheme clusters,
   224  			// which is inconsistent with our string processing elsewhere but
   225  			// would be a breaking change to fix that bug now.
   226  			cty.StringVal("\U0001f937\u200d\u2642"), // "Man Shrugging" is encoded as "Person Shrugging" followed by zero-width joiner and then the masculine gender presentation modifier
   227  			cty.StringVal("\U0001f937"),             // Just the "Person Shrugging" character without any modifiers
   228  			cty.True,
   229  			``,
   230  		},
   231  	}
   232  
   233  	for _, test := range tests {
   234  		t.Run(fmt.Sprintf("StartsWith(%#v, %#v)", test.String, test.Prefix), func(t *testing.T) {
   235  			got, err := StartsWithFunc.Call([]cty.Value{test.String, test.Prefix})
   236  
   237  			if test.WantError != "" {
   238  				gotErr := fmt.Sprintf("%s", err)
   239  				if gotErr != test.WantError {
   240  					t.Errorf("wrong error\ngot:  %s\nwant: %s", gotErr, test.WantError)
   241  				}
   242  				return
   243  			} else if err != nil {
   244  				t.Fatalf("unexpected error: %s", err)
   245  			}
   246  
   247  			if !got.RawEquals(test.Want) {
   248  				t.Errorf(
   249  					"wrong result\nstring: %#v\nprefix: %#v\ngot:    %#v\nwant:   %#v",
   250  					test.String, test.Prefix, got, test.Want,
   251  				)
   252  			}
   253  		})
   254  	}
   255  }