[
  {
    "name": "Default listen handles a parent set",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        ".comment": "Now do a set at the parent. Expect only the 'a' child to get events",
        "type": "set",
        "path": "",
        "data": {
          "a": 1,
          "b": 2
        },
        "events": [
          {
            "path": "a",
            "type": "value",
            "data": 1
          }
        ]
      }
    ]
  },

  {
    "name": "Default listen handles a set at the same level",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        ".comment": "Do a set at the same level. Expect the full value to raise events",
        "type": "set",
        "path": "a",
        "data": {
          "foo": "bar",
          "yes": true
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "yes",
            "prevName": "foo",
            "data": true
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "foo": "bar",
              "yes": true
            }
          }
        ]
      }
    ]
  },

  {
    "name": "A query can get a complete cache then a merge",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "params": {
          "tag": 1,
          "limitToFirst": 3,
          "startAt": {"index": null}
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "tag": 1,
        "path": "a",
        "data": {
          "a": 1,
          "b": 2,
          "d": 4
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": 1
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": 2
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "d",
            "prevName": "b",
            "data": 4
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "a": 1,
              "b": 2,
              "d": 4
            }
          }
        ]
      },
      {
        "type": "serverMerge",
        "tag": 1,
        "path": "a",
        "data": {
          "a": 5,
          "c": 3
        },
        "events": [
          {
            "path": "a",
            "type": "child_removed",
            "name": "d",
            "data": 4
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": 3
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "a",
            "prevName": null,
            "data": 5
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "a": 5,
              "b": 2,
              "c": 3
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Server merge on listener with complete children",
    "steps": [
      {
        "type": "listen",
        "path": "a/b",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 1,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 1
          }
        ]
      },
      {
        "type": "listen",
        "path": "a",
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 1
          }
        ]
      },
      {
        "type": "serverMerge",
        "path": "a/b",
        "data": {"c": 3, "d": 4},
        "events": [
          {
            "path": "a/b",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": 3
          },
          {
            "path": "a/b",
            "type": "child_added",
            "name": "d",
            "prevName": "c",
            "data": 4
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"c": 3, "d": 4}
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": {"c": 3, "d": 4}
          }
        ]
      }
    ]
  },

  {
    "name": "Empty set doesn't prevent server updates",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "foo": "bar"
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "foo" : "bar"
            }
          }
        ]
      },
      {
        "type": "set",
        "path": "empty-path",
        "data": null,
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": { "foo": "new-bar" },
        "events": [
          {
            "path": "",
            "type": "child_changed",
            "name": "foo",
            "prevName": null,
            "data": "new-bar"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "foo" : "new-bar"
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Deep merge on listener with complete children",
    "steps": [
      {
        "type": "listen",
        "path": "a/b",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 2
          }
        ]
      },
      {
        "type": "listen",
        "path": "a/x/y/z",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/x/y/z",
        "data": null,
        "events": [
          {
            "path": "a/x/y/z",
            "type": "value",
            "data": null
          }
        ]
      },
      {
        "type": "listen",
        "path": "a",
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          }
        ]
      },
      {
        "type": "serverMerge",
        "path": "a/x/y/z",
        "data": {"c": 3, "d": 4},
        ".comment": "No events for the top-level listener, since it's not a complete child",
        "events": [
          {
            "path": "a/x/y/z",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": 3
          },
          {
            "path": "a/x/y/z",
            "type": "child_added",
            "name": "d",
            "prevName": "c",
            "data": 4
          },
          {
            "path": "a/x/y/z",
            "type": "value",
            "data": {"c": 3, "d": 4}
          }
        ]
      }
    ]
  },

  {
    "name": "Update child listener twice",
    "steps": [
      {
        "type": "listen",
        "path": "a/b",
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 1,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 1
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 1
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b/c",
        "data": "foo",
        "events": [
          {
            "path": "a/b",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": "foo"
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"c": "foo"}
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": {"c": "foo"}
          }
        ]
      }
    ]
  },

  {
    "name": "Update child of default listen that already has a complete cache",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        ".comment": "Fill the listen's cache so we can test a child set with an existing cache",
        "type": "serverUpdate",
        "path": "a",
        "data": {
          "b": 2,
          "c": 3
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": 3
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "b": 2,
              "c": 3
            }
          }
        ]
      },
      {
        ".comment": "Now do a set at a child, expect the child event and a value event",
        "type": "set",
        "path": "a/b",
        "data": 4,
        "events": [
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": 4
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "b": 4,
              "c": 3
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Update child of default listen that has no cache",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        ".comment": "Now do a set at a child, expect the child event only",
        "type": "set",
        "path": "a/b",
        "data": 4,
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 4
          }
        ]
      }
    ]
  },

  {
    "name": "Update (via set) the child of a co-located default listener and query",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "params": {
          "tag": 1,
          "startAt": {"index": null, "name": "b"},
          "endAt": {"index": null, "name": "g"}
        },
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        ".comment": "Fill the cache. Since the default listener is there, no tag needed",
        "type": "serverUpdate",
        "path": "a",
        "data": {
          "a": 1,
          "c": 3,
          "d": 4
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": 1
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": 3
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": "a",
            "data": 3
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "d",
            "prevName": "c",
            "data": 4
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "d",
            "prevName": "c",
            "data": 4
          },
          {
            "path": "a",
            "type": "value",
            "data": {"a": 1, "c": 3, "d": 4}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"c": 3, "d": 4}
          }
        ]
      },
      {
        ".comment": "Cache is primed. Now do the child set",
        "type": "set",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": 2
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          },
          {
            "path": "a",
            "type": "value",
            "data": {"a": 1, "b":2, "c": 3, "d": 4}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b":2, "c": 3, "d": 4}
          }
        ]
      }
    ]
  },

  {
    "name": "Update (via set) the child of a query with a full cache",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "params": {
          "tag": 1,
          "startAt": {"index": null, "name": "b"},
          "endAt": {"index": null, "name": "g"}
        },
        "events": []
      },
      {
        ".comment": "Fill the cache first",
        "type": "serverUpdate",
        "path": "a",
        "tag": 1,
        "data": {
          "c": 3,
          "d": 4
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": 3
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "d",
            "prevName": "c",
            "data": 4
          },
          {
            "path": "a",
            "type": "value",
            "data": {"c": 3, "d": 4}
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": 2, "c": 3, "d": 4}
          }
        ]
      }
    ]
  },

  {
    "name": "Update (via set) a child below an empty query",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "params": {
          "tag": 1,
          "startAt": {"name": "b", "index": null},
          "endAt": {"name": "g", "index": null}
        },
        "events": []
      },
      {
        ".comment": "Set a single child, outside the window",
        "type": "set",
        "path": "a/h",
        "data": 8,
        "events": []
      },
      {
        ".comment": "Now set a single child inside the window",
        "type": "set",
        "path": "a/e",
        "data": 5,
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "e",
            "prevName": null,
            "data": 5
          }
        ]
      }
    ]
  },

  {
    "name": "Update descendant of default listener with full cache",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        ".comment": "Fill the cache",
        "type": "serverUpdate",
        "path": "a",
        "data": {
          "b": {
            "d": 4
          },
          "e": 5
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": {
              "d": 4
            }
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "e",
            "prevName": "b",
            "data": 5
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "b": {
                "d": 4
              },
              "e": 5
            }
          }
        ]
      },
      {
        ".comment": "Now do a set at a/b/c, expect child event + new value event",
        "type": "set",
        "path": "a/b/c",
        "data": 3,
        "events": [
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": {
              "c": 3,
              "d": 4
            }
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "b": {
                "c": 3,
                "d": 4
              },
              "e": 5
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Descendant set below an empty default listener is ignored",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        ".comment": "Now do a set at a/b/c, expect no events",
        "type": "set",
        "path": "a/b/c",
        "data": 3,
        "events": []
      }
    ]
  },

  {
    "name": "Update of a child. This can happen if a child listener is added and removed",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          }
        ]
      }
    ]
  },

  {
    "name": "Revert set with only child caches",
    "steps": [
      {
        "type": "listen",
        "path": "a/b",
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 2
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b",
        "data": 3,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 3
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": 3
          }
        ]
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": true,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 2
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": 2
          }
        ]
      }
    ]
  },

  {
    "name": "Can revert a duplicate child set",
    "steps": [
      {
        "type": "listen",
        "path": "a/b",
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 2
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b",
        "data": 3,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 3
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": 3
          }
        ]
      },
      {
        ".comment": "This set duplicates the data in the previous one, so no events expected",
        "type": "set",
        "path": "a/b",
        "data": 3,
        "events": []
      },
      {
        ".comment": "Clearing the second set should have no effect, as the underlying set still exists",
        "type": "ackUserWrite",
        "writeId": 1,
        "revert": true,
        "events": []
      }
    ]
  },

  {
    "name": "Can revert a child set and see the underlying data",
    "steps": [
      {
        "type": "listen",
        "path": "a/b",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 2
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b",
        "data": 3,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 3
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b",
        "data": 4,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 4
          }
        ]
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 3,
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 4
          }
        ]
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "events": []
      },
      {
        ".comment": "Clearing the second set should make the underlying set visible again, as it is now confirmed",
        "type": "ackUserWrite",
        "writeId": 1,
        "revert": true,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 3
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": 3
          }
        ]
      }
    ]
  },

  {
    "name": "Revert child set with no server data",
    "steps": [
      {
        "type": "set",
        "path": "a/b",
        "data": {"d": 4, "e": 5},
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": {"d": 4, "e": 5}
          }
        ]
      },
      {
        "type": "set",
        "path": "a/c",
        "data": {"z": 26},
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": {"z": 26}
          }
        ]
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": true,
        "events": [
          {
            "path": "a",
            "type": "child_removed",
            "name": "b",
            "data": {"d": 4, "e": 5}
          }
        ]
      }
    ]
  },

  {
    "name": "Revert deep set with no server data",
    "steps": [
      {
        "type": "set",
        "path": "a/b/c",
        "data": {"d": 4, "e": 5},
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "set",
        "path": "a/x/y",
        "data": {"z": 26},
        "events": []
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": true,
        "events": []
      }
    ]
  },

  {
    "name": "Revert set covered by non-visible transaction",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        ".comment": "Initial server value is X.",
        "type": "serverUpdate",
        "path": "",
        "data": "X",
        "events": [
          {
            "path": "",
            "type": "value",
            "data": "X"
          }
        ]
      },
      {
        ".comment": "Set to Y.",
        "type": "set",
        "path": "",
        "data": "Y",
        "events": [
          {
            "path": "",
            "type": "value",
            "data": "Y"
          }
        ]
      },
      {
        ".comment": "Overwrite with a non-visible 'transaction'.",
        "type": "set",
        "path": "",
        "data": "Z",
        "visible": false,
        "events": []
      },
      {
        ".comment": "Revert set to Y (e.g. security failed), so we should see it go back to Y.",
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": true,
        "events": [
          {
            "path": "",
            "type": "value",
            "data": "X"
          }
        ]
      }
    ]
  },

  {
    "name": "Clear parent shadowing server values set with server children",
    "steps": [
      {
        "type": "listen",
        "path": "a/b",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 2
          }
        ]
      },
      {
        "type": "set",
        "path": "a",
        "data": {"b": 28, "c": 3},
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 28
          }
        ]
      },
      {
        ".comment": "This listen should get a complete event snap, as well as complete server children",
        "type": "listen",
        "path": "a",
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 28
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": 3
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": 28, "c": 3}
          }
        ]
      },
      {
        ".comment": "Do a serverUpdate with a conflicting value for b, simulates a server value. It's still shadowed though",
        "type": "serverUpdate",
        "path": "a/b",
        "data": 29,
        "events": []
      },
      {
        ".comment": "Clearing the set should result in updated values for b",
        "type": "ackUserWrite",
        "writeId": 0,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 29
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": 29
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": 29, "c": 3}
          }
        ]
      }
    ]
  },

  {
    "name": "Clear child shadowing server values set with server children",
    "steps": [
      {
        "type": "listen",
        "path": "a/b",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 2
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b",
        "data": 28,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 28
          }
        ]
      },
      {
        ".comment": "This listen should get an event child snap, as well as a complete server child: b",
        "type": "listen",
        "path": "a",
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 28
          }
        ]
      },
      {
        ".comment": "Do a serverUpdate with a conflicting value for b, simulates a server value. It's still shadowed though",
        "type": "serverUpdate",
        "path": "a/b",
        "data": 29,
        "events": []
      },
      {
        ".comment": "Clearing the set should result in no events. We don't yet have the server data at the parent",
        "type": "ackUserWrite",
        "writeId": 0,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 29
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": 29
          }
        ]
      }
    ]
  },

  {
    "name": "Unrelated merge doesn't shadow server updates",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "data": null,
        "events": [
          {
            "path": "a",
            "type": "value",
            "data": null
          }
        ]
      },
      {
        "type": "update",
        "path": "a",
        "data": {"b": 2, "c": 3},
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": 3
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": 2, "c": 3}
          }
        ]
      },
      {
        "type": "serverUpdate",
        "path": "a/d",
        "data": 4,
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "d",
            "prevName": "c",
            "data": 4
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": 2, "c": 3, "d": 4}
          }
        ]
      }
    ]
  },

  {
    "name": "Can set alongside a remote merge",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "data": null,
        "events": [
          {
            "path": "a",
            "type": "value",
            "data": null
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": 2}
          }
        ]
      },
      {
        "type": "serverMerge",
        "path": "a",
        "data": {"b": 28, "c": 3},
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": 3
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": 2, "c": 3}
          }
        ]
      }
    ]
  },

  {
    "name": "setPriority on a location with no cache",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "set",
        "path": "a/.priority",
        "data": "foo",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "data": "bar",
        "events": [
          {
            "path": "a",
            "type": "value",
            "data": { ".priority": "foo", ".value": "bar" }
          }
        ]
      }
    ]
  },

  {
    "name": "deep update deletes child from limit window and pulls in new child",
    "steps": [
      {
        "type": "set",
        "path": "a",
        "data": {
          "a": {"aa": 2, "aaa": 3},
          "b": {"bb": 2, "bbb": 3},
          "c": {"cc": 2, "ccc": 3}
        },
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "params": {
          "tag": 1,
          "limitToLast": 2
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": {"bb": 2, "bbb": 3}
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": {"cc": 2, "ccc": 3}
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "b": {"bb": 2, "bbb": 3},
              "c": {"cc": 2, "ccc": 3}
            }
          }
        ]
      },
      {
        "type": "update",
        "path": "a/b",
        "data": {
          "bb": null,
          "bbb": null
        },
        "events": [
          {
            "path": "a",
            "type": "child_removed",
            "name": "b",
            "data": {"bb": 2, "bbb": 3}
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": {"aa": 2, "aaa": 3}
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "a": {"aa": 2, "aaa": 3},
              "c": {"cc": 2, "ccc": 3}
            }
          }
        ]
      }
    ]
  },

  {
    "name": "deep set deletes child from limit window and pulls in new child",
    "steps": [
      {
        "type": "set",
        "path": "a",
        "data": {
          "a": {"aa": 2},
          "b": {"bb": 2},
          "c": {"cc": 2}
        },
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "params": {
          "tag": 1,
          "limitToLast": 2
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": {"bb": 2}
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": {"cc": 2}
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "b": {"bb": 2},
              "c": {"cc": 2}
            }
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b/bb",
        "data": null,
        "events": [
          {
            "path": "a",
            "type": "child_removed",
            "name": "b",
            "data": {"bb": 2}
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": {"aa": 2}
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "a": {"aa": 2},
              "c": {"cc": 2}
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Edge case in newChildForChange_",
    "steps": [
      {
        "type": "listen",
        "path": "a/d",
        "events": []
      },
      {
        "type": "listen",
        "path": "a/b/c",
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/d",
        "data": 4,
        "events": [
          {
            "type": "value",
            "path": "a/d",
            "data": 4
          },
          {
            "type": "child_added",
            "path": "a",
            "name": "d",
            "prevName": null,
            "data": 4
          }
        ]
      },
      {
        "type": "serverUpdate",
        "path": "a/b/c",
        "data": 3,
        "events": [
          {
            "path": "a/b/c",
            "type": "value",
            "data": 3
          }
        ]
      }
    ]
  },

  {
    "name": "Revert set in query window",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "params": {
          "limitToLast": 1,
          "tag": 1
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "tag": 1,
        "data": {"b": 2},
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": 2}
          }
        ]
      },
      {
        "type": "set",
        "path": "a/c",
        "data": 3,
        "events": [
          {
            "path": "a",
            "type": "child_removed",
            "name": "b",
            "data": 2
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": 3
          },
          {
            "path": "a",
            "type": "value",
            "data": {"c": 3}
          }
        ]
      },
      {
        "type": "ackUserWrite",
        "revert": true,
        "writeId": 0,
        "events": [
          {
            "path": "a",
            "type": "child_removed",
            "name": "c",
            "data": 3
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": 2}
          }
        ]
      }
    ]
  },

  {
    "name": "Handles a server value moving a child out of a query window",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "data": {"b": {"c": {"value": 3}, "d": {"value": 4}}},
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": {"c": {"value": 3}, "d": {"value": 4}}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": {"c": {"value": 3}, "d": {"value": 4}}}
          }
        ]
      },
      {
        "type": "listen",
        "params": {
          "tag": 1,
          "limitToLast": 1,
          "orderBy": "value"
        },
        "path": "a/b",
        "events": [
          {
            "path": "a/b",
            "type": "child_added",
            "name": "d",
            "prevName": null,
            "data": {"value": 4}
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"d": {"value": 4}}
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b/d/value",
        "data": 5,
        "events": [
          {
            "path": "a/b",
            "type": "child_moved",
            "name": "d",
            "prevName": null,
            "data": {"value": 5}
          },
          {
            "path": "a/b",
            "type": "child_changed",
            "name": "d",
            "prevName": null,
            "data": {"value": 5}
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"d": {"value": 5}}
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": {"c": {"value": 3}, "d": {"value": 5}}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": {"c": {"value": 3}, "d": {"value": 5}}}
          }
        ]
      },
      {
        ".comment": "The query is shadowed, so only one data update arrives. We're simulating a server value, so it's different than what was set",
        "type": "serverUpdate",
        "path": "a/b/d/value",
        "data": 2,
        "events": []
      },
      {
        ".comment": "Now that we're acking the write, we should see the effect of the change",
        "type": "ackUserWrite",
        "writeId": 0,
        "events": [
          {
            "path": "a/b",
            "type": "child_removed",
            "name": "d",
            "data": {"value": 5}
          },
          {
            "path": "a/b",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": {"value": 3}
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"c": {"value": 3}}
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": {"c": {"value": 3}, "d": {"value": 2}}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": {"c": {"value": 3}, "d": {"value": 2}}}
          }
        ]
      }
    ]
  },

  {
    "name": "Update of indexed child works",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "data": {"b": {"c": {"value": 3}, "d": {"value": 4}}},
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": {"c": {"value": 3}, "d": {"value": 4}}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": {"c": {"value": 3}, "d": {"value": 4}}}
          }
        ]
      },
      {
        "type": "listen",
        "params": {
          "tag": 1,
          "limitToLast": 1,
          "orderBy": "value"
        },
        "path": "a/b",
        "events": [
          {
            "path": "a/b",
            "type": "child_added",
            "name": "d",
            "prevName": null,
            "data": {"value": 4}
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"d": {"value": 4}}
          }
        ]
      },
      {
        "type": "update",
        "path": "a/b/c",
        "data": {"value": 5},
        "events": [
          {
            "path": "a/b",
            "type": "child_removed",
            "name": "d",
            "data": {"value": 4}
          },
          {
            "path": "a/b",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": {"value": 5}
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"c": {"value": 5}}
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": {"c": {"value": 5}, "d": {"value": 4}}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": {"c": {"value": 5}, "d": {"value": 4}}}
          }
        ]
      }
    ]
  },

  {
    "name": "Merge applied to empty limit",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "params": {
          "limitToLast": 1,
          "tag": 1
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "tag": 1,
        "data": null,
        "events": [
          {
            "path": "a",
            "type": "value",
            "data": null
          }
        ]
      },
      {
        "type": "update",
        "path": "a",
        "data": {"b": 1},
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 1
          },
          {
            "path": "a",
            "type": "value",
            "data": {"b": 1}
          }
        ]
      }
    ]
  },

  {
    "name": "Limit is refilled from server data after merge",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "listen",
        "path": "a/b",
        "params": {
          "tag": 1,
          "limitToLast": 1
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "data": {"a": 1, "b": {"c": 3, "d": 4}},
        "events": [
          {
            "path": "a/b",
            "type": "child_added",
            "name": "d",
            "prevName": null,
            "data": 4
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"d": 4}
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": 1
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": {"c": 3, "d": 4}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"a": 1, "b": {"c": 3, "d": 4}}
          }
        ]
      },
      {
        "type": "update",
        "path": "a/b",
        "data": {"d": null},
        "events": [
          {
            "path": "a/b",
            "type": "child_removed",
            "name": "d",
            "data": 4
          },
          {
            "path": "a/b",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": 3
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"c": 3}
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": "a",
            "data": {"c": 3}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"a": 1, "b": {"c": 3}}
          }
        ]
      }
    ]
  },

  {
    "name": "Handle repeated listen with merge as first update",
    "steps": [
      {
        ".comment": "Assume that we just unlistened on this path, and before the unlisten arrives, a merge was sent by the server",
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        ".comment": "This happens when a merge arriving from the server while the 2nd listen is in flight",
        "type": "serverMerge",
        "path": "a",
        "data": {"c": 3},
        "events": []
      }
    ]
  },

  {
    "name": "Limit is refilled from server data after set",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "listen",
        "path": "a/b",
        "params": {
          "tag": 1,
          "limitToLast": 1
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "data": {"a": 1, "b": {"c": 3, "d": 4}},
        "events": [
          {
            "path": "a/b",
            "type": "child_added",
            "name": "d",
            "prevName": null,
            "data": 4
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"d": 4}
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": 1
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": {"c": 3, "d": 4}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"a": 1, "b": {"c": 3, "d": 4}}
          }
        ]
      },
      {
        "type": "set",
        "path": "a/b/d",
        "data": null,
        "events": [
          {
            "path": "a/b",
            "type": "child_removed",
            "name": "d",
            "data": 4
          },
          {
            "path": "a/b",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": 3
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {"c": 3}
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": "a",
            "data": {"c": 3}
          },
          {
            "path": "a",
            "type": "value",
            "data": {"a": 1, "b": {"c": 3}}
          }
        ]
      }
    ]
  },

  {
    "name": "query on weird path.",
    ".comment": "We used to use '|' as a separator, which broke with paths containing |",
    "steps": [
      {
        "type": "listen",
        "path": "foo|!@%^&*()_<>?+={}blah",
        "params": {
          "tag": 1,
          "limitToLast": 5
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "foo|!@%^&*()_<>?+={}blah",
        "tag": 1,
        "data": { "a": "a" },
        "events": [
          {
            "path": "foo|!@%^&*()_<>?+={}blah",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": "a"
          },
          {
            "path": "foo|!@%^&*()_<>?+={}blah",
            "type": "value",
            "data": { "a": "a" }
          }
        ]
      }
    ]
  },

  {
    "name": "runs, round2",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "events": []
      },
      {
        "type": "set",
        "path": "foo",
        "data": "baz",
        "events": [
          {
            "path": "foo",
            "type": "value",
            "data": "baz"
          }
        ]
      },
      {
        "type": "set",
        "path": "foo/new",
        "data": "bar",
        "events": [
          {
            "path": "foo",
            "type": "child_added",
            "name": "new",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "foo",
            "type": "value",
            "data": { "new" : "bar"}
          }
        ]
      },
      {
        "type": "serverUpdate",
        "path": "foo",
        "data": "baz",
        "events": []
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "foo",
        "data": { "new" : "bar"},
        "events": []
      },
      {
        "type": "ackUserWrite",
        "writeId": 1,
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "foo",
        "data": { "new" : true, "other" : "bar"},
        "events": [
          {
            "path": "foo",
            "type": "child_added",
            "name": "other",
            "prevName": "new",
            "data": "bar"
          },
          {
            "path": "foo",
            "type": "child_changed",
            "name": "new",
            "prevName": null,
            "data": true
          },
          {
            "path": "foo",
            "type": "value",
            "data": { "new": true, "other": "bar"}
          }
        ]
      }
    ]
  },

  {
    "name": "handles nested listens",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "events": []
      },
      {
        "type": "listen",
        "path": "foo/bar",
        "events": []
      },
      {
        "type": "set",
        "path": "",
        "data": {
          "foo": {
            "a": 1,
            "b": 2,
            "bar": {
              "c": true,
              "d": false
            }
          },
          "baz": false
        },
        "events": [
          {
            "path": "foo/bar",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": true
          },
          {
            "path": "foo/bar",
            "type": "child_added",
            "name": "d",
            "prevName": "c",
            "data": false
          },
          {
            "path": "foo/bar",
            "type": "value",
            "data": {"c": true, "d": false}
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": 1
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": 2
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "bar",
            "prevName": "b",
            "data": {"c": true, "d": false}
          },
          {
            "path": "foo",
            "type": "value",
            "data": {
              "a": 1,
              "b": 2,
              "bar": {
                "c": true,
                "d": false
              }
            }
          }
        ]
      },
      {
        "type": "set",
        "path": "",
        "data": {
          "foo": {
            "a": 1,
            "b": 2,
            "bar": {
              "c": false,
              "d": false,
              "e": true
            },
            "f": 3
          },
          "baz": false
        },
        "events": [
          {
            "path": "foo/bar",
            "type": "child_added",
            "name": "e",
            "prevName": "d",
            "data": true
          },
          {
            "path": "foo/bar",
            "type": "child_changed",
            "name": "c",
            "prevName": null,
            "data": false
          },
          {
            "path": "foo/bar",
            "type": "value",
            "data": {
              "c": false,
              "d": false,
              "e": true
            }
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "f",
            "prevName": "bar",
            "data": 3
          },
          {
            "path": "foo",
            "type": "child_changed",
            "name": "bar",
            "prevName": "b",
            "data": {
              "c": false,
              "d": false,
              "e": true
            }
          },
          {
            "path": "foo",
            "type": "value",
            "data": {
              "a": 1,
              "b": 2,
              "bar": {
                "c": false,
                "d": false,
                "e": true
              },
              "f": 3
            }
          }
        ]
      },
      {
        ".comment": "Duplicate set, no events raised",
        "type": "set",
        "path": "",
        "data": {
          "foo": {
            "a": 1,
            "b": 2,
            "bar": {
              "c": false,
              "d": false,
              "e": true
            },
            "f": 3
          },
          "baz": false
        },
        "events": []
      }
    ]
  },

  {
    "name": "Handles a set below a listen",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "set",
        "path": "foo",
        "data": 1,
        ".comment": "We only expect a child_added, since it does not completely fill the view",
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": 1
          }
        ]
      }
    ]
  },

  {
    "name": "does non-default queries",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "params": {
          "tag": 1,
          "limitToLast": 1
        },
        "events": []
      },
      {
        "type": "set",
        "path": "",
        "data": {
          "foo": {
            "a": 1,
            "b": 2
          }
        },
        "events": [
          {
            "path": "foo",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          },
          {
            "path": "foo",
            "type": "value",
            "data": {
              "b": 2
            }
          }
        ]
      },
      {
        ".comment": "Now have the server send the same data to the query. No events result because there is no change",
        "type": "serverUpdate",
        "tag": 1,
        "path": "foo",
        "data": {
          "b": 2
        },
        "events": []
      }
    ]
  },

  {
    "name": "handles a co-located default listener and query",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "events": []
      },
      {
        "type": "listen",
        "path": "foo",
        "params": {
          "tag": 1,
          "limitToLast": 1
        },
        "events": []
      },
      {
        "type": "set",
        "path": "foo",
        "data": { "a": 1, "b": 2},
        "events": [
          {
            "path": "foo",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": 1
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": 2
          },
          {
            "path": "foo",
            "type": "value",
            "data": { "a": 1, "b": 2}
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 2
          },
          {
            "path": "foo",
            "type": "value",
            "data": {"b": 2}
          }
        ]
      }
    ]
  },

  {
    "name": "Default and non-default listener at same location with server update",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "events": []
      },
      {
        "type": "listen",
        "path": "foo",
        "params": {
          "tag": 1,
          "limitToLast": 1
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "foo",
        "data": {"a": 1, "b": 2},
        "events": [
          {
            "path": "foo",
            "type": "child_added",
            "name": "a",
            "data": 1,
            "prevName": null
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "b",
            "data": 2,
            "prevName": "a"
          },
          {
            "path": "foo",
            "type": "value",
            "data": {"a": 1, "b": 2}
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "b",
            "data": 2,
            "prevName": null
          },
          {
            "path": "foo",
            "type": "value",
            "data": {"b": 2}
          }
        ]
      }
    ]
  },

  {
    "name": "Add a parent listener to a complete child listener, expect child event",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "foo",
        "data": 1,
        "events": [
          {
            "path": "foo",
            "type": "value",
            "data": 1
          }
        ]
      },
      {
        "type": "listen",
        "path": "",
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": 1
          }
        ]
      }
    ]
  },

  {
    "name": "Add listens to a set, expect correct events, including a child event",
    "steps": [
      {
        "type": "set",
        "path": "foo",
        "data": {"bar": 1, "baz": 2},
        "events": []
      },
      {
        "type": "listen",
        "path": "foo/bar",
        "events": [
          {
            "path": "foo/bar",
            "type": "value",
            "data": 1
          }
        ]
      },
      {
        "type": "listen",
        "path": "",
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": {"bar": 1, "baz": 2}
          }
        ]
      }
    ]
  },

  {
    "name": "ServerUpdate to a child listener raises child events at parent",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "events": []
      },
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "foo",
        "data": 1,
        "events": [
          {
            "path": "foo",
            "type": "value",
            "data": 1
          },
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": 1
          }
        ]
      }
    ]
  },

  {
    "name": "ServerUpdate to a child listener raises child events at parent query",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "events": []
      },
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "limitToLast": 1
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "foo",
        "data": 1,
        "events": [
          {
            "path": "foo",
            "type": "value",
            "data": 1
          },
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": 1
          }
        ]
      }
    ]
  },

  {
    "name": "Multiple complete children are handled properly",
    "steps": [
      {
        "type": "listen",
        "path": "foo/a",
        "events": []
      },
      {
        "type": "listen",
        "path": "foo/b",
        "events": []
      },
      {
        "type": "listen",
        "path": "foo",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "foo/a",
        "data": 1,
        "events": [
          {
            "path": "foo/a",
            "type": "value",
            "data": 1
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": 1
          }
        ]
      },
      {
        "type": "serverUpdate",
        "path": "foo/b",
        "data": 2,
        "events": [
          {
            "path": "foo/b",
            "type": "value",
            "data": 2
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": 2
          }
        ]
      }
    ]
  },

  {
    "name": "Write leaf node, overwrite at parent node",
    "steps": [
      {
        "type": "listen",
        "path": "a/aa",
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "set",
        "path": "a/aa",
        "data": 1,
        "events": [
          {
            "path": "a/aa",
            "type": "value",
            "data": 1
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "aa",
            "prevName": null,
            "data": 1
          }
        ]
      },
      {
        "type": "set",
        "path": "a",
        "data": {
          "aa": 2
        },
        "events": [
          {
            "path": "a/aa",
            "type": "value",
            "data": 2
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "aa",
            "prevName": null,
            "data": 2
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "aa": 2
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Confirm complete children from the server",
    "steps": [
      {
        "type": "listen",
        "path": "a/aa",
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/aa",
        "data": 1,
        "events": [
          {
            "path": "a/aa",
            "type": "value",
            "data": 1
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "aa",
            "prevName": null,
            "data": 1
          }
        ]
      },
      {
        "type": "serverUpdate",
        "path": "a",
        ".comment": "At some point in the future, we might consider sending a hash here to avoid duplicate data",
        "data": {"aa": 1},
        "events": [
          {
            "path": "a",
            "type": "value",
            "data": {"aa": 1}
          }
        ]
      },
      {
        ".comment": "Now, delete the same child and make sure we get the right events",
        "type": "serverUpdate",
        "path": "a/aa",
        "data": null,
        "events": [
          {
            "path": "a/aa",
            "type": "value",
            "data": null
          },
          {
            "path": "a",
            "type": "child_removed",
            "name": "aa",
            "prevName": null,
            "data": 1
          },
          {
            "path": "a",
            "type": "value",
            "data": null
          }
        ]
      }
    ]
  },

  {
    "name": "Write leaf, overwrite from parent",
    "steps": [
      {
        "type": "listen",
        "path": "a/aa",
        "events": []
      },
      {
        "type": "listen",
        "path": "a/bb",
        "events": []
      },
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        ".comment": "First set is at leaf. Expect only a child_added for the parent, nothing for the sibling",
        "type": "set",
        "path": "a/aa",
        "data": 1,
        "events": [
          {
            "path": "a/aa",
            "type": "value",
            "data": 1
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "aa",
            "prevName": null,
            "data": 1
          }
        ]
      },
      {
        ".comment": "Now set at the parent. Expect value events for everyone",
        "type": "set",
        "path": "a",
        "data": {"aa": 2},
        "events": [
          {
            "path": "a/aa",
            "type": "value",
            "data": 2
          },
          {
            "path": "a/bb",
            "type": "value",
            "data": null
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "aa",
            "prevName": null,
            "data": 2
          },
          {
            "path": "a",
            "type": "value",
            "data": {"aa": 2}
          }
        ]
      }
    ]
  },

  {
    "name": "Basic update test",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "listen",
        "path": "b",
        "events": []
      },
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        ".comment": "Initial data",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": 1,
          "b": 2,
          "c": 3
        },
        "events": [
          {
            "path": "a",
            "type": "value",
            "data": 1
          },
          {
            "path": "b",
            "type": "value",
            "data": 2
          },
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": 1
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": 2
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": 3
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": 1,
              "b": 2,
              "c": 3
            }
          }
        ]
      },
      {
        ".comment": "Now update two children. Not b, there should be no events at b",
        "type": "update",
        "path": "",
        "data": {
          "a": true,
          "c": false
        },
        "events": [
          {
            "path": "a",
            "type": "value",
            "data": true
          },
          {
            "path": "",
            "type": "child_changed",
            "name": "a",
            "prevName": null,
            "data": true
          },
          {
            "path": "",
            "type": "child_changed",
            "name": "c",
            "prevName": "b",
            "data": false
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": true,
              "b": 2,
              "c": false
            }
          }
        ]
      }
    ]
  },
  {
    "name": "No double value events for user ack",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "params": {
          "tag": 1,
          "limitToLast": 1,
          "endAt": {"index": null, "name": "d"}
        },
        "events": []
      },
      {
        ".comment": "user sets data",
        "type": "set",
        "path": "foo",
        "data": {
          "a": 1,
          "b": 2,
          "c": 3
        },
        "events": [
          {
            "path": "foo",
            "type": "value",
            "data": { "c": 3 }
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": 3
          }
        ]
      },
      {
        ".comment": "server acks data, but local overwrite causes no events to fire",
        "type": "serverUpdate",
        "path": "foo",
        "tag": 1,
        "data": null,
        "events": []
      },
      {
        ".comment": "server sends data with merge",
        "type": "serverMerge",
        "path": "foo",
        "tag": 1,
        "data": {
          "c": 3
        },
        "events": [ ]
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": false,
        "events": []
      },
      {
        "type": "set",
        "path": "foo/d",
        "data": 4,
        "events": [
          {
            "path": "foo",
            "type": "value",
            "data": { "d": 4 }
          },
          {
            "path": "foo",
            "type": "child_removed",
            "name": "c",
            "prevName": null,
            "data": 3
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "d",
            "prevName": null,
            "data": 4
          }
        ]
      },
      {
        "type": "serverMerge",
        "path": "foo",
        "tag": 1,
        "data": {
          "d": 4
        },
        "events": [ ]
      },
      {
        "type": "ackUserWrite",
        "writeId": 1,
        "revert": false,
        "events": []
      }
    ]
  },
  {
    "name": "Basic key index sanity check",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderByKey": true,
          "startAt": { "index": "aa" },
          "endAt": { "index": "e" }
        },
        "events": []
      },
      {
        "type": "set",
        "path": "",
        "data": {
          "a": { ".priority": 10, ".value": "a" },
          "b": { ".priority": 5, ".value": "b" },
          "c": { ".priority": 20, ".value": "c" },
          "d": { ".priority": 7, ".value": "d" },
          "e": { ".priority": 30, ".value": "e" },
          "f": { ".priority": 8, ".value": "f" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": {".priority": 5, ".value": "b"}
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": {".priority": 20, ".value": "c"}
          },
          {
            "path": "",
            "type": "child_added",
            "name": "d",
            "prevName": "c",
            "data": {".priority": 7, ".value": "d"}
          },
          {
            "path": "",
            "type": "child_added",
            "name": "e",
            "prevName": "d",
            "data": {".priority": 30, ".value": "e"}
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": { ".priority": 5, ".value": "b" },
              "c": { ".priority": 20, ".value": "c" },
              "d": { ".priority": 7, ".value": "d" },
              "e": { ".priority": 30, ".value": "e" }
            }
          }
        ]
      },
      {
        ".comment": "Add a new item outside of range and make sure we get events.",
        "type": "set",
        "path": "a",
        "data": "hello!",
        "events": [ ]
      },
      {
        ".comment": "Add a new item within range and ensure we get ld_added.",
        "type": "set",
        "path": "bass",
        "data": 3.14,
        "events":
        [
          {
            "path": "",
            "type": "child_added",
            "name": "bass",
            "prevName": "b",
            "data": 3.14
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": { ".priority": 5, ".value": "b" },
              "bass": 3.14,
              "c": { ".priority": 20, ".value": "c" },
              "d": { ".priority": 7, ".value": "d" },
              "e": { ".priority": 30, ".value": "e" }
            }
          }
        ]
      },
      {
        ".comment": "Modify an item and ensure we get child_changed.",
        "type": "set",
        "path": "b",
        "data": 42,
        "events":
        [
          {
            "path": "",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": 42
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": 42,
              "bass": 3.14,
              "c": { ".priority": 20, ".value": "c" },
              "d": { ".priority": 7, ".value": "d" },
              "e": { ".priority": 30, ".value": "e" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Collect correct subviews to listen on",
    "steps":
    [
      {
        "type": "listen",
        "callbackId": 1,
        "path": "",
        "events": []
      },
      {
        "type": "listen",
        "callbackId": 1,
        "path": "/a",
        "events": []
      },
      {
        "type": "listen",
        "callbackId": 1,
        "path": "/a/b",
        "events": []
      },
      {
        ".comment": "should not cause /a/b to be listened upon",
        "type": "unlisten",
        "callbackId": 1,
        "path": "",
        "events": []
      },
      {
        ".comment": "should now cause /a/b to be listened upon",
        "type": "unlisten",
        "callbackId": 1,
        "path": "/a",
        "events": []
      }
    ]
  },
  {
    "name": "Limit to first one on ordered query",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "vanished",
          "limitToFirst": 1
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "triceratops": {"vanished": -66000000},
          "stegosaurus": {"vanished": -155000000},
          "pterodactyl": {"vanished": -75000000}
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "stegosaurus",
            "prevName": null,
            "data": {"vanished": -155000000}
          },
          {
            "path": "",
            "type": "value",
            "name": "",
            "prevName": null,
            "data": {"stegosaurus": {"vanished": -155000000}}
          }
        ]
      }
    ]
  },
  {
    "name": "Limit to last one on ordered query",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "vanished",
          "limitToLast": 1
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "triceratops": {"vanished": -66000000},
          "stegosaurus": {"vanished": -155000000},
          "pterodactyl": {"vanished": -75000000}
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "triceratops",
            "prevName": null,
            "data": {"vanished": -66000000}
          },
          {
            "path": "",
            "type": "value",
            "name": "",
            "prevName": null,
            "data": {"triceratops": {"vanished": -66000000}}
          }
        ]
      }
    ]
  },
  {
    "name": "Update indexed value on existing child from limited query",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "age",
          "limitToLast": 4
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "tag": 1,
        "path": "",
        "data": {
          "4": { "age": 41, "highscore": 400, "name": "old mama"},
          "5": { "age": 18, "highscore": 1200, "name": "young mama"},
          "6": { "age": 20, "highscore": 1003, "name": "micheal blub"},
          "7": { "age": 30, "highscore": 10000, "name": "no. 7"}
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "4",
            "prevName": "7",
            "data": { "age": 41, "highscore": 400, "name": "old mama"}
          },
          {
            "path": "",
            "type": "child_added",
            "name": "5",
            "prevName": null,
            "data": { "age": 18, "highscore": 1200, "name": "young mama"}
          },
          {
            "path": "",
            "type": "child_added",
            "name": "6",
            "prevName": "5",
            "data": { "age": 20, "highscore": 1003, "name": "micheal blub"}
          },
          {
            "path": "",
            "type": "child_added",
            "name": "7",
            "prevName": "6",
            "data": { "age": 30, "highscore": 10000, "name": "no. 7"}
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "4": { "age": 41, "highscore": 400, "name": "old mama"},
              "5": { "age": 18, "highscore": 1200, "name": "young mama"},
              "6": { "age": 20, "highscore": 1003, "name": "micheal blub"},
              "7": { "age": 30, "highscore": 10000, "name": "no. 7"}
            }
          }
        ]
      },
      {
        ".comment": "update the order by value, should cause a new value event",
        "type": "serverUpdate",
        "path": "4/age",
        "tag": 1,
        "data": 25,
        "events": [
          {
            "path": "",
            "type": "child_moved",
            "name": "4",
            "prevName": "6",
            "data": { "age": 25, "highscore": 400, "name": "old mama"}
          },
          {
            "path": "",
            "type": "child_changed",
            "name": "4",
            "prevName": "6",
            "data": { "age": 25, "highscore": 400, "name": "old mama"}
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "4": { "age": 25, "highscore": 400, "name": "old mama"},
              "5": { "age": 18, "highscore": 1200, "name": "young mama"},
              "6": { "age": 20, "highscore": 1003, "name": "micheal blub"},
              "7": { "age": 30, "highscore": 10000, "name": "no. 7"}
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Can create startAt, endAt, equalTo queries with bool",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "boolKey",
          "startAt": {"index": true}
        },
        "events": []
      },
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 2,
          "orderBy": "boolKey",
          "endAt": {"index": true}
        },
        "events": []
      },
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 3,
          "orderBy": "boolKey",
          "equalTo": {"index": true}
        },
        "events": []
      },
      {
        "type": "suppressWarning",
        "events": []
      }
    ]
  },
  {
    "name": "Query with existing server snap",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "age"
        },
        "events": []
      },
      {
        ".comment": "untagged update, since index doesn't exist",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "foo": { "age": 10, "score": 100, "bar": "baz" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": { "age": 10, "score": 100, "bar": "baz" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "foo": { "age": 10, "score": 100, "bar": "baz" }
            }
          }
        ]
      },
      {
        ".comment": "new listen should use existing data and index correctly",
        "type": "listen",
        "path": "",
        "params": {
          "tag": 2,
          "orderBy": "score"
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": { "age": 10, "score": 100, "bar": "baz" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "foo": { "age": 10, "score": 100, "bar": "baz" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Server data is not purged for non-server-indexed queries",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "highscore",
          "limitToLast": 2
        },
        "events": []
      },
      {
        ".comment": "server has no index, so it sends down everything",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "highscore": 100, "value": "a" },
          "b": { "highscore": 200, "value": "b" },
          "c": { "highscore": 0, "value": "c" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "highscore": 100, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "highscore": 200, "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "highscore": 100, "value": "a" },
              "b": { "highscore": 200, "value": "b" }
            }
          }
        ]
      },
      {
        ".comment": "update of highscore leads to only a partial update",
        "type": "serverUpdate",
        "path": "c/highscore",
        "data": 300,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "a",
            "prevName": null,
            "data": { "highscore": 100, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": { "highscore": 300, "value": "c" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": { "highscore": 200, "value": "b" },
              "c": { "highscore": 300, "value": "c" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Limit with custom orderBy is refilled with correct item",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "age",
          "limitToLast": 1
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "age": 4 },
          "b": { "age": 3 },
          "c": { "age": 2 },
          "d": { "age": 1 }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "age": 4 }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "age": 4 }
            }
          }
        ]
      },
      {
        ".comment": "delete 'a' and make sure 'b' comes into view.",
        "type": "set",
        "path": "a",
        "data": null,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "a",
            "data": { "age": 4 }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": { "age": 3 }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": { "age": 3 }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "startAt/endAt dominates limit",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "startAt": { "index": 1 },
          "endAt": { "index": 2 },
          "limitToFirst": 2
        },
        "events": []
      },
      {
        ".comment": "server has no index, so it sends down everything",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "index": 1, "value": "a" },
          "b": { "index": 1000, "value": "b" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" }
            }
          }
        ]
      },
      {
        ".comment": "update from server to fill limit and beyond",
        "type": "serverMerge",
        "path": "",
        "data": {
          "b": { "index": 1, "value": "b" },
          "c": { "index": 2, "value": "c" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "index": 1, "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 1, "value": "b" }
            }
          }
        ]
      },
      {
        ".comment": "update from server to move entry out of window",
        "type": "serverUpdate",
        "path": "a/index",
        "data": 1000,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "a",
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": { "index": 2, "value": "c" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": { "index": 1, "value": "b" },
              "c": { "index": 2, "value": "c" }
            }
          }
        ]
      },
      {
        ".comment": "update from server to move all but one entry out of window",
        "type": "serverUpdate",
        "path": "b/index",
        "data": 1000,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "b",
            "data": { "index": 1, "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "c": { "index": 2, "value": "c" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Update to single child that moves out of window",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "startAt": { "index": 1 },
          "endAt": { "index": 10 },
          "limitToFirst": 2
        },
        "events": []
      },
      {
        ".comment": "update from server sends all data",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "index": 1, "value": "a" },
          "b": { "index": 2, "value": "b" },
          "c": { "index": 3, "value": "c" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "index": 2, "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 2, "value": "b" }
            }
          }
        ]
      },
      {
        ".comment": "update from server to move child out of query",
        "type": "serverUpdate",
        "path": "a/index",
        "data": -1,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "a",
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": { "index": 3, "value": "c" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": { "index": 2, "value": "b" },
              "c": { "index": 3, "value": "c" }
            }
          }
        ]
      },
      {
        ".comment": "update from server to move child out of query",
        "type": "serverUpdate",
        "path": "b/index",
        "data": -1,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "b",
            "data": { "index": 2, "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "c": { "index": 3, "value": "c" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Limited query doesn't pull in out of range child",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "startAt": { "index": 1 },
          "endAt": { "index": 10 },
          "limitToFirst": 2
        },
        "events": []
      },
      {
        ".comment": "update from server sends all data",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "index": 1, "value": "a" },
          "b": { "index": 2, "value": "b" },
          "c": { "index": 1000, "value": "c" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "index": 2, "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 2, "value": "b" }
            }
          }
        ]
      },
      {
        ".comment": "update from server to move child out of query",
        "type": "serverUpdate",
        "path": "a/index",
        "data": -1,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "a",
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": { "index": 2, "value": "b" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Merge for location with default and limited listener",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        ".comment": "complete update",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "index": 1, "value": "a" },
          "b": { "index": 2, "value": "b" },
          "c": { "index": 3, "value": "c" },
          "d": { "index": 4, "value": "d" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "index": 2, "value": "b" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": { "index": 3, "value": "c" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "d",
            "prevName": "c",
            "data": { "index": 4, "value": "d" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 2, "value": "b" },
              "c": { "index": 3, "value": "c" },
              "d": { "index": 4, "value": "d" }
            }
          }
        ]
      },
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "limitToFirst": 2
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "index": 2, "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 2, "value": "b" }
            }
          }
        ]
      },
      {
        ".comment": "update from server pulls in other node",
        "type": "serverMerge",
        "path": "",
        "data": {
          "a": null,
          "d": null
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "a",
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_removed",
            "name": "a",
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_removed",
            "name": "d",
            "data": { "index": 4, "value": "d" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": { "index": 3, "value": "c" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": { "index": 2, "value": "b" },
              "c": { "index": 3, "value": "c" }
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": { "index": 2, "value": "b" },
              "c": { "index": 3, "value": "c" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "User merge pulls in correct values",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "startAt": { "index": 1 },
          "endAt": { "index": 10 },
          "limitToFirst": 3
        },
        "events": []
      },
      {
        ".comment": "update from server sends all data",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "index": 1, "value": "a" },
          "b": { "index": 2, "value": "b" },
          "c": { "index": 3, "value": "c" },
          "d": { "index": 1000, "value": "d" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "index": 2, "value": "b" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": { "index": 3, "value": "c" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 2, "value": "b" },
              "c": { "index": 3, "value": "c" }
            }
          }
        ]
      },
      {
        ".comment": "user merge pulls in existing value",
        "type": "update",
        "path": "d",
        "data": { "index": 2 },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "c",
            "data": { "index": 3, "value": "c" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "d",
            "prevName": "b",
            "data": { "index": 2, "value": "d" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 2, "value": "b" },
              "d": { "index": 2, "value": "d" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "User deep set pulls in correct values",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "startAt": { "index": 1 },
          "endAt": { "index": 10 },
          "limitToFirst": 3
        },
        "events": []
      },
      {
        ".comment": "update from server sends all data",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "index": 1, "value": "a" },
          "b": { "index": 2, "value": "b" },
          "c": { "index": 3, "value": "c" },
          "d": { "index": 1000, "value": "d" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "index": 2, "value": "b" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": { "index": 3, "value": "c" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 2, "value": "b" },
              "c": { "index": 3, "value": "c" }
            }
          }
        ]
      },
      {
        ".comment": "user deep set pulls in existing value",
        "type": "set",
        "path": "d/index",
        "data": 2,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "c",
            "data": { "index": 3, "value": "c" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "d",
            "prevName": "b",
            "data": { "index": 2, "value": "d" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 2, "value": "b" },
              "d": { "index": 2, "value": "d" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Queries with equalTo(null) work",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "startAt": { "index": null },
          "endAt": { "index": null }
        },
        "events": []
      },
      {
        ".comment": "update from server sends all data",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "value": "a" },
          "b": { "value": "b" },
          "c": { "value": "c", "index": 1 }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "value": "a" },
              "b": { "value": "b" }
            }
          }
        ]
      },
      {
        ".comment": "server updates existing value (bringing c into query)",
        "type": "serverUpdate",
        "path": "c/index",
        "data": null,
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": { "value": "c" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "value": "a" },
              "b": { "value": "b" },
              "c": { "value": "c" }
            }
          }
        ]
      },
      {
        ".comment": "server updates existing value (sending c out of query)",
        "type": "serverUpdate",
        "path": "c/index",
        "data": 1,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "c",
            "data": { "value": "c" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "value": "a" },
              "b": { "value": "b" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Reverted writes update query",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "startAt": { "index": 1 },
          "endAt": { "index": 10 },
          "limitToFirst": 2
        },
        "events": []
      },
      {
        ".comment": "update from server sends only query data",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "index": 1, "value": "a" },
          "b": { "index": 5, "value": "b" },
          "d": { "index": 6, "value": "d" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "index": 5, "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 5, "value": "b" }
            }
          }
        ]
      },
      {
        ".comment": "user adds new value should update query",
        "type": "set",
        "path": "",
        "data": {
          "c": { "index": 2, "value": "c" },
          "a": { "index": 1, "value": "a" }
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "b",
            "data": { "index": 5, "value": "b" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "a",
            "data": { "index": 2, "value": "c" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "c": { "index": 2, "value": "c" }
            }
          }
        ]
      },
      {
        ".comment": "write is reverted should revert query to old state",
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": true,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "c",
            "data": { "index": 2, "value": "c" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "index": 5, "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 5, "value": "b" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Deep set for non-local data doesn't raise events",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "startAt": { "index": 1 },
          "endAt": { "index": 10 },
          "limitToFirst": 2
        },
        "events": []
      },
      {
        ".comment": "update from server sends only query data",
        "type": "serverUpdate",
        "path": "",
        "tag": 1,
        "data": {
          "a": { "index": 1, "value": "a" },
          "b": { "index": 5, "value": "b" }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "index": 1, "value": "a" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "index": 5, "value": "b" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "b": { "index": 5, "value": "b" }
            }
          }
        ]
      },
      {
        ".comment": "user updates a value for node outside of query, should trigger no events",
        "type": "set",
        "path": "c/index",
        "data": 1,
        "events": [ ]
      },
      {
        ".comment": "update from server now contains complete data",
        "type": "serverMerge",
        "path": "",
        "tag": 1,
        "data": {
          "c": { "index": 1, "value": "c" }
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "b",
            "data": { "index": 5, "value": "b" }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "a",
            "data": { "index": 1, "value": "c" }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": { "index": 1, "value": "a" },
              "c": { "index": 1, "value": "c" }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "User update with new children triggers events",
    "steps":
    [
      {
        "type": "listen",
        "path": "",
        "params": {
          "orderBy": "value",
          "tag": 1
        },
        "events": []
      },
      {
        ".comment": "update from server sends query data",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "value": 5 },
          "c": { "value": 3 }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": null,
            "data": { "value": 3 }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": "c",
            "data": { "value": 5 }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "c": { "value": 3 },
              "a": { "value": 5 }
            }
          }
        ]
      },
      {
        ".comment": "user adds new children through an update",
        "type": "update",
        "path": "",
        "data": {
          "b": { "value": 4 },
          "d": { "value": 2 }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "d",
            "prevName": null,
            "data": { "value": 2 }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "c",
            "data": { "value": 4 }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "d": { "value": 2 },
              "c": { "value": 3 },
              "b": { "value": 4 },
              "a": { "value": 5 }
            }
          }
        ]
      },
      {
        ".comment": "server send new server",
        "type": "serverMerge",
        "path": "",
        "data": {
          "b": { "value": 4 },
          "d": { "value": 2 }
        },
        "events": []
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": false,
        "events": [ ]
      }
    ]
  },
  {
    "name": "User write with deep user overwrite",
    "steps":
    [
      {
        "type": "listen",
        "path": "/foo",
        "params": {
          "orderBy": "value",
          "tag": 1
        },
        "events": []
      },
      {
        ".comment": "user sets initial data",
        "type": "set",
        "path": "/foo",
        "data": {
          "a": { "value": 1 },
          "b": { "value": 5 },
          "c": { "value": 10 }
        },
        "events": [
          {
            "path": "/foo",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": { "value": 1 }
          },
          {
            "path": "/foo",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": { "value": 5 }
          },
          {
            "path": "/foo",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": { "value": 10 }
          },
          {
            "path": "/foo",
            "type": "value",
            "data": {
              "a": { "value": 1 },
              "b": { "value": 5 },
              "c": { "value": 10 }
            }
          }
        ]
      },
      {
        ".comment": "user quickly overwrites value",
        "type": "set",
        "path": "/foo/c/value",
        "data": 3,
        "events": [
          {
            "path": "/foo",
            "type": "child_moved",
            "name": "c",
            "prevName": "a",
            "data": { "value": 3 }
          },
          {
            "path": "/foo",
            "type": "child_changed",
            "name": "c",
            "prevName": "a",
            "data": { "value": 3 }
          },
          {
            "path": "/foo",
            "type": "value",
            "data": {
              "a": { "value": 1 },
              "c": { "value": 3 },
              "b": { "value": 5 }
            }
          }
        ]
      },
      {
        ".comment": "server sends complete but outdated data",
        "type": "serverUpdate",
        "path": "/foo",
        "data": {
          "a": { "value": 1 },
          "b": { "value": 5 },
          "c": { "value": 10 }
        },
        "events": [ ]
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "events": [ ]
      },
      {
        ".comment": "server sends update",
        "type": "serverUpdate",
        "path": "/foo/c/value",
        "data": 3,
        "events": [ ]
      },
      {
        "type": "ackUserWrite",
        "writeId": 1,
        "events": [ ]
      }
    ]
  },

  {
    "name": "Deep server merge",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "foo": {
            "bar1" : { "a": "baz1", "b": "qux1" },
            "bar2" : { "a": "baz2", "b": "qux2" }
          }
        },
        "events": [
          {
            "path": "foo",
            "type": "child_added",
            "name": "bar1",
            "prevName": null,
            "data": { "a": "baz1", "b": "qux1" }
          },
          {
            "path": "foo",
            "type": "child_added",
            "name": "bar2",
            "prevName": "bar1",
            "data": { "a": "baz2", "b": "qux2" }
          },
          {
            "path": "foo",
            "type": "value",
            "data": {
              "bar1" : { "a": "baz1", "b": "qux1" },
              "bar2" : { "a": "baz2", "b": "qux2" }
            }
          }
        ]
      },
      {
        "type": "serverMerge",
        "path": "foo",
        "data": {
          "bar1/a": "newbaz1",
          "bar2/b": "newqux2"
        },
        "events": [
          {
            "path": "foo",
            "type": "child_changed",
            "name": "bar1",
            "prevName": null,
            "data": { "a": "newbaz1", "b": "qux1" }
          },
          {
            "path": "foo",
            "type": "child_changed",
            "name": "bar2",
            "prevName": "bar1",
            "data": { "a": "baz2", "b": "newqux2" }
          },
          {
            "path": "foo",
            "type": "value",
            "data": {
              "bar1" : { "a": "newbaz1", "b": "qux1" },
              "bar2" : { "a": "baz2", "b": "newqux2" }
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Server updates priority",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "listen",
        "path": "a/foo",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "data": { "foo": "bar" },
        "events": [
          {
            "path": "a/foo",
            "type": "value",
            "data": "bar"
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "a",
            "type": "value",
            "data": { "foo": "bar" }
          }
        ]
      },
      {
        "type": "serverUpdate",
        "path": "a/foo/.priority",
        "data": "qux",
        "events": [
          {
            "path": "a/foo",
            "type": "value",
            "data": { ".value": "bar", ".priority": "qux" }
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "foo",
            "prevName": null,
            "data": { ".value": "bar", ".priority": "qux" }
          },
          {
            "path": "a",
            "type": "child_moved",
            "name": "foo",
            "prevName": null,
            "data": { ".value": "bar", ".priority": "qux" }
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "foo": { ".value": "bar", ".priority": "qux" }
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Revert underlying full overwrite",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "key-a": "val-a",
          "key-b": "val-b"
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-a",
            "prevName": null,
            "data": "val-a"
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-b",
            "prevName": "key-a",
            "data": "val-b"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-a": "val-a",
              "key-b": "val-b"
            }
          }
        ]
      },
      {
        "type": "set",
        "path": "",
        "data": {
          "key-c": "val-c",
          "key-d": "val-d"
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "key-a",
            "data": "val-a"
          },
          {
            "path": "",
            "type": "child_removed",
            "name": "key-b",
            "data": "val-b"
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-c",
            "prevName": null,
            "data": "val-c"
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-d",
            "prevName": "key-c",
            "data": "val-d"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-c": "val-c",
              "key-d": "val-d"
            }
          }
        ]
      },
      {
        "type": "set",
        "path": "",
        "data": {
          "key-e": "val-e",
          "key-f": "val-f"
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "key-c",
            "data": "val-c"
          },
          {
            "path": "",
            "type": "child_removed",
            "name": "key-d",
            "data": "val-d"
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-e",
            "prevName": null,
            "data": "val-e"
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-f",
            "prevName": "key-e",
            "data": "val-f"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-e": "val-e",
              "key-f": "val-f"
            }
          }
        ]
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": true,
        "events": [ ]
      }
    ]
  },

  {
    "name": "User child overwrite for non-existent server node",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "set",
        "path": "foo",
        "data": { "bar": "qux" },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": { "bar": "qux" }
          }
        ]
      }
    ]
  },

  {
    "name": "Revert user overwrite of child on leaf node",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": "foo",
        "events": [
          {
            "path": "",
            "type": "value",
            "data": "foo"
          }
        ]
      },
      {
        "type": "set",
        "path": "key",
        "data": "value",
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key",
            "prevName": null,
            "data": "value"
          },
          {
            "path": "",
            "type": "value",
            "data": { "key": "value" }
          }
        ]
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": true,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "key",
            "prevName": null,
            "data": "value"
          },
          {
            "path": "",
            "type": "value",
            "data": "foo"
          }
        ]
      }
    ]
  },

  {
    "name": "Server overwrite with deep user delete",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "key-1": "value-1"
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-1",
            "prevName": null,
            "data": "value-1"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": "value-1"
            }
          }
        ]
      },
      {
        ".comment": "User deletes non-existent key, which shouldn't trigger events",
        "type": "set",
        "path": "key-2/non-key",
        "data": null,
        "events": []
      },
      {
        ".comment": "Server updates node with deep user delete",
        "type": "serverUpdate",
        "path": "key-2",
        "data": {
          "deep-key": "deep-value"
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-2",
            "prevName": "key-1",
            "data": {
              "deep-key": "deep-value"
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": "value-1",
              "key-2": {
                "deep-key": "deep-value"
              }
            }
          }
        ]
      }
    ]
  },

  {
    "name": "User overwrites leaf node with priority",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          ".value": "value",
          ".priority": "prio"
        },
        "events": [
          {
            "path": "",
            "type": "value",
            "data": {
              ".value": "value",
              ".priority": "prio"
            }
          }
        ]
      },
      {
        ".comment": "Overwrite leaf with children node",
        "type": "set",
        "path": "foo",
        "data": "bar",
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              ".priority": "prio",
              "foo": "bar"
            }
          }
        ]
      }
    ]
  },

  {
    "name": "User overwrites inherit priority values from leaf nodes",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "set",
        "path": "",
        "data": {
          ".value": "value",
          ".priority": "prio"
        },
        "events": [
          {
            "path": "",
            "type": "value",
            "data": {
              ".value": "value",
              ".priority": "prio"
            }
          }
        ]
      },
      {
        ".comment": "user updates the node",
        "type": "set",
        "path": "foo",
        "data": "foo-value",
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "foo-value"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              ".priority": "prio",
              "foo": "foo-value"
            }
          }
        ]
      },
      {
        ".comment": "The server updates the data for the set",
        "type": "serverUpdate",
        "path": "",
        "data": {
          ".value": "value",
          ".priority": "prio"
        },
        "events": []
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": false,
        "events": []
      },
      {
        ".comment": "Add another update, should not have old priority",
        "type": "set",
        "path": "bar",
        "data": "bar-value",
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "bar",
            "prevName": null,
            "data": "bar-value"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              ".priority": "prio",
              "foo": "foo-value",
              "bar": "bar-value"
            }
          }
        ]
      }
    ]
  },

  {
    "name": "User update on user set leaf node with priority after server update",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": null,
        "events": [
          {
            "path": "",
            "type": "value",
            "data": null
          }
        ]
      },
      {
        "type": "set",
        "path": "",
        "data": {
          ".value": "value",
          ".priority": "prio"
        },
        "events": [
          {
            "path": "",
            "type": "value",
            "data": {
              ".value": "value",
              ".priority": "prio"
            }
          }
        ]
      },
      {
        ".comment": "user overwrite shadows server data",
        "type": "serverMerge",
        "path": "",
        "data": {
          "foo": "bar"
        },
        "events": [ ]
      },
      {
        ".comment": "user updates the node",
        "type": "update",
        "path": "deep/deeper",
        "data": {
            "0-key": null,
            "key": "value"
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "deep",
            "prevName": null,
            "data": { "deeper": { "key": "value" } }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              ".priority": "prio",
              "deep": { "deeper": { "key": "value" } }
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Server deep delete on leaf node",
    ".comment": "This is a contrived example, as the server will probably not send null updates to leaf nodes",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": "foo",
        "events": [
          {
            "path": "",
            "type": "value",
            "data": "foo"
          }
        ]
      },
      {
        ".comment": "this should trigger no events",
        "type": "serverUpdate",
        "path": "deep/child",
        "data": null,
        "events": []
      }
    ]
  },

  {
    "name": "User sets root priority",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "foo": "bar"
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "foo": "bar"
            }
          }
        ]
      },
      {
        "type": "set",
        "path": ".priority",
        "data": "prio",
        "events": [
          {
            "path": "",
            "type": "value",
            "data": {
              ".priority": "prio",
              "foo": "bar"
            }
          }
        ]
      }
    ]
  },

  {
    "name": "User updates priority on empty root",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        ".comment": "Priority on empty root should not trigger events",
        "type": "set",
        "path": ".priority",
        "data": "prio",
        "events": []
      },
      {
        ".comment": "This should a value event without priority",
        "type": "serverUpdate",
        "path": "",
        "data": null,
        "events": [
          {
            "path": "",
            "type": "value",
            "data": null
          }
        ]
      },
      {
        ".comment": "This should now have the user priority",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "foo": "bar"
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              ".priority": "prio",
              "foo": "bar"
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Revert set at root with priority",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "foo": "bar"
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "foo": "bar"
            }
          }
        ]
      },
      {
        ".comment": "User overwrites root",
        "type": "set",
        "path": "",
        "data": {
          "baz": "qux",
          ".priority": "prio"
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "",
            "type": "child_added",
            "name": "baz",
            "prevName": null,
            "data": "qux"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              ".priority": "prio",
              "baz": "qux"
            }
          }
        ]
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": true,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "baz",
            "prevName": null,
            "data": "qux"
          },
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "foo": "bar"
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Server updates priority after user sets priority",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": { ".value": "foo", ".priority": "prio" },
        "events": [
          {
            "path": "",
            "type": "value",
            "data": { ".value": "foo", ".priority": "prio" }
          }
        ]
      },
      {
        ".comment": "User overwrites priority",
        "type": "set",
        "path": ".priority",
        "data": "prio-2",
        "events": [
          {
            "path": "",
            "type": "value",
            "data": { ".value": "foo", ".priority": "prio-2" }
          }
        ]
      },
      {
        ".comment": "this should not trigger any events since a user write is shadowing",
        "type": "serverUpdate",
        "path": ".priority",
        "data": null,
        "events": [ ]
      }
    ]
  },

  {
    "name": "User updates priority twice, first is reverted",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": { "foo": "bar" },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          },
          {
            "path": "",
            "type": "value",
            "data": { "foo": "bar" }
          }
        ]
      },
      {
        ".comment": "User overwrites priority first time",
        "type": "set",
        "path": ".priority",
        "data": "prio-1",
        "events": [
          {
            "path": "",
            "type": "value",
            "data": {
              "foo": "bar",
              ".priority": "prio-1"
            }
          }
        ]
      },
      {
        ".comment": "User overwrites priority second time",
        "type": "set",
        "path": ".priority",
        "data": "prio-2",
        "events": [
          {
            "path": "",
            "type": "value",
            "data": {
              "foo": "bar",
              ".priority": "prio-2"
            }
          }
        ]
      },
      {
        ".comment": "revert should not trigger event",
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": true,
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "foo",
        "data": "new-bar",
        "events": [
          {
            "path": "",
            "type": "child_changed",
            "name": "foo",
            "prevName": null,
            "data": "new-bar"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "foo": "new-bar",
              ".priority": "prio-2"
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Server acks root priority set after user deletes root node",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": "foo",
        "events": [
          {
            "path": "",
            "type": "value",
            "data": "foo"
          }
        ]
      },
      {
        ".comment": "User overwrites root priority",
        "type": "set",
        "path": ".priority",
        "data": "prio",
        "events": [
          {
            "path": "",
            "type": "value",
            "data": {
              ".value": "foo",
              ".priority": "prio"
            }
          }
        ]
      },
      {
        ".comment": "User deletes root node",
        "type": "set",
        "path": "",
        "data": null,
        "events": [
          {
            "path": "",
            "type": "value",
            "data": null
          }
        ]
      },
      {
        "type": "serverUpdate",
        "path": ".priority",
        "data": "prio",
        "events": []
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": false,
        "events": []
      }
    ]
  },

  {
    "name": "A delete in a merge doesn't push out nodes",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "limitToFirst": 3,
          "startAt": {"index": null}
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "key-1": 1,
          "key-3": 3,
          "key-4": 4
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-1",
            "prevName": null,
            "data": 1
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-3",
            "prevName": "key-1",
            "data": 3
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-4",
            "prevName": "key-3",
            "data": 4
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": 1,
              "key-3": 3,
              "key-4": 4
            }
          }
        ]
      },
      {
        ".comment": "Since key-3 is deleted, key-5 should still remain in the query",
        "type": "serverMerge",
        "path": "",
        "data": {
          "key-3": null,
          "key-2": 2
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "key-3",
            "data": 3
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-2",
            "prevName": "key-1",
            "data": 2
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": 1,
              "key-2": 2,
              "key-4": 4
            }
          }
        ]
      }
    ]
  },

  {
    "name": "A tagged query fires events eventually",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "limitToLast": 2
        },
        "events": []
      },
      {
        "type": "set",
        "path": "",
        "data": {
          "key-1": 1,
          "key-2": 2,
          "key-3": 3
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-2",
            "prevName": null,
            "data": 2
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-3",
            "prevName": "key-2",
            "data": 3
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-2": 2,
              "key-3": 3
            }
          }
        ]
      },
      {
        ".comment": "Server updates tagged data, should filter key-1 node",
        "type": "serverUpdate",
        "path": "",
        "tag": 1,
        "data": {
          "key-2": 2,
          "key-3": 3
        },
        "events": []
      },
      {
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": false,
        "events": []
      },
      {
        ".comment": "User deletes element, only child removed event is fired, since data is not available",
        "type": "set",
        "path": "key-2",
        "data": null,
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "key-2",
            "data": 2
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-3": 3
            }
          }
        ]
      },
      {
        ".comment": "Server updates tagged data, should filter key-1 node",
        "type": "serverMerge",
        "path": "",
        "tag": 1,
        "data": {
          "key-1": 1,
          "key-2": null
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-1",
            "prevName": null,
            "data": 1
          },
          {
            "path": "",
            "type": "value",
            "name": "",
            "data": {
              "key-1": 1,
              "key-3": 3
            }
          }
        ]
      }
    ]
  },

  {
    "name": "A server update that leaves user sets unchanged is not ignored",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "key-1": 1,
          "key-2": 2
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-1",
            "prevName": null,
            "data": 1
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-2",
            "prevName": "key-1",
            "data": 2
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": 1,
              "key-2": 2
            }
          }
        ]
      },
      {
        ".comment": "user adds a new node",
        "type": "set",
        "path": "key-3",
        "data": 3,
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-3",
            "prevName": "key-2",
            "data": 3
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": 1,
              "key-2": 2,
              "key-3": 3
            }
          }
        ]
      },
      {
        ".comment": "Server adds new children with full overwrite",
        "type": "serverUpdate",
        "path": "",
        "data": {
          "key-1": 1,
          "key-2": 2,
          "key-4": 4
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-4",
            "prevName": "key-3",
            "data": 4
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": 1,
              "key-2": 2,
              "key-3": 3,
              "key-4": 4
            }
          }
        ]
      }
    ]
  },

  {
    "name": "User write outside of limit is ignored for tagged queries",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "limitToFirst": 2,
          "startAt": {"index": null}
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "tag": 1,
        "path": "",
        "data": {
          "key-1": {
            "index": 1,
            "other-key": "foo"
          },
          "key-4": {
            "index": 4,
            "other-key": "bar"
          }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-1",
            "prevName": null,
            "data": {
              "index": 1,
              "other-key": "foo"
            }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-4",
            "prevName": "key-1",
            "data": {
              "index": 4,
              "other-key": "bar"
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": {
                "index": 1,
                "other-key": "foo"
              },
              "key-4": {
                "index": 4,
                "other-key": "bar"
              }
            }
          }
        ]
      },
      {
        ".comment": "user updates index of child outside, which should bring it in view eventually, but not before the server sends the complete node",
        "type": "set",
        "path": "key-2/index",
        "data": 2,
        "events": []
      },
      {
        ".comment": "In the meantime the server adds another node",
        "type": "serverUpdate",
        "tag": 1,
        "path": "",
        "data": {
          "key-1": {
            "index": 1,
            "other-key": "new-foo"
          },
          "key-3": {
            "index": 3,
            "other-key": "baz"
          }
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "key-4",
            "data": {
              "index": 4,
              "other-key": "bar"
            }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-3",
            "prevName": "key-1",
            "data": {
              "index": 3,
              "other-key": "baz"
            }
          },
          {
            "path": "",
            "type": "child_changed",
            "name": "key-1",
            "prevName": null,
            "data": {
              "index": 1,
              "other-key": "new-foo"
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": {
                "index": 1,
                "other-key": "new-foo"
              },
              "key-3": {
                "index": 3,
                "other-key": "baz"
              }
            }
          }
        ]
      },
      {
        ".comment": "Server now incorporates user update",
        "type": "serverUpdate",
        "tag": 1,
        "path": "key-2",
        "data": {
          "index": 2,
          "other-key": "qux"
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "key-3",
            "data": {
              "index": 3,
              "other-key": "baz"
            }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "key-2",
            "prevName": "key-1",
            "data": {
              "index": 2,
              "other-key": "qux"
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": {
                "index": 1,
                "other-key": "new-foo"
              },
              "key-2": {
                "index": 2,
                "other-key": "qux"
              }
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Ack for merge doesn't raise value event for later listen",
    "steps": [
      {
        "type": "update",
        "path": "",
        "data": {
          "foo": "bar"
        },
        "events": []
      },
      {
        "type": "listen",
        "path": "",
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": "bar"
          }
        ]
      },
      {
        "type": "ackUserWrite",
        ".comment": "This acks a merge, so we can't raise a value event yet",
        "writeId": 0,
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "foo": "bar",
          "qux": "quux"
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "qux",
            "prevName": "foo",
            "data": "quux"
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "foo": "bar",
              "qux": "quux"
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Clear parent shadowing server values merge with server children",
    "steps": [
      {
        "type": "listen",
        "path": "a/b",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "a/b",
        "data": 2,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 2
          }
        ]
      },
      {
        "type": "update",
        "path": "a",
        "data": {"b": 28, "c": 3},
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 28
          }
        ]
      },
      {
        ".comment": "This listen should get a complete event snap, as well as complete server children",
        "type": "listen",
        "path": "a",
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": 28
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": 3
          }
        ]
      },
      {
        ".comment": "Do a serverUpdate with a conflicting value for b, simulates a server value. It's still shadowed though",
        "type": "serverUpdate",
        "path": "a/b",
        "data": 29,
        "events": []
      },
      {
        ".comment": "Clearing the set should result in updated values for b",
        "type": "ackUserWrite",
        "writeId": 0,
        "events": [
          {
            "path": "a/b",
            "type": "value",
            "data": 29
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": 29
          }
        ]
      }
    ]
  },

  {
    "name": "Priorities don't make me sick",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "set",
        "path": "a/foo",
        "data": {
          "bar": "baz",
          ".priority": "prio"
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "foo",
            "prevName": null,
            "data": {
              "bar": "baz",
              ".priority": "prio"
            }
          }
        ]
      },
      {
        "type": "set",
        "path": "a/foo/bar",
        "data": null,
        "events": [
          {
            "path": "a",
            "type": "child_removed",
            "name": "foo",
            "data": {
              "bar": "baz",
              ".priority": "prio"
            }
          }
        ]
      },
      {
        ".comment": "this caused vomiting in the past...",
        "type": "set",
        "path": "a/foo/bar",
        "data": null,
        "events": []
      }
    ]
  },

  {
    "name": "Merge that moves child from window to boundary does not cause child to be readded",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": [],
        "params": {
          "tag": 1,
          "limitToFirst": 2,
          "startAt": {"index": 1},
          "orderBy": "index"
        }
      },
      {
        "type": "serverUpdate",
        "path": "a",
        "tag": 1,
        "data": {
          "2-a": {
            "index": 10
          },
          "1-b": {
            "index": 20
          }
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "2-a",
            "prevName": null,
            "data": {
              "index": 10
            }
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "1-b",
            "prevName": "2-a",
            "data": {
              "index": 20
            }
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "2-a": { "index": 10 },
              "1-b": { "index": 20 }
            }
          }
        ]
      },
      {
        ".comment": "2-a will be the 'next' child after the old '1-b' which will be updated first, but it shouldn't be added because it will actually be out of the window...",
        "type": "update",
        "path": "a",
        "data": {
          "1-b": { "index": 0 },
          "2-a": { "index": 30 },
          "3-c": { "index": 5 },
          "4-d": { "index": 6 }
        },
        "events": [
          {
            "path": "a",
            "type": "child_removed",
            "name": "2-a",
            "data": { "index": 10 }
          },
          {
            "path": "a",
            "type": "child_removed",
            "name": "1-b",
            "data": { "index": 20 }
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "3-c",
            "prevName": null,
            "data": { "index": 5 }
          },
          {
            "path": "a",
            "type": "child_added",
            "name": "4-d",
            "prevName": "3-c",
            "data": { "index": 6 }
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "3-c": { "index": 5 },
              "4-d": { "index": 6 }
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Deep merge ack is handled correctly.",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        ".comment": "Initial server data.",
        "type": "serverUpdate",
        "path": "a",
        "data": null,
        "events": [
          {
            "path": "a",
            "type": "value",
            "data": null
          }
        ]
      },
      {
        ".comment": "Do deep merge.",
        "type": "update",
        "path": "a/b",
        "data": {
          "c": 42,
          "d": "hi"
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": {
              "c": 42,
              "d": "hi"
            }
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "b": {
                "c": 42,
                "d": "hi"
              }
            }
          }
        ]
      },
      {
        ".comment": "Server update for our deep merge.",
        "type": "serverUpdate",
        "path": "a/b",
        "data": {
          "c": 42,
          "d": "hi"
        },
        "events": []
      },
      {
        ".comment": "ack deep merge.",
        "type": "ackUserWrite",
        "writeId": 0,
        "events": []
      }
    ]
  },

  {
    "name": "Deep merge ack (on incomplete data, and with server values)",
    "steps": [
      {
        "type": "listen",
        "path": "a/b",
        "events": []
      },
      {
        ".comment": "Initial server data.",
        "type": "serverUpdate",
        "path": "a/b",
        "data": {
          "c": "original-server-value"
        },
        "events": [
          {
            "path": "a/b",
            "type": "child_added",
            "name": "c",
            "data": "original-server-value",
            "prevName": null
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {
              "c": "original-server-value"
            }
          }
        ]
      },
      {
        ".comment": "Do deep merge.",
        "type": "update",
        "path": "a/b",
        "data": {
          "c": "user-merge-value"
        },
        "events": [
          {
            "path": "a/b",
            "type": "child_changed",
            "name": "c",
            "data": "user-merge-value",
            "prevName": null
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {
              "c": "user-merge-value"
            }
          }
        ]
      },
      {
        ".comment": "Listen on a (which won't have complete data).",
        "type": "listen",
        "path": "a",
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "name": "b",
            "prevName": null,
            "data": {
              "c": "user-merge-value"
            }
          }
        ]
      },
      {
        ".comment": "Server update for our deep merge, but change data (simulate server value).",
        "type": "serverUpdate",
        "path": "a/b",
        "data": {
          "c": "user-merge-value-after-server-resolution"
        },
        "events": []
      },
      {
        ".comment": "ack deep merge.",
        "type": "ackUserWrite",
        "writeId": 0,
        "events": [
          {
            "path": "a/b",
            "type": "child_changed",
            "name": "c",
            "data": "user-merge-value-after-server-resolution",
            "prevName": null
          },
          {
            "path": "a/b",
            "type": "value",
            "data": {
              "c": "user-merge-value-after-server-resolution"
            }
          },
          {
            "path": "a",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": {
              "c": "user-merge-value-after-server-resolution"
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Limit query handles deep server merge for out-of-view item.",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "params": {
          "tag": 1,
          "limitToFirst": 1
        },
        "events": []
      },
      {
        ".comment": "Initial server data.",
        "type": "serverUpdate",
        "path": "foo",
        "tag": 1,
        "data": {
          "a": {
            "val": "a-val",
            ".priority": "a-pri"
          }
        },
        "events": [
          {
            "path": "foo",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": {
              "val": "a-val",
              ".priority": "a-pri"
            }
          },
          {
            "path": "foo",
            "type": "value",
            "data": {
              "a": {
                "val": "a-val",
                ".priority": "a-pri"
              }
            }
          }
        ]
      },
      {
        ".comment": "Server merge for out-of-view child 'b' (perhaps for another listener).  Shouldn't trigger events since we don't have complete data.",
        "type": "serverMerge",
        "path": "foo/b",
        "data": {
          "val": "b-val"
        },
        "events": [ ]
      }
    ]
  },

  {
    "name": "Limit query handles deep user merge for out-of-view item.",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "params": {
          "tag": 1,
          "limitToFirst": 1
        },
        "events": []
      },
      {
        ".comment": "Initial server data.",
        "type": "serverUpdate",
        "path": "foo",
        "tag": 1,
        "data": {
          "a": {
            "val": "a-val",
            ".priority": "a-pri"
          }
        },
        "events": [
          {
            "path": "foo",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": {
              "val": "a-val",
              ".priority": "a-pri"
            }
          },
          {
            "path": "foo",
            "type": "value",
            "data": {
              "a": {
                "val": "a-val",
                ".priority": "a-pri"
              }
            }
          }
        ]
      },
      {
        ".comment": "User merge for out-of-view child 'b'.  Shouldn't trigger events since we don't have complete data.",
        "type": "update",
        "path": "foo/b",
        "data": {
          "val": "b-val"
        },
        "events": [ ]
      }
    ]
  },

  {
    "name": "Limit query handles deep user merge for out-of-view item followed by server update.",
    "steps": [
      {
        "type": "listen",
        "path": "foo",
        "params": {
          "tag": 1,
          "limitToFirst": 1
        },
        "events": []
      },
      {
        ".comment": "Initial server data.",
        "type": "serverUpdate",
        "path": "foo",
        "tag": 1,
        "data": {
          "a": {
            "val": "a-val",
            ".priority": "a-pri"
          }
        },
        "events": [
          {
            "path": "foo",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": {
              "val": "a-val",
              ".priority": "a-pri"
            }
          },
          {
            "path": "foo",
            "type": "value",
            "data": {
              "a": {
                "val": "a-val",
                ".priority": "a-pri"
              }
            }
          }
        ]
      },
      {
        ".comment": "User merge for out-of-view child 'b'.  Shouldn't trigger events since we don't have complete data.",
        "type": "update",
        "path": "foo/b",
        "data": {
          "val": "b-val-new"
        },
        "events": [ ]
      },
      {
        ".comment": "Server update for 'b', bringing it into view.",
        "type": "serverUpdate",
        "path": "foo/b",
        "data": {
          "val": "b-val-old",
          "val2": "b-val2"
        },
        "events": [
          {
            "type": "child_removed",
            "path": "foo",
            "name": "a",
            "prevName": null,
            "data": {
              "val": "a-val",
              ".priority": "a-pri"
            }
          },
          {
            "type": "child_added",
            "path": "foo",
            "name": "b",
            "prevName": null,
            "data": {
              "val": "b-val-new",
              "val2": "b-val2"
            }
          },
          {
            "type": "value",
            "path": "foo",
            "data": {
              "b": {
                "val": "b-val-new",
                "val2": "b-val2"
              }
            }
          }
        ]
      }
    ]
  },

  {
    "name": "Unrelated, untagged update is not cached in tagged listen",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "limitToFirst": 1,
          "startAt": {"index": null}
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "tag": 1,
        "path": "",
        "data": {
          "key-1": {
            "index": 1,
            "other-key": "foo"
          }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-1",
            "prevName": null,
            "data": {
              "index": 1,
              "other-key": "foo"
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": {
                "index": 1,
                "other-key": "foo"
              }
            }
          }
        ]
      },
      {
        ".comment": "server sends update for key-2 which should not be cached or marked complete",
        "type": "serverUpdate",
        "path": "key-2",
        "data": {
          "index": 2,
          "other-key": "bar"
        },
        "events": []
      },
      {
        ".comment": "Now an update for key-1 comes in, marking query as filtered",
        "type": "serverMerge",
        "tag": 1,
        "path": "key-1",
        "data": {
          "other-key": "new-foo"
        },
        "events": [
          {
            "path": "",
            "type": "child_changed",
            "name": "key-1",
            "prevName": null,
            "data": {
              "index": 1,
              "other-key": "new-foo"
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": {
                "index": 1,
                "other-key": "new-foo"
              }
            }
          }
        ]
      },
      {
        ".comment": "Server now updates node out of view, should not mark view unfiltered",
        "type": "serverUpdate",
        "path": "key-3",
        "data": { "index": 3, "other-key": "qux" },
        "events": []
      },
      {
        ".comment": "Server now updates node out of view, should not raise any events",
        "type": "serverMerge",
        "path": "key-2",
        "data": { "index": 0 },
        "events": []
      }
    ]
  },

  {
    "name": "Unrelated, acked set is not cached in tagged listen",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "limitToFirst": 1,
          "startAt": {"index": null}
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "tag": 1,
        "path": "",
        "data": {
          "key-1": {
            "index": 1,
            "other-key": "foo"
          }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-1",
            "prevName": null,
            "data": {
              "index": 1,
              "other-key": "foo"
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": {
                "index": 1,
                "other-key": "foo"
              }
            }
          }
        ]
      },
      {
        "type": "set",
        "path": "key-1/other-key",
        "data": "new-foo",
        "events": [
          {
            "path": "",
            "type": "child_changed",
            "name": "key-1",
            "prevName": null,
            "data": {
              "index": 1,
              "other-key": "new-foo"
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": {
                "index": 1,
                "other-key": "new-foo"
              }
            }
          }
        ]
      },
      {
        "type": "serverUpdate",
        "path": "key-1/other-key",
        "tag": 1,
        "data": "new-foo",
        "events": []
      },
      {
        ".comment": "The ack should not mark key-2 complete in tagged listen",
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": false,
        "events": []
      },
      {
        ".comment": "Server now updates node out of view, should not raise any events",
        "type": "serverMerge",
        "path": "key-2",
        "data": { "index": 0 },
        "events": []
      }
    ]
  },

  {
    "name": "Unrelated, acked update is not cached in tagged listen",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "index",
          "limitToFirst": 1,
          "startAt": {"index": null}
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "tag": 1,
        "path": "",
        "data": {
          "key-1": {
            "index": 1,
            "other-key": "foo"
          }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "key-1",
            "prevName": null,
            "data": {
              "index": 1,
              "other-key": "foo"
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": {
                "index": 1,
                "other-key": "foo"
              }
            }
          }
        ]
      },
      {
        "type": "update",
        "path": "key-1",
        "data": {
          "other-key": "new-foo"
        },
        "events": [
          {
            "path": "",
            "type": "child_changed",
            "name": "key-1",
            "prevName": null,
            "data": {
              "index": 1,
              "other-key": "new-foo"
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "key-1": {
                "index": 1,
                "other-key": "new-foo"
              }
            }
          }
        ]
      },
      {
        "type": "serverMerge",
        "path": "key-1",
        "tag": 1,
        "data": {
          "other-key": "new-foo"
        },
        "events": []
      },
      {
        ".comment": "The ack should not mark key-2 complete in tagged listen",
        "type": "ackUserWrite",
        "writeId": 0,
        "revert": false,
        "events": []
      },
      {
        ".comment": "Server now updates node out of view, should not raise any events",
        "type": "serverMerge",
        "path": "key-2",
        "data": { "index": 0 },
        "events": []
      }
    ]
  },
  {
    "name": "Deep update raises immediate events only if has complete data",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "age",
          "limitToLast": 1
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "tag": 1,
        "data": {
          "a": {
            "age": 4
          }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": {
              "age": 4
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": {
                "age": 4
              }
            }
          }
        ]
      },
      {
        "type": "update",
        "path": "",
        "data": {
          "a/age": 0,
          "e": {
            "age": 4
          }
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "a",
            "data": {
              "age": 4
            }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "e",
            "prevName": null,
            "data": {
              "age": 4
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "e": {
                "age": 4
              }
            }
          }
        ]
      },
      {
        ".comment": "Now we don't have a full data for child /f, don't raise the event. The events for child /e are correct, although may be confusing for customers.",
        "type": "update",
        "path": "",
        "data": {
          "e/age": 0,
          "f/age": 4
        },
        "events": [
          {
            "path": "",
            "type": "child_moved",
            "name": "e",
            "prevName": null,
            "data": {
              "age": 0
            }
          },
          {
            "path": "",
            "type": "child_changed",
            "name": "e",
            "prevName": null,
            "data": {
              "age": 0
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "e": {
                "age": 0
              }
            }
          }
        ]
      },
      {
        "type": "serverMerge",
        "path": "",
        "tag": 1,
        "data": {
          "f": {
            "age": 4
          }
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "e",
            "data": {
              "age": 0
            }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "f",
            "prevName": null,
            "data": {
              "age": 4
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "f": {
                "age": 4
              }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Deep update returns minimum data required",
    "steps": [
      {
        "type": "listen",
        "path": "",
        "params": {
          "tag": 1,
          "orderBy": "idx",
          "equalTo": { "index": true }
        },
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "tag": 1,
        "data": {
          "a": {
            "name": "foo",
            "idx": true
          },
          "b": {
            "name": "bar",
            "idx": true
          }
        },
        "events": [
          {
            "path": "",
            "type": "child_added",
            "name": "a",
            "prevName": null,
            "data": {
              "name": "foo",
              "idx": true
            }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "b",
            "prevName": "a",
            "data": {
              "name": "bar",
              "idx": true
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "a": {
                "name": "foo",
                "idx": true
              },
              "b": {
                "name": "bar",
                "idx": true
              }
            }
          }
        ]
      },
      {
        "type": "serverMerge",
        "path": "",
        "tag": 1,
        "data": {
          "a/idx": false,
          "b/name": "blah",
          "c": {
            "name": "bar",
            "idx": true
          }
        },
        "events": [
          {
            "path": "",
            "type": "child_removed",
            "name": "a",
            "data": {
              "name": "foo",
              "idx": true
            }
          },
          {
            "path": "",
            "type": "child_changed",
            "name": "b",
            "prevName": null,
            "data": {
              "name": "blah",
              "idx": true
            }
          },
          {
            "path": "",
            "type": "child_added",
            "name": "c",
            "prevName": "b",
            "data": {
              "name": "bar",
              "idx": true
            }
          },
          {
            "path": "",
            "type": "value",
            "data": {
              "b": {
                "name": "blah",
                "idx": true
              },
              "c": {
                "name": "bar",
                "idx": true
              }
            }
          }
        ]
      }
    ]
  },
  {
    "name": "Deep update raises all events",
    "steps": [
      {
        "type": "listen",
        "path": "a",
        "events": []
      },
      {
        "type": "listen",
        "path": "b",
        "events": []
      },
      {
        "type": "serverUpdate",
        "path": "",
        "data": {
          "a": { "aa": 1, "ab": 2 },
          "b": { "ba": 3, "bb": 4 }
        },
        "events": [
          {
            "path": "a",
            "type": "child_added",
            "prevName": null,
            "name": "aa",
            "data": 1
          },
          {
            "path": "a",
            "type": "child_added",
            "prevName": "aa",
            "name": "ab",
            "data": 2
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "aa": 1,
              "ab": 2
            }
          },
          {
            "path": "b",
            "type": "child_added",
            "prevName": null,
            "name": "ba",
            "data": 3
          },
          {
            "path": "b",
            "type": "child_added",
            "prevName": "ba",
            "name": "bb",
            "data": 4
          },
          {
            "path": "b",
            "type": "value",
            "data": {
              "ba": 3,
              "bb": 4
            }
          }
        ]
      },
      {
        "type": "update",
        "path": "",
        "data": {
          "a/aa": 0,
          "b/ba": 0
        },
        "events": [
          {
            "path": "a",
            "type": "child_changed",
            "prevName": null,
            "name": "aa",
            "data": 0
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "aa": 0,
              "ab": 2
            }
          },
          {
            "path": "b",
            "type": "child_changed",
            "prevName": null,
            "name": "ba",
            "data": 0
          },
          {
            "path": "b",
            "type": "value",
            "data": {
              "ba": 0,
              "bb": 4
            }
          }
        ]
      },
      {
        "type": "serverMerge",
        "path": "a",
        "data": {
          "ab/abc": 1,
          "ac/acd": 2
        },
        "events": [
          {
            "path": "a",
            "type": "child_changed",
            "prevName": "aa",
            "name": "ab",
            "data": { "abc": 1 }
          },
          {
            "path": "a",
            "type": "child_added",
            "prevName": "ab",
            "name": "ac",
            "data": { "acd": 2 }
          },
          {
            "path": "a",
            "type": "value",
            "data": {
              "aa": 0,
              "ab": { "abc": 1 },
              "ac": { "acd": 2 }
            }
          }
        ]
      }
    ]
  }
]
