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 }