|
| 1 | +import { expect } from "chai"; |
| 2 | +import { PRIORITY_INDEX } from "../../src/database/core/snap/indexes/PriorityIndex"; |
| 3 | +import { LeafNode } from "../../src/database/core/snap/LeafNode"; |
| 4 | +import { IndexMap } from "../../src/database/core/snap/IndexMap"; |
| 5 | +import { Path } from "../../src/database/core/util/Path"; |
| 6 | +import { SortedMap } from "../../src/database/core/util/SortedMap"; |
| 7 | +import { ChildrenNode } from "../../src/database/core/snap/ChildrenNode"; |
| 8 | +import { NAME_COMPARATOR } from "../../src/database/core/snap/comparators"; |
| 9 | +import { nodeFromJSON } from "../../src/database/core/snap/nodeFromJSON"; |
| 10 | + |
| 11 | +describe.only('Node Tests', function() { |
| 12 | + var DEFAULT_INDEX = PRIORITY_INDEX; |
| 13 | + |
| 14 | + it('Create leaf nodes of various types.', function() { |
| 15 | + var x = new LeafNode(5, new LeafNode(42)); |
| 16 | + expect(x.getValue()).to.equal(5); |
| 17 | + expect(x.getPriority().val()).to.equal(42); |
| 18 | + expect(x.isLeafNode()).to.equal(true); |
| 19 | + |
| 20 | + x = new LeafNode('test'); |
| 21 | + expect(x.getValue()).to.equal('test'); |
| 22 | + x = new LeafNode(true); |
| 23 | + expect(x.getValue()).to.equal(true); |
| 24 | + }); |
| 25 | + |
| 26 | + it("LeafNode.updatePriority returns a new leaf node without changing the old.", function() { |
| 27 | + var x = new LeafNode("test", new LeafNode(42)); |
| 28 | + var y = x.updatePriority(new LeafNode(187)); |
| 29 | + |
| 30 | + // old node is the same. |
| 31 | + expect(x.getValue()).to.equal("test"); |
| 32 | + expect(x.getPriority().val()).to.equal(42); |
| 33 | + |
| 34 | + // new node has the new priority but the old value. |
| 35 | + expect(y.getValue()).to.equal("test"); |
| 36 | + expect(y.getPriority().val()).to.equal(187); |
| 37 | + }); |
| 38 | + |
| 39 | + it("LeafNode.updateImmediateChild returns a new children node.", function() { |
| 40 | + var x = new LeafNode("test", new LeafNode(42)); |
| 41 | + var y = x.updateImmediateChild('test', new LeafNode("foo")); |
| 42 | + |
| 43 | + expect(y.isLeafNode()).to.equal(false); |
| 44 | + expect(y.getPriority().val()).to.equal(42); |
| 45 | + expect(y.getImmediateChild('test').getValue()).to.equal('foo'); |
| 46 | + }); |
| 47 | + |
| 48 | + it("LeafNode.getImmediateChild returns an empty node.", function() { |
| 49 | + var x = new LeafNode("test"); |
| 50 | + expect(x.getImmediateChild('foo')).to.equal(ChildrenNode.EMPTY_NODE); |
| 51 | + }); |
| 52 | + |
| 53 | + it("LeafNode.getChild returns an empty node.", function() { |
| 54 | + var x = new LeafNode('test'); |
| 55 | + expect(x.getChild(new Path('foo/bar'))).to.equal(ChildrenNode.EMPTY_NODE); |
| 56 | + }); |
| 57 | + |
| 58 | + it('ChildrenNode.updatePriority returns a new internal node without changing the old.', function() { |
| 59 | + var x = ChildrenNode.EMPTY_NODE.updateImmediateChild("child", new LeafNode(5)); |
| 60 | + var children = x.children_; |
| 61 | + var y = x.updatePriority(new LeafNode(17)); |
| 62 | + expect(y.children_).to.equal(x.children_); |
| 63 | + expect(x.children_).to.equal(children); |
| 64 | + expect(x.getPriority().val()).to.equal(null); |
| 65 | + expect(y.getPriority().val()).to.equal(17); |
| 66 | + }); |
| 67 | + |
| 68 | + it('ChildrenNode.updateImmediateChild returns a new internal node with the new child, without changing the old.', |
| 69 | + function() { |
| 70 | + var children = new SortedMap(NAME_COMPARATOR); |
| 71 | + var x = new ChildrenNode(children, ChildrenNode.EMPTY_NODE, IndexMap.Default); |
| 72 | + var newValue = new LeafNode('new value'); |
| 73 | + var y = x.updateImmediateChild('test', newValue); |
| 74 | + expect(x.children_).to.equal(children); |
| 75 | + expect(y.children_.get('test')).to.equal(newValue); |
| 76 | + }); |
| 77 | + |
| 78 | + it("ChildrenNode.updateChild returns a new internal node with the new child, without changing the old.", function() { |
| 79 | + var children = new SortedMap(NAME_COMPARATOR); |
| 80 | + var x = new ChildrenNode(children, ChildrenNode.EMPTY_NODE, IndexMap.Default); |
| 81 | + var newValue = new LeafNode("new value"); |
| 82 | + var y = x.updateChild(new Path('test/foo'), newValue); |
| 83 | + expect(x.children_).to.equal(children); |
| 84 | + expect(y.getChild(new Path('test/foo'))).to.equal(newValue); |
| 85 | + }); |
| 86 | + |
| 87 | + it("Node.hash() works correctly.", function() { |
| 88 | + var node = nodeFromJSON({ |
| 89 | + intNode:4, |
| 90 | + doubleNode:4.5623, |
| 91 | + stringNode:"hey guys", |
| 92 | + boolNode:true |
| 93 | + }); |
| 94 | + |
| 95 | + // !!!NOTE!!! These hashes must match what the server generates. If you change anything so these hashes change, |
| 96 | + // make sure you change the corresponding server code. |
| 97 | + expect(node.getImmediateChild("intNode").hash()).to.equal("eVih19a6ZDz3NL32uVBtg9KSgQY="); |
| 98 | + expect(node.getImmediateChild("doubleNode").hash()).to.equal("vf1CL0tIRwXXunHcG/irRECk3lY="); |
| 99 | + expect(node.getImmediateChild("stringNode").hash()).to.equal("CUNLXWpCVoJE6z7z1vE57lGaKAU="); |
| 100 | + expect(node.getImmediateChild("boolNode").hash()).to.equal("E5z61QM0lN/U2WsOnusszCTkR8M="); |
| 101 | + |
| 102 | + expect(node.hash()).to.equal("6Mc4jFmNdrLVIlJJjz2/MakTK9I="); |
| 103 | + }); |
| 104 | + |
| 105 | + it("Node.hash() works correctly with priorities.", function() { |
| 106 | + var node = nodeFromJSON({ |
| 107 | + root: {c: {'.value': 99, '.priority': 'abc'}, '.priority': 'def'} |
| 108 | + }); |
| 109 | + |
| 110 | + expect(node.hash()).to.equal("Fm6tzN4CVEu5WxFDZUdTtqbTVaA="); |
| 111 | + }); |
| 112 | + |
| 113 | + it("Node.hash() works correctly with number priorities.", function() { |
| 114 | + var node = nodeFromJSON({ |
| 115 | + root: {c: {'.value': 99, '.priority': 42}, '.priority': 3.14} |
| 116 | + }); |
| 117 | + |
| 118 | + expect(node.hash()).to.equal("B15QCqrzCxrI5zz1y00arWqFRFg="); |
| 119 | + }); |
| 120 | + |
| 121 | + it("Node.hash() stress...", function() { |
| 122 | + var node = nodeFromJSON({ |
| 123 | + a:-1.7976931348623157e+308, |
| 124 | + b:1.7976931348623157e+308, |
| 125 | + c:"unicode ✔ 🐵 🌴 x͢", |
| 126 | + d:3.14159265358979323846264338327950, |
| 127 | + e: { |
| 128 | + '.value': 12345678901234568, |
| 129 | + '.priority': "🐵" |
| 130 | + }, |
| 131 | + "✔": "foo", |
| 132 | + '.priority':"✔" |
| 133 | + }); |
| 134 | + expect(node.getImmediateChild('a').hash()).to.equal('7HxgOBDEC92uQwhCuuvKA2rbXDA='); |
| 135 | + expect(node.getImmediateChild('b').hash()).to.equal('8R+ekVQmxs6ZWP0fdzFHxVeGnWo='); |
| 136 | + expect(node.getImmediateChild('c').hash()).to.equal('JoKoFUnbmg3/DlY70KaDWslfYPk='); |
| 137 | + expect(node.getImmediateChild('d').hash()).to.equal('Y41iC5+92GIqXfabOm33EanRI8s='); |
| 138 | + expect(node.getImmediateChild('e').hash()).to.equal('+E+Mxlqh5MhT+On05bjsZ6JaaxI='); |
| 139 | + expect(node.getImmediateChild('✔').hash()).to.equal('MRRL/+aA/uibaL//jghUpxXS/uY='); |
| 140 | + expect(node.hash()).to.equal('CyC0OU8GSkOAKnsPjheWtWC0Yxo='); |
| 141 | + }); |
| 142 | + |
| 143 | + it("ChildrenNode.getPredecessorChild works correctly.", function() { |
| 144 | + var node = nodeFromJSON({ |
| 145 | + d: true, a: true, g: true, c: true, e: true |
| 146 | + }); |
| 147 | + |
| 148 | + // HACK: Pass null instead of the actual childNode, since it's not actually needed. |
| 149 | + expect(node.getPredecessorChildName('a', null, DEFAULT_INDEX)).to.equal(null); |
| 150 | + expect(node.getPredecessorChildName('c', null, DEFAULT_INDEX)).to.equal('a'); |
| 151 | + expect(node.getPredecessorChildName('d', null, DEFAULT_INDEX)).to.equal('c'); |
| 152 | + expect(node.getPredecessorChildName('e', null, DEFAULT_INDEX)).to.equal('d'); |
| 153 | + expect(node.getPredecessorChildName('g', null, DEFAULT_INDEX)).to.equal('e'); |
| 154 | + }); |
| 155 | + |
| 156 | + it("SortedChildrenNode.getPredecessorChild works correctly.", function() { |
| 157 | + var node = nodeFromJSON({ |
| 158 | + d: { '.value': true, '.priority' : 22 }, |
| 159 | + a: { '.value': true, '.priority' : 25 }, |
| 160 | + g: { '.value': true, '.priority' : 19 }, |
| 161 | + c: { '.value': true, '.priority' : 23 }, |
| 162 | + e: { '.value': true, '.priority' : 21 } |
| 163 | + }); |
| 164 | + |
| 165 | + expect(node.getPredecessorChildName('a', node.getImmediateChild('a'), DEFAULT_INDEX)).to.equal('c'); |
| 166 | + expect(node.getPredecessorChildName('c', node.getImmediateChild('c'), DEFAULT_INDEX)).to.equal('d'); |
| 167 | + expect(node.getPredecessorChildName('d', node.getImmediateChild('d'), DEFAULT_INDEX)).to.equal('e'); |
| 168 | + expect(node.getPredecessorChildName('e', node.getImmediateChild('e'), DEFAULT_INDEX)).to.equal('g'); |
| 169 | + expect(node.getPredecessorChildName('g', node.getImmediateChild('g'), DEFAULT_INDEX)).to.equal(null); |
| 170 | + }); |
| 171 | + |
| 172 | + it("SortedChildrenNode.updateImmediateChild works correctly.", function() { |
| 173 | + var node = nodeFromJSON({ |
| 174 | + d: { '.value': true, '.priority' : 22 }, |
| 175 | + a: { '.value': true, '.priority' : 25 }, |
| 176 | + g: { '.value': true, '.priority' : 19 }, |
| 177 | + c: { '.value': true, '.priority' : 23 }, |
| 178 | + e: { '.value': true, '.priority' : 21 }, |
| 179 | + '.priority' : 1000 |
| 180 | + }); |
| 181 | + |
| 182 | + node = node.updateImmediateChild('c', nodeFromJSON(false)); |
| 183 | + expect(node.getImmediateChild('c').getValue()).to.equal(false); |
| 184 | + expect(node.getImmediateChild('c').getPriority().val()).to.equal(null); |
| 185 | + expect(node.getPriority().val()).to.equal(1000); |
| 186 | + }); |
| 187 | + |
| 188 | + it("removing nodes correctly removes intermediate nodes with no remaining children", function() { |
| 189 | + var json = {a: {b: {c: 1}}}; |
| 190 | + var node = nodeFromJSON(json); |
| 191 | + var newNode = node.updateChild(new Path('a/b/c'), ChildrenNode.EMPTY_NODE); |
| 192 | + expect(newNode.isEmpty()).to.equal(true); |
| 193 | + }); |
| 194 | + |
| 195 | + it("removing nodes leaves intermediate nodes with other children", function() { |
| 196 | + var json = {a: {b: {c: 1}, d: 2}}; |
| 197 | + var node = nodeFromJSON(json); |
| 198 | + var newNode = node.updateChild(new Path('a/b/c'), ChildrenNode.EMPTY_NODE); |
| 199 | + expect(newNode.isEmpty()).to.equal(false); |
| 200 | + expect(newNode.getChild(new Path('a/b/c')).isEmpty()).to.equal(true); |
| 201 | + expect(newNode.getChild(new Path('a/d')).val()).to.equal(2); |
| 202 | + }); |
| 203 | + |
| 204 | + it("removing nodes leaves other leaf nodes", function() { |
| 205 | + var json = {a: {b: {c: 1, d: 2}}}; |
| 206 | + var node = nodeFromJSON(json); |
| 207 | + var newNode = node.updateChild(new Path('a/b/c'), ChildrenNode.EMPTY_NODE); |
| 208 | + expect(newNode.isEmpty()).to.equal(false); |
| 209 | + expect(newNode.getChild(new Path('a/b/c')).isEmpty()).to.equal(true); |
| 210 | + expect(newNode.getChild(new Path('a/b/d')).val()).to.equal(2); |
| 211 | + }); |
| 212 | + |
| 213 | + it("removing nodes correctly removes the root", function() { |
| 214 | + var json = null; |
| 215 | + var node = nodeFromJSON(json); |
| 216 | + var newNode = node.updateChild(new Path(''), ChildrenNode.EMPTY_NODE); |
| 217 | + expect(newNode.isEmpty()).to.equal(true); |
| 218 | + |
| 219 | + json = {a: 1}; |
| 220 | + node = nodeFromJSON(json); |
| 221 | + newNode = node.updateChild(new Path('a'), ChildrenNode.EMPTY_NODE); |
| 222 | + expect(newNode.isEmpty()).to.equal(true); |
| 223 | + }); |
| 224 | + |
| 225 | + it("ignores null values", function() { |
| 226 | + var json = {a: 1, b: null}; |
| 227 | + var node = nodeFromJSON(json); |
| 228 | + expect(node.children_.get('b')).to.equal(null); |
| 229 | + }); |
| 230 | + |
| 231 | + it("Leading zeroes in path are handled properly", function() { |
| 232 | + var json = {"1": 1, "01": 2, "001": 3}; |
| 233 | + var tree = nodeFromJSON(json); |
| 234 | + expect(tree.getChild(new Path("1")).val()).to.equal(1); |
| 235 | + expect(tree.getChild(new Path("01")).val()).to.equal(2); |
| 236 | + expect(tree.getChild(new Path("001")).val()).to.equal(3); |
| 237 | + }); |
| 238 | + |
| 239 | + it("Treats leading zeroes as objects, not array", function() { |
| 240 | + var json = {"3": 1, "03": 2}; |
| 241 | + var tree = nodeFromJSON(json); |
| 242 | + var val = tree.val(); |
| 243 | + expect(val).to.deep.equal(json); |
| 244 | + }); |
| 245 | + |
| 246 | + it("Updating empty children doesn't overwrite leaf node", function() { |
| 247 | + var empty = ChildrenNode.EMPTY_NODE; |
| 248 | + var node = nodeFromJSON("value"); |
| 249 | + expect(node).to.deep.equal(node.updateChild(new Path(".priority"), empty)); |
| 250 | + expect(node).to.deep.equal(node.updateChild(new Path("child"), empty)); |
| 251 | + expect(node).to.deep.equal(node.updateChild(new Path("child/.priority"), empty)); |
| 252 | + expect(node).to.deep.equal(node.updateImmediateChild("child", empty)); |
| 253 | + expect(node).to.deep.equal(node.updateImmediateChild(".priority", empty)); |
| 254 | + }); |
| 255 | +}); |
0 commit comments