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 170func TestPointableInterface(t *testing.T) { 171 p := &pointableImpl{"hello"} 172 173 result, _, err := GetForToken(p, "some") 174 assert.NoError(t, err) 175 assert.Equal(t, p.a, result) 176 177 result, _, err = GetForToken(p, "something") 178 assert.Error(t, err) 179 assert.Nil(t, result) 180} 181 182func TestGetNode(t *testing.T) { 183 184 in := `/obj` 185 186 p, err := New(in) 187 assert.NoError(t, err) 188 result, _, err := p.Get(testDocumentJSON) 189 assert.NoError(t, err) 190 assert.Len(t, result, TestNodeObjNBItems) 191 192 result, _, err = p.Get(aliasedMap(testDocumentJSON.(map[string]interface{}))) 193 assert.NoError(t, err) 194 assert.Len(t, result, TestNodeObjNBItems) 195 196 result, _, err = p.Get(testStructJSONDoc) 197 assert.NoError(t, err) 198 assert.Equal(t, testStructJSONDoc.Obj, result) 199 200 result, _, err = p.Get(testStructJSONPtr) 201 assert.NoError(t, err) 202 assert.Equal(t, testStructJSONDoc.Obj, result) 203} 204 205func TestArray(t *testing.T) { 206 207 ins := []string{`/foo/0`, `/foo/0`, `/foo/1`} 208 outs := []string{"bar", "bar", "baz"} 209 210 for i := range ins { 211 p, err := New(ins[i]) 212 assert.NoError(t, err) 213 214 result, _, err := p.Get(testStructJSONDoc) 215 assert.NoError(t, err) 216 assert.Equal(t, outs[i], result) 217 218 result, _, err = p.Get(testStructJSONPtr) 219 assert.NoError(t, err) 220 assert.Equal(t, outs[i], result) 221 222 result, _, err = p.Get(testDocumentJSON) 223 assert.NoError(t, err) 224 assert.Equal(t, outs[i], result) 225 } 226} 227 228func TestOtherThings(t *testing.T) { 229 _, err := New("abc") 230 assert.Error(t, err) 231 232 p, err := New("") 233 assert.NoError(t, err) 234 assert.Equal(t, "", p.String()) 235 236 p, err = New("/obj/a") 237 assert.Equal(t, "/obj/a", p.String()) 238 239 s := Escape("m~n") 240 assert.Equal(t, "m~0n", s) 241 s = Escape("m/n") 242 assert.Equal(t, "m~1n", s) 243 244 p, err = New("/foo/3") 245 assert.NoError(t, err) 246 _, _, err = p.Get(testDocumentJSON) 247 assert.Error(t, err) 248 249 p, err = New("/foo/a") 250 assert.NoError(t, err) 251 _, _, err = p.Get(testDocumentJSON) 252 assert.Error(t, err) 253 254 p, err = New("/notthere") 255 assert.NoError(t, err) 256 _, _, err = p.Get(testDocumentJSON) 257 assert.Error(t, err) 258 259 p, err = New("/invalid") 260 assert.NoError(t, err) 261 _, _, err = p.Get(1234) 262 assert.Error(t, err) 263 264 p, err = New("/foo/1") 265 assert.NoError(t, err) 266 expected := "hello" 267 bbb := testDocumentJSON.(map[string]interface{})["foo"] 268 bbb.([]interface{})[1] = "hello" 269 270 v, _, err := p.Get(testDocumentJSON) 271 assert.NoError(t, err) 272 assert.Equal(t, expected, v) 273 274 esc := Escape("a/") 275 assert.Equal(t, "a~1", esc) 276 unesc := Unescape(esc) 277 assert.Equal(t, "a/", unesc) 278 279 unesc = Unescape("~01") 280 assert.Equal(t, "~1", unesc) 281 assert.Equal(t, "~0~1", Escape("~/")) 282 assert.Equal(t, "~/", Unescape("~0~1")) 283} 284 285func TestObject(t *testing.T) { 286 287 ins := []string{`/obj/a`, `/obj/b`, `/obj/c/0`, `/obj/c/1`, `/obj/c/1`, `/obj/d/1/f/0`} 288 outs := []float64{1, 2, 3, 4, 4, 50} 289 290 for i := range ins { 291 292 p, err := New(ins[i]) 293 assert.NoError(t, err) 294 295 result, _, err := p.Get(testDocumentJSON) 296 assert.NoError(t, err) 297 assert.Equal(t, outs[i], result) 298 299 result, _, err = p.Get(testStructJSONDoc) 300 assert.NoError(t, err) 301 assert.EqualValues(t, outs[i], result) 302 303 result, _, err = p.Get(testStructJSONPtr) 304 assert.NoError(t, err) 305 assert.EqualValues(t, outs[i], result) 306 } 307} 308 309type setJsonDocEle struct { 310 B int `json:"b"` 311 C int `json:"c"` 312} 313type setJsonDoc struct { 314 A []struct { 315 B int `json:"b"` 316 C int `json:"c"` 317 } `json:"a"` 318 D int `json:"d"` 319} 320 321type settableDoc struct { 322 Coll settableColl 323 Int settableInt 324} 325 326func (s settableDoc) MarshalJSON() ([]byte, error) { 327 var res struct { 328 A settableColl `json:"a"` 329 D settableInt `json:"d"` 330 } 331 res.A = s.Coll 332 res.D = s.Int 333 return json.Marshal(res) 334} 335func (s *settableDoc) UnmarshalJSON(data []byte) error { 336 var res struct { 337 A settableColl `json:"a"` 338 D settableInt `json:"d"` 339 } 340 341 if err := json.Unmarshal(data, &res); err != nil { 342 return err 343 } 344 s.Coll = res.A 345 s.Int = res.D 346 return nil 347} 348 349// JSONLookup implements an interface to customize json pointer lookup 350func (s settableDoc) JSONLookup(token string) (interface{}, error) { 351 switch token { 352 case "a": 353 return &s.Coll, nil 354 case "d": 355 return &s.Int, nil 356 default: 357 return nil, fmt.Errorf("%s is not a known field", token) 358 } 359} 360 361// JSONLookup implements an interface to customize json pointer lookup 362func (s *settableDoc) JSONSet(token string, data interface{}) error { 363 switch token { 364 case "a": 365 switch dt := data.(type) { 366 case settableColl: 367 s.Coll = dt 368 return nil 369 case *settableColl: 370 if dt != nil { 371 s.Coll = *dt 372 } else { 373 s.Coll = settableColl{} 374 } 375 return nil 376 case []settableCollItem: 377 s.Coll.Items = dt 378 return nil 379 } 380 case "d": 381 switch dt := data.(type) { 382 case settableInt: 383 s.Int = dt 384 return nil 385 case int: 386 s.Int.Value = dt 387 return nil 388 case int8: 389 s.Int.Value = int(dt) 390 return nil 391 case int16: 392 s.Int.Value = int(dt) 393 return nil 394 case int32: 395 s.Int.Value = int(dt) 396 return nil 397 case int64: 398 s.Int.Value = int(dt) 399 return nil 400 default: 401 return fmt.Errorf("invalid type %T for %s", data, token) 402 } 403 } 404 return fmt.Errorf("%s is not a known field", token) 405} 406 407type settableColl struct { 408 Items []settableCollItem 409} 410 411func (s settableColl) MarshalJSON() ([]byte, error) { 412 return json.Marshal(s.Items) 413} 414func (s *settableColl) UnmarshalJSON(data []byte) error { 415 return json.Unmarshal(data, &s.Items) 416} 417 418// JSONLookup implements an interface to customize json pointer lookup 419func (s settableColl) JSONLookup(token string) (interface{}, error) { 420 if tok, err := strconv.Atoi(token); err == nil { 421 return &s.Items[tok], nil 422 } 423 return nil, fmt.Errorf("%s is not a valid index", token) 424} 425 426// JSONLookup implements an interface to customize json pointer lookup 427func (s *settableColl) JSONSet(token string, data interface{}) error { 428 if _, err := strconv.Atoi(token); err == nil { 429 _, err := SetForToken(s.Items, token, data) 430 return err 431 } 432 return fmt.Errorf("%s is not a valid index", token) 433} 434 435type settableCollItem struct { 436 B int `json:"b"` 437 C int `json:"c"` 438} 439 440type settableInt struct { 441 Value int 442} 443 444func (s settableInt) MarshalJSON() ([]byte, error) { 445 return json.Marshal(s.Value) 446} 447func (s *settableInt) UnmarshalJSON(data []byte) error { 448 return json.Unmarshal(data, &s.Value) 449} 450 451func TestSetNode(t *testing.T) { 452 453 jsonText := `{"a":[{"b": 1, "c": 2}], "d": 3}` 454 455 var jsonDocument interface{} 456 if assert.NoError(t, json.Unmarshal([]byte(jsonText), &jsonDocument)) { 457 in := "/a/0/c" 458 p, err := New(in) 459 if assert.NoError(t, err) { 460 461 _, err = p.Set(jsonDocument, 999) 462 assert.NoError(t, err) 463 464 firstNode := jsonDocument.(map[string]interface{}) 465 assert.Len(t, firstNode, 2) 466 467 sliceNode := firstNode["a"].([]interface{}) 468 assert.Len(t, sliceNode, 1) 469 470 changedNode := sliceNode[0].(map[string]interface{}) 471 chNodeVI := changedNode["c"] 472 if assert.IsType(t, 0, chNodeVI) { 473 changedNodeValue := chNodeVI.(int) 474 if assert.Equal(t, 999, changedNodeValue) { 475 assert.Len(t, sliceNode, 1) 476 } 477 } 478 } 479 480 v, err := New("/a/0") 481 if assert.NoError(t, err) { 482 _, err = v.Set(jsonDocument, map[string]interface{}{"b": 3, "c": 8}) 483 if assert.NoError(t, err) { 484 firstNode := jsonDocument.(map[string]interface{}) 485 assert.Len(t, firstNode, 2) 486 487 sliceNode := firstNode["a"].([]interface{}) 488 assert.Len(t, sliceNode, 1) 489 changedNode := sliceNode[0].(map[string]interface{}) 490 assert.Equal(t, 3, changedNode["b"]) 491 assert.Equal(t, 8, changedNode["c"]) 492 } 493 } 494 } 495 496 var structDoc setJsonDoc 497 if assert.NoError(t, json.Unmarshal([]byte(jsonText), &structDoc)) { 498 g, err := New("/a") 499 if assert.NoError(t, err) { 500 _, err = g.Set(&structDoc, []struct { 501 B int `json:"b"` 502 C int `json:"c"` 503 }{{B: 4, C: 7}}) 504 505 if assert.NoError(t, err) { 506 assert.Len(t, structDoc.A, 1) 507 changedNode := structDoc.A[0] 508 assert.Equal(t, 4, changedNode.B) 509 assert.Equal(t, 7, changedNode.C) 510 } 511 } 512 513 v, err := New("/a/0") 514 if assert.NoError(t, err) { 515 _, err = v.Set(structDoc, struct { 516 B int `json:"b"` 517 C int `json:"c"` 518 }{B: 3, C: 8}) 519 520 if assert.NoError(t, err) { 521 assert.Len(t, structDoc.A, 1) 522 changedNode := structDoc.A[0] 523 assert.Equal(t, 3, changedNode.B) 524 assert.Equal(t, 8, changedNode.C) 525 } 526 } 527 528 p, err := New("/a/0/c") 529 if assert.NoError(t, err) { 530 _, err = p.Set(&structDoc, 999) 531 assert.NoError(t, err) 532 if assert.Len(t, structDoc.A, 1) { 533 assert.Equal(t, 999, structDoc.A[0].C) 534 } 535 } 536 } 537 538 var setDoc settableDoc 539 if assert.NoError(t, json.Unmarshal([]byte(jsonText), &setDoc)) { 540 g, err := New("/a") 541 if assert.NoError(t, err) { 542 _, err = g.Set(&setDoc, []settableCollItem{{B: 4, C: 7}}) 543 544 if assert.NoError(t, err) { 545 assert.Len(t, setDoc.Coll.Items, 1) 546 changedNode := setDoc.Coll.Items[0] 547 assert.Equal(t, 4, changedNode.B) 548 assert.Equal(t, 7, changedNode.C) 549 } 550 } 551 552 v, err := New("/a/0") 553 if assert.NoError(t, err) { 554 _, err = v.Set(setDoc, settableCollItem{B: 3, C: 8}) 555 556 if assert.NoError(t, err) { 557 assert.Len(t, setDoc.Coll.Items, 1) 558 changedNode := setDoc.Coll.Items[0] 559 assert.Equal(t, 3, changedNode.B) 560 assert.Equal(t, 8, changedNode.C) 561 } 562 } 563 564 p, err := New("/a/0/c") 565 if assert.NoError(t, err) { 566 _, err = p.Set(setDoc, 999) 567 assert.NoError(t, err) 568 if assert.Len(t, setDoc.Coll.Items, 1) { 569 assert.Equal(t, 999, setDoc.Coll.Items[0].C) 570 } 571 } 572 } 573} 574