Skip to content

Commit

Permalink
Added OT tests and fixed some minor issues
Browse files Browse the repository at this point in the history
  • Loading branch information
josephg committed Jul 30, 2013
1 parent 4348d79 commit 06bf8b7
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 7 deletions.
24 changes: 17 additions & 7 deletions lib/ot.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@

otTypes = require 'ottypes'

exports.normalize = (opData) ->
if opData.create
# We should store the full URI of the type, not just its short name
opData.create.type = otTypes[opData.create.type].uri

# Returns an error string on failure.
exports.checkOpData = (opData) ->
return 'Missing opData' unless typeof opData is 'object'
Expand All @@ -19,6 +14,14 @@ exports.checkOpData = (opData) ->
return 'Invalid seq' if opData.seq? and typeof opData.seq isnt 'number'
return 'seq but not src' if !!opData.seq isnt !!opData.src

exports.normalize = (opData) ->
# I'd love to also normalize opData.op if it exists, but I don't know the
# type of the operation. And I can't find that out until after transforming
# the operation anyway.
if opData.create
# We should store the full URI of the type, not just its short name
opData.create.type = otTypes[opData.create.type].uri

defaultValidate = ->

# This is the super apply function that takes in snapshot data (including the type) and edits it in-place.
Expand Down Expand Up @@ -99,18 +102,25 @@ exports.apply = (data, opData) ->
return

exports.transform = (type, opData, appliedOpData) ->
return 'Document was deleted' if appliedOpData.del
if appliedOpData.del
if opData.del
opData.v++ if opData.v?
return
else
return 'Document was deleted'

return 'Document created remotely' if appliedOpData.create # type will be null / undefined in this case.

return 'Document does not exist' unless type

return 'Version mismatch' if opData.v? and opData.v != appliedOpData.v

if typeof type is 'string'
type = otTypes[type]
return "Type not found" unless type

opData.op = type.transform opData.op, appliedOpData.op, 'left'
opData.v++
opData.v++ if opData.v?

return

Expand Down
128 changes: 128 additions & 0 deletions test/ot.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Unit tests for lib/ot.js
#
# This tests to make sure it does some of the right things WRT OT.
#
# Note that there's also OT code in other livedb files. This file does not
# contain any integration tests.

assert = require 'assert'

# For the simple OT type.
{simple, text} = require 'ottypes'

ot = require '../lib/ot'

describe 'ot', ->
describe 'checkOpData', ->
it 'fails if opdata is not an object', ->
assert.ok ot.checkOpData 'hi'
assert.ok ot.checkOpData()
assert.ok ot.checkOpData 123
assert.ok ot.checkOpData []

it 'fails if op data is missing op, create and del', ->
assert.ok ot.checkOpData {v:5}

it 'fails if src/seq data is invalid', ->
assert.ok ot.checkOpData {del:true, v:5, src:'hi'}
assert.ok ot.checkOpData {del:true, v:5, seq:123}
assert.ok ot.checkOpData {del:true, v:5, src:'hi', seq:'there'}

it 'accepts valid create operations', ->
assert.equal null, ot.checkOpData {create:{type:simple.uri}}
assert.equal null, ot.checkOpData {create:{type:simple.uri, data:'hi there'}}

it 'accepts valid delete operations', ->
assert.equal null, ot.checkOpData {del:true}

it 'accepts valid ops', ->
assert.equal null, ot.checkOpData {op:[1,2,3]}

describe 'normalize', ->
it 'expands type names', ->
opData = create:type:'simple'
ot.normalize opData
assert.deepEqual opData, {create:type:simple.uri}

describe 'apply', ->
it 'fails if the versions dont match', ->
assert.equal 'Version mismatch', ot.apply {v:5}, {v:6, create:{type:simple.uri}}
assert.equal 'Version mismatch', ot.apply {v:5}, {v:6, del:true}
assert.equal 'Version mismatch', ot.apply {v:5}, {v:6, op:[]}

it 'allows the version field to be missing', ->
assert.equal null, ot.apply {v:5}, {create:{type:simple.uri}}
assert.equal null, ot.apply {}, {v:6, create:{type:simple.uri}}

describe 'create', ->
it 'fails if the document already exists', ->
doc = {v:6, create:{type:simple.uri}}
assert.equal 'Document already exists', ot.apply {v:6, type:simple.uri, data:'hi'}, doc
# The doc should be unmodified
assert.deepEqual doc, {v:6, create:{type:simple.uri}}

it 'creates doc data correctly when no initial data is passed', ->
doc = {v:5}
assert.equal null, ot.apply doc, {v:5, create:{type:simple.uri}}
assert.deepEqual doc, {v:6, type:simple.uri, data:str:''}

it 'creates doc data when it is given initial data', ->
doc = {v:5}
assert.equal null, ot.apply doc, {v:5, create:{type:simple.uri, data:'Hi there'}}
assert.deepEqual doc, {v:6, type:simple.uri, data:str:'Hi there'}

it.skip 'runs pre and post validation functions'

describe 'del', ->
it 'deletes the document data', ->
doc = {v:6, type:simple.uri, data:str:'Hi there'}
assert.equal null, ot.apply doc, {v:6, del:true}
assert.deepEqual doc, {v:7}

it 'still works if the document doesnt exist anyway', ->
doc = {v:6}
assert.equal null, ot.apply doc, {v:6, del:true}
assert.deepEqual doc, {v:7}

describe 'op', ->
it 'fails if the document does not exist', ->
assert.equal 'Document does not exist', ot.apply {v:6}, {v:6, op:[1,2,3]}

it 'fails if the type is missing', ->
assert.equal 'Type not found', ot.apply {v:6, type:'some non existant type'}, {v:6, op:[1,2,3]}

it 'applies the operation to the document data', ->
doc = {v:6, type:simple.uri, data:str:'Hi'}
assert.equal null, ot.apply doc, {v:6, op:{position:2, text:' there'}}
assert.deepEqual doc, {v:7, type:simple.uri, data:str:'Hi there'}

it.skip 'shatters the operation if it can, and applies it incrementally'

describe 'transform', ->
it 'fails if the version is specified on both and does not match', ->
op1 = {v:5, op:{position:10, text:'hi'}}
op2 = {v:6, op:{position:5, text:'abcde'}}
assert.equal 'Version mismatch', ot.transform simple.uri, op1, op2
assert.deepEqual op1, {v:5, op:{position:10, text:'hi'}}

it 'works if the version is only specified on the first op', ->
op1 = {op:{position:10, text:'hi'}}
op2 = {v:6, op:{position:5, text:'abcde'}}
assert.equal null, ot.transform simple.uri, op1, op2
assert.deepEqual op1, {op:{position:15, text:'hi'}}

it 'fails if you transform an op by a delete', ->
assert.equal 'Document was deleted', ot.transform simple.uri, {v:10, op:{}}, {v:10, del:true}

it 'lets you transform a delete by a delete', ->
op = del:true
assert.equal null, ot.transform simple.uri, op, {del:true}
assert.deepEqual op, del:true

it 'fails if you transform a create by a create', ->
assert.equal 'Document created remotely', ot.transform null, {v:10, create:type:simple.uri}, {v:10, create:type:simple.uri}

# Not considering create vs delete, del vs create, etc because transform
# shouldn't be called in those cases anyway.


0 comments on commit 06bf8b7

Please sign in to comment.