1package stdlib
2
3import (
4	"testing"
5
6	"github.com/zclconf/go-cty/cty"
7)
8
9func TestUpper(t *testing.T) {
10	tests := []struct {
11		Input cty.Value
12		Want  cty.Value
13	}{
14		{
15			cty.StringVal("hello"),
16			cty.StringVal("HELLO"),
17		},
18		{
19			cty.StringVal("HELLO"),
20			cty.StringVal("HELLO"),
21		},
22		{
23			cty.StringVal(""),
24			cty.StringVal(""),
25		},
26		{
27			cty.StringVal("1"),
28			cty.StringVal("1"),
29		},
30		{
31			cty.StringVal("жж"),
32			cty.StringVal("ЖЖ"),
33		},
34		{
35			cty.StringVal("noël"),
36			cty.StringVal("NOËL"),
37		},
38		{
39			// Go's case conversions don't handle this ligature, which is
40			// unfortunate but is now a compatibility constraint since it
41			// would be potentially-breaking to behave differently here in
42			// future.
43			cty.StringVal("baffle"),
44			cty.StringVal("BAfflE"),
45		},
46		{
47			cty.StringVal("����"),
48			cty.StringVal("����"),
49		},
50		{
51			cty.UnknownVal(cty.String),
52			cty.UnknownVal(cty.String),
53		},
54		{
55			cty.DynamicVal,
56			cty.UnknownVal(cty.String),
57		},
58		{
59			cty.StringVal("hello").Mark(1),
60			cty.StringVal("HELLO").Mark(1),
61		},
62	}
63
64	for _, test := range tests {
65		t.Run(test.Input.GoString(), func(t *testing.T) {
66			got, err := Upper(test.Input)
67
68			if err != nil {
69				t.Fatalf("unexpected error: %s", err)
70			}
71
72			if !got.RawEquals(test.Want) {
73				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
74			}
75		})
76	}
77}
78
79func TestLower(t *testing.T) {
80	tests := []struct {
81		Input cty.Value
82		Want  cty.Value
83	}{
84		{
85			cty.StringVal("HELLO"),
86			cty.StringVal("hello"),
87		},
88		{
89			cty.StringVal("hello"),
90			cty.StringVal("hello"),
91		},
92		{
93			cty.StringVal(""),
94			cty.StringVal(""),
95		},
96		{
97			cty.StringVal("1"),
98			cty.StringVal("1"),
99		},
100		{
101			cty.StringVal("ЖЖ"),
102			cty.StringVal("жж"),
103		},
104		{
105			cty.UnknownVal(cty.String),
106			cty.UnknownVal(cty.String),
107		},
108		{
109			cty.DynamicVal,
110			cty.UnknownVal(cty.String),
111		},
112	}
113
114	for _, test := range tests {
115		t.Run(test.Input.GoString(), func(t *testing.T) {
116			got, err := Lower(test.Input)
117
118			if err != nil {
119				t.Fatalf("unexpected error: %s", err)
120			}
121
122			if !got.RawEquals(test.Want) {
123				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
124			}
125		})
126	}
127}
128
129func TestReverse(t *testing.T) {
130	tests := []struct {
131		Input cty.Value
132		Want  cty.Value
133	}{
134		{
135			cty.StringVal("hello"),
136			cty.StringVal("olleh"),
137		},
138		{
139			cty.StringVal(""),
140			cty.StringVal(""),
141		},
142		{
143			cty.StringVal("1"),
144			cty.StringVal("1"),
145		},
146		{
147			cty.StringVal("Живой Журнал"),
148			cty.StringVal("ланруЖ йовиЖ"),
149		},
150		{
151			// note that the dieresis here is intentionally a combining
152			// ligature.
153			cty.StringVal("noël"),
154			cty.StringVal("lëon"),
155		},
156		{
157			// The Es in this string has three combining acute accents.
158			// This tests something that NFC-normalization cannot collapse
159			// into a single precombined codepoint, since otherwise we might
160			// be cheating and relying on the single-codepoint forms.
161			cty.StringVal("wé́́é́́é́́!"),
162			cty.StringVal("!é́́é́́é́́w"),
163		},
164		{
165			// Go's normalization forms don't handle this ligature, so we
166			// will produce the wrong result but this is now a compatibility
167			// constraint and so we'll test it.
168			cty.StringVal("baffle"),
169			cty.StringVal("efflab"),
170		},
171		{
172			cty.StringVal("����"),
173			cty.StringVal("����"),
174		},
175		{
176			cty.UnknownVal(cty.String),
177			cty.UnknownVal(cty.String),
178		},
179		{
180			cty.DynamicVal,
181			cty.UnknownVal(cty.String),
182		},
183	}
184
185	for _, test := range tests {
186		t.Run(test.Input.GoString(), func(t *testing.T) {
187			got, err := Reverse(test.Input)
188
189			if err != nil {
190				t.Fatalf("unexpected error: %s", err)
191			}
192
193			if !got.RawEquals(test.Want) {
194				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
195			}
196		})
197	}
198}
199
200func TestStrlen(t *testing.T) {
201	tests := []struct {
202		Input cty.Value
203		Want  cty.Value
204	}{
205		{
206			cty.StringVal("hello"),
207			cty.NumberIntVal(5),
208		},
209		{
210			cty.StringVal(""),
211			cty.NumberIntVal(0),
212		},
213		{
214			cty.StringVal("1"),
215			cty.NumberIntVal(1),
216		},
217		{
218			cty.StringVal("Живой Журнал"),
219			cty.NumberIntVal(12),
220		},
221		{
222			// note that the dieresis here is intentionally a combining
223			// ligature.
224			cty.StringVal("noël"),
225			cty.NumberIntVal(4),
226		},
227		{
228			// The Es in this string has three combining acute accents.
229			// This tests something that NFC-normalization cannot collapse
230			// into a single precombined codepoint, since otherwise we might
231			// be cheating and relying on the single-codepoint forms.
232			cty.StringVal("wé́́é́́é́́!"),
233			cty.NumberIntVal(5),
234		},
235		{
236			// Go's normalization forms don't handle this ligature, so we
237			// will produce the wrong result but this is now a compatibility
238			// constraint and so we'll test it.
239			cty.StringVal("baffle"),
240			cty.NumberIntVal(4),
241		},
242		{
243			cty.StringVal("����"),
244			cty.NumberIntVal(2),
245		},
246		{
247			cty.UnknownVal(cty.String),
248			cty.UnknownVal(cty.Number),
249		},
250		{
251			cty.DynamicVal,
252			cty.UnknownVal(cty.Number),
253		},
254	}
255
256	for _, test := range tests {
257		t.Run(test.Input.GoString(), func(t *testing.T) {
258			got, err := Strlen(test.Input)
259
260			if err != nil {
261				t.Fatalf("unexpected error: %s", err)
262			}
263
264			if !got.RawEquals(test.Want) {
265				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
266			}
267		})
268	}
269}
270
271func TestSubstr(t *testing.T) {
272	tests := []struct {
273		Input  cty.Value
274		Offset cty.Value
275		Length cty.Value
276		Want   cty.Value
277	}{
278		{
279			cty.StringVal("hello"),
280			cty.NumberIntVal(0),
281			cty.NumberIntVal(2),
282			cty.StringVal("he"),
283		},
284		{
285			cty.StringVal("hello"),
286			cty.NumberIntVal(1),
287			cty.NumberIntVal(3),
288			cty.StringVal("ell"),
289		},
290		{
291			cty.StringVal("hello"),
292			cty.NumberIntVal(1),
293			cty.NumberIntVal(-1),
294			cty.StringVal("ello"),
295		},
296		{
297			cty.StringVal("hello"),
298			cty.NumberIntVal(1),
299			cty.NumberIntVal(-10), // not documented, but <0 is the same as -1
300			cty.StringVal("ello"),
301		},
302		{
303			cty.StringVal("hello"),
304			cty.NumberIntVal(1),
305			cty.NumberIntVal(10),
306			cty.StringVal("ello"),
307		},
308		{
309			cty.StringVal("hello"),
310			cty.NumberIntVal(-3),
311			cty.NumberIntVal(-1),
312			cty.StringVal("llo"),
313		},
314		{
315			cty.StringVal("hello"),
316			cty.NumberIntVal(-3),
317			cty.NumberIntVal(2),
318			cty.StringVal("ll"),
319		},
320		{
321			cty.StringVal("hello"),
322			cty.NumberIntVal(10),
323			cty.NumberIntVal(10),
324			cty.StringVal(""),
325		},
326		{
327			cty.StringVal("hello"),
328			cty.NumberIntVal(0),
329			cty.NumberIntVal(0),
330			cty.StringVal(""),
331		},
332		{
333			cty.StringVal("noël"),
334			cty.NumberIntVal(0),
335			cty.NumberIntVal(3),
336			cty.StringVal("noë"),
337		},
338		{
339			cty.StringVal("noël"),
340			cty.NumberIntVal(3),
341			cty.NumberIntVal(-1),
342			cty.StringVal("l"),
343		},
344		{
345			cty.StringVal("wé́́é́́é́́!"),
346			cty.NumberIntVal(2),
347			cty.NumberIntVal(2),
348			cty.StringVal("é́́é́́"),
349		},
350		{
351			cty.StringVal("wé́́é́́é́́!"),
352			cty.NumberIntVal(3),
353			cty.NumberIntVal(2),
354			cty.StringVal("é́́!"),
355		},
356		{
357			cty.StringVal("wé́́é́́é́́!"),
358			cty.NumberIntVal(-2),
359			cty.NumberIntVal(-1),
360			cty.StringVal("é́́!"),
361		},
362		{
363			cty.StringVal("noël"),
364			cty.NumberIntVal(-2),
365			cty.NumberIntVal(-1),
366			cty.StringVal("ël"),
367		},
368		{
369			cty.StringVal("����"),
370			cty.NumberIntVal(0),
371			cty.NumberIntVal(1),
372			cty.StringVal("��"),
373		},
374		{
375			cty.StringVal("����"),
376			cty.NumberIntVal(1),
377			cty.NumberIntVal(1),
378			cty.StringVal("��"),
379		},
380	}
381
382	for _, test := range tests {
383		t.Run(test.Input.GoString(), func(t *testing.T) {
384			got, err := Substr(test.Input, test.Offset, test.Length)
385
386			if err != nil {
387				t.Fatalf("unexpected error: %s", err)
388			}
389
390			if !got.RawEquals(test.Want) {
391				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
392			}
393		})
394	}
395}
396
397func TestJoin(t *testing.T) {
398	tests := map[string]struct {
399		Separator cty.Value
400		Lists     []cty.Value
401		Want      cty.Value
402	}{
403		"single two-element list": {
404			cty.StringVal("-"),
405			[]cty.Value{
406				cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}),
407			},
408			cty.StringVal("hello-world"),
409		},
410		"multiple single-element lists": {
411			cty.StringVal("-"),
412			[]cty.Value{
413				cty.ListVal([]cty.Value{cty.StringVal("chicken")}),
414				cty.ListVal([]cty.Value{cty.StringVal("egg")}),
415			},
416			cty.StringVal("chicken-egg"),
417		},
418		"single single-element list": {
419			cty.StringVal("-"),
420			[]cty.Value{
421				cty.ListVal([]cty.Value{cty.StringVal("chicken")}),
422			},
423			cty.StringVal("chicken"),
424		},
425		"blank separator": {
426			cty.StringVal(""),
427			[]cty.Value{
428				cty.ListVal([]cty.Value{cty.StringVal("horse"), cty.StringVal("face")}),
429			},
430			cty.StringVal("horseface"),
431		},
432		"marked list": {
433			cty.StringVal("-"),
434			[]cty.Value{
435				cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}).Mark("sensitive"),
436			},
437			cty.StringVal("hello-world").Mark("sensitive"),
438		},
439		"marked separator": {
440			cty.StringVal("-").Mark("sensitive"),
441			[]cty.Value{
442				cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}),
443			},
444			cty.StringVal("hello-world").Mark("sensitive"),
445		},
446		"list with some marked elements": {
447			cty.StringVal("-"),
448			[]cty.Value{
449				cty.ListVal([]cty.Value{cty.StringVal("hello").Mark("sensitive"), cty.StringVal("world")}),
450			},
451			cty.StringVal("hello-world").Mark("sensitive"),
452		},
453		"multiple marks": {
454			cty.StringVal("-").Mark("a"),
455			[]cty.Value{
456				cty.ListVal([]cty.Value{cty.StringVal("hello").Mark("b"), cty.StringVal("world").Mark("c")}),
457			},
458			cty.StringVal("hello-world").WithMarks(cty.NewValueMarks("a", "b", "c")),
459		},
460	}
461
462	for name, test := range tests {
463		t.Run(name, func(t *testing.T) {
464			got, err := Join(test.Separator, test.Lists...)
465
466			if err != nil {
467				t.Fatalf("unexpected error: %s", err)
468			}
469
470			if !got.RawEquals(test.Want) {
471				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
472			}
473		})
474	}
475}
476