1// Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// author sigu-399 16// author-github https://github.com/sigu-399 17// author-mail sigu.399@gmail.com 18// 19// repository-name jsonpointer 20// repository-desc An implementation of JSON Pointer - Go language 21// 22// description Automated tests on package. 23// 24// created 03-03-2013 25 26package jsonpointer 27 28import ( 29 "encoding/json" 30 "fmt" 31 "strconv" 32 "testing" 33 34 "github.com/stretchr/testify/assert" 35) 36 37const ( 38 TestDocumentNBItems = 11 39 TestNodeObjNBItems = 4 40 TestDocumentString = `{ 41"foo": ["bar", "baz"], 42"obj": { "a":1, "b":2, "c":[3,4], "d":[ {"e":9}, {"f":[50,51]} ] }, 43"": 0, 44"a/b": 1, 45"c%d": 2, 46"e^f": 3, 47"g|h": 4, 48"i\\j": 5, 49"k\"l": 6, 50" ": 7, 51"m~n": 8 52}` 53) 54 55var testDocumentJSON interface{} 56 57type testStructJSON struct { 58 Foo []string `json:"foo"` 59 Obj struct { 60 A int `json:"a"` 61 B int `json:"b"` 62 C []int `json:"c"` 63 D []struct { 64 E int `json:"e"` 65 F []int `json:"f"` 66 } `json:"d"` 67 } `json:"obj"` 68} 69 70type aliasedMap map[string]interface{} 71 72var testStructJSONDoc testStructJSON 73var testStructJSONPtr *testStructJSON 74 75func init() { 76 json.Unmarshal([]byte(TestDocumentString), &testDocumentJSON) 77 json.Unmarshal([]byte(TestDocumentString), &testStructJSONDoc) 78 testStructJSONPtr = &testStructJSONDoc 79} 80 81func TestEscaping(t *testing.T) { 82 83 ins := []string{`/`, `/`, `/a~1b`, `/a~1b`, `/c%d`, `/e^f`, `/g|h`, `/i\j`, `/k"l`, `/ `, `/m~0n`} 84 outs := []float64{0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8} 85 86 for i := range ins { 87 p, err := New(ins[i]) 88 if assert.NoError(t, err, "input: %v", ins[i]) { 89 result, _, err := p.Get(testDocumentJSON) 90 if assert.NoError(t, err, "input: %v", ins[i]) { 91 assert.Equal(t, outs[i], result, "input: %v", ins[i]) 92 } 93 } 94 } 95 96} 97 98func TestFullDocument(t *testing.T) { 99 100 in := `` 101 102 p, err := New(in) 103 if err != nil { 104 t.Errorf("New(%v) error %v", in, err.Error()) 105 } 106 107 result, _, err := p.Get(testDocumentJSON) 108 if err != nil { 109 t.Errorf("Get(%v) error %v", in, err.Error()) 110 } 111 112 if len(result.(map[string]interface{})) != TestDocumentNBItems { 113 t.Errorf("Get(%v) = %v, expect full document", in, result) 114 } 115 116 result, _, err = p.get(testDocumentJSON, nil) 117 if err != nil { 118 t.Errorf("Get(%v) error %v", in, err.Error()) 119 } 120 121 if len(result.(map[string]interface{})) != TestDocumentNBItems { 122 t.Errorf("Get(%v) = %v, expect full document", in, result) 123 } 124} 125 126func TestDecodedTokens(t *testing.T) { 127 p, err := New("/obj/a~1b") 128 assert.NoError(t, err) 129 assert.Equal(t, []string{"obj", "a/b"}, p.DecodedTokens()) 130} 131 132func TestIsEmpty(t *testing.T) { 133 p, err := New("") 134 assert.NoError(t, err) 135 assert.True(t, p.IsEmpty()) 136 p, err = New("/obj") 137 assert.NoError(t, err) 138 assert.False(t, p.IsEmpty()) 139} 140 141func TestGetSingle(t *testing.T) { 142 in := `/obj` 143 144 _, err := New(in) 145 assert.NoError(t, err) 146 result, _, err := GetForToken(testDocumentJSON, "obj") 147 assert.NoError(t, err) 148 assert.Len(t, result, TestNodeObjNBItems) 149 150 result, _, err = GetForToken(testStructJSONDoc, "Obj") 151 assert.Error(t, err) 152 assert.Nil(t, result) 153 154 result, _, err = GetForToken(testStructJSONDoc, "Obj2") 155 assert.Error(t, err) 156 assert.Nil(t, result) 157} 158 159type pointableImpl struct { 160 a string 161} 162 163func (p pointableImpl) JSONLookup(token string) (interface{}, error) { 164 if token == "some" { 165 return p.a, nil 166 } 167 return nil, fmt.Errorf("object has no field %q", token) 168} 169 170type pointableMap map[string]string 171 172func (p pointableMap) JSONLookup(token string) (interface{}, error) { 173 if token == "swap" { 174 return p["swapped"], nil 175 } 176 177 v, ok := p[token] 178 if ok { 179 return v, nil 180 } 181 182 return nil, fmt.Errorf("object has no key %q", token) 183} 184 185func TestPointableInterface(t *testing.T) { 186 p := &pointableImpl{"hello"} 187 188 result, _, err := GetForToken(p, "some") 189 assert.NoError(t, err) 190 assert.Equal(t, p.a, result) 191 192 result, _, err = GetForToken(p, "something") 193 assert.Error(t, err) 194 assert.Nil(t, result) 195 196 pm := pointableMap{"swapped": "hello", "a": "world"} 197 result, _, err = GetForToken(pm, "swap") 198 assert.NoError(t, err) 199 assert.Equal(t, pm["swapped"], result) 200 201 result, _, err = GetForToken(pm, "a") 202 assert.NoError(t, err) 203 assert.Equal(t, pm["a"], result) 204} 205 206func TestGetNode(t *testing.T) { 207 208 in := `/obj` 209 210 p, err := New(in) 211 assert.NoError(t, err) 212 result, _, err := p.Get(testDocumentJSON) 213 assert.NoError(t, err) 214 assert.Len(t, result, TestNodeObjNBItems) 215 216 result, _, err = p.Get(aliasedMap(testDocumentJSON.(map[string]interface{}))) 217 assert.NoError(t, err) 218 assert.Len(t, result, TestNodeObjNBItems) 219 220 result, _, err = p.Get(testStructJSONDoc) 221 assert.NoError(t, err) 222 assert.Equal(t, testStructJSONDoc.Obj, result) 223 224 result, _, err = p.Get(testStructJSONPtr) 225 assert.NoError(t, err) 226 assert.Equal(t, testStructJSONDoc.Obj, result) 227} 228 229func TestArray(t *testing.T) { 230 231 ins := []string{`/foo/0`, `/foo/0`, `/foo/1`} 232 outs := []string{"bar", "bar", "baz"} 233 234 for i := range ins { 235 p, err := New(ins[i]) 236 assert.NoError(t, err) 237 238 result, _, err := p.Get(testStructJSONDoc) 239 assert.NoError(t, err) 240 assert.Equal(t, outs[i], result) 241 242 result, _, err = p.Get(testStructJSONPtr) 243 assert.NoError(t, err) 244 assert.Equal(t, outs[i], result) 245 246 result, _, err = p.Get(testDocumentJSON) 247 assert.NoError(t, err) 248 assert.Equal(t, outs[i], result) 249 } 250} 251 252func TestOtherThings(t *testing.T) { 253 _, err := New("abc") 254 assert.Error(t, err) 255 256 p, err := New("") 257 assert.NoError(t, err) 258 assert.Equal(t, "", p.String()) 259 260 p, err = New("/obj/a") 261 assert.Equal(t, "/obj/a", p.String()) 262 263 s := Escape("m~n") 264 assert.Equal(t, "m~0n", s) 265 s = Escape("m/n") 266 assert.Equal(t, "m~1n", s) 267 268 p, err = New("/foo/3") 269 assert.NoError(t, err) 270 _, _, err = p.Get(testDocumentJSON) 271 assert.Error(t, err) 272 273 p, err = New("/foo/a") 274 assert.NoError(t, err) 275 _, _, err = p.Get(testDocumentJSON) 276 assert.Error(t, err) 277 278 p, err = New("/notthere") 279 assert.NoError(t, err) 280 _, _, err = p.Get(testDocumentJSON) 281 assert.Error(t, err) 282 283 p, err = New("/invalid") 284 assert.NoError(t, err) 285 _, _, err = p.Get(1234) 286 assert.Error(t, err) 287 288 p, err = New("/foo/1") 289 assert.NoError(t, err) 290 expected := "hello" 291 bbb := testDocumentJSON.(map[string]interface{})["foo"] 292 bbb.([]interface{})[1] = "hello" 293 294 v, _, err := p.Get(testDocumentJSON) 295 assert.NoError(t, err) 296 assert.Equal(t, expected, v) 297 298 esc := Escape("a/") 299 assert.Equal(t, "a~1", esc) 300 unesc := Unescape(esc) 301 assert.Equal(t, "a/", unesc) 302 303 unesc = Unescape("~01") 304 assert.Equal(t, "~1", unesc) 305 assert.Equal(t, "~0~1", Escape("~/")) 306 assert.Equal(t, "~/", Unescape("~0~1")) 307} 308 309func TestObject(t *testing.T) { 310 311 ins := []string{`/obj/a`, `/obj/b`, `/obj/c/0`, `/obj/c/1`, `/obj/c/1`, `/obj/d/1/f/0`} 312 outs := []float64{1, 2, 3, 4, 4, 50} 313 314 for i := range ins { 315 316 p, err := New(ins[i]) 317 assert.NoError(t, err) 318 319 result, _, err := p.Get(testDocumentJSON) 320 assert.NoError(t, err) 321 assert.Equal(t, outs[i], result) 322 323 result, _, err = p.Get(testStructJSONDoc) 324 assert.NoError(t, err) 325 assert.EqualValues(t, outs[i], result) 326 327 result, _, err = p.Get(testStructJSONPtr) 328 assert.NoError(t, err) 329 assert.EqualValues(t, outs[i], result) 330 } 331} 332 333type setJsonDocEle struct { 334 B int `json:"b"` 335 C int `json:"c"` 336} 337type setJsonDoc struct { 338 A []struct { 339 B int `json:"b"` 340 C int `json:"c"` 341 } `json:"a"` 342 D int `json:"d"` 343} 344 345type settableDoc struct { 346 Coll settableColl 347 Int settableInt 348} 349 350func (s settableDoc) MarshalJSON() ([]byte, error) { 351 var res struct { 352 A settableColl `json:"a"` 353 D settableInt `json:"d"` 354 } 355 res.A = s.Coll 356 res.D = s.Int 357 return json.Marshal(res) 358} 359func (s *settableDoc) UnmarshalJSON(data []byte) error { 360 var res struct { 361 A settableColl `json:"a"` 362 D settableInt `json:"d"` 363 } 364 365 if err := json.Unmarshal(data, &res); err != nil { 366 return err 367 } 368 s.Coll = res.A 369 s.Int = res.D 370 return nil 371} 372 373// JSONLookup implements an interface to customize json pointer lookup 374func (s settableDoc) JSONLookup(token string) (interface{}, error) { 375 switch token { 376 case "a": 377 return &s.Coll, nil 378 case "d": 379 return &s.Int, nil 380 default: 381 return nil, fmt.Errorf("%s is not a known field", token) 382 } 383} 384 385// JSONLookup implements an interface to customize json pointer lookup 386func (s *settableDoc) JSONSet(token string, data interface{}) error { 387 switch token { 388 case "a": 389 switch dt := data.(type) { 390 case settableColl: 391 s.Coll = dt 392 return nil 393 case *settableColl: 394 if dt != nil { 395 s.Coll = *dt 396 } else { 397 s.Coll = settableColl{} 398 } 399 return nil 400 case []settableCollItem: 401 s.Coll.Items = dt 402 return nil 403 } 404 case "d": 405 switch dt := data.(type) { 406 case settableInt: 407 s.Int = dt 408 return nil 409 case int: 410 s.Int.Value = dt 411 return nil 412 case int8: 413 s.Int.Value = int(dt) 414 return nil 415 case int16: 416 s.Int.Value = int(dt) 417 return nil 418 case int32: 419 s.Int.Value = int(dt) 420 return nil 421 case int64: 422 s.Int.Value = int(dt) 423 return nil 424 default: 425 return fmt.Errorf("invalid type %T for %s", data, token) 426 } 427 } 428 return fmt.Errorf("%s is not a known field", token) 429} 430 431type settableColl struct { 432 Items []settableCollItem 433} 434 435func (s settableColl) MarshalJSON() ([]byte, error) { 436 return json.Marshal(s.Items) 437} 438func (s *settableColl) UnmarshalJSON(data []byte) error { 439 return json.Unmarshal(data, &s.Items) 440} 441 442// JSONLookup implements an interface to customize json pointer lookup 443func (s settableColl) JSONLookup(token string) (interface{}, error) { 444 if tok, err := strconv.Atoi(token); err == nil { 445 return &s.Items[tok], nil 446 } 447 return nil, fmt.Errorf("%s is not a valid index", token) 448} 449 450// JSONLookup implements an interface to customize json pointer lookup 451func (s *settableColl) JSONSet(token string, data interface{}) error { 452 if _, err := strconv.Atoi(token); err == nil { 453 _, err := SetForToken(s.Items, token, data) 454 return err 455 } 456 return fmt.Errorf("%s is not a valid index", token) 457} 458 459type settableCollItem struct { 460 B int `json:"b"` 461 C int `json:"c"` 462} 463 464type settableInt struct { 465 Value int 466} 467 468func (s settableInt) MarshalJSON() ([]byte, error) { 469 return json.Marshal(s.Value) 470} 471func (s *settableInt) UnmarshalJSON(data []byte) error { 472 return json.Unmarshal(data, &s.Value) 473} 474 475func TestSetNode(t *testing.T) { 476 477 jsonText := `{"a":[{"b": 1, "c": 2}], "d": 3}` 478 479 var jsonDocument interface{} 480 if assert.NoError(t, json.Unmarshal([]byte(jsonText), &jsonDocument)) { 481 in := "/a/0/c" 482 p, err := New(in) 483 if assert.NoError(t, err) { 484 485 _, err = p.Set(jsonDocument, 999) 486 assert.NoError(t, err) 487 488 firstNode := jsonDocument.(map[string]interface{}) 489 assert.Len(t, firstNode, 2) 490 491 sliceNode := firstNode["a"].([]interface{}) 492 assert.Len(t, sliceNode, 1) 493 494 changedNode := sliceNode[0].(map[string]interface{}) 495 chNodeVI := changedNode["c"] 496 if assert.IsType(t, 0, chNodeVI) { 497 changedNodeValue := chNodeVI.(int) 498 if assert.Equal(t, 999, changedNodeValue) { 499 assert.Len(t, sliceNode, 1) 500 } 501 } 502 } 503 504 v, err := New("/a/0") 505 if assert.NoError(t, err) { 506 _, err = v.Set(jsonDocument, map[string]interface{}{"b": 3, "c": 8}) 507 if assert.NoError(t, err) { 508 firstNode := jsonDocument.(map[string]interface{}) 509 assert.Len(t, firstNode, 2) 510 511 sliceNode := firstNode["a"].([]interface{}) 512 assert.Len(t, sliceNode, 1) 513 changedNode := sliceNode[0].(map[string]interface{}) 514 assert.Equal(t, 3, changedNode["b"]) 515 assert.Equal(t, 8, changedNode["c"]) 516 } 517 } 518 } 519 520 var structDoc setJsonDoc 521 if assert.NoError(t, json.Unmarshal([]byte(jsonText), &structDoc)) { 522 g, err := New("/a") 523 if assert.NoError(t, err) { 524 _, err = g.Set(&structDoc, []struct { 525 B int `json:"b"` 526 C int `json:"c"` 527 }{{B: 4, C: 7}}) 528 529 if assert.NoError(t, err) { 530 assert.Len(t, structDoc.A, 1) 531 changedNode := structDoc.A[0] 532 assert.Equal(t, 4, changedNode.B) 533 assert.Equal(t, 7, changedNode.C) 534 } 535 } 536 537 v, err := New("/a/0") 538 if assert.NoError(t, err) { 539 _, err = v.Set(structDoc, struct { 540 B int `json:"b"` 541 C int `json:"c"` 542 }{B: 3, C: 8}) 543 544 if assert.NoError(t, err) { 545 assert.Len(t, structDoc.A, 1) 546 changedNode := structDoc.A[0] 547 assert.Equal(t, 3, changedNode.B) 548 assert.Equal(t, 8, changedNode.C) 549 } 550 } 551 552 p, err := New("/a/0/c") 553 if assert.NoError(t, err) { 554 _, err = p.Set(&structDoc, 999) 555 assert.NoError(t, err) 556 if assert.Len(t, structDoc.A, 1) { 557 assert.Equal(t, 999, structDoc.A[0].C) 558 } 559 } 560 } 561 562 var setDoc settableDoc 563 if assert.NoError(t, json.Unmarshal([]byte(jsonText), &setDoc)) { 564 g, err := New("/a") 565 if assert.NoError(t, err) { 566 _, err = g.Set(&setDoc, []settableCollItem{{B: 4, C: 7}}) 567 568 if assert.NoError(t, err) { 569 assert.Len(t, setDoc.Coll.Items, 1) 570 changedNode := setDoc.Coll.Items[0] 571 assert.Equal(t, 4, changedNode.B) 572 assert.Equal(t, 7, changedNode.C) 573 } 574 } 575 576 v, err := New("/a/0") 577 if assert.NoError(t, err) { 578 _, err = v.Set(setDoc, settableCollItem{B: 3, C: 8}) 579 580 if assert.NoError(t, err) { 581 assert.Len(t, setDoc.Coll.Items, 1) 582 changedNode := setDoc.Coll.Items[0] 583 assert.Equal(t, 3, changedNode.B) 584 assert.Equal(t, 8, changedNode.C) 585 } 586 } 587 588 p, err := New("/a/0/c") 589 if assert.NoError(t, err) { 590 _, err = p.Set(setDoc, 999) 591 assert.NoError(t, err) 592 if assert.Len(t, setDoc.Coll.Items, 1) { 593 assert.Equal(t, 999, setDoc.Coll.Items[0].C) 594 } 595 } 596 } 597} 598