bf51655a7b
Signed-off-by: Jacek J. Łakis <jacek.lakis@intel.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
573 lines
13 KiB
Go
573 lines
13 KiB
Go
// 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)
|
|
}
|
|
}
|
|
}
|
|
}
|