diff --git a/lib/json0.js b/lib/json0.js index dc3a405..a93ffe4 100644 --- a/lib/json0.js +++ b/lib/json0.js @@ -43,6 +43,16 @@ var clone = function(o) { return JSON.parse(JSON.stringify(o)); }; +var validateListIndex = function(key) { + if (typeof key !== 'number') + throw new Error('List index must be a number'); +}; + +var validateObjectKey = function (key) { + if (typeof key !== 'string') + throw new Error('Object key must be a number'); +}; + /** * JSON OT Type * @type {*} @@ -162,6 +172,12 @@ json.apply = function(snapshot, op) { elem = elem[key]; key = p; + if (isArray(elem) && typeof key !== 'number') + throw new Error('List index must be a number'); + + if (isObject(elem) && typeof key !== 'string') + throw new Error('Object key must be a string'); + if (parent == null) throw new Error('Path invalid'); } @@ -175,6 +191,9 @@ json.apply = function(snapshot, op) { if (typeof elem[key] != 'number') throw new Error('Referenced element not a number'); + if (typeof c.na !== 'number') + throw new Error('Number addition is not a number'); + elem[key] += c.na; } @@ -200,6 +219,9 @@ json.apply = function(snapshot, op) { // List move else if (c.lm !== void 0) { + if (typeof c.lm !== 'number') + throw new Error('List move target index must be a number'); + json.checkList(elem); if (c.lm != key) { var e = elem[key]; diff --git a/lib/text0.js b/lib/text0.js index e26c6a9..238d448 100644 --- a/lib/text0.js +++ b/lib/text0.js @@ -60,6 +60,10 @@ var checkValidOp = function(op) { text.apply = function(snapshot, op) { var deleted; + var type = typeof snapshot; + if (type !== 'string') + throw new Error('text0 operations cannot be applied to type: ' + type); + checkValidOp(op); for (var i = 0; i < op.length; i++) { var component = op[i]; diff --git a/test/json0.coffee b/test/json0.coffee index 531f76e..e2ee6df 100644 --- a/test/json0.coffee +++ b/test/json0.coffee @@ -64,6 +64,11 @@ genTests = (type) -> c_s = type.apply leftHas, right_ assert.deepEqual s_c, c_s + it 'throws when adding a string to a number', -> + assert.throws -> type.apply 1, [{p: [], na: 'a'}] + + it 'throws when adding a number to a string', -> + assert.throws -> type.apply 'a', [{p: [], na: 1}] # Strings should be handled internally by the text type. We'll just do some basic sanity checks here. describe 'string', -> @@ -72,6 +77,12 @@ genTests = (type) -> assert.deepEqual 'bc', type.apply 'abc', [{p:[0], sd:'a'}] assert.deepEqual {x:'abc'}, type.apply {x:'a'}, [{p:['x', 1], si:'bc'}] + it 'throws when the target is not a string', -> + assert.throws -> type.apply [1], [{p: [0], si: 'a'}] + + it 'throws when the inserted content is not a string', -> + assert.throws -> type.apply 'a', [{p: [0], si: 1}] + describe '#transform()', -> it 'splits deletes', -> assert.deepEqual type.transform([{p:[0], sd:'ab'}], [{p:[1], si:'x'}], 'left'), [{p:[0], sd:'a'}, {p:[1], sd:'b'}] @@ -127,6 +138,24 @@ genTests = (type) -> assert.deepEqual ['a', 'b', 'c'], type.apply ['b', 'a', 'c'], [{p:[1], lm:0}] assert.deepEqual ['a', 'b', 'c'], type.apply ['b', 'a', 'c'], [{p:[0], lm:1}] + it 'throws when keying a list replacement with a string', -> + assert.throws -> type.apply ['a', 'b', 'c'], [{p: ['0'], li: 'x', ld: 'a'}] + + it 'throws when keying a list insertion with a string', -> + assert.throws -> type.apply ['a', 'b', 'c'], [{p: ['0'], li: 'x'}] + + it 'throws when keying a list deletion with a string', -> + assert.throws -> type.apply ['a', 'b', 'c'], [{p: ['0'], ld: 'a'}] + + it 'throws when keying a list move with a string', -> + assert.throws -> type.apply ['a', 'b', 'c'], [{p: ['0'], lm: 0}] + + it 'throws when specifying a string as a list move target', -> + assert.throws -> type.apply ['a', 'b', 'c'], [{p: [1], lm: '0'}] + + it 'throws when an array index part-way through the path is a string', -> + assert.throws -> type.apply {arr:[{x:'a'}]}, [{p:['arr', '0', 'x'], od: 'a'}] + ### 'null moves compose to nops', -> assert.deepEqual [], type.compose [], [{p:[3],lm:3}] @@ -389,6 +418,15 @@ genTests = (type) -> assert.deepEqual [], type.transform [{p:['k'], od:'x'}], [{p:['k'], od:'x'}], 'left' assert.deepEqual [], type.transform [{p:['k'], od:'x'}], [{p:['k'], od:'x'}], 'right' + it 'throws when the insertion key is a number', -> + assert.throws -> type.apply {'1':'a'}, [{p:[2], oi: 'a'}] + + it 'throws when the deletion key is a number', -> + assert.throws -> type.apply {'1':'a'}, [{p:[1], od: 'a'}] + + it 'throws when an object key part-way through the path is a number', -> + assert.throws -> type.apply {'1': {x: 'a'}}, [{p:[1, 'x'], od: 'a'}] + describe 'randomizer', -> @timeout 20000 @slow 6000