1package sjson
2
3import (
4	"encoding/hex"
5	"fmt"
6	"math/rand"
7	"testing"
8	"time"
9
10	"github.com/tidwall/pretty"
11
12	"github.com/tidwall/gjson"
13)
14
15func TestInvalidPaths(t *testing.T) {
16	var err error
17	_, err = SetRaw(`{"hello":"world"}`, "", `"planet"`)
18	if err == nil || err.Error() != "path cannot be empty" {
19		t.Fatalf("expecting '%v', got '%v'", "path cannot be empty", err)
20	}
21	_, err = SetRaw("", "name.last.#", "")
22	if err == nil || err.Error() != "array access character not allowed in path" {
23		t.Fatalf("expecting '%v', got '%v'", "array access character not allowed in path", err)
24	}
25	_, err = SetRaw("", "name.last.\\1#", "")
26	if err == nil || err.Error() != "array access character not allowed in path" {
27		t.Fatalf("expecting '%v', got '%v'", "array access character not allowed in path", err)
28	}
29	_, err = SetRaw("", "name.las?t", "")
30	if err == nil || err.Error() != "wildcard characters not allowed in path" {
31		t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
32	}
33	_, err = SetRaw("", "name.la\\s?t", "")
34	if err == nil || err.Error() != "wildcard characters not allowed in path" {
35		t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
36	}
37	_, err = SetRaw("", "name.las*t", "")
38	if err == nil || err.Error() != "wildcard characters not allowed in path" {
39		t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
40	}
41	_, err = SetRaw("", "name.las\\a*t", "")
42	if err == nil || err.Error() != "wildcard characters not allowed in path" {
43		t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
44	}
45}
46
47const (
48	setRaw    = 1
49	setBool   = 2
50	setInt    = 3
51	setFloat  = 4
52	setString = 5
53	setDelete = 6
54)
55
56func sortJSON(json string) string {
57	opts := pretty.Options{SortKeys: true}
58	return string(pretty.Ugly(pretty.PrettyOptions([]byte(json), &opts)))
59}
60
61func testRaw(t *testing.T, kind int, expect, json, path string, value interface{}) {
62	t.Helper()
63	expect = sortJSON(expect)
64	var json2 string
65	var err error
66	switch kind {
67	default:
68		json2, err = Set(json, path, value)
69	case setRaw:
70		json2, err = SetRaw(json, path, value.(string))
71	case setDelete:
72		json2, err = Delete(json, path)
73	}
74
75	if err != nil {
76		t.Fatal(err)
77	}
78	json2 = sortJSON(json2)
79	if json2 != expect {
80		t.Fatalf("expected '%v', got '%v'", expect, json2)
81	}
82	var json3 []byte
83	switch kind {
84	default:
85		json3, err = SetBytes([]byte(json), path, value)
86	case setRaw:
87		json3, err = SetRawBytes([]byte(json), path, []byte(value.(string)))
88	case setDelete:
89		json3, err = DeleteBytes([]byte(json), path)
90	}
91	json3 = []byte(sortJSON(string(json3)))
92	if err != nil {
93		t.Fatal(err)
94	} else if string(json3) != expect {
95		t.Fatalf("expected '%v', got '%v'", expect, string(json3))
96	}
97}
98func TestBasic(t *testing.T) {
99	testRaw(t, setRaw, `[{"hiw":"planet","hi":"world"}]`, `[{"hi":"world"}]`, "0.hiw", `"planet"`)
100	testRaw(t, setRaw, `[true]`, ``, "0", `true`)
101	testRaw(t, setRaw, `[null,true]`, ``, "1", `true`)
102	testRaw(t, setRaw, `[1,null,true]`, `[1]`, "2", `true`)
103	testRaw(t, setRaw, `[1,true,false]`, `[1,null,false]`, "1", `true`)
104	testRaw(t, setRaw,
105		`[1,{"hello":"when","this":[0,null,2]},false]`,
106		`[1,{"hello":"when","this":[0,1,2]},false]`,
107		"1.this.1", `null`)
108	testRaw(t, setRaw,
109		`{"a":1,"b":{"hello":"when","this":[0,null,2]},"c":false}`,
110		`{"a":1,"b":{"hello":"when","this":[0,1,2]},"c":false}`,
111		"b.this.1", `null`)
112	testRaw(t, setRaw,
113		`{"a":1,"b":{"hello":"when","this":[0,null,2,null,4]},"c":false}`,
114		`{"a":1,"b":{"hello":"when","this":[0,null,2]},"c":false}`,
115		"b.this.4", `4`)
116	testRaw(t, setRaw,
117		`{"b":{"this":[null,null,null,null,4]}}`,
118		``,
119		"b.this.4", `4`)
120	testRaw(t, setRaw,
121		`[null,{"this":[null,null,null,null,4]}]`,
122		``,
123		"1.this.4", `4`)
124	testRaw(t, setRaw,
125		`{"1":{"this":[null,null,null,null,4]}}`,
126		``,
127		":1.this.4", `4`)
128	testRaw(t, setRaw,
129		`{":1":{"this":[null,null,null,null,4]}}`,
130		``,
131		"\\:1.this.4", `4`)
132	testRaw(t, setRaw,
133		`{":\\1":{"this":[null,null,null,null,{".HI":4}]}}`,
134		``,
135		"\\:\\\\1.this.4.\\.HI", `4`)
136	testRaw(t, setRaw,
137		`{"app.token":"cde"}`,
138		`{"app.token":"abc"}`,
139		"app\\.token", `"cde"`)
140	testRaw(t, setRaw,
141		`{"b":{"this":{"��":""}}}`,
142		``,
143		"b.this.��", `""`)
144	testRaw(t, setRaw,
145		`[ 1,2  ,3]`,
146		`  [ 1,2  ] `,
147		"-1", `3`)
148	testRaw(t, setInt, `[1234]`, ``, `0`, int64(1234))
149	testRaw(t, setFloat, `[1234.5]`, ``, `0`, float64(1234.5))
150	testRaw(t, setString, `["1234.5"]`, ``, `0`, "1234.5")
151	testRaw(t, setBool, `[true]`, ``, `0`, true)
152	testRaw(t, setBool, `[null]`, ``, `0`, nil)
153	testRaw(t, setString, `{"arr":[1]}`, ``, `arr.-1`, 1)
154	testRaw(t, setString, `{"a":"\\"}`, ``, `a`, "\\")
155	testRaw(t, setString, `{"a":"C:\\Windows\\System32"}`, ``, `a`, `C:\Windows\System32`)
156}
157
158func TestDelete(t *testing.T) {
159	testRaw(t, setDelete, `[456]`, `[123,456]`, `0`, nil)
160	testRaw(t, setDelete, `[123,789]`, `[123,456,789]`, `1`, nil)
161	testRaw(t, setDelete, `[123,456]`, `[123,456,789]`, `-1`, nil)
162	testRaw(t, setDelete, `{"a":[123,456]}`, `{"a":[123,456,789]}`, `a.-1`, nil)
163	testRaw(t, setDelete, `{"and":"another"}`, `{"this":"that","and":"another"}`, `this`, nil)
164	testRaw(t, setDelete, `{"this":"that"}`, `{"this":"that","and":"another"}`, `and`, nil)
165	testRaw(t, setDelete, `{}`, `{"and":"another"}`, `and`, nil)
166	testRaw(t, setDelete, `{"1":"2"}`, `{"1":"2"}`, `3`, nil)
167}
168
169// TestRandomData is a fuzzing test that throws random data at SetRaw
170// function looking for panics.
171func TestRandomData(t *testing.T) {
172	var lstr string
173	defer func() {
174		if v := recover(); v != nil {
175			println("'" + hex.EncodeToString([]byte(lstr)) + "'")
176			println("'" + lstr + "'")
177			panic(v)
178		}
179	}()
180	rand.Seed(time.Now().UnixNano())
181	b := make([]byte, 200)
182	for i := 0; i < 2000000; i++ {
183		n, err := rand.Read(b[:rand.Int()%len(b)])
184		if err != nil {
185			t.Fatal(err)
186		}
187		lstr = string(b[:n])
188		SetRaw(lstr, "zzzz.zzzz.zzzz", "123")
189	}
190}
191
192func TestDeleteIssue21(t *testing.T) {
193	json := `{"country_code_from":"NZ","country_code_to":"SA","date_created":"2018-09-13T02:56:11.25783Z","date_updated":"2018-09-14T03:15:16.67356Z","disabled":false,"last_edited_by":"Developers","id":"a3e...bc454","merchant_id":"f2b...b91abf","signed_date":"2018-02-01T00:00:00Z","start_date":"2018-03-01T00:00:00Z","url":"https://www.google.com"}`
194	res1 := gjson.Get(json, "date_updated")
195	var err error
196	json, err = Delete(json, "date_updated")
197	if err != nil {
198		t.Fatal(err)
199	}
200	res2 := gjson.Get(json, "date_updated")
201	res3 := gjson.Get(json, "date_created")
202	if !res1.Exists() || res2.Exists() || !res3.Exists() {
203		t.Fatal("bad news")
204	}
205
206	// We change the number of characters in this to make the section of the string before the section that we want to delete a certain length
207
208	//---------------------------
209	lenBeforeToDeleteIs307AsBytes := `{"1":"","0":"012345678901234567890123456789012345678901234567890123456789012345678901234567","to_delete":"0","2":""}`
210
211	expectedForLenBefore307AsBytes := `{"1":"","0":"012345678901234567890123456789012345678901234567890123456789012345678901234567","2":""}`
212	//---------------------------
213
214	//---------------------------
215	lenBeforeToDeleteIs308AsBytes := `{"1":"","0":"0123456789012345678901234567890123456789012345678901234567890123456789012345678","to_delete":"0","2":""}`
216
217	expectedForLenBefore308AsBytes := `{"1":"","0":"0123456789012345678901234567890123456789012345678901234567890123456789012345678","2":""}`
218	//---------------------------
219
220	//---------------------------
221	lenBeforeToDeleteIs309AsBytes := `{"1":"","0":"01234567890123456789012345678901234567890123456789012345678901234567890123456","to_delete":"0","2":""}`
222
223	expectedForLenBefore309AsBytes := `{"1":"","0":"01234567890123456789012345678901234567890123456789012345678901234567890123456","2":""}`
224	//---------------------------
225
226	var data = []struct {
227		desc     string
228		input    string
229		expected string
230	}{
231		{
232			desc:     "len before \"to_delete\"... = 307",
233			input:    lenBeforeToDeleteIs307AsBytes,
234			expected: expectedForLenBefore307AsBytes,
235		},
236		{
237			desc:     "len before \"to_delete\"... = 308",
238			input:    lenBeforeToDeleteIs308AsBytes,
239			expected: expectedForLenBefore308AsBytes,
240		},
241		{
242			desc:     "len before \"to_delete\"... = 309",
243			input:    lenBeforeToDeleteIs309AsBytes,
244			expected: expectedForLenBefore309AsBytes,
245		},
246	}
247
248	for i, d := range data {
249		result, err := Delete(d.input, "to_delete")
250
251		if err != nil {
252			t.Error(fmtErrorf(testError{
253				unexpected: "error",
254				desc:       d.desc,
255				i:          i,
256				lenInput:   len(d.input),
257				input:      d.input,
258				expected:   d.expected,
259				result:     result,
260			}))
261		}
262		if result != d.expected {
263			t.Error(fmtErrorf(testError{
264				unexpected: "result",
265				desc:       d.desc,
266				i:          i,
267				lenInput:   len(d.input),
268				input:      d.input,
269				expected:   d.expected,
270				result:     result,
271			}))
272		}
273	}
274}
275
276type testError struct {
277	unexpected string
278	desc       string
279	i          int
280	lenInput   int
281	input      interface{}
282	expected   interface{}
283	result     interface{}
284}
285
286func fmtErrorf(e testError) string {
287	return fmt.Sprintf(
288		"Unexpected %s:\n\t"+
289			"for=%q\n\t"+
290			"i=%d\n\t"+
291			"len(input)=%d\n\t"+
292			"input=%v\n\t"+
293			"expected=%v\n\t"+
294			"result=%v",
295		e.unexpected, e.desc, e.i, e.lenInput, e.input, e.expected, e.result,
296	)
297}
298
299func TestSetDotKeyIssue10(t *testing.T) {
300	json := `{"app.token":"abc"}`
301	json, _ = Set(json, `app\.token`, "cde")
302	if json != `{"app.token":"cde"}` {
303		t.Fatalf("expected '%v', got '%v'", `{"app.token":"cde"}`, json)
304	}
305}
306func TestDeleteDotKeyIssue19(t *testing.T) {
307	json := []byte(`{"data":{"key1":"value1","key2.something":"value2"}}`)
308	json, _ = DeleteBytes(json, `data.key2\.something`)
309	if string(json) != `{"data":{"key1":"value1"}}` {
310		t.Fatalf("expected '%v', got '%v'", `{"data":{"key1":"value1"}}`, json)
311	}
312}
313
314func TestIssue36(t *testing.T) {
315	var json = `
316	{
317	    "size": 1000
318    }
319`
320	var raw = `
321	{
322	    "sample": "hello"
323	}
324`
325	_ = raw
326	if true {
327		json, _ = SetRaw(json, "aggs", raw)
328	}
329	if !gjson.Valid(json) {
330		t.Fatal("invalid json")
331	}
332	res := gjson.Get(json, "aggs.sample").String()
333	if res != "hello" {
334		t.Fatal("unexpected result")
335	}
336}
337