1package diff
2
3import (
4	"encoding/json"
5	"errors"
6	"io"
7	"strings"
8	"testing"
9)
10
11func TestResultString(t *testing.T) {
12	t.Run("nil", func(t *testing.T) {
13		var r *Result
14		expected := ""
15		if result := r.String(); result != expected {
16			t.Errorf("Unexpected result: %s", result)
17		}
18	})
19	t.Run("diff", func(t *testing.T) {
20		expected := "foo"
21		r := &Result{diff: expected}
22		if result := r.String(); result != expected {
23			t.Errorf("Unexpected result: %s", result)
24		}
25	})
26}
27
28func TestSliceDiff(t *testing.T) {
29	tests := []struct {
30		name             string
31		expected, actual []string
32		result           string
33	}{
34		{
35			name:     "equal",
36			expected: []string{"foo"},
37			actual:   []string{"foo"},
38		},
39		{
40			name:     "different",
41			expected: []string{"foo"},
42			actual:   []string{"bar"},
43			result:   "--- expected\n+++ actual\n@@ -1 +1 @@\n-foo+bar",
44		},
45	}
46	for _, test := range tests {
47		result := sliceDiff(test.expected, test.actual)
48		var resultText string
49		if result != nil {
50			resultText = result.String()
51		}
52		if resultText != test.result {
53			t.Errorf("Unexpected result:\n%s\n", resultText)
54		}
55	}
56}
57
58func TestTextSlices(t *testing.T) {
59	tests := []struct {
60		name             string
61		expected, actual []string
62		result           string
63	}{
64		{
65			name:     "equal",
66			expected: []string{"foo", "bar"},
67			actual:   []string{"foo", "bar"},
68		},
69		{
70			name:     "different",
71			expected: []string{"foo", "bar"},
72			actual:   []string{"bar", "bar"},
73			result:   "--- expected\n+++ actual\n@@ -1,2 +1,2 @@\n-foo\n bar\n+bar\n",
74		},
75	}
76	for _, test := range tests {
77		result := TextSlices(test.expected, test.actual)
78		var resultText string
79		if result != nil {
80			resultText = result.String()
81		}
82		if resultText != test.result {
83			t.Errorf("Unexpected result:\n%s\n", resultText)
84		}
85	}
86}
87
88func TestText(t *testing.T) {
89	tests := []struct {
90		name             string
91		expected, actual interface{}
92		result           string
93	}{
94		{
95			name:     "equal",
96			expected: "foo\nbar\n",
97			actual:   "foo\nbar\n",
98		},
99		{
100			name:     "different",
101			expected: "foo\nbar",
102			actual:   "bar\nbar",
103			result:   "--- expected\n+++ actual\n@@ -1,2 +1,2 @@\n-foo\n bar\n+bar\n",
104		},
105		{
106			name:     "string vs []byte",
107			expected: "foo",
108			actual:   []byte("foo"),
109		},
110		{
111			name:     "invalid exp type",
112			expected: 123,
113			result:   "[diff] expected: input must be of type string, []byte, or io.Reader",
114		},
115		{
116			name:     "invalid act type",
117			expected: "123",
118			actual:   123,
119			result:   "[diff] actual: input must be of type string, []byte, or io.Reader",
120		},
121		{
122			name:     "string vs io.Reader",
123			expected: strings.NewReader("foo"),
124			actual:   "foo",
125		},
126		{
127			name:     "string vs nil",
128			expected: "foo",
129			actual:   nil,
130			result:   "--- expected\n+++ actual\n@@ -1 +1 @@\n-foo\n+\n",
131		},
132	}
133	for _, test := range tests {
134		t.Run(test.name, func(t *testing.T) {
135			result := Text(test.expected, test.actual)
136			var resultText string
137			if result != nil {
138				resultText = result.String()
139			}
140			if resultText != test.result {
141				t.Errorf("Unexpected result:\n%s\n", resultText)
142			}
143		})
144	}
145}
146
147func TestIsJSON(t *testing.T) {
148	tests := []struct {
149		name   string
150		input  interface{}
151		isJSON bool
152		result string
153	}{
154		{
155			name:   "io.Reader",
156			input:  strings.NewReader("foo"),
157			isJSON: true,
158			result: "foo",
159		},
160		{
161			name:   "byte slice",
162			input:  []byte("foo"),
163			isJSON: true,
164			result: "foo",
165		},
166		{
167			name:   "json.RawMessage",
168			input:  json.RawMessage("foo"),
169			isJSON: true,
170			result: "foo",
171		},
172		{
173			name:   "string",
174			input:  "foo",
175			isJSON: false,
176		},
177	}
178	for _, test := range tests {
179		t.Run(test.name, func(t *testing.T) {
180			isJSON, result, _ := isJSON(test.input)
181			if isJSON != test.isJSON {
182				t.Errorf("Unexpected result: %t", isJSON)
183			}
184			if string(result) != test.result {
185				t.Errorf("Unexpected result: %s", string(result))
186			}
187		})
188	}
189}
190
191type errorReader struct{}
192
193var _ io.Reader = &errorReader{}
194
195func (r *errorReader) Read(_ []byte) (int, error) {
196	return 0, errors.New("read error")
197}
198
199func TestMarshal(t *testing.T) {
200	tests := []struct {
201		name     string
202		input    interface{}
203		expected string
204		err      string
205	}{
206		{
207			name:     "byte slice",
208			input:    []byte(`"foo"`),
209			expected: `"foo"`,
210		},
211		{
212			name:     "string",
213			input:    "foo",
214			expected: `"foo"`,
215		},
216		{
217			name:  "invalid json",
218			input: []byte("invalid json"),
219			err:   "invalid character 'i' looking for beginning of value",
220		},
221		{
222			name:  "error reader",
223			input: &errorReader{},
224			err:   "read error",
225		},
226	}
227	for _, test := range tests {
228		t.Run(test.name, func(t *testing.T) {
229			result, err := marshal(test.input)
230			var errMsg string
231			if err != nil {
232				errMsg = err.Error()
233			}
234			if test.err != errMsg {
235				t.Errorf("Unexpected error: %s", errMsg)
236			}
237			if string(result) != test.expected {
238				t.Errorf("Unexpected result: %s", string(result))
239			}
240		})
241	}
242}
243
244func TestAsJSON(t *testing.T) {
245	tests := []struct {
246		name             string
247		expected, actual interface{}
248		result           string
249	}{
250		{
251			name:     "equal",
252			expected: []string{"foo", "bar"},
253			actual:   []string{"foo", "bar"},
254		},
255		{
256			name:     "different",
257			expected: []string{"foo", "bar"},
258			actual:   []string{"bar", "bar"},
259			result: `--- expected
260+++ actual
261@@ -1,4 +1,4 @@
262 [
263-    "foo",
264+    "bar",
265     "bar"
266 ]
267`,
268		},
269		{
270			name:     "Unmarshalable expected",
271			expected: make(chan int),
272			actual:   "foo",
273			result:   "failed to marshal expected value: json: unsupported type: chan int",
274		},
275		{
276			name:     "Unmarshalable actual",
277			expected: "foo",
278			actual:   make(chan int),
279			result:   "failed to marshal actual value: json: unsupported type: chan int",
280		},
281		{
282			name:     "empty reader",
283			expected: strings.NewReader(""),
284			actual:   nil,
285			result:   "",
286		},
287	}
288	for _, test := range tests {
289		result := AsJSON(test.expected, test.actual)
290		var resultText string
291		if result != nil {
292			resultText = result.String()
293		}
294		if resultText != test.result {
295			t.Errorf("Unexpected result:\n%s\n", resultText)
296		}
297	}
298}
299
300func TestJSON(t *testing.T) {
301	tests := []struct {
302		name             string
303		expected, actual string
304		result           string
305	}{
306		{
307			name:     "equal",
308			expected: `["foo","bar"]`,
309			actual:   `["foo","bar"]`,
310		},
311		{
312			name:     "different",
313			expected: `["foo","bar"]`,
314			actual:   `["bar","bar"]`,
315			result: `--- expected
316+++ actual
317@@ -1,4 +1,4 @@
318 [
319-    "foo",
320+    "bar",
321     "bar"
322 ]
323`,
324		},
325		{
326			name:     "invalid expected",
327			expected: "invalid json",
328			actual:   `"foo"`,
329			result:   "failed to unmarshal expected value: invalid character 'i' looking for beginning of value",
330		},
331		{
332			name:     "invalid actual",
333			expected: `"foo"`,
334			actual:   "invalid json",
335			result:   "failed to unmarshal actual value: invalid character 'i' looking for beginning of value",
336		},
337		{
338			name:     "empty",
339			expected: "",
340			actual:   "",
341		},
342	}
343	for _, test := range tests {
344		result := JSON([]byte(test.expected), []byte(test.actual))
345		var resultText string
346		if result != nil {
347			resultText = result.String()
348		}
349		if resultText != test.result {
350			t.Errorf("Unexpected result:\n%s\n", resultText)
351		}
352	}
353}
354
355func TestInterface(t *testing.T) {
356	tests := []struct {
357		name             string
358		expected, actual interface{}
359		result           string
360	}{
361		{
362			name:     "equal",
363			expected: []string{"foo", "bar"},
364			actual:   []string{"foo", "bar"},
365		},
366		{
367			name:     "different",
368			expected: []string{"foo", "bar"},
369			actual:   []string{"bar", "bar"},
370			result: `--- expected
371+++ actual
372@@ -1,4 +1,4 @@
373 ([]string) (len=2) {
374-  (string) (len=3) "foo",
375+  (string) (len=3) "bar",
376   (string) (len=3) "bar"
377 }
378`,
379		},
380	}
381	for _, test := range tests {
382		result := Interface(test.expected, test.actual)
383		var resultText string
384		if result != nil {
385			resultText = result.String()
386		}
387		if resultText != test.result {
388			t.Errorf("Unexpected result:\n%s\n", resultText)
389		}
390	}
391}
392