| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- chai = require 'chai'
- should = chai.should()
- sinon = require 'sinon'
- chai.use require('sinon-chai')
- describe 'OptionsSync', ->
- OptionsSync = require '../src/options_sync'
- Storage = require '../src/storage'
- Log = require '../src/log'
- Promise = require 'bluebird'
- before ->
- # Silence storage and sync logging.
- sinon.stub(Log, 'log')
- after ->
- Log.log.restore()
- # coffeelint: disable=missing_fat_arrows
- hookPostBasic = (func, hook) -> ->
- result = func.apply(this, arguments)
- hook.apply(this, arguments)
- return result
- # coffeelint: enable=missing_fat_arrows
- hookPost = (args...) ->
- if args.length == 2
- [func, hook] = args
- hostPostBasic(func, hook)
- else
- [obj, method, hook] = args
- obj[method] = hookPostBasic(obj[method], hook)
- describe '#merge', ->
- sync = new OptionsSync()
- it 'should choose the one with newer revision', ->
- newVal = {revision: '2'}
- oldVal = {revision: '1'}
- sync.merge('example', newVal, oldVal).should.equal(newVal)
- it 'should use oldVal when sync is disabled in newVal', ->
- newVal = {revision: '2', is: 'newVal', syncOptions: 'disabled'}
- oldVal = {revision: '1', is: 'oldVal'}
- sync.merge('example', newVal, oldVal).should.equal(oldVal)
- it 'should use oldVal when sync is disabled in oldVal', ->
- newVal = {revision: '2', is: 'newVal'}
- oldVal = {revision: '1', is: 'oldVal', syncOptions: 'disabled'}
- sync.merge('example', newVal, oldVal).should.equal(oldVal)
- it 'should favor oldVal when revisions are equal', ->
- newVal = {revision: '1', is: 'newVal'}
- oldVal = {revision: '1', is: 'oldVal'}
- sync.merge('example', newVal, oldVal).should.equal(oldVal)
- it 'should favor oldVal when newVal deeply equals oldVal', ->
- newVal = {they: 'are', the: 'same'}
- oldVal = {they: 'are', the: 'same'}
- sync.merge('example', newVal, oldVal).should.equal(oldVal)
- it 'should choose newVal when newVal is different', ->
- newVal = {they: 'are', not: 'equal'}
- oldVal = {they: 'are', not: 'identical'}
- sync.merge('example', newVal, oldVal).should.equal(newVal)
- describe '#requestPush', ->
- unlimited = new OptionsSync.TokenBucket()
- it 'should store pendingChanges', ->
- sync = new OptionsSync()
- sync.enabled = false
- sync.requestPush({a: 1})
- sync.pendingChanges().should.eql({a: 1})
- it 'should schedule storage write', (done) ->
- check = ->
- return if storage.set.callCount == 0 or storage.remove.callCount == 0
- storage.set.should.have.been.calledOnce.and.calledWith({b: 1})
- storage.remove.should.have.been.calledOnce.and.calledWith(['a'])
- done()
- storage = new Storage()
- storage.set({a: 1})
- hookPost storage, 'set', check
- hookPost storage, 'remove', check
- sinon.spy(storage, 'set')
- sinon.spy(storage, 'remove')
- sync = new OptionsSync(storage, unlimited)
- sync.debounce = 0
- sync.requestPush({a: undefined, b: 1})
- it 'should combine multiple write operations', (done) ->
- check = ->
- return if storage.set.callCount == 0 or storage.remove.callCount == 0
- storage.set.should.have.been.calledOnce.and.calledWith({c: 1, d: 1})
- storage.remove.should.have.been.calledOnce.and.calledWith(['a', 'b'])
- done()
- storage = new Storage()
- storage.set({a: 1, b: 1})
- hookPost storage, 'set', check
- hookPost storage, 'remove', check
- sinon.spy(storage, 'set')
- sinon.spy(storage, 'remove')
- sync = new OptionsSync(storage, unlimited)
- sync.debounce = 0
- sync.requestPush({a: undefined})
- sync.requestPush({b: 2})
- sync.requestPush({b: undefined})
- sync.requestPush({c: 1})
- sync.requestPush({d: 1})
- sync.requestPush({e: 1})
- sync.requestPush({e: undefined})
- it 'should disable syncing for the profiles if quota is exceeded', (done) ->
- options = {'+a': {is: 'a', oversized: true}, b: {is: 'b'}}
- storage = new Storage()
- storage.set = (changes) ->
- for key, value of changes
- if value.oversized
- err = new Storage.QuotaExceededError()
- err.perItem = true
- return Promise.reject(err)
- storage.set.should.have.been.calledTwice
- storage.set.should.have.been.calledWith(options)
- storage.set.should.have.been.calledWith({b: {is: 'b'}})
- options['+a'].syncOptions.should.equal('disabled')
- options['+a'].syncError.reason.should.equal('quotaPerItem')
- done()
- Promise.resolve()
- sinon.spy(storage, 'set')
- sync = new OptionsSync(storage, unlimited)
- sync.debounce = 0
- sync.requestPush(options)
- describe '#copyTo', ->
- it 'should fetch all items from remote storage', (done) ->
- remote = new Storage()
- remote.set({a: 1, b: 2, c: 3})
- storage = new Storage()
- hookPost storage, 'set', ->
- storage.set.should.have.been.calledOnce.and.calledWith(
- {a: 1, b: 2, c: 3}
- )
- done()
- sinon.spy(storage, 'set')
- sync = new OptionsSync(remote)
- sync.copyTo(storage)
- it 'should merge with local as base', (done) ->
- check = ->
- return if storage.set.callCount == 0 or storage.remove.callCount == 0
- storage.set.should.have.been.calledOnce.and.calledWith({b: 2, c: 3})
- storage.remove.should.have.been.calledOnce.and.calledWith(['d'])
- done()
- remote = new Storage()
- remote.set({a: 1, b: 2, c: 3, d: undefined})
- storage = new Storage()
- storage.set({a: 1, b: 0, d: 4})
- hookPost storage, 'set', check
- hookPost storage, 'remove', check
- sinon.spy(storage, 'set')
- sinon.spy(storage, 'remove')
- sync = new OptionsSync(remote)
- sync.copyTo(storage)
- describe '#watchAndPull', ->
- it 'should pull changes into local when remote changes', (done) ->
- check = ->
- return if storage.set.callCount == 0 or storage.remove.callCount == 0
- remote.watch.should.have.been.calledOnce
- storage.set.should.have.been.calledOnce.and.calledWith({b: 2, c: 3})
- storage.remove.should.have.been.calledOnce.and.calledWith(['d'])
- done()
- remote = new Storage()
- hookPost remote, 'watch', (_, callback) ->
- setTimeout (->
- callback({a: 1})
- callback({b: 2})
- callback({c: 3})
- callback({d: undefined})
- ), 10
- sinon.spy(remote, 'watch')
- storage = new Storage()
- storage.set({a: 1, b: 0, d: 4})
- hookPost storage, 'set', check
- hookPost storage, 'remove', check
- sinon.spy(storage, 'set')
- sinon.spy(storage, 'remove')
- sync = new OptionsSync(remote)
- sync.pullThrottle = 0
- sync.watchAndPull(storage)
|