mirror of https://github.com/tidwall/sjson.git
337 lines
10 KiB
Go
337 lines
10 KiB
Go
package sjson
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/tidwall/pretty"
|
|
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
func TestInvalidPaths(t *testing.T) {
|
|
var err error
|
|
_, err = SetRaw(`{"hello":"world"}`, "", `"planet"`)
|
|
if err == nil || err.Error() != "path cannot be empty" {
|
|
t.Fatalf("expecting '%v', got '%v'", "path cannot be empty", err)
|
|
}
|
|
_, err = SetRaw("", "name.last.#", "")
|
|
if err == nil || err.Error() != "array access character not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "array access character not allowed in path", err)
|
|
}
|
|
_, err = SetRaw("", "name.last.\\1#", "")
|
|
if err == nil || err.Error() != "array access character not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "array access character not allowed in path", err)
|
|
}
|
|
_, err = SetRaw("", "name.las?t", "")
|
|
if err == nil || err.Error() != "wildcard characters not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
|
|
}
|
|
_, err = SetRaw("", "name.la\\s?t", "")
|
|
if err == nil || err.Error() != "wildcard characters not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
|
|
}
|
|
_, err = SetRaw("", "name.las*t", "")
|
|
if err == nil || err.Error() != "wildcard characters not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
|
|
}
|
|
_, err = SetRaw("", "name.las\\a*t", "")
|
|
if err == nil || err.Error() != "wildcard characters not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
|
|
}
|
|
}
|
|
|
|
const (
|
|
setRaw = 1
|
|
setBool = 2
|
|
setInt = 3
|
|
setFloat = 4
|
|
setString = 5
|
|
setDelete = 6
|
|
)
|
|
|
|
func sortJSON(json string) string {
|
|
opts := pretty.Options{SortKeys: true}
|
|
return string(pretty.Ugly(pretty.PrettyOptions([]byte(json), &opts)))
|
|
}
|
|
|
|
func testRaw(t *testing.T, kind int, expect, json, path string, value interface{}) {
|
|
t.Helper()
|
|
expect = sortJSON(expect)
|
|
var json2 string
|
|
var err error
|
|
switch kind {
|
|
default:
|
|
json2, err = Set(json, path, value)
|
|
case setRaw:
|
|
json2, err = SetRaw(json, path, value.(string))
|
|
case setDelete:
|
|
json2, err = Delete(json, path)
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
json2 = sortJSON(json2)
|
|
if json2 != expect {
|
|
t.Fatalf("expected '%v', got '%v'", expect, json2)
|
|
}
|
|
var json3 []byte
|
|
switch kind {
|
|
default:
|
|
json3, err = SetBytes([]byte(json), path, value)
|
|
case setRaw:
|
|
json3, err = SetRawBytes([]byte(json), path, []byte(value.(string)))
|
|
case setDelete:
|
|
json3, err = DeleteBytes([]byte(json), path)
|
|
}
|
|
json3 = []byte(sortJSON(string(json3)))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if string(json3) != expect {
|
|
t.Fatalf("expected '%v', got '%v'", expect, string(json3))
|
|
}
|
|
}
|
|
func TestBasic(t *testing.T) {
|
|
testRaw(t, setRaw, `[{"hiw":"planet","hi":"world"}]`, `[{"hi":"world"}]`, "0.hiw", `"planet"`)
|
|
testRaw(t, setRaw, `[true]`, ``, "0", `true`)
|
|
testRaw(t, setRaw, `[null,true]`, ``, "1", `true`)
|
|
testRaw(t, setRaw, `[1,null,true]`, `[1]`, "2", `true`)
|
|
testRaw(t, setRaw, `[1,true,false]`, `[1,null,false]`, "1", `true`)
|
|
testRaw(t, setRaw,
|
|
`[1,{"hello":"when","this":[0,null,2]},false]`,
|
|
`[1,{"hello":"when","this":[0,1,2]},false]`,
|
|
"1.this.1", `null`)
|
|
testRaw(t, setRaw,
|
|
`{"a":1,"b":{"hello":"when","this":[0,null,2]},"c":false}`,
|
|
`{"a":1,"b":{"hello":"when","this":[0,1,2]},"c":false}`,
|
|
"b.this.1", `null`)
|
|
testRaw(t, setRaw,
|
|
`{"a":1,"b":{"hello":"when","this":[0,null,2,null,4]},"c":false}`,
|
|
`{"a":1,"b":{"hello":"when","this":[0,null,2]},"c":false}`,
|
|
"b.this.4", `4`)
|
|
testRaw(t, setRaw,
|
|
`{"b":{"this":[null,null,null,null,4]}}`,
|
|
``,
|
|
"b.this.4", `4`)
|
|
testRaw(t, setRaw,
|
|
`[null,{"this":[null,null,null,null,4]}]`,
|
|
``,
|
|
"1.this.4", `4`)
|
|
testRaw(t, setRaw,
|
|
`{"1":{"this":[null,null,null,null,4]}}`,
|
|
``,
|
|
":1.this.4", `4`)
|
|
testRaw(t, setRaw,
|
|
`{":1":{"this":[null,null,null,null,4]}}`,
|
|
``,
|
|
"\\:1.this.4", `4`)
|
|
testRaw(t, setRaw,
|
|
`{":\\1":{"this":[null,null,null,null,{".HI":4}]}}`,
|
|
``,
|
|
"\\:\\\\1.this.4.\\.HI", `4`)
|
|
testRaw(t, setRaw,
|
|
`{"app.token":"cde"}`,
|
|
`{"app.token":"abc"}`,
|
|
"app\\.token", `"cde"`)
|
|
testRaw(t, setRaw,
|
|
`{"b":{"this":{"😇":""}}}`,
|
|
``,
|
|
"b.this.😇", `""`)
|
|
testRaw(t, setRaw,
|
|
`[ 1,2 ,3]`,
|
|
` [ 1,2 ] `,
|
|
"-1", `3`)
|
|
testRaw(t, setInt, `[1234]`, ``, `0`, int64(1234))
|
|
testRaw(t, setFloat, `[1234.5]`, ``, `0`, float64(1234.5))
|
|
testRaw(t, setString, `["1234.5"]`, ``, `0`, "1234.5")
|
|
testRaw(t, setBool, `[true]`, ``, `0`, true)
|
|
testRaw(t, setBool, `[null]`, ``, `0`, nil)
|
|
testRaw(t, setString, `{"arr":[1]}`, ``, `arr.-1`, 1)
|
|
testRaw(t, setString, `{"a":"\\"}`, ``, `a`, "\\")
|
|
testRaw(t, setString, `{"a":"C:\\Windows\\System32"}`, ``, `a`, `C:\Windows\System32`)
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
testRaw(t, setDelete, `[456]`, `[123,456]`, `0`, nil)
|
|
testRaw(t, setDelete, `[123,789]`, `[123,456,789]`, `1`, nil)
|
|
testRaw(t, setDelete, `[123,456]`, `[123,456,789]`, `-1`, nil)
|
|
testRaw(t, setDelete, `{"a":[123,456]}`, `{"a":[123,456,789]}`, `a.-1`, nil)
|
|
testRaw(t, setDelete, `{"and":"another"}`, `{"this":"that","and":"another"}`, `this`, nil)
|
|
testRaw(t, setDelete, `{"this":"that"}`, `{"this":"that","and":"another"}`, `and`, nil)
|
|
testRaw(t, setDelete, `{}`, `{"and":"another"}`, `and`, nil)
|
|
testRaw(t, setDelete, `{"1":"2"}`, `{"1":"2"}`, `3`, nil)
|
|
}
|
|
|
|
// TestRandomData is a fuzzing test that throws random data at SetRaw
|
|
// function looking for panics.
|
|
func TestRandomData(t *testing.T) {
|
|
var lstr string
|
|
defer func() {
|
|
if v := recover(); v != nil {
|
|
println("'" + hex.EncodeToString([]byte(lstr)) + "'")
|
|
println("'" + lstr + "'")
|
|
panic(v)
|
|
}
|
|
}()
|
|
rand.Seed(time.Now().UnixNano())
|
|
b := make([]byte, 200)
|
|
for i := 0; i < 2000000; i++ {
|
|
n, err := rand.Read(b[:rand.Int()%len(b)])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
lstr = string(b[:n])
|
|
SetRaw(lstr, "zzzz.zzzz.zzzz", "123")
|
|
}
|
|
}
|
|
|
|
func TestDeleteIssue21(t *testing.T) {
|
|
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"}`
|
|
res1 := gjson.Get(json, "date_updated")
|
|
var err error
|
|
json, err = Delete(json, "date_updated")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res2 := gjson.Get(json, "date_updated")
|
|
res3 := gjson.Get(json, "date_created")
|
|
if !res1.Exists() || res2.Exists() || !res3.Exists() {
|
|
t.Fatal("bad news")
|
|
}
|
|
|
|
// 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
|
|
|
|
//---------------------------
|
|
lenBeforeToDeleteIs307AsBytes := `{"1":"","0":"012345678901234567890123456789012345678901234567890123456789012345678901234567","to_delete":"0","2":""}`
|
|
|
|
expectedForLenBefore307AsBytes := `{"1":"","0":"012345678901234567890123456789012345678901234567890123456789012345678901234567","2":""}`
|
|
//---------------------------
|
|
|
|
//---------------------------
|
|
lenBeforeToDeleteIs308AsBytes := `{"1":"","0":"0123456789012345678901234567890123456789012345678901234567890123456789012345678","to_delete":"0","2":""}`
|
|
|
|
expectedForLenBefore308AsBytes := `{"1":"","0":"0123456789012345678901234567890123456789012345678901234567890123456789012345678","2":""}`
|
|
//---------------------------
|
|
|
|
//---------------------------
|
|
lenBeforeToDeleteIs309AsBytes := `{"1":"","0":"01234567890123456789012345678901234567890123456789012345678901234567890123456","to_delete":"0","2":""}`
|
|
|
|
expectedForLenBefore309AsBytes := `{"1":"","0":"01234567890123456789012345678901234567890123456789012345678901234567890123456","2":""}`
|
|
//---------------------------
|
|
|
|
var data = []struct {
|
|
desc string
|
|
input string
|
|
expected string
|
|
}{
|
|
{
|
|
desc: "len before \"to_delete\"... = 307",
|
|
input: lenBeforeToDeleteIs307AsBytes,
|
|
expected: expectedForLenBefore307AsBytes,
|
|
},
|
|
{
|
|
desc: "len before \"to_delete\"... = 308",
|
|
input: lenBeforeToDeleteIs308AsBytes,
|
|
expected: expectedForLenBefore308AsBytes,
|
|
},
|
|
{
|
|
desc: "len before \"to_delete\"... = 309",
|
|
input: lenBeforeToDeleteIs309AsBytes,
|
|
expected: expectedForLenBefore309AsBytes,
|
|
},
|
|
}
|
|
|
|
for i, d := range data {
|
|
result, err := Delete(d.input, "to_delete")
|
|
|
|
if err != nil {
|
|
t.Error(fmtErrorf(testError{
|
|
unexpected: "error",
|
|
desc: d.desc,
|
|
i: i,
|
|
lenInput: len(d.input),
|
|
input: d.input,
|
|
expected: d.expected,
|
|
result: result,
|
|
}))
|
|
}
|
|
if result != d.expected {
|
|
t.Error(fmtErrorf(testError{
|
|
unexpected: "result",
|
|
desc: d.desc,
|
|
i: i,
|
|
lenInput: len(d.input),
|
|
input: d.input,
|
|
expected: d.expected,
|
|
result: result,
|
|
}))
|
|
}
|
|
}
|
|
}
|
|
|
|
type testError struct {
|
|
unexpected string
|
|
desc string
|
|
i int
|
|
lenInput int
|
|
input interface{}
|
|
expected interface{}
|
|
result interface{}
|
|
}
|
|
|
|
func fmtErrorf(e testError) string {
|
|
return fmt.Sprintf(
|
|
"Unexpected %s:\n\t"+
|
|
"for=%q\n\t"+
|
|
"i=%d\n\t"+
|
|
"len(input)=%d\n\t"+
|
|
"input=%v\n\t"+
|
|
"expected=%v\n\t"+
|
|
"result=%v",
|
|
e.unexpected, e.desc, e.i, e.lenInput, e.input, e.expected, e.result,
|
|
)
|
|
}
|
|
|
|
func TestSetDotKeyIssue10(t *testing.T) {
|
|
json := `{"app.token":"abc"}`
|
|
json, _ = Set(json, `app\.token`, "cde")
|
|
if json != `{"app.token":"cde"}` {
|
|
t.Fatalf("expected '%v', got '%v'", `{"app.token":"cde"}`, json)
|
|
}
|
|
}
|
|
func TestDeleteDotKeyIssue19(t *testing.T) {
|
|
json := []byte(`{"data":{"key1":"value1","key2.something":"value2"}}`)
|
|
json, _ = DeleteBytes(json, `data.key2\.something`)
|
|
if string(json) != `{"data":{"key1":"value1"}}` {
|
|
t.Fatalf("expected '%v', got '%v'", `{"data":{"key1":"value1"}}`, json)
|
|
}
|
|
}
|
|
|
|
func TestIssue36(t *testing.T) {
|
|
var json = `
|
|
{
|
|
"size": 1000
|
|
}
|
|
`
|
|
var raw = `
|
|
{
|
|
"sample": "hello"
|
|
}
|
|
`
|
|
_ = raw
|
|
if true {
|
|
json, _ = SetRaw(json, "aggs", raw)
|
|
}
|
|
if !gjson.Valid(json) {
|
|
t.Fatal("invalid json")
|
|
}
|
|
res := gjson.Get(json, "aggs.sample").String()
|
|
if res != "hello" {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
}
|