wheelhouse-couch
Version:
A wheelhouse library to use couchdb as a database while useing backbone on ther server.
1,101 lines (882 loc) • 31.6 kB
JavaScript
/*global describe, it, before, after, beforeEach, afterEach */
'use strict';
var chai = require('chai')
// , expect = chai.expect
, should = chai.should()
, _ = require('lodash')
, sinon = require('sinon')
, sinonChai = require('sinon-chai')
, plugin = require('../lib/db.js')
, Backbone = require('backbone')
, app = {}
chai.use(sinonChai)
describe('db unit tests', function(){
beforeEach(function(){
// fake the log
app.log = {
debug: sinon.stub()
, info: sinon.stub()
, warn: sinon.stub()
, error: sinon.stub()
}
})
afterEach(function(){
_.each(app.log, function(stub){
stub.reset()
})
})
describe('#toJSON', function(){
var fn = plugin.internals.toJSON
, model
beforeEach(function(){
model = new (Backbone.Model.extend({
defaults: {
value: 1
, value2: 'string'
, value3: false
}
}))()
})
it('is attached to the plugin', function(){
fn.should.be.a.function
})
it('returns json', function(){
fn(model).should.be.an.object
JSON.stringify(fn(model)).should.not.throw
})
it('always as an `_id` attribute', function(){
// set an id which is the default idAttribute
model.set({id: 'id'})
fn(model).should.include.keys('_id')
fn(model)._id.should.equal('id')
// change the idAttribute
model.idAttribute = 'value'
// call a set so that model.id can be defined
model.set({value: 'value'})
fn(model)._id.should.equal('value')
})
})
describe('#getUUIDList', function(){
var uuids = []
, fn
, uuidStub
for (var i = 0; i < 100; i++){
uuids.push(_.uniqueId('getUUIDList'))
}
beforeEach(function(){
plugin.internals.connection = {uuids: function(){}}
uuidStub = sinon.stub(plugin.internals.connection, 'uuids')
fn = sinon.spy(plugin.internals, 'getUUIDList')
})
afterEach(function(){
plugin.internals.uuids = []
uuidStub.restore()
fn.restore()
})
it('gets a list of 100 uuids', function(done){
uuidStub.callsArgWith(1, null, uuids)
fn(function(res){
res.should.deep.equal(uuids)
done()
})
})
it('retrys on a failure', function(done){
uuidStub.callsArgWith(1, null, [])
fn(function(res){
fn.should.have.been.called.twice
res.should.deep.equal(uuids)
done()
})
uuidStub.restore()
uuidStub = sinon.stub(plugin.internals.connection, 'uuids')
.callsArgWith(1, null, uuids)
})
})
describe('#getUUID', function(){
var uuids
, fn
beforeEach(function(){
uuids = ['uuid1', 'uuid2', 'uuid3']
fn = plugin.internals.getUUID
sinon.stub(plugin.internals, 'getUUIDList')
.yields(uuids)
})
afterEach(function(){
plugin.internals.uuids = []
plugin.internals.getUUIDList.restore()
})
it('saves the uuid list to internals', function(done){
fn(function(){
plugin.internals.uuids.should.deep.equal(uuids)
done()
})
})
it('calls getUUIDList if there are no stored UUIDs', function(done){
plugin.internals.uuids = []
fn(function(){
plugin.internals.getUUIDList.should.have.been.calledOnce
plugin.internals.uuids.should.deep.equal.uuids
done()
})
})
it('returns a single uuid', function(){
fn(function(uuid){
uuid.should.equal('uuid3')
})
})
})
describe('#sync', function(){
var fn
, model
, collection
before(function(){
// call attach so that app is avaliable in the plugin
plugin.attach.call(app)
})
beforeEach(function(){
// fake that we have a db
plugin.internals.dbExists = true
fn = plugin.internals.sync
model = new (Backbone.Model.extend({
idAttribute: '_id'
, urlRoot: '/model'
}))()
collection = new (Backbone.Collection.extend({
url: '/collection'
}))()
plugin.internals.feed = {
resume: sinon.stub()
, pause: sinon.stub()
}
})
afterEach(function(done){
// enusre the call stack is clear
// we have tests that will do async operations that we don't need to wait for to complete the test, but they'll run anyway, so we'll just ensure we're in an isoated environment for all tests
setTimeout(done, 8)
})
it('logs an error if the db doesn\'t exist', function(){
plugin.internals.dbExists = false
fn('read', model)
app.log.error.should.have.been.calledOnce
})
describe('read', function(){
describe('model', function(){
var getStub
beforeEach(function(){
getStub = sinon.stub(plugin.internals.db, 'get')
model.set({_id: 1})
})
afterEach(function(){
getStub.restore()
})
it('operates only on models with an id', function(){
model.id.should.equal(1)
fn('read', model)
getStub.should.have.been.calledOnce
})
it('gets from the database', function(){
fn('read', model)
getStub.should.have.been.calledOnce
})
it('calls success with the doc', function(){
var success = sinon.spy()
, doc = {_id: 1, _rev: 2, value: 2}
, options = {success: success}
getStub.yields(null, doc)
fn('read', model, options)
success.should.have.been.calledOnce
success.should.have.been.calledWith(doc)
})
it('logs errors', function(){
var error = {error: 'err', reason: 'reason'}
getStub.yields(error)
fn('read', model)
app.log.error.should.have.been.calledOnce
})
it('calls error with the error response on error', function(){
var options = {
error: sinon.stub()
}
, error = {error: 'err', reason: 'reason'}
getStub.yields(error)
fn('read', model, options)
options.error.should.have.been.calledOnce
options.error.should.have.been.calledWith(error)
})
})
describe('collection', function(){
var viewStub
beforeEach(function(){
viewStub = sinon.stub(plugin.internals.db, 'view')
})
afterEach(function(){
viewStub.restore()
})
it('operates only on "models" without an id', function(){
fn('read', collection)
viewStub.should.have.been.calledOnce
})
it('uses the backbone/colleciton view to get models', function(){
fn('read', collection)
viewStub.should.have.been.calledWith('backbone/collection', {key: 'collection'})
})
it('calls success with the collection models', function(){
var options = {
success: sinon.stub()
}
, res = {
rows: [
{value: {_id: 1, _rev: 1, value: true}}
, {value: {_id: 2, _rev: 1, value: true}}
]
}
viewStub.yields(null, res)
fn('read', collection, options)
options.success.should.have.been.calledWith([res.rows[0].value, res.rows[1].value])
})
it('logs errors', function(){
var options = {
error: sinon.stub()
}
, error = {error: 'err', reason: 'reason'}
viewStub.yields(error)
fn('read', collection, options)
app.log.error.should.have.been.calledOnce
})
it('calls error with the error response on error', function(){
var options = {
error: sinon.stub()
}
, error = {error: 'err', reason: 'reason'}
viewStub.yields(error)
fn('read', collection, options)
options.error.should.have.been.calledWith(error)
})
})
})
describe('create', function(){
beforeEach(function(){
sinon.stub(plugin.internals, 'getUUID')
.yields('uuid')
sinon.stub(plugin.internals.db, 'save')
})
afterEach(function(){
plugin.internals.getUUID.restore()
plugin.internals.db.save.restore()
})
it('creates an id with the collection name', function(){
fn('create', model)
plugin.internals.db.save.should.have.been.calledWith('model/uuid')
})
it('errors if `isDeleted` is modified', function(){
var options = {
error: sinon.stub()
}
// when isDeleted is set to a value
model.set({value: false, isDeleted: 'value'})
fn('create', model, options)
options.error.should.have.been.calledOnce
app.log.warn.should.have.been.calledOnce
should.not.exist(model.get('isDeleted'))
plugin.internals.db.save.should.not.have.been.called
// when isDeleted is set to false
model.set({value: false, isDeleted: false})
fn('create', model, options)
options.error.should.have.been.called.twice
app.log.warn.should.have.been.called.twice
should.not.exist(model.get('isDeleted'))
plugin.internals.db.save.should.not.have.been.called
})
it('pauses the feed', function(){
fn('create', model)
plugin.internals.feed.pause.should.have.been.calledOnce
})
it('inserts into the db', function(){
plugin.internals.db.save.yields(null, {ok: true})
fn('create', model)
plugin.internals.db.save.should.have.been.calledOnce
plugin.internals.db.save.should.have.been.calledWith('model/uuid')
})
it('sets `createdAt` and `updatedAt`', function(){
var date = new Date().toString()
plugin.internals.db.save.yields(null, {ok: true})
fn('create', model)
plugin.internals.db.save.should.have.been.calledOnce
plugin.internals.db.save.should.have.been.calledWith('model/uuid', _.extend(model.toJSON(), {createdAt: date, updatedAt: date}))
})
it('logs on error', function(){
var error = {error: 'error', reason: 'reason'}
plugin.internals.db.save.yields(error)
fn('create', model)
app.log.error.should.have.been.calledOnce
})
it('calls error with the error response', function(){
var options = {
error: sinon.stub()
}
, error = {error: 'error', reason: 'reason'}
plugin.internals.db.save.yields(error)
fn('create', model, options)
options.error.should.have.been.calledOnce
options.error.should.have.been.calledWith(error)
})
it('retries if the res didn\'t return `ok`', function(done){
var options = {
success: function(){
plugin.internals.db.save.should.have.been.calledOnce
done()
}
}
plugin.internals.db.save.yieldsAsync(null, {ok: false})
fn('create', model, options)
plugin.internals.db.save.should.have.been.calledOnce
plugin.internals.db.save.restore()
sinon.stub(plugin.internals.db, 'save')
.yieldsAsync(null, {ok: true})
})
it('logs on success', function(){
plugin.internals.db.save.yields(null, {ok: true})
fn('create', model)
app.log.info.should.have.been.calledOnce
})
it('calls success with the `_rev`, `_id`, `updatedAt`, and `createdAt`', function(){
var options = {
success: sinon.spy()
}
, date = new Date().toString()
plugin.internals.db.save.yields(null, {ok: true, rev: 1, id: 'id'})
fn('create', model, options)
options.success.should.have.been.calledWith({_rev: 1, _id: 'id', createdAt: date, updatedAt: date})
})
it('resumes the feed after the stack has cleared', function(done){
plugin.internals.db.save.yields(null, {ok: true, rev: 1, id: 'id'})
fn('create', model)
setImmediate(function(){
plugin.internals.feed.resume.should.have.been.calledOnce
done()
})
})
})
describe('update', function(){
var model
, defaults
beforeEach(function(){
defaults = {
value: true
, value2: 'testing'
, _id: _.uniqueId('syncUpdate')
, _rev: 1
}
model = new (Backbone.Model.extend({
idAttribute: '_id'
, urlRoot: '/model'
, defaults: defaults
}))()
sinon.stub(plugin.internals.db, 'get')
sinon.stub(plugin.internals.db, 'save')
})
afterEach(function(){
plugin.internals.db.get.restore()
plugin.internals.db.save.restore()
})
it('gets the model from the db', function(){
fn('update', model)
plugin.internals.db.get.should.have.been.calledOnce
})
it('logs on a get error', function(){
plugin.internals.db.get.yields({error: 'error', reason: 'reason'})
fn('update', model)
app.log.error.should.have.been.calledOnce
})
it('calls the error callback with the error on a get error', function(){
var options = {
error: sinon.stub()
}
plugin.internals.db.get.yields({error: 'error', reason: 'reason'})
fn('update', model, options)
options.error.should.have.been.calledOnce
})
it('pauses the feed', function(){
plugin.internals.db.get.yields(null, {})
fn('update', model)
plugin.internals.feed.pause.should.have.been.calledOnce
})
it('inserts into the db with merged attributes', function(){
var newModel
plugin.internals.db.get.yields(null, defaults)
model.set({value: false})
fn('update', model)
newModel = _.extend({}, defaults, model.toJSON(), {_rev: defaults._rev, updatedAt: new Date().toString()})
newModel.updatedAt.should.be.a.string
plugin.internals.db.save.should.have.been.calledWith(model.id, defaults._rev, newModel)
})
it('inserts into the db with the most recent `_rev`', function(){
var newModel
defaults._rev = 10
plugin.internals.db.get.yields(null, defaults)
model.set({value: false})
fn('update', model)
newModel = _.extend({}, defaults, model.toJSON(), {_rev: defaults._rev, updatedAt: new Date().toString()})
newModel._rev.should.equal(defaults._rev)
plugin.internals.db.save.should.have.been.calledWith(model.id, defaults._rev, newModel)
})
it('inserts into the db with an updated `updatedAt` time', function(){
var newModel
defaults._rev = 10
defaults.updatedAt = 'a date'
plugin.internals.db.get.yields(null, defaults)
model.set({value: false})
fn('update', model)
newModel = _.extend({}, defaults, model.toJSON(), {_rev: defaults._rev, updatedAt: new Date().toString()})
newModel._rev.should.equal(defaults._rev)
plugin.internals.db.save.should.have.been.calledWith(model.id, defaults._rev, newModel)
})
it('errors if `createdAt` is modified', function(){
var options = {
error: sinon.stub()
}
defaults._rev = 10
defaults.createdAt = 'a date'
plugin.internals.db.get.yields(null, defaults)
model.set({value: false, createdAt: 'new date'})
fn('update', model, options)
options.error.should.have.been.calledOnce
app.log.warn.should.have.been.calledOnce
model.get('createdAt').should.equal(defaults.createdAt)
plugin.internals.db.save.should.not.have.been.called
})
it('errors if `isDeleted` is modified', function(){
var options = {
error: sinon.stub()
}
defaults._rev = 10
defaults.createdAt = 'a date'
plugin.internals.db.get.yields(null, defaults)
// when isDeleted is set to a value
model.set({value: false, isDeleted: 'value'})
fn('update', model, options)
options.error.should.have.been.calledOnce
app.log.warn.should.have.been.calledOnce
should.not.exist(model.get('isDeleted'))
plugin.internals.db.save.should.not.have.been.called
// when isDeleted is set to false
model.set({value: false, isDeleted: false})
fn('update', model, options)
options.error.should.have.been.called.twice
app.log.warn.should.have.been.called.twice
should.not.exist(model.get('isDeleted'))
plugin.internals.db.save.should.not.have.been.called
// when isDeleted is modfied on a deleted doc
defaults.isDeleted = true
model.set({value: false, isDeleted: false})
fn('update', model, options)
options.error.should.have.been.called.thrice
app.log.warn.should.have.been.called.thrice
model.get('isDeleted').should.be.true
plugin.internals.db.save.should.not.have.been.called
})
it('retries on a conflict error', function(done){
var options = {
success: function(){
options.success.should.have.been.calledOnce
options.success.should.have.been.calledWith({_rev: 2, updatedAt: new Date().toString()})
done()
}
, error: function(err){
should.not.exist(err)
done()
}
}
sinon.spy(options, 'success')
model.set({value: false})
plugin.internals.db.get.yields(null, defaults)
// the inital save will throw an error
plugin.internals.db.save.yieldsAsync({error: 'conflict'})
fn('update', model, options)
// our next attempt to save will suceeed
plugin.internals.db.save.restore()
sinon.stub(plugin.internals.db, 'save')
.yieldsAsync(null, {
doc: _.extend(defaults, model.toJSON, {_rev: 2})
, rev: 2
, ok: true
, id: model.id
})
})
it('calls the error callback with the error on a insert error', function(){
var err = {error: 'err', reason: 'reason'}
, options = {
error: sinon.stub()
}
plugin.internals.db.get.yields(null, defaults)
plugin.internals.db.save.yields(err)
fn('update', model, options)
options.error.should.have.been.calledOnce
options.error.should.have.been.calledWith(err)
})
it('retries on a null response', function(done){
plugin.internals.db.get.yields(null, defaults)
plugin.internals.db.save.yieldsAsync(null, {})
fn('update', model, {
success: function(){
done()
}
})
plugin.internals.feed.pause.should.have.been.called.twice
plugin.internals.db.save.restore()
sinon.stub(plugin.internals.db, 'save').yieldsAsync(null, {ok: true, rev: 2, id: defaults.id})
})
it('logs a success', function(){
plugin.internals.db.get.yields(null, defaults)
plugin.internals.db.save.yields(null, {ok: true, rev: 2, id: defaults.id})
fn('update', model)
app.log.info.should.have.been.calledOnce
})
it('calls the success callback with the new `_rev` and `updatedAt`', function(){
plugin.internals.db.get.yields(null, defaults)
plugin.internals.db.save.yields(null, {ok: true, rev: 2, id: defaults.id})
fn('update', model, {
success: function(res){
res._rev.should.equal(2)
should.exist(res.updatedAt)
res.updatedAt.should.equal(new Date().toString())
}
})
})
it('resumes the feed after the stack has cleared', function(done){
plugin.internals.db.get.yields(null, defaults)
plugin.internals.db.save.yields(null, {ok: true, rev: 2, id: defaults.id})
fn('update', model)
setImmediate(function(){
plugin.internals.feed.resume.should.have.been.calledOnce
done()
})
})
})
describe('delete', function(){
beforeEach(function(){
sinon.stub(plugin.internals.db, 'save')
sinon.spy(plugin.internals, 'sync')
})
afterEach(function(){
plugin.internals.db.save.restore()
plugin.internals.sync.restore()
})
it('updates the model with the `isDeleted` attribute', function(){
fn('delete', model)
model.toJSON().should.include.keys('isDeleted')
model.get('isDeleted').should.be.true
})
it('calls sync as an update with updated model', function(){
fn('delete', model)
plugin.internals.sync.should.have.been.called.twice
model.attributes.isDeleted = true
plugin.internals.sync.should.have.been.calledWith('update', model)
})
})
})
describe('#feedSetup', function(){
var fn = plugin.internals.feedSetup
, Events = require('events').EventEmitter
, change
before(function(){
// call attach so that app is avaliable in the plugin
plugin.attach.call(app)
})
beforeEach(function(){
plugin.internals.db = {
changes: sinon.stub().returns(new Events())
}
change = {
doc: {
_id: ''
}
}
})
afterEach(function(){
delete plugin.internals.db
delete plugin.internals.feed
})
it('creates a follow instance on `internals.feed`', function(){
fn()
plugin.internals.db.changes.should.have.been.calledOnce
should.exist(plugin.internals.feed)
})
describe('on update', function(){
beforeEach(function(){
fn()
})
afterEach(function(){
delete plugin.internals.feed
delete app.collections
})
describe('no collection found', function(){
it('bails with no `app.collections`', function(){
plugin.internals.feed.emit('change', change)
app.log.debug.should.have.been.calledOnce
app.log.debug.should.have.been.calledWith('db: changes: ignoring: no collection found')
})
it('bails if the collection can\'t be found', function(){
app.collections = {}
plugin.internals.feed.emit('change', change)
app.log.debug.should.have.been.calledOnce
app.log.debug.should.have.been.calledWith('db: changes: ignoring: no collection found')
})
})
describe('collection found', function(){
var id = 'feed/uuid'
beforeEach(function(){
app.collections = {}
app.collections.feed = new (Backbone.Collection.extend({
model: Backbone.Model.extend({
idAttribute: '_id'
})
}))()
change.doc._id = id
})
afterEach(function(){
delete app.collections
})
it('removes from the collection on a delete', function(done){
change.deleted = true
app.collections.feed.add({_id: id, _rev: '2'})
sinon.spy(app.collections.feed, 'remove')
plugin.internals.feed.emit('change', change)
process.nextTick(function(){
app.log.debug.should.have.been.calledOnce
app.log.error.should.not.have.been.called
app.collections.feed.remove.should.have.been.calledOnce
done()
})
})
it('removes from the collection on a faux delete', function(done){
change.doc.isDeleted = true
app.collections.feed.add({_id: id, _rev: '2'})
sinon.spy(app.collections.feed, 'remove')
plugin.internals.feed.emit('change', change)
process.nextTick(function(){
app.log.debug.should.have.been.calledOnce
app.collections.feed.remove.should.have.been.calledOnce
done()
})
})
it('adds when this is the first _rev', function(done){
change.doc._rev = '1'
sinon.spy(app.collections.feed, 'add')
plugin.internals.feed.emit('change', change)
process.nextTick(function(){
app.log.debug.should.have.been.calledOnce
app.collections.feed.add.should.have.been.calledOnce
done()
})
})
it('updates when the _rev doesn\'t match', function(done){
change.doc._rev = '3'
app.collections.feed.add({_id: id, _rev: '2'})
sinon.spy(app.collections.feed.first(), 'set')
plugin.internals.feed.emit('change', change)
process.nextTick(function(){
app.log.debug.should.have.been.calledOnce
// once to clear, and once to set the new attributes
app.collections.feed.first().set.should.have.been.called.twice
done()
})
})
it('removes attributes from the model that were removed from the doc', function(done){
change.doc._rev = '3'
app.collections.feed.add({_id: id, _rev: '2', value: true})
sinon.spy(app.collections.feed.first(), 'set')
sinon.spy(app.collections.feed.first(), 'clear')
plugin.internals.feed.emit('change', change)
process.nextTick(function(){
app.log.debug.should.have.been.calledOnce
// once to clear, and once to set the new attributes
app.collections.feed.first().clear.should.have.been.called.twice
app.collections.feed.first().set.should.have.been.called.twice
should.not.exist(app.collections.feed.first().get('value'))
done()
})
})
it('ignores in all other cases', function(done){
change.doc._rev = '2'
app.collections.feed.add({_id: id, _rev: '2'})
plugin.internals.feed.emit('change', change)
process.nextTick(function(){
app.log.debug.should.have.been.calledOnce
app.log.debug.should.have.been.calledWith('db: changes: ignoring:')
done()
})
})
})
})
describe('on error', function(){
beforeEach(function(){
fn()
})
afterEach(function(){
delete plugin.internals.feed
})
it('logs on error', function(){
plugin.internals.feed.emit('error')
app.log.error.should.have.been.calledOnce
})
})
})
describe('#install', function(){
var fn
before(function(){
// call attach so that app is avaliable in the plugin
plugin.attach.call(app)
})
after(function(){
delete plugin.internals.dbExists
})
beforeEach(function(){
fn = plugin.internals.install
sinon.stub(plugin.internals.db, 'create')
sinon.stub(plugin.internals.db, 'save')
sinon.stub(plugin.internals, 'feedSetup')
})
afterEach(function(){
plugin.internals.db.create.restore()
plugin.internals.db.save.restore()
plugin.internals.feedSetup.restore()
})
it('creates the db', function(){
fn()
plugin.internals.db.create.should.have.been.calledOnce
})
it('logs on create error', function(){
plugin.internals.db.create.yields({})
fn()
app.log.error.should.have.been.calledOnce
})
it('saves the view', function(){
plugin.internals.db.create.yields()
fn()
plugin.internals.db.save.should.have.been.calledOnce
plugin.internals.db.save.should.have.been.calledWith('_design/backbone')
})
it('logs on save error', function(){
plugin.internals.db.create.yields()
plugin.internals.db.save.yields({})
fn()
app.log.error.should.have.been.calledOnce
})
it('listens to the changes feed', function(){
plugin.internals.db.create.yields()
plugin.internals.db.save.yields()
fn()
plugin.internals.feedSetup.should.have.been.calledOnce
})
it('sets that the db exists', function(){
plugin.internals.db.create.yields()
plugin.internals.db.save.yields()
fn()
plugin.internals.dbExists.should.be.true
})
})
describe('#attach', function(){
beforeEach(function(){
plugin.attach.call(app, {name: 'mydb'})
})
afterEach(function(){
delete plugin.internals.connection
delete plugin.internals.db
delete app.db
delete app.Backbone
})
it('assigns options', function(){
plugin.internals.options.should.be.an.object
plugin.internals.options.should.include.keys('host', 'name')
})
it('creates a new cradle connection', function(){
should.exist(plugin.internals.connection)
plugin.internals.connection.should.include.keys('host', 'port')
})
it('sets the database', function(){
should.exist(plugin.internals.db)
plugin.internals.db.name.should.equal('mydb')
})
it('sets the db on the app', function(){
app.db.should.deep.equal(plugin.internals.db)
})
it('overrides Backbone.Sync', function(){
Backbone.sync.should.deep.equal(plugin.internals.sync)
})
it('stores Backbone on the app', function(){
app.Backbone.should.deep.equal(Backbone)
})
})
describe('#init', function(){
before(function(){
plugin.attach.call(app)
})
beforeEach(function(){
sinon.stub(plugin.internals.db, 'exists')
})
afterEach(function(){
plugin.internals.db.exists.restore()
})
it('checks if the db exists', function(){
plugin.init()
plugin.internals.db.exists.should.have.been.calledOnce
})
describe('when the existence check throws an error', function(){
var done
beforeEach(function(){
done = sinon.stub()
plugin.internals.db.exists.yields({})
plugin.init(done)
})
it('logs on error', function(){
app.log.error.should.have.been.calledOnce
})
it('calls the callback on error', function(){
done.should.have.been.calledOnce
})
})
describe('when db exists', function(){
var done
beforeEach(function(){
done = sinon.stub()
sinon.stub(plugin.internals, 'feedSetup')
plugin.internals.db.exists.yields(null, true)
plugin.init(done)
})
afterEach(function(){
plugin.internals.feedSetup.restore()
})
it('logs that it exists', function(){
app.log.info.should.have.been.calledOnce
})
it('sets that the db exists', function(){
plugin.internals.dbExists.should.be.true
})
it('listens to the changes feed', function(){
plugin.internals.feedSetup.should.have.been.calledOnce
})
it('calls the callback', function(){
done.should.have.been.calledOnce
})
})
describe('when db doesn\'t exist', function(){
var done
beforeEach(function(){
done = sinon.stub()
sinon.stub(plugin.internals, 'install')
plugin.internals.db.exists.yields(null, false)
plugin.init(done)
})
afterEach(function(){
plugin.internals.install.restore()
})
it('logs the install', function(){
app.log.info.should.have.been.calledOnce
})
it('installs', function(){
plugin.internals.install.should.have.been.calledWith(done)
})
})
})
})