[
  { "name": "$push pushes", "input": [1], "spec": { "$push": [7] }, "expect": [1, 7] },
  { "name": "$push onto empty", "input": [], "spec": { "$push": [7] }, "expect": [7] },
  { "name": "$push only pushes an array (spec not array)", "input": [], "spec": { "$push": 7 }, "error": "update(): expected spec of $push to be an array; got 7. Did you forget to wrap your parameter in an array?" },
  { "name": "$push only pushes unto an array (target not array)", "input": 1, "spec": { "$push": 7 }, "error": "update(): expected target of $push to be an array; got 1." },

  { "name": "$unshift unshifts", "input": [1], "spec": { "$unshift": [7] }, "expect": [7, 1] },
  { "name": "$unshift onto empty", "input": [], "spec": { "$unshift": [7] }, "expect": [7] },
  { "name": "$unshift only unshifts an array (spec not array)", "input": [], "spec": { "$unshift": 7 }, "error": "update(): expected spec of $unshift to be an array; got 7. Did you forget to wrap your parameter in an array?" },
  { "name": "$unshift only unshifts unto an array (target not array)", "input": 1, "spec": { "$unshift": 7 }, "error": "update(): expected target of $unshift to be an array; got 1." },

  { "name": "$splice delete to end", "input": [7, 8, 9], "spec": { "$splice": [[2]] }, "expect": [7, 8] },
  { "name": "$splice delete range", "input": [5, 6, 7, 8], "spec": { "$splice": [[1, 2]] }, "expect": [5, 8] },
  { "name": "$splice replace one", "input": [1, 4, 3], "spec": { "$splice": [[1, 1, 2]] }, "expect": [1, 2, 3] },
  { "name": "$splice insert several", "input": [5, 4, 9], "spec": { "$splice": [[1, 1, 6, 7, 8]] }, "expect": [5, 6, 7, 8, 9] },
  { "name": "$splice multi-op leading no-op", "input": [1, 2, 3], "spec": { "$splice": [[], [0, 1]] }, "expect": [2, 3] },
  { "name": "$splice multiple ops index shift", "input": [0, 1, 2, 3, 4], "spec": { "$splice": [[0, 1], [0, 1]] }, "expect": [2, 3, 4] },
  { "name": "$splice spec not array of arrays", "input": [], "spec": { "$splice": 1 }, "error": "update(): expected spec of $splice to be an array of arrays; got 1. Did you forget to wrap your parameters in an array?" },
  { "name": "$splice inner not array", "input": [], "spec": { "$splice": [1] }, "error": "update(): expected spec of $splice to be an array of arrays; got 1. Did you forget to wrap your parameters in an array?" },
  { "name": "$splice target not array", "input": 1, "spec": { "$splice": 7 }, "error": "Expected $splice target to be an array; got 1" },

  { "name": "$merge merges", "input": { "a": "b" }, "spec": { "$merge": { "c": "d" } }, "expect": { "a": "b", "c": "d" } },
  { "name": "$merge adds new key", "input": { "a": 1 }, "spec": { "$merge": { "b": 2 } }, "expect": { "a": 1, "b": 2 } },
  { "name": "$merge spec not object", "input": { "a": "b" }, "spec": { "$merge": 7 }, "error": "update(): $merge expects a spec of type 'object'; got 7" },
  { "name": "$merge target not object", "input": 7, "spec": { "$merge": { "a": "b" } }, "error": "update(): $merge expects a target of type 'object'; got 7" },

  { "name": "$set sets", "input": { "a": "b" }, "spec": { "$set": { "c": "d" } }, "expect": { "c": "d" } },
  { "name": "$set more than one key", "input": { "a": false }, "spec": { "$set": true, "bad": 123 }, "error": "Cannot have more than one key in an object with $set" },

  { "name": "$toggle flips", "input": { "a": false, "b": true }, "spec": { "$toggle": ["a", "b"] }, "expect": { "a": true, "b": false } },
  { "name": "$toggle spec not array", "input": { "a": false }, "spec": { "$toggle": "a" }, "error": "update(): expected spec of $toggle to be an array; got a. Did you forget to wrap your parameter in an array?" },

  { "name": "$unset single", "input": { "a": "b" }, "spec": { "$unset": ["a"] }, "expect": {} },
  { "name": "$unset multiple", "input": { "a": "b", "c": "d", "e": "f" }, "spec": { "$unset": ["a", "e"] }, "expect": { "c": "d" } },
  { "name": "$unset spec not array", "input": { "a": "b" }, "spec": { "$unset": "a" }, "error": "update(): expected spec of $unset to be an array; got a. Did you forget to wrap your parameter in an array?" },

  { "name": "$add non Map or Set", "input": 2, "spec": { "$add": [1] }, "error": "update(): $add expects a target of type Set or Map; got Number" },
  { "name": "$remove non Map or Set", "input": 2, "spec": { "$remove": [1] }, "error": "update(): $remove expects a target of type Set or Map; got Number" },

  { "name": "deep update without apply", "input": { "a": "b", "c": { "d": "e", "f": [1], "g": [2], "h": [3], "i": { "j": "k" } } }, "spec": { "c": { "d": { "$set": "m" }, "f": { "$push": [5] }, "g": { "$unshift": [6] }, "h": { "$splice": [[0, 1, 7]] }, "i": { "$merge": { "n": "o" } } } }, "expect": { "a": "b", "c": { "d": "m", "f": [1, 5], "g": [6, 2], "h": [7], "i": { "j": "k", "n": "o" } } } },

  { "name": "$apply spec not function", "input": 2, "spec": { "$apply": 123 }, "error": "update(): expected spec of $apply to be a function; got 123." },

  { "name": "array spec modifies arrays", "input": { "value": [{ "a": 0 }] }, "spec": { "value": [{ "a": { "$set": 1 } }] }, "expect": { "value": [{ "a": 1 }] } },
  { "name": "object index spec modifies arrays", "input": { "value": [{ "a": 0 }] }, "spec": { "value": { "0": { "a": { "$set": 1 } } } }, "expect": { "value": [{ "a": 1 }] } },

  { "name": "nested collection splice", "input": [1, 2, { "a": [12, 17, 15] }], "spec": { "2": { "a": { "$splice": [[1, 1, 13, 14]] } } }, "expect": [1, 2, { "a": [12, 13, 14, 15] }] },

  { "name": "reject top-level array spec", "input": { "a": "b" }, "spec": [], "error": "update(): You provided an invalid spec to update(). The spec may not contain an array except as the value of $set, $push, $unshift, $splice or any custom command allowing an array value." },
  { "name": "reject nested array spec", "input": { "a": "b" }, "spec": { "a": [] }, "error": "update(): You provided an invalid spec to update(). The spec may not contain an array except as the value of $set, $push, $unshift, $splice or any custom command allowing an array value." },
  { "name": "reject array sibling spec", "input": { "a": "b" }, "spec": { "a": { "$set": [] }, "b": [[]] }, "error": "update(): You provided an invalid spec to update(). The spec may not contain an array except as the value of $set, $push, $unshift, $splice or any custom command allowing an array value." },

  { "name": "reject null spec", "input": { "a": "b" }, "spec": null, "error": "update(): You provided an invalid spec to update(). The spec and every included key path must be plain objects containing one of the following commands: $push, $unshift, $splice, $set, $toggle, $unset, $add, $remove, $merge, $apply." },
  { "name": "reject bool spec", "input": { "a": "b" }, "spec": false, "error": "update(): You provided an invalid spec to update(). The spec and every included key path must be plain objects containing one of the following commands: $push, $unshift, $splice, $set, $toggle, $unset, $add, $remove, $merge, $apply." },
  { "name": "reject primitive value spec", "input": { "a": "b" }, "spec": { "a": "c" }, "error": "update(): You provided an invalid spec to update(). The spec and every included key path must be plain objects containing one of the following commands: $push, $unshift, $splice, $set, $toggle, $unset, $add, $remove, $merge, $apply." },
  { "name": "reject nested primitive value spec", "input": { "a": "b" }, "spec": { "a": { "b": "c" } }, "error": "update(): You provided an invalid spec to update(). The spec and every included key path must be plain objects containing one of the following commands: $push, $unshift, $splice, $set, $toggle, $unset, $add, $remove, $merge, $apply." },

  { "name": "safe hasOwnProperty data key", "input": {}, "spec": { "hasOwnProperty": { "$set": "a" } }, "expect": { "hasOwnProperty": "a" } },

  { "name": "smoke push", "input": ["x"], "spec": { "$push": ["y"] }, "expect": ["x", "y"] },

  { "name": "$set on Map E2E", "input": { "$$map": [["foo", "FOO"], ["bar", "BAR"]] }, "spec": { "foo": { "$set": "OFO" } }, "expect": { "$$map": [["foo", "OFO"], ["bar", "BAR"]] } },
  { "name": "$add on Map", "input": { "$$map": [[1, 2], [3, 4]] }, "spec": { "$add": [[5, 6]] }, "expect": { "$$map": [[1, 2], [3, 4], [5, 6]] } },
  { "name": "$add on Set", "input": { "$$set": [1, 2, 3, 4] }, "spec": { "$add": [5, 6] }, "expect": { "$$set": [1, 2, 3, 4, 5, 6] } },
  { "name": "$remove on Map", "input": { "$$map": [[1, 2], [3, 4], [5, 6]] }, "spec": { "$remove": [1, 5] }, "expect": { "$$map": [[3, 4]] } },
  { "name": "$remove on Set", "input": { "$$set": [1, 2, 3, 4] }, "spec": { "$remove": [2, 3] }, "expect": { "$$set": [1, 4] } },
  { "name": "nested object inside Map", "input": { "$$map": [["mapKey", { "banana": "yellow", "apple": ["red"], "blueberry": "purple" }]] }, "spec": { "mapKey": { "apple": { "$set": ["green", "red"] } } }, "expect": { "$$map": [["mapKey", { "banana": "yellow", "apple": ["green", "red"], "blueberry": "purple" }]] } },
  { "name": "Map keyed set", "input": { "$$map": [["mapKey", "mapValue"]] }, "spec": { "mapKey": { "$set": "updatedMapValue" } }, "expect": { "$$map": [["mapKey", "updatedMapValue"]] } }
]
