Skip to content

Commit

Permalink
test: add examples from the RFC Appendix A
Browse files Browse the repository at this point in the history
  • Loading branch information
wI2L committed Nov 23, 2023
1 parent 076a95c commit adcd818
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 9 deletions.
13 changes: 7 additions & 6 deletions apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,19 @@ func (p Patch) apply(src []byte, valid bool) ([]byte, error) {
if err != nil {
return nil, err
}
tgt, err = add(tgt, dp, op.Value)
if err != nil {
break // bail out to interpret error
}
// Finally, if the operation was a move, the
// source value must be deleted.
// If the operation is a move, delete the
// source value before adding it at its new
// position, to preserve array index position.
if op.Type == OperationMove {
tgt, err = sjson.DeleteBytes(tgt, fp)
if err != nil {
break
}
}
tgt, err = add(tgt, dp, op.Value)
if err != nil {
break // bail out to interpret error
}
case OperationTest:
r := gjson.GetBytes(tgt, dp)
if !r.Exists() {
Expand Down
26 changes: 23 additions & 3 deletions differ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type testcase struct {

type patchGetter func(tc *testcase) Patch

func TestRFCCases(t *testing.T) { runCasesFromFile(t, "testdata/tests/rfc.json", Factorize(), LCS()) } // https://datatracker.ietf.org/doc/html/rfc6902#appendix-A
func TestArrayCases(t *testing.T) { runCasesFromFile(t, "testdata/tests/array.json") }
func TestObjectCases(t *testing.T) { runCasesFromFile(t, "testdata/tests/object.json") }
func TestRootCases(t *testing.T) { runCasesFromFile(t, "testdata/tests/root.json") }
Expand Down Expand Up @@ -183,10 +184,15 @@ func runTestCase(t *testing.T, tc testcase, pc patchGetter, opts ...Option) {
if err != nil {
t.Errorf("failed to apply patch: %s", err)
}
if !bytes.Equal(b, mustMarshal(tc.After)) {
// Re-marshal the patched document to ensure it follows
// the Golang JSON convention of ordering map keys, and
// can be compared to the target document.
before, after := unmarshalMarshal(t, b), mustMarshal(tc.After)

if !bytes.Equal(before, after) {
t.Errorf("patch does not produce the expected changes")
t.Logf("got: %s", string(b))
t.Logf("want: %s", string(mustMarshal(tc.After)))
t.Logf("got: %s", string(before))
t.Logf("want: %s", string(after))
}
}

Expand Down Expand Up @@ -397,3 +403,17 @@ func Benchmark_sortStrings(b *testing.B) {
})
}
}

func unmarshalMarshal(t *testing.T, b []byte) []byte {
t.Helper()

var i interface{}
if err := json.Unmarshal(b, &i); err != nil {
t.Error(err)
}
b2, err := json.Marshal(i)
if err != nil {
t.Error(err)
}
return b2
}
140 changes: 140 additions & 0 deletions testdata/tests/rfc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
[{
"name": "a.1 adding an object member",
"before": {
"foo": "bar"
},
"after": {
"baz": "qux",
"foo": "bar"
},
"patch": [
{ "op": "add", "path": "/baz", "value": "qux" }
]
}, {
"name": "a.2 adding an array element",
"before": {
"foo": [ "bar", "baz" ]
},
"after": {
"foo": [ "bar", "qux", "baz" ]
},
"patch": [
{ "op": "add", "path": "/foo/1", "value": "qux" }
]
}, {
"name": "a.3 removing an object member",
"before": {
"baz": "qux",
"foo": "bar"
},
"after": {
"foo": "bar"
},
"patch": [
{ "op": "remove", "path": "/baz" }
]
}, {
"name": "a.4 removing an array element",
"before": {
"baz": "qux",
"foo": "bar"
},
"after": {
"baz": "boo",
"foo": "bar"
},
"patch": [
{ "op": "replace", "path": "/baz", "value": "boo" }
]
}, {
"name": "a.5 replacing a value",
"before": {
"baz": "qux",
"foo": "bar"
},
"after": {
"baz": "boo",
"foo": "bar"
},
"patch": [
{ "op": "replace", "path": "/baz", "value": "boo" }
]
}, {
"name": "a.6 moving a value",
"before": {
"foo": {
"bar": "baz",
"waldo": "fred"
},
"qux": {
"corge": "grault"
}
},
"after": {
"foo": {
"bar": "baz"
},
"qux": {
"corge": "grault",
"thud": "fred"
}
},
"patch": [
{ "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }
]
}, {
"name": "a.7 moving an array element",
"before": {
"foo": [ "all", "grass", "cows", "eat" ]
},
"after": {
"foo": [ "all", "cows", "eat", "grass" ]
},
"patch": [
{ "op": "move", "from": "/foo/1", "path": "/foo/3" }
]
}, {
"name": "a.10 adding a nested member object",
"before": {
"foo": "bar"
},
"after": {
"foo": "bar",
"child": {
"grandchild": {
}
}
},
"patch": [
{ "op": "add", "path": "/child", "value": { "grandchild": { } } }
]
}, {
"name": "a.16 adding an array value",
"before": {
"foo": ["bar"]
},
"after": {
"foo": ["bar", ["abc", "def"]]
},
"patch": [
{ "op": "add", "path": "/foo/1", "value": ["abc", "def"] }
]
}, {
"name": "numeric equivalence",
"before": {
"foo": 1
},
"after": {
"foo": 1.0
},
"patch": []
}, {
"name": "numeric equivalence",
"before": {
"foo": 1
},
"after": {
"foo": 1e0
},
"patch": []
}]

0 comments on commit adcd818

Please sign in to comment.