1package core
2
3import (
4	"fmt"
5	"reflect"
6	"testing"
7
8	"github.com/stretchr/testify/assert"
9)
10
11func TestWrite_returnsErrorIfTargetNotPtr(t *testing.T) {
12	// try to copy a value to a non-pointer
13	err := WriteAnswer(true, "hello", true)
14	// make sure there was an error
15	if err == nil {
16		t.Error("Did not encounter error when writing to non-pointer.")
17	}
18}
19
20func TestWrite_canWriteToBool(t *testing.T) {
21	// a pointer to hold the boolean value
22	ptr := true
23
24	// try to copy a false value to the pointer
25	WriteAnswer(&ptr, "", false)
26
27	// if the value is true
28	if ptr {
29		// the test failed
30		t.Error("Could not write a false bool to a pointer")
31	}
32}
33
34func TestWrite_canWriteString(t *testing.T) {
35	// a pointer to hold the boolean value
36	ptr := ""
37
38	// try to copy a false value to the pointer
39	err := WriteAnswer(&ptr, "", "hello")
40	if err != nil {
41		t.Error(err)
42	}
43
44	// if the value is not what we wrote
45	if ptr != "hello" {
46		t.Error("Could not write a string value to a pointer")
47	}
48}
49
50func TestWrite_canWriteSlice(t *testing.T) {
51	// a pointer to hold the value
52	ptr := []string{}
53
54	// copy in a value
55	WriteAnswer(&ptr, "", []string{"hello", "world"})
56
57	// make sure there are two entries
58	assert.Equal(t, []string{"hello", "world"}, ptr)
59}
60
61func TestWrite_recoversInvalidReflection(t *testing.T) {
62	// a variable to mutate
63	ptr := false
64
65	// write a boolean value to the string
66	err := WriteAnswer(&ptr, "", "hello")
67
68	// if there was no error
69	if err == nil {
70		// the test failed
71		t.Error("Did not encounter error when forced invalid write.")
72	}
73}
74
75func TestWriteAnswer_handlesNonStructValues(t *testing.T) {
76	// the value to write to
77	ptr := ""
78
79	// write a value to the pointer
80	WriteAnswer(&ptr, "", "world")
81
82	// if we didn't change the value appropriate
83	if ptr != "world" {
84		// the test failed
85		t.Error("Did not write value to primitive pointer")
86	}
87}
88
89func TestWriteAnswer_canMutateStruct(t *testing.T) {
90	// the struct to hold the answer
91	ptr := struct{ Name string }{}
92
93	// write a value to an existing field
94	err := WriteAnswer(&ptr, "name", "world")
95	if err != nil {
96		// the test failed
97		t.Errorf("Encountered error while writing answer: %v", err.Error())
98		// we're done here
99		return
100	}
101
102	// make sure we changed the field
103	if ptr.Name != "world" {
104		// the test failed
105		t.Error("Did not mutate struct field when writing answer.")
106	}
107}
108
109func TestWriteAnswer_canMutateMap(t *testing.T) {
110	// the map to hold the answer
111	ptr := make(map[string]interface{})
112
113	// write a value to an existing field
114	err := WriteAnswer(&ptr, "name", "world")
115	if err != nil {
116		// the test failed
117		t.Errorf("Encountered error while writing answer: %v", err.Error())
118		// we're done here
119		return
120	}
121
122	// make sure we changed the field
123	if ptr["name"] != "world" {
124		// the test failed
125		t.Error("Did not mutate map when writing answer.")
126	}
127}
128
129func TestWrite_returnsErrorIfInvalidMapType(t *testing.T) {
130	// try to copy a value to a non map[string]interface{}
131	ptr := make(map[int]string)
132
133	err := WriteAnswer(&ptr, "name", "world")
134	// make sure there was an error
135	if err == nil {
136		t.Error("Did not encounter error when writing to invalid map.")
137	}
138}
139
140func TestWrite_writesStringSliceToIntSlice(t *testing.T) {
141	// make a slice of int to write to
142	target := []int{}
143
144	// write the answer
145	err := WriteAnswer(&target, "name", []string{"1", "2", "3"})
146
147	// make sure there was no error
148	assert.Nil(t, err, "WriteSlice to Int Slice")
149	// and we got what we wanted
150	assert.Equal(t, []int{1, 2, 3}, target)
151}
152
153func TestWrite_writesStringArrayToIntArray(t *testing.T) {
154	// make an array of int to write to
155	target := [3]int{}
156
157	// write the answer
158	err := WriteAnswer(&target, "name", [3]string{"1", "2", "3"})
159
160	// make sure there was no error
161	assert.Nil(t, err, "WriteArray to Int Array")
162	// and we got what we wanted
163	assert.Equal(t, [3]int{1, 2, 3}, target)
164}
165
166func TestWriteAnswer_returnsErrWhenFieldNotFound(t *testing.T) {
167	// the struct to hold the answer
168	ptr := struct{ Name string }{}
169
170	// write a value to an existing field
171	err := WriteAnswer(&ptr, "", "world")
172
173	if err == nil {
174		// the test failed
175		t.Error("Did not encountered error while writing answer to non-existing field.")
176	}
177}
178
179func TestFindFieldIndex_canFindExportedField(t *testing.T) {
180	// create a reflective wrapper over the struct to look through
181	val := reflect.ValueOf(struct{ Name string }{})
182
183	// find the field matching "name"
184	fieldIndex, err := findFieldIndex(val, "name")
185	// if something went wrong
186	if err != nil {
187		// the test failed
188		t.Error(err.Error())
189		return
190	}
191
192	// make sure we got the right value
193	if val.Type().Field(fieldIndex).Name != "Name" {
194		// the test failed
195		t.Errorf("Did not find the correct field name. Expected 'Name' found %v.", val.Type().Field(fieldIndex).Name)
196	}
197}
198
199func TestFindFieldIndex_canFindTaggedField(t *testing.T) {
200	// the struct to look through
201	val := reflect.ValueOf(struct {
202		Username string `survey:"name"`
203	}{})
204
205	// find the field matching "name"
206	fieldIndex, err := findFieldIndex(val, "name")
207	// if something went wrong
208	if err != nil {
209		// the test failed
210		t.Error(err.Error())
211		return
212	}
213
214	// make sure we got the right value
215	if val.Type().Field(fieldIndex).Name != "Username" {
216		// the test failed
217		t.Errorf("Did not find the correct field name. Expected 'Username' found %v.", val.Type().Field(fieldIndex).Name)
218	}
219}
220
221func TestFindFieldIndex_canHandleCapitalAnswerNames(t *testing.T) {
222	// create a reflective wrapper over the struct to look through
223	val := reflect.ValueOf(struct{ Name string }{})
224
225	// find the field matching "name"
226	fieldIndex, err := findFieldIndex(val, "Name")
227	// if something went wrong
228	if err != nil {
229		// the test failed
230		t.Error(err.Error())
231		return
232	}
233
234	// make sure we got the right value
235	if val.Type().Field(fieldIndex).Name != "Name" {
236		// the test failed
237		t.Errorf("Did not find the correct field name. Expected 'Name' found %v.", val.Type().Field(fieldIndex).Name)
238	}
239}
240
241func TestFindFieldIndex_tagOverwriteFieldName(t *testing.T) {
242	// the struct to look through
243	val := reflect.ValueOf(struct {
244		Name     string
245		Username string `survey:"name"`
246	}{})
247
248	// find the field matching "name"
249	fieldIndex, err := findFieldIndex(val, "name")
250	// if something went wrong
251	if err != nil {
252		// the test failed
253		t.Error(err.Error())
254		return
255	}
256
257	// make sure we got the right value
258	if val.Type().Field(fieldIndex).Name != "Username" {
259		// the test failed
260		t.Errorf("Did not find the correct field name. Expected 'Username' found %v.", val.Type().Field(fieldIndex).Name)
261	}
262}
263
264type testFieldSettable struct {
265	Values map[string]string
266}
267
268type testStringSettable struct {
269	Value string `survey:"string"`
270}
271
272type testTaggedStruct struct {
273	TaggedValue testStringSettable `survey:"tagged"`
274}
275
276type testPtrTaggedStruct struct {
277	TaggedValue *testStringSettable `survey:"tagged"`
278}
279
280func (t *testFieldSettable) WriteAnswer(name string, value interface{}) error {
281	if t.Values == nil {
282		t.Values = map[string]string{}
283	}
284	if v, ok := value.(string); ok {
285		t.Values[name] = v
286		return nil
287	}
288	return fmt.Errorf("Incompatible type %T", value)
289}
290
291func (t *testStringSettable) WriteAnswer(_ string, value interface{}) error {
292	t.Value = value.(string)
293	return nil
294}
295
296func TestWriteWithFieldSettable(t *testing.T) {
297	testSet1 := testFieldSettable{}
298	err := WriteAnswer(&testSet1, "values", "stringVal")
299	assert.Nil(t, err)
300	assert.Equal(t, map[string]string{"values": "stringVal"}, testSet1.Values)
301
302	testSet2 := testFieldSettable{}
303	err = WriteAnswer(&testSet2, "values", 123)
304	assert.Error(t, fmt.Errorf("Incompatible type int64"), err)
305	assert.Equal(t, map[string]string{}, testSet2.Values)
306
307	testString1 := testStringSettable{}
308	err = WriteAnswer(&testString1, "", "value1")
309	assert.Nil(t, err)
310	assert.Equal(t, testStringSettable{"value1"}, testString1)
311
312	testSetStruct := testTaggedStruct{}
313	err = WriteAnswer(&testSetStruct, "tagged", "stringVal1")
314	assert.Nil(t, err)
315	assert.Equal(t, testTaggedStruct{TaggedValue: testStringSettable{"stringVal1"}}, testSetStruct)
316
317	testPtrSetStruct := testPtrTaggedStruct{&testStringSettable{}}
318	err = WriteAnswer(&testPtrSetStruct, "tagged", "stringVal1")
319	assert.Nil(t, err)
320	assert.Equal(t, testPtrTaggedStruct{TaggedValue: &testStringSettable{"stringVal1"}}, testPtrSetStruct)
321}
322
323// CONVERSION TESTS
324func TestWrite_canStringToBool(t *testing.T) {
325	// a pointer to hold the boolean value
326	ptr := true
327
328	// try to copy a false value to the pointer
329	WriteAnswer(&ptr, "", "false")
330
331	// if the value is true
332	if ptr {
333		// the test failed
334		t.Error("Could not convert string to pointer type")
335	}
336}
337
338func TestWrite_canStringToInt(t *testing.T) {
339	// a pointer to hold the value
340	var ptr int = 1
341
342	// try to copy a value to the pointer
343	WriteAnswer(&ptr, "", "2")
344
345	// if the value is true
346	if ptr != 2 {
347		// the test failed
348		t.Error("Could not convert string to pointer type")
349	}
350}
351
352func TestWrite_canStringToInt8(t *testing.T) {
353	// a pointer to hold the value
354	var ptr int8 = 1
355
356	// try to copy a value to the pointer
357	WriteAnswer(&ptr, "", "2")
358
359	// if the value is true
360	if ptr != 2 {
361		// the test failed
362		t.Error("Could not convert string to pointer type")
363	}
364}
365
366func TestWrite_canStringToInt16(t *testing.T) {
367	// a pointer to hold the value
368	var ptr int16 = 1
369
370	// try to copy a value to the pointer
371	WriteAnswer(&ptr, "", "2")
372
373	// if the value is true
374	if ptr != 2 {
375		// the test failed
376		t.Error("Could not convert string to pointer type")
377	}
378}
379
380func TestWrite_canStringToInt32(t *testing.T) {
381	// a pointer to hold the value
382	var ptr int32 = 1
383
384	// try to copy a value to the pointer
385	WriteAnswer(&ptr, "", "2")
386
387	// if the value is true
388	if ptr != 2 {
389		// the test failed
390		t.Error("Could not convert string to pointer type")
391	}
392}
393
394func TestWrite_canStringToInt64(t *testing.T) {
395	// a pointer to hold the value
396	var ptr int64 = 1
397
398	// try to copy a value to the pointer
399	WriteAnswer(&ptr, "", "2")
400
401	// if the value is true
402	if ptr != 2 {
403		// the test failed
404		t.Error("Could not convert string to pointer type")
405	}
406}
407
408func TestWrite_canStringToUint(t *testing.T) {
409	// a pointer to hold the value
410	var ptr uint = 1
411
412	// try to copy a value to the pointer
413	WriteAnswer(&ptr, "", "2")
414
415	// if the value is true
416	if ptr != 2 {
417		// the test failed
418		t.Error("Could not convert string to pointer type")
419	}
420}
421
422func TestWrite_canStringToUint8(t *testing.T) {
423	// a pointer to hold the value
424	var ptr uint8 = 1
425
426	// try to copy a value to the pointer
427	WriteAnswer(&ptr, "", "2")
428
429	// if the value is true
430	if ptr != 2 {
431		// the test failed
432		t.Error("Could not convert string to pointer type")
433	}
434}
435
436func TestWrite_canStringToUint16(t *testing.T) {
437	// a pointer to hold the value
438	var ptr uint16 = 1
439
440	// try to copy a value to the pointer
441	WriteAnswer(&ptr, "", "2")
442
443	// if the value is true
444	if ptr != 2 {
445		// the test failed
446		t.Error("Could not convert string to pointer type")
447	}
448}
449
450func TestWrite_canStringToUint32(t *testing.T) {
451	// a pointer to hold the value
452	var ptr uint32 = 1
453
454	// try to copy a value to the pointer
455	WriteAnswer(&ptr, "", "2")
456
457	// if the value is true
458	if ptr != 2 {
459		// the test failed
460		t.Error("Could not convert string to pointer type")
461	}
462}
463
464func TestWrite_canStringToUint64(t *testing.T) {
465	// a pointer to hold the value
466	var ptr uint64 = 1
467
468	// try to copy a value to the pointer
469	WriteAnswer(&ptr, "", "2")
470
471	// if the value is true
472	if ptr != 2 {
473		// the test failed
474		t.Error("Could not convert string to pointer type")
475	}
476}
477
478func TestWrite_canStringToFloat32(t *testing.T) {
479	// a pointer to hold the value
480	var ptr float32 = 1.0
481
482	// try to copy a value to the pointer
483	WriteAnswer(&ptr, "", "2.5")
484
485	// if the value is true
486	if ptr != 2.5 {
487		// the test failed
488		t.Error("Could not convert string to pointer type")
489	}
490}
491
492func TestWrite_canStringToFloat64(t *testing.T) {
493	// a pointer to hold the value
494	var ptr float64 = 1.0
495
496	// try to copy a value to the pointer
497	WriteAnswer(&ptr, "", "2.5")
498
499	// if the value is true
500	if ptr != 2.5 {
501		// the test failed
502		t.Error("Could not convert string to pointer type")
503	}
504}
505
506func TestWrite_canConvertStructFieldTypes(t *testing.T) {
507	// the struct to hold the answer
508	ptr := struct {
509		Name   string
510		Age    uint
511		Male   bool
512		Height float64
513	}{}
514
515	// write the values as strings
516	check(t, WriteAnswer(&ptr, "name", "Bob"))
517	check(t, WriteAnswer(&ptr, "age", "22"))
518	check(t, WriteAnswer(&ptr, "male", "true"))
519	check(t, WriteAnswer(&ptr, "height", "6.2"))
520
521	// make sure we changed the fields
522	if ptr.Name != "Bob" {
523		t.Error("Did not mutate Name when writing answer.")
524	}
525
526	if ptr.Age != 22 {
527		t.Error("Did not mutate Age when writing answer.")
528	}
529
530	if !ptr.Male {
531		t.Error("Did not mutate Male when writing answer.")
532	}
533
534	if ptr.Height != 6.2 {
535		t.Error("Did not mutate Height when writing answer.")
536	}
537}
538
539func check(t *testing.T, err error) {
540	if err != nil {
541		t.Fatalf("Encountered error while writing answer: %v", err.Error())
542	}
543}
544