github.com/3JoB/go-json@v0.10.4/json_test.go (about)

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