github.com/goccy/go-json@v0.10.3-0.20240509105655-5e2ae3f23c1d/json_test.go (about)

     1  package json_test
     2  
     3  import (
     4  	"bytes"
     5  	stdjson "encoding/json"
     6  	"math"
     7  	"math/rand"
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/goccy/go-json"
    12  )
    13  
    14  var validTests = []struct {
    15  	data string
    16  	ok   bool
    17  }{
    18  	{`foo`, false},
    19  	{`}{`, false},
    20  	{`{]`, false},
    21  	{`{}`, true},
    22  	{`{"foo":"bar"}`, true},
    23  	{`{"foo":"bar","bar":{"baz":["qux"]}}`, true},
    24  	{`[""],`, false},
    25  }
    26  
    27  func TestValid(t *testing.T) {
    28  	for _, tt := range validTests {
    29  		if ok := json.Valid([]byte(tt.data)); ok != tt.ok {
    30  			t.Errorf("Valid(%#q) = %v, want %v", tt.data, ok, tt.ok)
    31  		}
    32  	}
    33  }
    34  
    35  func TestValidWithComplexData(t *testing.T) {
    36  	data := []byte(`{"ABCDEFGHIJKL":[{"MNOPQRSTUVWX":[{"YABC":{"DEFG":[{"HIJKLMNO":{"PQRS":"TUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDE","FGHIJKLM":"NOPQRSTUVW"},"XYABCDEFGH":[{"IJKLMNOP":"!=","Q":{"RSTU":"V","WXYABCDE":"FGHIJKLMNO"},"P":{"QRSTUVWX":"YAB"},"CDEFGHIJ":"KLMNOP","QRSTUVWX":"YABCDEFGHI"}],"JKLMNOPQRSTUVW":null,"XYABCDEF":"GHIJ"},{"KLMNOPQR":{"STUVWXY":{"ABCDEFGH":"IJKLMN_OPQ_RST","U":{"VWXY":"A","BCDEFGHI":"JKLMNOPQRS"},"TUVWXYAB":"CDEFG","HIJKLMNO":"PQRSTUVWXY"},"ABCDEFGH":"IJKLMNOP!Q41R8ST98U00V204W9800998XYA8427B","CDEFGHIJ":"KLMNOP","QRSTUVWX":"YABCDEFGHI"},"JKLMNOPQRS":null,"TUVWXYABCDEFGH":null,"IJKLMNOP":"QRST"}],"UVWXYABC":"DEFGH","IJKLMNOP":"QRSTUVWXY"},"ABCDEFGH":9,"IJKL":"MNOPQRST/UVWXYABCDE//FGHIJKLMNOPQRST!4UV2WXYABC7826D7659EF223GH40I91J","KLMNOPQRST":[{"UVWXYABCDEFG":null,"HIJKLMNO":0,"PQRS":"T","UVWX":{"YABC":{"DEFG":"HIJK/LMNO/PQRSTU","VWXYABCD":"EFGHIJKLM","NOPQRSTU":"VWXY"},"ABCDEFGH":"IJKLMNO","PQRSTUVW":"XYAB"},"CDEFGHIJ":"KLMNOPQR"}],"STUVWXYA":null,"BCDEFGH":null,"IJKLMN":null,"OPQRSTUVWXYABC":null,"DEFGHIJK":"LMNOPQRS"},{"TUVW":{"XYAB":[{"CDEFGHIJ":{"KLMN":"OPQRSTUV/WXYABCDEFG//HIJKLMNOPQRSTUV!4WX2YABCDE7826F7659GH223IJ40K91L","MNOPQRST":"UVWXYABCDE"},"FGHIJKLMNO":[{"PQRS":"T","UVWXYABC":"DEFGHIJKLM"}],"NOPQRSTUVWXYAB":null,"CDEFGHIJ":"KLMN"}],"OPQRSTUV":"WXYAB","CDEFGHIJ":"KLMNOPQRS"},"TUVWXYAB":9,"CDEF":"GHIJKLMN/OPQRSTUVWX//YABCDEFGHIJKLM!4NO2PQRSTU7826V7659WX223YA40B91C","DEFGHIJKLM":[{"NOPQRSTUVWXY":null,"ABCDEFGH":0,"IJKL":"M","NOPQ":{"RSTU":{"VWXY":"ABCD/EFGH/IJKLMN","OPQRSTUV":"WXYABCDEF","GHIJKLMN":"OPQR"},"STUVWXYA":"BCDEFGH","IJKLMNOP":"QRST"},"UVWXYABC":"DEFGHIJK"}],"LMNOPQRS":null,"TUVWXYA":null,"BCDEFG":null,"HIJKLMNOPQRSTU":null,"VWXYABCD":"EFGHIJKL"},{"MNOP":{"QRST":[{"UVWXYABC":0,"DEFG":["HIJK"],"LMNO":[{"PQRS":{"TUVW":"XYABCDEF/GHIJKLMNOP","QRSTUVWX":"YABCDEFGH","IJKLMNOP":"QRST"},"UVWXYABC":"DEFGHIJ","KLMNOPQR":"STUV"}],"WXYAB":[{"CDEF":{"GHIJ":{"KLMN":"OPQRSTUV/WXYABCDEFG","HIJKLMNO":"PQRSTUVWX","YABCDEFG":"HIJK"},"LMNOPQRS":"TUVWXYA","BCDEFGHI":"JKLM"},"NOPQRSTU":"VWX"}],"YABCDEFG":"HIJKLMNOPQR","STUVWXYA":"BCDEFGHIJ"},{"KLMNOPQR":"=","S":{"TUVWXYA":{"BCDE":"FGHI","JKLMNOPQ":"RSTUVWXYAB"},"CDEFGHIJ":"@KLMN/OPQR/STUVWX","YABCDEFG":"HIJKLM","NOPQRSTU":"VWXYABCDEF"},"G":{"HIJKLMNO":{"PQRS":"TUVW/XYAB/CDEFGH//IJKLMN!O41P8QR98S00T204U9800998VWX8427Y","ABCDEFGH":"IJKLMNOPQR"},"STUVWXYABC":null,"DEFGHIJKLMNOPQ":null,"RSTUVWXY":"ABCD"},"EFGHIJKL":"MNOPQR","STUVWXYA":"BCDEFGHIJK"},{"LMNOPQR":[{"STUV":"WXYA","BCDEFGHI":"JKLMNOPQRS"}],"TUVWXYAB":"CDEFGH","IJKLMNOP":"QRSTUVWXY"}],"ABCDEFGH":"IJKLM","NOPQRSTU":"VWXYABCDE"},"FGHIJKLM":37,"NOPQ":"RSTUVWXY/ABCDEFGHIJ//KLMNOPQRST!U41V8WX98Y00A204B9800998CDE8427F","GHIJKLMNOP":null,"QRSTUVWX":null,"YABCDEF":[{"GHIJKLMNOPQR":null,"STUVWXYA":0,"BCDE":"","FGHI":{"JKLM":{"NOPQ":"RSTUVWXY/ABCDEFGHIJ","KLMNOPQR":"STUVWXYAB","CDEFGHIJ":"KLMN"},"OPQRSTUV":"WXYABCD","EFGHIJKL":"MNOP"},"QRSTUVWX":"YABCDEFG"}],"HIJKLM":null,"NOPQRSTUVWXYAB":null,"CDEFGHIJ":"KLMNOPQR"}],"STUVWXYABC":null,"DEFGHIJK":[{"LMNO":{"PQRS":"TUVW/XYAB/CDEFGH","IJKLMNOP":"QRSTUVWXY","ABCDEFGH":"IJKL"},"MNOPQRST":"UVWXYAB","CDEFGHIJ":"KLMN"}],"OPQRSTUV":1,"WXYA":"BCDEFGHI/JKLMNOPQRS","TUVWXYAB":null,"CDEFGHIJKLMNOP":null,"QRSTUVWX":"YABCDE","FGHIJKLM":"NOPQ"}]}`)
    37  	expected := stdjson.Valid(data)
    38  	actual := json.Valid(data)
    39  	if expected != actual {
    40  		t.Fatalf("failed to valid: expected %v but got %v", expected, actual)
    41  	}
    42  }
    43  
    44  type example struct {
    45  	compact string
    46  	indent  string
    47  }
    48  
    49  var examples = []example{
    50  	{`1`, `1`},
    51  	{`{}`, `{}`},
    52  	{`[]`, `[]`},
    53  	{`{"":2}`, "{\n\t\"\": 2\n}"},
    54  	{`[3]`, "[\n\t3\n]"},
    55  	{`[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"},
    56  	{`{"x":1}`, "{\n\t\"x\": 1\n}"},
    57  	{ex1, ex1i},
    58  	{"{\"\":\"<>&\u2028\u2029\"}", "{\n\t\"\": \"<>&\u2028\u2029\"\n}"}, // See golang.org/issue/34070
    59  }
    60  
    61  var ex1 = `[true,false,null,"x",1,1.5,0,-5e+2]`
    62  
    63  var ex1i = `[
    64  	true,
    65  	false,
    66  	null,
    67  	"x",
    68  	1,
    69  	1.5,
    70  	0,
    71  	-5e+2
    72  ]`
    73  
    74  func TestCompact(t *testing.T) {
    75  	var buf bytes.Buffer
    76  	for _, tt := range examples {
    77  		buf.Reset()
    78  		t.Log("src = ", tt.compact)
    79  		if err := json.Compact(&buf, []byte(tt.compact)); err != nil {
    80  			t.Errorf("Compact(%#q): %v", tt.compact, err)
    81  		} else if s := buf.String(); s != tt.compact {
    82  			t.Errorf("Compact(%#q) = %#q, want original", tt.compact, s)
    83  		}
    84  
    85  		buf.Reset()
    86  		if err := json.Compact(&buf, []byte(tt.indent)); err != nil {
    87  			t.Errorf("Compact(%#q): %v", tt.indent, err)
    88  			continue
    89  		} else if s := buf.String(); s != tt.compact {
    90  			t.Errorf("Compact(%#q) = %#q, want %#q", tt.indent, s, tt.compact)
    91  		}
    92  	}
    93  	t.Run("invalid", func(t *testing.T) {
    94  		for _, src := range []string{
    95  			`invalid`,
    96  			`}`,
    97  			`]`,
    98  			`{"a":1}}`,
    99  			`{"a" 1}`,
   100  			`{"a": 1 "b": 2}`,
   101  			`["a" "b"]`,
   102  			`"\`,
   103  			`{"a":"\\""}`,
   104  			`tr`,
   105  			`{"a": tru, "b": 1}`,
   106  			`fal`,
   107  			`{"a": fals, "b": 1}`,
   108  			`nu`,
   109  			`{"a": nul, "b": 1}`,
   110  			`1.234.567`,
   111  			`[nul]`,
   112  			`{}   1`,
   113  		} {
   114  			buf.Reset()
   115  			if err := stdjson.Compact(&buf, []byte(src)); err == nil {
   116  				t.Fatal("invalid test case")
   117  			}
   118  			buf.Reset()
   119  			if err := json.Compact(&buf, []byte(src)); err == nil {
   120  				t.Fatalf("%q: expected error", src)
   121  			}
   122  		}
   123  	})
   124  }
   125  
   126  func TestCompactSeparators(t *testing.T) {
   127  	// U+2028 and U+2029 should be escaped inside strings.
   128  	// They should not appear outside strings.
   129  	tests := []struct {
   130  		in, compact string
   131  	}{
   132  		{"{\"\u2028\": 1}", "{\"\u2028\":1}"},
   133  		{"{\"\u2029\" :2}", "{\"\u2029\":2}"},
   134  	}
   135  	for _, tt := range tests {
   136  		var buf bytes.Buffer
   137  		if err := json.Compact(&buf, []byte(tt.in)); err != nil {
   138  			t.Errorf("Compact(%q): %v", tt.in, err)
   139  		} else if s := buf.String(); s != tt.compact {
   140  			t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact)
   141  		}
   142  	}
   143  }
   144  
   145  func TestIndent(t *testing.T) {
   146  	var buf bytes.Buffer
   147  	for _, tt := range examples {
   148  		buf.Reset()
   149  		if err := json.Indent(&buf, []byte(tt.indent), "", "\t"); err != nil {
   150  			t.Errorf("Indent(%#q): %v", tt.indent, err)
   151  		} else if s := buf.String(); s != tt.indent {
   152  			t.Errorf("Indent(%#q) = %#q, want original", tt.indent, s)
   153  		}
   154  
   155  		buf.Reset()
   156  		if err := json.Indent(&buf, []byte(tt.compact), "", "\t"); err != nil {
   157  			t.Errorf("Indent(%#q): %v", tt.compact, err)
   158  			continue
   159  		} else if s := buf.String(); s != tt.indent {
   160  			t.Errorf("Indent(%#q) = %#q, want %#q", tt.compact, s, tt.indent)
   161  		}
   162  	}
   163  	t.Run("invalid", func(t *testing.T) {
   164  		for _, src := range []string{
   165  			`invalid`,
   166  			`}`,
   167  			`]`,
   168  			`{"a":1}}`,
   169  			`{"a" 1}`,
   170  			`{"a": 1 "b": 2}`,
   171  			`["a" "b"]`,
   172  			`"\`,
   173  			`{"a":"\\""}`,
   174  			`tr`,
   175  			`{"a": tru, "b": 1}`,
   176  			`fal`,
   177  			`{"a": fals, "b": 1}`,
   178  			`nu`,
   179  			`{"a": nul, "b": 1}`,
   180  			`1.234.567`,
   181  			`[nul]`,
   182  			`{}   1`,
   183  		} {
   184  			buf.Reset()
   185  			if err := stdjson.Indent(&buf, []byte(src), "", " "); err == nil {
   186  				t.Fatal("invalid test case")
   187  			}
   188  			buf.Reset()
   189  			if err := json.Indent(&buf, []byte(src), "", " "); err == nil {
   190  				t.Fatalf("%q: expected error", src)
   191  			}
   192  		}
   193  	})
   194  }
   195  
   196  // Tests of a large random structure.
   197  func TestCompactBig(t *testing.T) {
   198  	initBig()
   199  	var buf bytes.Buffer
   200  	if err := json.Compact(&buf, jsonBig); err != nil {
   201  		t.Fatalf("Compact: %v", err)
   202  	}
   203  	b := buf.Bytes()
   204  	if !bytes.Equal(b, jsonBig) {
   205  		t.Error("Compact(jsonBig) != jsonBig")
   206  		diff(t, b, jsonBig)
   207  		return
   208  	}
   209  }
   210  
   211  func TestIndentBig(t *testing.T) {
   212  	//t.Parallel()
   213  	initBig()
   214  	var buf bytes.Buffer
   215  	if err := json.Indent(&buf, jsonBig, "", "\t"); err != nil {
   216  		t.Fatalf("Indent1: %v", err)
   217  	}
   218  	b := buf.Bytes()
   219  	if len(b) == len(jsonBig) {
   220  		// jsonBig is compact (no unnecessary spaces);
   221  		// indenting should make it bigger
   222  		t.Fatalf("Indent(jsonBig) did not get bigger")
   223  	}
   224  
   225  	// should be idempotent
   226  	var buf1 bytes.Buffer
   227  	if err := json.Indent(&buf1, b, "", "\t"); err != nil {
   228  		t.Fatalf("Indent2: %v", err)
   229  	}
   230  	b1 := buf1.Bytes()
   231  	if !bytes.Equal(b1, b) {
   232  		t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)")
   233  		diff(t, b1, b)
   234  		return
   235  	}
   236  
   237  	// should get back to original
   238  	buf1.Reset()
   239  	if err := json.Compact(&buf1, b); err != nil {
   240  		t.Fatalf("Compact: %v", err)
   241  	}
   242  	b1 = buf1.Bytes()
   243  	if !bytes.Equal(b1, jsonBig) {
   244  		t.Error("Compact(Indent(jsonBig)) != jsonBig")
   245  		diff(t, b1, jsonBig)
   246  		return
   247  	}
   248  }
   249  
   250  type indentErrorTest struct {
   251  	in  string
   252  	err error
   253  }
   254  
   255  var indentErrorTests = []indentErrorTest{
   256  	{`{"X": "foo", "Y"}`, json.NewSyntaxError("invalid character '}' after object key", 17)},
   257  	{`{"X": "foo" "Y": "bar"}`, json.NewSyntaxError("invalid character '\"' after object key:value pair", 13)},
   258  }
   259  
   260  func TestIndentErrors(t *testing.T) {
   261  	for i, tt := range indentErrorTests {
   262  		slice := make([]uint8, 0)
   263  		buf := bytes.NewBuffer(slice)
   264  		if err := json.Indent(buf, []uint8(tt.in), "", ""); err != nil {
   265  			if !reflect.DeepEqual(err, tt.err) {
   266  				t.Errorf("#%d: Indent: expected %#v but got %#v", i, tt.err, err)
   267  				continue
   268  			}
   269  		}
   270  	}
   271  }
   272  
   273  func diff(t *testing.T, a, b []byte) {
   274  	t.Helper()
   275  	for i := 0; ; i++ {
   276  		if i >= len(a) || i >= len(b) || a[i] != b[i] {
   277  			j := i - 10
   278  			if j < 0 {
   279  				j = 0
   280  			}
   281  			t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:]))
   282  			return
   283  		}
   284  	}
   285  }
   286  
   287  func trim(b []byte) []byte {
   288  	if len(b) > 20 {
   289  		return b[0:20]
   290  	}
   291  	return b
   292  }
   293  
   294  // Generate a random JSON object.
   295  
   296  var (
   297  	jsonBig []byte
   298  )
   299  
   300  func initBig() {
   301  	n := 10000
   302  	if testing.Short() {
   303  		n = 100
   304  	}
   305  	v := genValue(n)
   306  	b, err := json.Marshal(v)
   307  	if err != nil {
   308  		panic(err)
   309  	}
   310  	jsonBig = b
   311  }
   312  
   313  func genValue(n int) interface{} {
   314  	if n > 1 {
   315  		switch rand.Intn(2) {
   316  		case 0:
   317  			return genArray(n)
   318  		case 1:
   319  			return genMap(n)
   320  		}
   321  	}
   322  	switch rand.Intn(3) {
   323  	case 0:
   324  		return rand.Intn(2) == 0
   325  	case 1:
   326  		return rand.NormFloat64()
   327  	case 2:
   328  		return genString(30)
   329  	}
   330  	panic("unreachable")
   331  }
   332  
   333  func genString(stddev float64) string {
   334  	n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2))
   335  	c := make([]rune, n)
   336  	for i := range c {
   337  		f := math.Abs(rand.NormFloat64()*64 + 32)
   338  		if f > 0x10ffff {
   339  			f = 0x10ffff
   340  		}
   341  		c[i] = rune(f)
   342  	}
   343  	return string(c)
   344  }
   345  
   346  func genArray(n int) []interface{} {
   347  	f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
   348  	if f > n {
   349  		f = n
   350  	}
   351  	if f < 1 {
   352  		f = 1
   353  	}
   354  	x := make([]interface{}, f)
   355  	for i := range x {
   356  		x[i] = genValue(((i+1)*n)/f - (i*n)/f)
   357  	}
   358  	return x
   359  }
   360  
   361  func genMap(n int) map[string]interface{} {
   362  	f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
   363  	if f > n {
   364  		f = n
   365  	}
   366  	if n > 0 && f == 0 {
   367  		f = 1
   368  	}
   369  	x := make(map[string]interface{})
   370  	for i := 0; i < f; i++ {
   371  		x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f)
   372  	}
   373  	return x
   374  }