1package jd
2
3import (
4	"testing"
5)
6
7func TestMultisetJson(t *testing.T) {
8	cases := []struct {
9		name     string
10		metadata Metadata
11		given    string
12		want     string
13	}{{
14		name:     "empty mulitset",
15		metadata: MULTISET,
16		given:    `[]`,
17		want:     `[]`,
18	}, {
19		name:     "empty multiset with space",
20		metadata: MULTISET,
21		given:    ` [ ] `,
22		want:     `[]`,
23	}, {
24		name:     "ordered multiset",
25		metadata: MULTISET,
26		given:    `[1,2,3]`,
27		want:     `[1,2,3]`,
28	}, {
29		name:     "ordered multiset with space",
30		metadata: MULTISET,
31		given:    ` [1, 2, 3] `,
32		want:     `[1,2,3]`,
33	}, {
34		name:     "multset with multiple duplicates",
35		metadata: MULTISET,
36		given:    `[1,1,1]`,
37		want:     `[1,1,1]`,
38	}}
39
40	for _, c := range cases {
41		t.Run(c.name, func(t *testing.T) {
42			ctx := newTestContext(t).
43				withMetadata(c.metadata)
44			checkJson(ctx, c.given, c.want)
45		})
46	}
47}
48
49func TestMultisetEquals(t *testing.T) {
50	cases := []struct {
51		name     string
52		metadata Metadata
53		a        string
54		b        string
55	}{{
56		name:     "empty multisets",
57		metadata: MULTISET,
58		a:        `[]`,
59		b:        `[]`,
60	}, {
61		name:     "different ordered multisets 1",
62		metadata: MULTISET,
63		a:        `[1,2,3]`,
64		b:        `[3,2,1]`,
65	}, {
66		name:     "different ordered multisets 2",
67		metadata: MULTISET,
68		a:        `[1,2,3]`,
69		b:        `[2,3,1]`,
70	}, {
71		name:     "different ordered multisets 2",
72		metadata: MULTISET,
73		a:        `[1,2,3]`,
74		b:        `[1,3,2]`,
75	}, {
76		name:     "multsets with empty objects",
77		metadata: MULTISET,
78		a:        `[{},{}]`,
79		b:        `[{},{}]`,
80	}, {
81		name:     "nested multisets",
82		metadata: MULTISET,
83		a:        `[[1,2],[3,4]]`,
84		b:        `[[2,1],[4,3]]`,
85	}}
86
87	for _, c := range cases {
88		t.Run(c.name, func(t *testing.T) {
89			// TODO: implement multiset equals with metadata
90			ctx := newTestContext(t).
91				withMetadata(c.metadata)
92			checkEqual(ctx, c.a, c.b)
93		})
94	}
95}
96
97func TestMultisetNotEquals(t *testing.T) {
98	cases := []struct {
99		name     string
100		metadata Metadata
101		a        string
102		b        string
103	}{{
104		name:     "empty multiset and multiset with number",
105		metadata: MULTISET,
106		a:        `[]`,
107		b:        `[1]`,
108	}, {
109		name:     "multisets with different numbers",
110		metadata: MULTISET,
111		a:        `[1,2,3]`,
112		b:        `[1,2,2]`,
113	}, {
114		name:     "multiset missing a number",
115		metadata: MULTISET,
116		a:        `[1,2,3]`,
117		b:        `[1,2]`,
118	}, {
119		name:     "nested multisets with different numbers",
120		a:        `[[],[1]]`,
121		b:        `[[],[2]]`,
122		metadata: MULTISET,
123	}}
124
125	for _, c := range cases {
126		t.Run(c.name, func(t *testing.T) {
127			ctx := newTestContext(t).
128				withMetadata(c.metadata)
129			checkNotEqual(ctx, c.a, c.b)
130		})
131	}
132}
133
134func TestMultisetDiff(t *testing.T) {
135	cases := []struct {
136		name     string
137		metadata Metadata
138		a        string
139		b        string
140		want     []string
141	}{{
142		name:     "two empty multisets",
143		metadata: MULTISET,
144		a:        `[]`,
145		b:        `[]`,
146		want:     s(``),
147	}, {
148		name:     "two multisets with different numbers",
149		metadata: MULTISET,
150		a:        `[1]`,
151		b:        `[1,2]`,
152		want: s(
153			`@ [["multiset"],{}]`,
154			`+ 2`,
155		),
156	}, {
157		name:     "two multisets with the same number",
158		metadata: MULTISET,
159		a:        `[1,2]`,
160		b:        `[1,2]`,
161		want:     s(``),
162	}, {
163		name:     "adding two numbers",
164		metadata: MULTISET,
165		a:        `[1]`,
166		b:        `[1,2,2]`,
167		want: s(
168			`@ [["multiset"],{}]`,
169			`+ 2`,
170			`+ 2`,
171		),
172	}, {
173		name:     "removing a number",
174		metadata: MULTISET,
175		a:        `[1,2,3]`,
176		b:        `[1,3]`,
177		want: s(
178			`@ [["multiset"],{}]`,
179			`- 2`,
180		),
181	}, {
182		name:     "replacing one object with another",
183		metadata: MULTISET,
184		a:        `[{"a":1}]`,
185		b:        `[{"a":2}]`,
186		want: s(
187			`@ [["multiset"],{}]`,
188			`- {"a":1}`,
189			`+ {"a":2}`,
190		),
191	}, {
192		name:     "replacing two objects with one object",
193		metadata: MULTISET,
194		a:        `[{"a":1},{"a":1}]`,
195		b:        `[{"a":2}]`,
196		want: s(
197			`@ [["multiset"],{}]`,
198			`- {"a":1}`,
199			`- {"a":1}`,
200			`+ {"a":2}`,
201		),
202	}, {
203		name:     "replacing three strings repeated with one string",
204		metadata: MULTISET,
205		a:        `["foo","foo","bar"]`,
206		b:        `["baz"]`,
207		want: s(
208			`@ [["multiset"],{}]`,
209			`- "bar"`,
210			`- "foo"`,
211			`- "foo"`,
212			`+ "baz"`,
213		),
214	}, {
215		name:     "replacing one string with three repeated",
216		metadata: MULTISET,
217		a:        `["foo"]`,
218		b:        `["bar","baz","bar"]`,
219		want: s(
220			`@ [["multiset"],{}]`,
221			`- "foo"`,
222			`+ "bar"`,
223			`+ "bar"`,
224			`+ "baz"`,
225		),
226	}, {
227		name:     "replacing multiset with array",
228		metadata: MULTISET,
229		a:        `{}`,
230		b:        `[]`,
231		want: s(
232			`@ []`,
233			`- {}`,
234			`+ []`,
235		),
236	}}
237
238	for _, c := range cases {
239		t.Run(c.name, func(t *testing.T) {
240			ctx := newTestContext(t).
241				withMetadata(c.metadata)
242			checkDiff(ctx, c.a, c.b, c.want...)
243		})
244	}
245}
246
247func TestMultisetPatch(t *testing.T) {
248	cases := []struct {
249		name     string
250		metadata Metadata
251		given    string
252		patch    []string
253		want     string
254	}{{
255		name:     "empty patch on empty multiset",
256		metadata: MULTISET,
257		given:    `[]`,
258		patch:    s(``),
259		want:     `[]`,
260	}, {
261		name:     "add a number",
262		metadata: MULTISET,
263		given:    `[1]`,
264		patch: s(
265			`@ [["multiset"],{}]`,
266			`+ 2`,
267		),
268		want: `[1,2]`,
269	}, {
270		name:     "empty patch on multiset with numbers",
271		metadata: MULTISET,
272		given:    `[1,2]`,
273		patch:    s(``),
274		want:     `[1,2]`,
275	}, {
276		name:     "add two numbers",
277		metadata: MULTISET,
278		given:    `[1]`,
279		patch: s(
280			`@ [["multiset"],{}]`,
281			`+ 2`,
282			`+ 2`,
283		),
284		want: `[1,2,2]`,
285	}, {
286		name:     "remove a number",
287		metadata: MULTISET,
288		given:    `[1,2,3]`,
289		patch: s(
290			`@ [["multiset"],{}]`,
291			`- 2`,
292		),
293		want: `[1,3]`,
294	}, {
295		name:     "replace one object with another",
296		metadata: MULTISET,
297		given:    `[{"a":1}]`,
298		patch: s(
299			`@ [["multiset"],{}]`,
300			`- {"a":1}`,
301			`+ {"a":2}`,
302		),
303		want: `[{"a":2}]`,
304	}, {
305		name:     "remove two objects and add one",
306		metadata: MULTISET,
307		given:    `[{"a":1},{"a":1}]`,
308		patch: s(
309			`@ [["multiset"],{}]`,
310			`- {"a":1}`,
311			`- {"a":1}`,
312			`+ {"a":2}`,
313		),
314		want: `[{"a":2}]`,
315	}, {
316		name:     "remove three objects repeated and add one",
317		metadata: MULTISET,
318		given:    `["foo","foo","bar"]`,
319		patch: s(
320			`@ [["multiset"],{}]`,
321			`- "bar"`,
322			`- "foo"`,
323			`- "foo"`,
324			`+ "baz"`,
325		),
326		want: `["baz"]`,
327	}, {
328		name:     "remove one object and add three repeated",
329		metadata: MULTISET,
330		given:    `["foo"]`,
331		patch: s(
332			`@ [["multiset"],{}]`,
333			`- "foo"`,
334			`+ "bar"`,
335			`+ "bar"`,
336			`+ "baz"`,
337		),
338		want: `["bar","baz","bar"]`,
339	}, {
340		name:     "replace multiset with array",
341		metadata: MULTISET,
342		given:    `{}`,
343		patch: s(
344			`@ []`,
345			`- {}`,
346			`+ []`,
347		),
348		want: `[]`,
349	}}
350
351	for _, c := range cases {
352		t.Run(c.name, func(t *testing.T) {
353			// TODO: implement multiset patch with metadata
354			ctx := newTestContext(t).
355				withMetadata(c.metadata)
356			checkPatch(ctx, c.given, c.want, c.patch...)
357		})
358	}
359}
360
361func TestMultisetPatchError(t *testing.T) {
362	cases := []struct {
363		name     string
364		metadata Metadata
365		given    string
366		patch    []string
367	}{{
368		name:     "remove number from empty multiset",
369		metadata: MULTISET,
370		given:    `[]`,
371		patch: s(
372			`@ [{}]`,
373			`- 1`,
374		),
375	}, {
376		name:     "remove a single number twice",
377		metadata: MULTISET,
378		given:    `[1]`,
379		patch: s(
380			`@ [{}]`,
381			`- 1`,
382			`- 1`,
383		),
384	}, {
385		name:     "remove an object when there is a multiset",
386		metadata: MULTISET,
387		given:    `[]`,
388		patch: s(
389			`@ []`,
390			`- {}`,
391		),
392	}}
393
394	for _, c := range cases {
395		t.Run(c.name, func(t *testing.T) {
396			ctx := newTestContext(t).
397				withMetadata(c.metadata)
398			checkPatchError(ctx, c.given, c.patch...)
399		})
400	}
401}
402