| // Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // author sigu-399 |
| // author-github https://github.com/sigu-399 |
| // author-mail sigu.399@gmail.com |
| // |
| // repository-name jsonpointer |
| // repository-desc An implementation of JSON Pointer - Go language |
| // |
| // description Automated tests on package. |
| // |
| // created 03-03-2013 |
| |
| package jsonpointer |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "strconv" |
| "testing" |
| |
| "github.com/stretchr/testify/assert" |
| ) |
| |
| const ( |
| TestDocumentNBItems = 11 |
| TestNodeObjNBItems = 4 |
| TestDocumentString = `{ |
| "foo": ["bar", "baz"], |
| "obj": { "a":1, "b":2, "c":[3,4], "d":[ {"e":9}, {"f":[50,51]} ] }, |
| "": 0, |
| "a/b": 1, |
| "c%d": 2, |
| "e^f": 3, |
| "g|h": 4, |
| "i\\j": 5, |
| "k\"l": 6, |
| " ": 7, |
| "m~n": 8 |
| }` |
| ) |
| |
| var testDocumentJSON interface{} |
| |
| type testStructJSON struct { |
| Foo []string `json:"foo"` |
| Obj struct { |
| A int `json:"a"` |
| B int `json:"b"` |
| C []int `json:"c"` |
| D []struct { |
| E int `json:"e"` |
| F []int `json:"f"` |
| } `json:"d"` |
| } `json:"obj"` |
| } |
| |
| type aliasedMap map[string]interface{} |
| |
| var testStructJSONDoc testStructJSON |
| var testStructJSONPtr *testStructJSON |
| |
| func init() { |
| json.Unmarshal([]byte(TestDocumentString), &testDocumentJSON) |
| json.Unmarshal([]byte(TestDocumentString), &testStructJSONDoc) |
| testStructJSONPtr = &testStructJSONDoc |
| } |
| |
| func TestEscaping(t *testing.T) { |
| |
| ins := []string{`/`, `/`, `/a~1b`, `/a~1b`, `/c%d`, `/e^f`, `/g|h`, `/i\j`, `/k"l`, `/ `, `/m~0n`} |
| outs := []float64{0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8} |
| |
| for i := range ins { |
| p, err := New(ins[i]) |
| if assert.NoError(t, err, "input: %v", ins[i]) { |
| result, _, err := p.Get(testDocumentJSON) |
| if assert.NoError(t, err, "input: %v", ins[i]) { |
| assert.Equal(t, outs[i], result, "input: %v", ins[i]) |
| } |
| } |
| } |
| |
| } |
| |
| func TestFullDocument(t *testing.T) { |
| |
| in := `` |
| |
| p, err := New(in) |
| if err != nil { |
| t.Errorf("New(%v) error %v", in, err.Error()) |
| } |
| |
| result, _, err := p.Get(testDocumentJSON) |
| if err != nil { |
| t.Errorf("Get(%v) error %v", in, err.Error()) |
| } |
| |
| if len(result.(map[string]interface{})) != TestDocumentNBItems { |
| t.Errorf("Get(%v) = %v, expect full document", in, result) |
| } |
| |
| result, _, err = p.get(testDocumentJSON, nil) |
| if err != nil { |
| t.Errorf("Get(%v) error %v", in, err.Error()) |
| } |
| |
| if len(result.(map[string]interface{})) != TestDocumentNBItems { |
| t.Errorf("Get(%v) = %v, expect full document", in, result) |
| } |
| } |
| |
| func TestDecodedTokens(t *testing.T) { |
| p, err := New("/obj/a~1b") |
| assert.NoError(t, err) |
| assert.Equal(t, []string{"obj", "a/b"}, p.DecodedTokens()) |
| } |
| |
| func TestIsEmpty(t *testing.T) { |
| p, err := New("") |
| assert.NoError(t, err) |
| assert.True(t, p.IsEmpty()) |
| p, err = New("/obj") |
| assert.NoError(t, err) |
| assert.False(t, p.IsEmpty()) |
| } |
| |
| func TestGetSingle(t *testing.T) { |
| in := `/obj` |
| |
| _, err := New(in) |
| assert.NoError(t, err) |
| result, _, err := GetForToken(testDocumentJSON, "obj") |
| assert.NoError(t, err) |
| assert.Len(t, result, TestNodeObjNBItems) |
| |
| result, _, err = GetForToken(testStructJSONDoc, "Obj") |
| assert.Error(t, err) |
| assert.Nil(t, result) |
| |
| result, _, err = GetForToken(testStructJSONDoc, "Obj2") |
| assert.Error(t, err) |
| assert.Nil(t, result) |
| } |
| |
| type pointableImpl struct { |
| a string |
| } |
| |
| func (p pointableImpl) JSONLookup(token string) (interface{}, error) { |
| if token == "some" { |
| return p.a, nil |
| } |
| return nil, fmt.Errorf("object has no field %q", token) |
| } |
| |
| func TestPointableInterface(t *testing.T) { |
| p := &pointableImpl{"hello"} |
| |
| result, _, err := GetForToken(p, "some") |
| assert.NoError(t, err) |
| assert.Equal(t, p.a, result) |
| |
| result, _, err = GetForToken(p, "something") |
| assert.Error(t, err) |
| assert.Nil(t, result) |
| } |
| |
| func TestGetNode(t *testing.T) { |
| |
| in := `/obj` |
| |
| p, err := New(in) |
| assert.NoError(t, err) |
| result, _, err := p.Get(testDocumentJSON) |
| assert.NoError(t, err) |
| assert.Len(t, result, TestNodeObjNBItems) |
| |
| result, _, err = p.Get(aliasedMap(testDocumentJSON.(map[string]interface{}))) |
| assert.NoError(t, err) |
| assert.Len(t, result, TestNodeObjNBItems) |
| |
| result, _, err = p.Get(testStructJSONDoc) |
| assert.NoError(t, err) |
| assert.Equal(t, testStructJSONDoc.Obj, result) |
| |
| result, _, err = p.Get(testStructJSONPtr) |
| assert.NoError(t, err) |
| assert.Equal(t, testStructJSONDoc.Obj, result) |
| } |
| |
| func TestArray(t *testing.T) { |
| |
| ins := []string{`/foo/0`, `/foo/0`, `/foo/1`} |
| outs := []string{"bar", "bar", "baz"} |
| |
| for i := range ins { |
| p, err := New(ins[i]) |
| assert.NoError(t, err) |
| |
| result, _, err := p.Get(testStructJSONDoc) |
| assert.NoError(t, err) |
| assert.Equal(t, outs[i], result) |
| |
| result, _, err = p.Get(testStructJSONPtr) |
| assert.NoError(t, err) |
| assert.Equal(t, outs[i], result) |
| |
| result, _, err = p.Get(testDocumentJSON) |
| assert.NoError(t, err) |
| assert.Equal(t, outs[i], result) |
| } |
| } |
| |
| func TestOtherThings(t *testing.T) { |
| _, err := New("abc") |
| assert.Error(t, err) |
| |
| p, err := New("") |
| assert.NoError(t, err) |
| assert.Equal(t, "", p.String()) |
| |
| p, err = New("/obj/a") |
| assert.Equal(t, "/obj/a", p.String()) |
| |
| s := Escape("m~n") |
| assert.Equal(t, "m~0n", s) |
| s = Escape("m/n") |
| assert.Equal(t, "m~1n", s) |
| |
| p, err = New("/foo/3") |
| assert.NoError(t, err) |
| _, _, err = p.Get(testDocumentJSON) |
| assert.Error(t, err) |
| |
| p, err = New("/foo/a") |
| assert.NoError(t, err) |
| _, _, err = p.Get(testDocumentJSON) |
| assert.Error(t, err) |
| |
| p, err = New("/notthere") |
| assert.NoError(t, err) |
| _, _, err = p.Get(testDocumentJSON) |
| assert.Error(t, err) |
| |
| p, err = New("/invalid") |
| assert.NoError(t, err) |
| _, _, err = p.Get(1234) |
| assert.Error(t, err) |
| |
| p, err = New("/foo/1") |
| assert.NoError(t, err) |
| expected := "hello" |
| bbb := testDocumentJSON.(map[string]interface{})["foo"] |
| bbb.([]interface{})[1] = "hello" |
| |
| v, _, err := p.Get(testDocumentJSON) |
| assert.NoError(t, err) |
| assert.Equal(t, expected, v) |
| |
| esc := Escape("a/") |
| assert.Equal(t, "a~1", esc) |
| unesc := Unescape(esc) |
| assert.Equal(t, "a/", unesc) |
| |
| unesc = Unescape("~01") |
| assert.Equal(t, "~1", unesc) |
| assert.Equal(t, "~0~1", Escape("~/")) |
| assert.Equal(t, "~/", Unescape("~0~1")) |
| } |
| |
| func TestObject(t *testing.T) { |
| |
| ins := []string{`/obj/a`, `/obj/b`, `/obj/c/0`, `/obj/c/1`, `/obj/c/1`, `/obj/d/1/f/0`} |
| outs := []float64{1, 2, 3, 4, 4, 50} |
| |
| for i := range ins { |
| |
| p, err := New(ins[i]) |
| assert.NoError(t, err) |
| |
| result, _, err := p.Get(testDocumentJSON) |
| assert.NoError(t, err) |
| assert.Equal(t, outs[i], result) |
| |
| result, _, err = p.Get(testStructJSONDoc) |
| assert.NoError(t, err) |
| assert.EqualValues(t, outs[i], result) |
| |
| result, _, err = p.Get(testStructJSONPtr) |
| assert.NoError(t, err) |
| assert.EqualValues(t, outs[i], result) |
| } |
| } |
| |
| type setJsonDocEle struct { |
| B int `json:"b"` |
| C int `json:"c"` |
| } |
| type setJsonDoc struct { |
| A []struct { |
| B int `json:"b"` |
| C int `json:"c"` |
| } `json:"a"` |
| D int `json:"d"` |
| } |
| |
| type settableDoc struct { |
| Coll settableColl |
| Int settableInt |
| } |
| |
| func (s settableDoc) MarshalJSON() ([]byte, error) { |
| var res struct { |
| A settableColl `json:"a"` |
| D settableInt `json:"d"` |
| } |
| res.A = s.Coll |
| res.D = s.Int |
| return json.Marshal(res) |
| } |
| func (s *settableDoc) UnmarshalJSON(data []byte) error { |
| var res struct { |
| A settableColl `json:"a"` |
| D settableInt `json:"d"` |
| } |
| |
| if err := json.Unmarshal(data, &res); err != nil { |
| return err |
| } |
| s.Coll = res.A |
| s.Int = res.D |
| return nil |
| } |
| |
| // JSONLookup implements an interface to customize json pointer lookup |
| func (s settableDoc) JSONLookup(token string) (interface{}, error) { |
| switch token { |
| case "a": |
| return &s.Coll, nil |
| case "d": |
| return &s.Int, nil |
| default: |
| return nil, fmt.Errorf("%s is not a known field", token) |
| } |
| } |
| |
| // JSONLookup implements an interface to customize json pointer lookup |
| func (s *settableDoc) JSONSet(token string, data interface{}) error { |
| switch token { |
| case "a": |
| switch dt := data.(type) { |
| case settableColl: |
| s.Coll = dt |
| return nil |
| case *settableColl: |
| if dt != nil { |
| s.Coll = *dt |
| } else { |
| s.Coll = settableColl{} |
| } |
| return nil |
| case []settableCollItem: |
| s.Coll.Items = dt |
| return nil |
| } |
| case "d": |
| switch dt := data.(type) { |
| case settableInt: |
| s.Int = dt |
| return nil |
| case int: |
| s.Int.Value = dt |
| return nil |
| case int8: |
| s.Int.Value = int(dt) |
| return nil |
| case int16: |
| s.Int.Value = int(dt) |
| return nil |
| case int32: |
| s.Int.Value = int(dt) |
| return nil |
| case int64: |
| s.Int.Value = int(dt) |
| return nil |
| default: |
| return fmt.Errorf("invalid type %T for %s", data, token) |
| } |
| } |
| return fmt.Errorf("%s is not a known field", token) |
| } |
| |
| type settableColl struct { |
| Items []settableCollItem |
| } |
| |
| func (s settableColl) MarshalJSON() ([]byte, error) { |
| return json.Marshal(s.Items) |
| } |
| func (s *settableColl) UnmarshalJSON(data []byte) error { |
| return json.Unmarshal(data, &s.Items) |
| } |
| |
| // JSONLookup implements an interface to customize json pointer lookup |
| func (s settableColl) JSONLookup(token string) (interface{}, error) { |
| if tok, err := strconv.Atoi(token); err == nil { |
| return &s.Items[tok], nil |
| } |
| return nil, fmt.Errorf("%s is not a valid index", token) |
| } |
| |
| // JSONLookup implements an interface to customize json pointer lookup |
| func (s *settableColl) JSONSet(token string, data interface{}) error { |
| if _, err := strconv.Atoi(token); err == nil { |
| _, err := SetForToken(s.Items, token, data) |
| return err |
| } |
| return fmt.Errorf("%s is not a valid index", token) |
| } |
| |
| type settableCollItem struct { |
| B int `json:"b"` |
| C int `json:"c"` |
| } |
| |
| type settableInt struct { |
| Value int |
| } |
| |
| func (s settableInt) MarshalJSON() ([]byte, error) { |
| return json.Marshal(s.Value) |
| } |
| func (s *settableInt) UnmarshalJSON(data []byte) error { |
| return json.Unmarshal(data, &s.Value) |
| } |
| |
| func TestSetNode(t *testing.T) { |
| |
| jsonText := `{"a":[{"b": 1, "c": 2}], "d": 3}` |
| |
| var jsonDocument interface{} |
| if assert.NoError(t, json.Unmarshal([]byte(jsonText), &jsonDocument)) { |
| in := "/a/0/c" |
| p, err := New(in) |
| if assert.NoError(t, err) { |
| |
| _, err = p.Set(jsonDocument, 999) |
| assert.NoError(t, err) |
| |
| firstNode := jsonDocument.(map[string]interface{}) |
| assert.Len(t, firstNode, 2) |
| |
| sliceNode := firstNode["a"].([]interface{}) |
| assert.Len(t, sliceNode, 1) |
| |
| changedNode := sliceNode[0].(map[string]interface{}) |
| chNodeVI := changedNode["c"] |
| if assert.IsType(t, 0, chNodeVI) { |
| changedNodeValue := chNodeVI.(int) |
| if assert.Equal(t, 999, changedNodeValue) { |
| assert.Len(t, sliceNode, 1) |
| } |
| } |
| } |
| |
| v, err := New("/a/0") |
| if assert.NoError(t, err) { |
| _, err = v.Set(jsonDocument, map[string]interface{}{"b": 3, "c": 8}) |
| if assert.NoError(t, err) { |
| firstNode := jsonDocument.(map[string]interface{}) |
| assert.Len(t, firstNode, 2) |
| |
| sliceNode := firstNode["a"].([]interface{}) |
| assert.Len(t, sliceNode, 1) |
| changedNode := sliceNode[0].(map[string]interface{}) |
| assert.Equal(t, 3, changedNode["b"]) |
| assert.Equal(t, 8, changedNode["c"]) |
| } |
| } |
| } |
| |
| var structDoc setJsonDoc |
| if assert.NoError(t, json.Unmarshal([]byte(jsonText), &structDoc)) { |
| g, err := New("/a") |
| if assert.NoError(t, err) { |
| _, err = g.Set(&structDoc, []struct { |
| B int `json:"b"` |
| C int `json:"c"` |
| }{{B: 4, C: 7}}) |
| |
| if assert.NoError(t, err) { |
| assert.Len(t, structDoc.A, 1) |
| changedNode := structDoc.A[0] |
| assert.Equal(t, 4, changedNode.B) |
| assert.Equal(t, 7, changedNode.C) |
| } |
| } |
| |
| v, err := New("/a/0") |
| if assert.NoError(t, err) { |
| _, err = v.Set(structDoc, struct { |
| B int `json:"b"` |
| C int `json:"c"` |
| }{B: 3, C: 8}) |
| |
| if assert.NoError(t, err) { |
| assert.Len(t, structDoc.A, 1) |
| changedNode := structDoc.A[0] |
| assert.Equal(t, 3, changedNode.B) |
| assert.Equal(t, 8, changedNode.C) |
| } |
| } |
| |
| p, err := New("/a/0/c") |
| if assert.NoError(t, err) { |
| _, err = p.Set(&structDoc, 999) |
| assert.NoError(t, err) |
| if assert.Len(t, structDoc.A, 1) { |
| assert.Equal(t, 999, structDoc.A[0].C) |
| } |
| } |
| } |
| |
| var setDoc settableDoc |
| if assert.NoError(t, json.Unmarshal([]byte(jsonText), &setDoc)) { |
| g, err := New("/a") |
| if assert.NoError(t, err) { |
| _, err = g.Set(&setDoc, []settableCollItem{{B: 4, C: 7}}) |
| |
| if assert.NoError(t, err) { |
| assert.Len(t, setDoc.Coll.Items, 1) |
| changedNode := setDoc.Coll.Items[0] |
| assert.Equal(t, 4, changedNode.B) |
| assert.Equal(t, 7, changedNode.C) |
| } |
| } |
| |
| v, err := New("/a/0") |
| if assert.NoError(t, err) { |
| _, err = v.Set(setDoc, settableCollItem{B: 3, C: 8}) |
| |
| if assert.NoError(t, err) { |
| assert.Len(t, setDoc.Coll.Items, 1) |
| changedNode := setDoc.Coll.Items[0] |
| assert.Equal(t, 3, changedNode.B) |
| assert.Equal(t, 8, changedNode.C) |
| } |
| } |
| |
| p, err := New("/a/0/c") |
| if assert.NoError(t, err) { |
| _, err = p.Set(setDoc, 999) |
| assert.NoError(t, err) |
| if assert.Len(t, setDoc.Coll.Items, 1) { |
| assert.Equal(t, 999, setDoc.Coll.Items[0].C) |
| } |
| } |
| } |
| } |