Files
Josh SorefandGitHub b4e60981a5 Spelling (#8280)
Numerous fixes to spelling across the SDK.
2024-07-18 16:53:00 -04:00

8447 lines
182 KiB
JSON

[
{
"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": "michael 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": "michael 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": "michael 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": "michael 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": "Queries with startAfter and endBefore work",
"steps":
[
{
"type": "listen",
"path": "",
"params": {
"tag": 1,
"orderBy": "index",
"startAfter": { "index": 1 },
"endBefore": { "index": 10 }
},
"events": []
},
{
".comment": "update from server sends all data",
"type": "serverUpdate",
"path": "",
"data": {
"a": { "index": 0, "value": "a" },
"b": { "index": 2, "value": "b" },
"c": { "index": 9, "value": "c" },
"d": { "index": 11, "value": "d" }
},
"events": [
{
"path": "",
"type": "child_added",
"name": "b",
"prevName": null,
"data": { "index": 2, "value": "b" }
},
{
"path": "",
"type": "child_added",
"name": "c",
"prevName": "b",
"data": { "index": 9, "value": "c" }
},
{
"path": "",
"type": "value",
"data": {
"b": { "index": 2, "value": "b" },
"c": { "index": 9, "value": "c" }
}
}
]
},
{
".comment": "update from server to move child c out of query",
"type": "serverUpdate",
"path": "c/index",
"data": 10,
"events": [
{
"path": "",
"type": "child_removed",
"name": "c",
"data": { "index": 9, "value": "c" }
},
{
"path": "",
"type": "value",
"data": {
"b": { "index": 2, "value": "b" }
}
}
]
},
{
".comment": "update from server to move child b out of window",
"type": "serverUpdate",
"path": "b/index",
"data": 1,
"events": [
{
"path": "",
"type": "child_removed",
"name": "b",
"data": { "index": 2, "value": "b" }
},
{
"path": "",
"type": "value",
"data": {}
}
]
}
]
},
{
"name": "Queries with startAfter, endBefore, and orderByChild work",
"steps":
[
{
"type": "listen",
"path": "",
"params": {
"tag": 1,
"orderBy": "foo/index",
"startAfter": { "index": 1, "name": "foo" },
"endBefore": { "index": 10, "name": "foo" }
},
"events": []
},
{
".comment": "update from server sends all data",
"type": "serverUpdate",
"path": "",
"data": {
"a": { "foo": { "index": 0, "value": "a" } },
"b": { "foo": { "index": 2, "value": "b" } },
"c": { "foo": { "index": 9, "value": "c" } },
"d": { "foo": { "index": 11, "value": "d" } }
},
"events": [
{
"path": "",
"type": "child_added",
"name": "b",
"prevName": null,
"data": { "foo": { "index": 2, "value": "b" } }
},
{
"path": "",
"type": "child_added",
"name": "c",
"prevName": "b",
"data": { "foo": { "index": 9, "value": "c" } }
},
{
"path": "",
"type": "value",
"data": {
"b": { "foo": { "index": 2, "value": "b" } },
"c": { "foo": { "index": 9, "value": "c" } }
}
}
]
}
]
},
{
"name": "Queries indexed by key with startAfter, endBefore, and limitToFirst work",
"steps":
[
{
"type": "listen",
"path": "",
"params": {
"tag": 1,
"orderByKey": true,
"startAfter": { "index": "a" },
"endBefore": { "index": "d" },
"limitToFirst": 1
},
"events": []
},
{
".comment": "update from server sends all data",
"type": "serverUpdate",
"path": "",
"data": {
"a": { ".value": "a" },
"b": { ".value": "b" },
"c": { ".value": "c" },
"d": { ".value": "d" }
},
"events": [
{
"path": "",
"type": "child_added",
"name": "b",
"prevName": null,
"data": { ".value": "b" }
},
{
"path": "",
"type": "value",
"data": {
"b": { ".value": "b" }
}
}
]
}
]
},
{
"name": "Queries indexed by key with startAfter, endBefore, and limitToLast work",
"steps":
[
{
"type": "listen",
"path": "",
"params": {
"tag": 1,
"orderByKey": true,
"startAfter": { "index": "a" },
"endBefore": { "index": "e" },
"limitToLast": 2
},
"events": []
},
{
".comment": "update from server sends all data",
"type": "serverUpdate",
"path": "",
"data": {
"a": { ".value": "a" },
"b": { ".value": "b" },
"c": { ".value": "c" },
"d": { ".value": "d" },
"e": { ".value": "e" }
},
"events": [
{
"path": "",
"type": "child_added",
"name": "c",
"prevName": null,
"data": { ".value": "c" }
},
{
"path": "",
"type": "child_added",
"name": "d",
"prevName": "c",
"data": { ".value": "d" }
},
{
"path": "",
"type": "value",
"data": {
"c": { ".value": "c" },
"d": { ".value": "d" }
}
}
]
}
]
},
{
"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 nonexistent 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 nonexistent 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 }
}
}
]
}
]
}
]