@wizard9/json-patch-apply
Version:
A library for describing, calculating, and applying patches to Javascript Objects.
363 lines (359 loc) • 20.8 kB
text/typescript
import {expect} from "chai";
import {Patch} from "../index";
/**
* DO NOT CHANGE - changes will be lost!
* dynamically generated using generate_conformance.ts script see README.md file
* for details on how to re-generate or change this file.
*/
describe("conformance", () => {
function run_test(doc: any, patch: any[], error: any, expected: any) {
try {
let result = Patch.apply(doc, ...patch);
if(error) {
throw new Error("Missing error: " + error);
} else {
expect(result).eql(expected);
}
} catch(e) {
if(error) {
expect(!!e.message, error).be.true;
} else {
expect("Unexpected error: " + e.message + ", missing expected: " + JSON.stringify(expected)).eql('to pass');
}
}
}
describe("tests.json - main tests", () => {
it("empty list, empty docs", () => {
run_test({}, [], undefined, {});
});
it("empty patch list", () => {
run_test({"foo":1}, [], undefined, {"foo":1});
});
it("rearrangements OK?", () => {
run_test({"foo":1,"bar":2}, [], undefined, {"bar":2,"foo":1});
});
it("rearrangements OK? How about one level down ... array", () => {
run_test([{"foo":1,"bar":2}], [], undefined, [{"bar":2,"foo":1}]);
});
it("rearrangements OK? How about one level down...", () => {
run_test({"foo":{"foo":1,"bar":2}}, [], undefined, {"foo":{"bar":2,"foo":1}});
});
it("add replaces any existing field", () => {
run_test({"foo":null}, [{"op":"add","path":"/foo","value":1}], undefined, {"foo":1});
});
it("toplevel array", () => {
run_test([], [{"op":"add","path":"/0","value":"foo"}], undefined, ["foo"]);
});
it("toplevel array, no change", () => {
run_test(["foo"], [], undefined, ["foo"]);
});
it("toplevel object, numeric string", () => {
run_test({}, [{"op":"add","path":"/foo","value":"1"}], undefined, {"foo":"1"});
});
it("toplevel object, integer", () => {
run_test({}, [{"op":"add","path":"/foo","value":1}], undefined, {"foo":1});
});
xit("Toplevel scalar values OK?", () => {
run_test("foo", [{"op":"replace","path":"","value":"bar"}], undefined, "bar");
});
it("replace object document with array document?", () => {
run_test({}, [{"op":"add","path":"","value":[]}], undefined, []);
});
it("replace array document with object document?", () => {
run_test([], [{"op":"add","path":"","value":{}}], undefined, {});
});
it("append to root array document?", () => {
run_test([], [{"op":"add","path":"/-","value":"hi"}], undefined, ["hi"]);
});
it("Add, / target", () => {
run_test({}, [{"op":"add","path":"/","value":1}], undefined, {"":1});
});
it("Add, /foo/ deep target (trailing slash)", () => {
run_test({"foo":{}}, [{"op":"add","path":"/foo/","value":1}], undefined, {"foo":{"":1}});
});
it("Add composite value at top level", () => {
run_test({"foo":1}, [{"op":"add","path":"/bar","value":[1,2]}], undefined, {"foo":1,"bar":[1,2]});
});
it("Add into composite value", () => {
run_test({"foo":1,"baz":[{"qux":"hello"}]}, [{"op":"add","path":"/baz/0/foo","value":"world"}], undefined, {"foo":1,"baz":[{"qux":"hello","foo":"world"}]});
});
it("conformance #18", () => {
run_test({"bar":[1,2]}, [{"op":"add","path":"/bar/8","value":"5"}], "Out of bounds (upper)", undefined);
});
it("conformance #19", () => {
run_test({"bar":[1,2]}, [{"op":"add","path":"/bar/-1","value":"5"}], "Out of bounds (lower)", undefined);
});
it("conformance #20", () => {
run_test({"foo":1}, [{"op":"add","path":"/bar","value":true}], undefined, {"foo":1,"bar":true});
});
it("conformance #21", () => {
run_test({"foo":1}, [{"op":"add","path":"/bar","value":false}], undefined, {"foo":1,"bar":false});
});
it("conformance #22", () => {
run_test({"foo":1}, [{"op":"add","path":"/bar","value":null}], undefined, {"foo":1,"bar":null});
});
it("0 can be an array index or object element name", () => {
run_test({"foo":1}, [{"op":"add","path":"/0","value":"bar"}], undefined, {"0":"bar","foo":1});
});
it("conformance #24", () => {
run_test(["foo"], [{"op":"add","path":"/1","value":"bar"}], undefined, ["foo","bar"]);
});
it("conformance #25", () => {
run_test(["foo","sil"], [{"op":"add","path":"/1","value":"bar"}], undefined, ["foo","bar","sil"]);
});
it("conformance #26", () => {
run_test(["foo","sil"], [{"op":"add","path":"/0","value":"bar"}], undefined, ["bar","foo","sil"]);
});
it("push item to array via last index + 1", () => {
run_test(["foo","sil"], [{"op":"add","path":"/2","value":"bar"}], undefined, ["foo","sil","bar"]);
});
it("add item to array at index > length should fail", () => {
run_test(["foo","sil"], [{"op":"add","path":"/3","value":"bar"}], "index is greater than number of items in array", undefined);
});
it("test against implementation-specific numeric parsing", () => {
run_test({"1e0":"foo"}, [{"op":"test","path":"/1e0","value":"foo"}], undefined, {"1e0":"foo"});
});
it("test with bad number should fail", () => {
run_test(["foo","bar"], [{"op":"test","path":"/1e0","value":"bar"}], "test op shouldn't get array element 1", undefined);
});
it("conformance #31", () => {
run_test(["foo","sil"], [{"op":"add","path":"/bar","value":42}], "Object operation on array target", undefined);
});
it("value in array add not flattened", () => {
run_test(["foo","sil"], [{"op":"add","path":"/1","value":["bar","baz"]}], undefined, ["foo",["bar","baz"],"sil"]);
});
it("conformance #33", () => {
run_test({"foo":1,"bar":[1,2,3,4]}, [{"op":"remove","path":"/bar"}], undefined, {"foo":1});
});
it("conformance #34", () => {
run_test({"foo":1,"baz":[{"qux":"hello"}]}, [{"op":"remove","path":"/baz/0/qux"}], undefined, {"foo":1,"baz":[{}]});
});
it("conformance #35", () => {
run_test({"foo":1,"baz":[{"qux":"hello"}]}, [{"op":"replace","path":"/foo","value":[1,2,3,4]}], undefined, {"foo":[1,2,3,4],"baz":[{"qux":"hello"}]});
});
it("conformance #36", () => {
run_test({"foo":[1,2,3,4],"baz":[{"qux":"hello"}]}, [{"op":"replace","path":"/baz/0/qux","value":"world"}], undefined, {"foo":[1,2,3,4],"baz":[{"qux":"world"}]});
});
it("conformance #37", () => {
run_test(["foo"], [{"op":"replace","path":"/0","value":"bar"}], undefined, ["bar"]);
});
it("conformance #38", () => {
run_test([""], [{"op":"replace","path":"/0","value":0}], undefined, [0]);
});
it("conformance #39", () => {
run_test([""], [{"op":"replace","path":"/0","value":true}], undefined, [true]);
});
it("conformance #40", () => {
run_test([""], [{"op":"replace","path":"/0","value":false}], undefined, [false]);
});
it("conformance #41", () => {
run_test([""], [{"op":"replace","path":"/0","value":null}], undefined, [null]);
});
it("value in array replace not flattened", () => {
run_test(["foo","sil"], [{"op":"replace","path":"/1","value":["bar","baz"]}], undefined, ["foo",["bar","baz"]]);
});
it("replace whole document", () => {
run_test({"foo":"bar"}, [{"op":"replace","path":"","value":{"baz":"qux"}}], undefined, {"baz":"qux"});
});
it("test replace with missing parent key should fail", () => {
run_test({"bar":"baz"}, [{"op":"replace","path":"/foo/bar","value":false}], "replace op should fail with missing parent key", undefined);
});
it("spurious patch properties", () => {
run_test({"foo":1}, [{"op":"test","path":"/foo","value":1,"spurious":1}], undefined, {"foo":1});
});
it("null value should be valid obj property", () => {
run_test({"foo":null}, [{"op":"test","path":"/foo","value":null}], undefined, {"foo":null});
});
it("null value should be valid obj property to be replaced with something truthy", () => {
run_test({"foo":null}, [{"op":"replace","path":"/foo","value":"truthy"}], undefined, {"foo":"truthy"});
});
it("null value should be valid obj property to be moved", () => {
run_test({"foo":null}, [{"op":"move","from":"/foo","path":"/bar"}], undefined, {"bar":null});
});
it("null value should be valid obj property to be copied", () => {
run_test({"foo":null}, [{"op":"copy","from":"/foo","path":"/bar"}], undefined, {"foo":null,"bar":null});
});
it("null value should be valid obj property to be removed", () => {
run_test({"foo":null}, [{"op":"remove","path":"/foo"}], undefined, {});
});
it("null value should still be valid obj property replace other value", () => {
run_test({"foo":"bar"}, [{"op":"replace","path":"/foo","value":null}], undefined, {"foo":null});
});
it("test should pass despite rearrangement", () => {
run_test({"foo":{"foo":1,"bar":2}}, [{"op":"test","path":"/foo","value":{"bar":2,"foo":1}}], undefined, {"foo":{"foo":1,"bar":2}});
});
it("test should pass despite (nested) rearrangement", () => {
run_test({"foo":[{"foo":1,"bar":2}]}, [{"op":"test","path":"/foo","value":[{"bar":2,"foo":1}]}], undefined, {"foo":[{"foo":1,"bar":2}]});
});
it("test should pass - no error", () => {
run_test({"foo":{"bar":[1,2,5,4]}}, [{"op":"test","path":"/foo","value":{"bar":[1,2,5,4]}}], undefined, {"foo":{"bar":[1,2,5,4]}});
});
it("conformance #55", () => {
run_test({"foo":{"bar":[1,2,5,4]}}, [{"op":"test","path":"/foo","value":[1,2]}], "test op should fail", undefined);
});
xit("Whole document", () => {
run_test({"foo":1}, [{"op":"test","path":"","value":{"foo":1}}], undefined, undefined);
});
it("Empty-string element", () => {
run_test({"":1}, [{"op":"test","path":"/","value":1}], undefined, {"":1});
});
it("conformance #58", () => {
run_test({"foo":["bar","baz"],"":0,"a/b":1,"c%d":2,"e^f":3,"g|h":4,"i\\j":5,"k\"l":6," ":7,"m~n":8}, [{"op":"test","path":"/foo","value":["bar","baz"]},{"op":"test","path":"/foo/0","value":"bar"},{"op":"test","path":"/","value":0},{"op":"test","path":"/a~1b","value":1},{"op":"test","path":"/c%d","value":2},{"op":"test","path":"/e^f","value":3},{"op":"test","path":"/g|h","value":4},{"op":"test","path":"/i\\j","value":5},{"op":"test","path":"/k\"l","value":6},{"op":"test","path":"/ ","value":7},{"op":"test","path":"/m~0n","value":8}], undefined, {"":0," ":7,"a/b":1,"c%d":2,"e^f":3,"foo":["bar","baz"],"g|h":4,"i\\j":5,"k\"l":6,"m~n":8});
});
it("Move to same location has no effect", () => {
run_test({"foo":1}, [{"op":"move","from":"/foo","path":"/foo"}], undefined, {"foo":1});
});
it("conformance #60", () => {
run_test({"foo":1,"baz":[{"qux":"hello"}]}, [{"op":"move","from":"/foo","path":"/bar"}], undefined, {"baz":[{"qux":"hello"}],"bar":1});
});
it("conformance #61", () => {
run_test({"baz":[{"qux":"hello"}],"bar":1}, [{"op":"move","from":"/baz/0/qux","path":"/baz/1"}], undefined, {"baz":[{},"hello"],"bar":1});
});
it("conformance #62", () => {
run_test({"baz":[{"qux":"hello"}],"bar":1}, [{"op":"copy","from":"/baz/0","path":"/boo"}], undefined, {"baz":[{"qux":"hello"}],"bar":1,"boo":{"qux":"hello"}});
});
it("replacing the root of the document is possible with add", () => {
run_test({"foo":"bar"}, [{"op":"add","path":"","value":{"baz":"qux"}}], undefined, {"baz":"qux"});
});
it("Adding to \"/-\" adds to the end of the array", () => {
run_test([1,2], [{"op":"add","path":"/-","value":{"foo":["bar","baz"]}}], undefined, [1,2,{"foo":["bar","baz"]}]);
});
it("Adding to \"/-\" adds to the end of the array, even n levels down", () => {
run_test([1,2,[3,[4,5]]], [{"op":"add","path":"/2/1/-","value":{"foo":["bar","baz"]}}], undefined, [1,2,[3,[4,5,{"foo":["bar","baz"]}]]]);
});
it("test remove with bad number should fail", () => {
run_test({"foo":1,"baz":[{"qux":"hello"}]}, [{"op":"remove","path":"/baz/1e0/qux"}], "remove op shouldn't remove from array with bad number", undefined);
});
it("test remove on array", () => {
run_test([1,2,3,4], [{"op":"remove","path":"/0"}], undefined, [2,3,4]);
});
it("test repeated removes", () => {
run_test([1,2,3,4], [{"op":"remove","path":"/1"},{"op":"remove","path":"/2"}], undefined, [1,3]);
});
it("test remove with bad index should fail", () => {
run_test([1,2,3,4], [{"op":"remove","path":"/1e0"}], "remove op shouldn't remove from array with bad number", undefined);
});
it("test replace with bad number should fail", () => {
run_test([""], [{"op":"replace","path":"/1e0","value":false}], "replace op shouldn't replace in array with bad number", undefined);
});
it("test copy with bad number should fail", () => {
run_test({"baz":[1,2,3],"bar":1}, [{"op":"copy","from":"/baz/1e0","path":"/boo"}], "copy op shouldn't work with bad number", undefined);
});
it("test move with bad number should fail", () => {
run_test({"foo":1,"baz":[1,2,3,4]}, [{"op":"move","from":"/baz/1e0","path":"/foo"}], "move op shouldn't work with bad number", undefined);
});
it("test add with bad number should fail", () => {
run_test(["foo","sil"], [{"op":"add","path":"/1e0","value":"bar"}], "add op shouldn't add to array with bad number", undefined);
});
it("missing 'path' parameter", () => {
run_test({}, [{"op":"add","value":"bar"}], "missing 'path' parameter", undefined);
});
it("'path' parameter with null value", () => {
run_test({}, [{"op":"add","path":null,"value":"bar"}], "null is not valid value for 'path'", undefined);
});
it("invalid JSON Pointer token", () => {
run_test({}, [{"op":"add","path":"foo","value":"bar"}], "JSON Pointer should start with a slash", undefined);
});
it("missing 'value' parameter to add", () => {
run_test([1], [{"op":"add","path":"/-"}], "missing 'value' parameter", undefined);
});
it("missing 'value' parameter to replace", () => {
run_test([1], [{"op":"replace","path":"/0"}], "missing 'value' parameter", undefined);
});
it("missing 'value' parameter to test", () => {
run_test([null], [{"op":"test","path":"/0"}], "missing 'value' parameter", undefined);
});
it("missing value parameter to test - where undef is falsy", () => {
run_test([false], [{"op":"test","path":"/0"}], "missing 'value' parameter", undefined);
});
it("missing from parameter to copy", () => {
run_test([1], [{"op":"copy","path":"/-"}], "missing 'from' parameter", undefined);
});
it("missing from location to copy", () => {
run_test({"foo":1}, [{"op":"copy","from":"/bar","path":"/foo"}], "missing 'from' location", undefined);
});
it("missing from parameter to move", () => {
run_test({"foo":1}, [{"op":"move","path":""}], "missing 'from' parameter", undefined);
});
it("missing from location to move", () => {
run_test({"foo":1}, [{"op":"move","from":"/bar","path":"/foo"}], "missing 'from' location", undefined);
});
xit("duplicate ops", () => {
run_test({"foo":"bar"}, [{"op":"move","path":"/baz","value":"qux","from":"/foo"}], "patch has two 'op' members", undefined);
});
it("unrecognized op should fail", () => {
run_test({"foo":1}, [{"op":"spam","path":"/foo","value":1}], "Unrecognized op 'spam'", undefined);
});
it("test with bad array number that has leading zeros", () => {
run_test(["foo","bar"], [{"op":"test","path":"/00","value":"foo"}], "test op should reject the array value, it has leading zeros", undefined);
});
it("test with bad array number that has leading zeros", () => {
run_test(["foo","bar"], [{"op":"test","path":"/01","value":"bar"}], "test op should reject the array value, it has leading zeros", undefined);
});
it("Removing nonexistent field", () => {
run_test({"foo":"bar"}, [{"op":"remove","path":"/baz"}], "removing a nonexistent field should fail", undefined);
});
it("Removing deep nonexistent path", () => {
run_test({"foo":"bar"}, [{"op":"remove","path":"/missing1/missing2"}], "removing a nonexistent field should fail", undefined);
});
it("Removing nonexistent index", () => {
run_test(["foo","bar"], [{"op":"remove","path":"/2"}], "removing a nonexistent index should fail", undefined);
});
it("Patch with different capitalisation than doc", () => {
run_test({"foo":"bar"}, [{"op":"add","path":"/FOO","value":"BAR"}], undefined, {"foo":"bar","FOO":"BAR"});
});
});
describe("spec_tests.json - RFC6902 spec", () => {
it("4.1. add with missing object", () => {
run_test({"q":{"bar":2}}, [{"op":"add","path":"/a/b","value":1}], "path /a does not exist -- missing objects are not created recursively", undefined);
});
it("A.1. Adding an Object Member", () => {
run_test({"foo":"bar"}, [{"op":"add","path":"/baz","value":"qux"}], undefined, {"baz":"qux","foo":"bar"});
});
it("A.2. Adding an Array Element", () => {
run_test({"foo":["bar","baz"]}, [{"op":"add","path":"/foo/1","value":"qux"}], undefined, {"foo":["bar","qux","baz"]});
});
it("A.3. Removing an Object Member", () => {
run_test({"baz":"qux","foo":"bar"}, [{"op":"remove","path":"/baz"}], undefined, {"foo":"bar"});
});
it("A.4. Removing an Array Element", () => {
run_test({"foo":["bar","qux","baz"]}, [{"op":"remove","path":"/foo/1"}], undefined, {"foo":["bar","baz"]});
});
it("A.5. Replacing a Value", () => {
run_test({"baz":"qux","foo":"bar"}, [{"op":"replace","path":"/baz","value":"boo"}], undefined, {"baz":"boo","foo":"bar"});
});
it("A.6. Moving a Value", () => {
run_test({"foo":{"bar":"baz","waldo":"fred"},"qux":{"corge":"grault"}}, [{"op":"move","from":"/foo/waldo","path":"/qux/thud"}], undefined, {"foo":{"bar":"baz"},"qux":{"corge":"grault","thud":"fred"}});
});
it("A.7. Moving an Array Element", () => {
run_test({"foo":["all","grass","cows","eat"]}, [{"op":"move","from":"/foo/1","path":"/foo/3"}], undefined, {"foo":["all","cows","eat","grass"]});
});
it("A.8. Testing a Value: Success", () => {
run_test({"baz":"qux","foo":["a",2,"c"]}, [{"op":"test","path":"/baz","value":"qux"},{"op":"test","path":"/foo/1","value":2}], undefined, {"baz":"qux","foo":["a",2,"c"]});
});
it("A.9. Testing a Value: Error", () => {
run_test({"baz":"qux"}, [{"op":"test","path":"/baz","value":"bar"}], "string not equivalent", undefined);
});
it("A.10. Adding a nested Member Object", () => {
run_test({"foo":"bar"}, [{"op":"add","path":"/child","value":{"grandchild":{}}}], undefined, {"foo":"bar","child":{"grandchild":{}}});
});
it("A.11. Ignoring Unrecognized Elements", () => {
run_test({"foo":"bar"}, [{"op":"add","path":"/baz","value":"qux","xyz":123}], undefined, {"foo":"bar","baz":"qux"});
});
it("A.12. Adding to a Non-existent Target", () => {
run_test({"foo":"bar"}, [{"op":"add","path":"/baz/bat","value":"qux"}], "add to a non-existent target", undefined);
});
xit("A.13 Invalid JSON Patch Document", () => {
run_test({"foo":"bar"}, [{"op":"remove","path":"/baz","value":"qux"}], "operation has two 'op' members", undefined);
});
it("A.14. ~ Escape Ordering", () => {
run_test({"/":9,"~1":10}, [{"op":"test","path":"/~01","value":10}], undefined, {"/":9,"~1":10});
});
it("A.15. Comparing Strings and Numbers", () => {
run_test({"/":9,"~1":10}, [{"op":"test","path":"/~01","value":"10"}], "number is not equal to string", undefined);
});
it("A.16. Adding an Array Value", () => {
run_test({"foo":["bar"]}, [{"op":"add","path":"/foo/-","value":["abc","def"]}], undefined, {"foo":["bar",["abc","def"]]});
});
});
});