UNPKG

sharedb

Version:
858 lines (788 loc) 31.8 kB
var async = require('async'); var expect = require('chai').expect; var Backend = require('../lib/backend'); var ot = require('../lib/ot'); /** * Run the DB test suite. Useful for testing external DB adapters: * require('sharedb/test/db')(options); * * options.create() should instantiate a database adapter and call the * provided callback with it. * options.create = function(callback) { * // Create instance of Db * callback(error, db); * } * * options.getQuery() is optional, and only needed if the adapter supports * queries. It needs to accept the "generic" query provided by the * test suite, and return a query specific to the adapter. * options.getQuery = function(genericQuery) { * // map genericQuery into adapter-specific query * return mappedQuery; * } */ module.exports = function(options) { var create = options.create; describe('db', function() { beforeEach(function(done) { var self = this; create(function(err, db) { if (err) return done(err); self.db = db; self.backend = new Backend({db: db}); done(); }); }); afterEach(function(done) { this.backend.close(done); }); it('can call db.close() without callback', function(done) { create(function(err, db) { if (err) return done(err); db.close(); done(); }); }); it('calls db.close() multiple times', function(done) { var db = this.db; db.close(function(error) { if (error) return done(error); db.close(function(error) { done(error); }); }); }); if (options.getQuery) { require('./client/query-subscribe')({getQuery: options.getQuery}); require('./client/query')({getQuery: options.getQuery}); require('./client/projections')({getQuery: options.getQuery}); } require('./client/submit')(); require('./client/submit-json1')(); require('./client/subscribe')(); // Simplified mock of how submit request applies operations. The // noteworthy dependency is that it always calls getSnapshot with // the {$submit: true} projection and applies the op to the returned // snapshot before committing. Thus, commit may rely on the behavior // of getSnapshot with this special projection function submit(db, collection, id, op, callback) { db.getSnapshot(collection, id, {$submit: true}, null, function(err, snapshot) { if (err) return callback(err); if (snapshot.v !== op.v) { var succeeded = false; return callback(null, succeeded); } var err = ot.apply(snapshot, op); if (err) return callback(err); db.commit(collection, id, op, snapshot, null, callback); }); } describe('commit', function() { function commitConcurrent(db, ops, test, done) { var numSucceeded = 0; async.each(ops, function(op, eachCb) { submit(db, 'testcollection', 'foo', op, function(err, succeeded) { if (err) return eachCb(err); if (succeeded) numSucceeded++; eachCb(); }); }, function(err) { if (err) return done(err); expect(numSucceeded).equal(1); if (!test) return done(); db.getOps('testcollection', 'foo', 0, null, null, function(err, opsOut) { if (err) return done(err); db.getSnapshot('testcollection', 'foo', null, null, function(err, snapshotOut) { if (err) return done(err); test(opsOut, snapshotOut); done(); }); }); }); } function testCreateCommit(ops, snapshot) { expect(snapshot.v).eql(1); expect(ops.length).equal(1); expect(ops[0].create).ok; } it('one commit succeeds from 2 simultaneous creates', function(done) { var ops = [ {v: 0, create: {type: 'json0', data: {x: 3}}}, {v: 0, create: {type: 'json0', data: {x: 5}}} ]; commitConcurrent(this.db, ops, testCreateCommit, done); }); it('one commit succeeds from 5 simultaneous creates', function(done) { var ops = [ {v: 0, create: {type: 'json0', data: {x: 3}}}, {v: 0, create: {type: 'json0', data: {x: 5}}}, {v: 0, create: {type: 'json0', data: {x: 7}}}, {v: 0, create: {type: 'json0', data: {x: 9}}}, {v: 0, create: {type: 'json0', data: {x: 11}}} ]; commitConcurrent(this.db, ops, testCreateCommit, done); }); function createDoc(db, callback) { var ops = [{v: 0, create: {type: 'json0', data: {x: 3}}}]; commitConcurrent(db, ops, null, callback); } function testOpCommit(ops, snapshot) { expect(snapshot.v).equal(2); expect(ops.length).equal(2); expect(ops[0].create).ok; expect(ops[1].op).ok; } function testDelCommit(ops, snapshot) { expect(snapshot.v).equal(2); expect(ops.length).equal(2); expect(snapshot.data).equal(undefined); expect(snapshot.type).equal(null); expect(ops[0].create).ok; expect(ops[1].del).equal(true); } function testOpOrDelCommit(ops, snapshot) { if (ops[1].op) { testOpCommit(ops, snapshot); } else { testDelCommit(ops, snapshot); } } it('one commit succeeds from 2 simultaneous ops', function(done) { var ops = [ {v: 1, op: []}, {v: 1, op: []} ]; var db = this.db; createDoc(db, function(err) { if (err) return done(err); commitConcurrent(db, ops, testOpCommit, done); }); }); it('one commit succeeds from 5 simultaneous ops', function(done) { var ops = [ {v: 1, op: []}, {v: 1, op: []}, {v: 1, op: []}, {v: 1, op: []}, {v: 1, op: []} ]; var db = this.db; createDoc(db, function(err) { if (err) return done(err); commitConcurrent(db, ops, testOpCommit, done); }); }); it('one commit succeeds from 2 simultaneous deletes', function(done) { var ops = [ {v: 1, del: true}, {v: 1, del: true} ]; var db = this.db; createDoc(db, function(err) { if (err) return done(err); commitConcurrent(db, ops, testDelCommit, done); }); }); it('one commit succeeds from 5 simultaneous deletes', function(done) { var ops = [ {v: 1, del: true}, {v: 1, del: true}, {v: 1, del: true}, {v: 1, del: true}, {v: 1, del: true} ]; var db = this.db; createDoc(db, function(err) { if (err) return done(err); commitConcurrent(db, ops, testDelCommit, done); }); }); it('one commit succeeds from delete simultaneous with op', function(done) { var ops = [ {v: 1, del: true}, {v: 1, op: []} ]; var db = this.db; createDoc(db, function(err) { if (err) return done(err); commitConcurrent(db, ops, testOpOrDelCommit, done); }); }); it('one commit succeeds from op simultaneous with delete', function(done) { var ops = [ {v: 1, op: []}, {v: 1, del: true} ]; var db = this.db; createDoc(db, function(err) { if (err) return done(err); commitConcurrent(db, ops, testOpOrDelCommit, done); }); }); }); function commitSnapshotWithMetadata(db, cb) { var data = {x: 5, y: 6}; var metadata = {test: 3}; var op = {v: 0, create: {type: 'http://sharejs.org/types/JSONv0', data: data}}; var snapshot = {v: 1, type: 'http://sharejs.org/types/JSONv0', data: data, m: metadata}; db.commit('testcollection', 'test', op, snapshot, null, cb); } describe('getSnapshot', function() { it('getSnapshot returns v0 snapshot', function(done) { this.db.getSnapshot('testcollection', 'test', null, null, function(err, result) { if (err) return done(err); expect(result).eql({id: 'test', type: null, v: 0, data: undefined, m: null}); done(); }); }); it('getSnapshot returns committed data', function(done) { var data = {x: 5, y: 6}; var op = {v: 0, create: {type: 'json0', data: data}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); db.getSnapshot('testcollection', 'test', null, null, function(err, result) { if (err) return done(err); expect(result).eql({id: 'test', type: 'http://sharejs.org/types/JSONv0', v: 1, data: data, m: null}); done(); }); }); }); it('getSnapshot does not return committed metadata by default', function(done) { var db = this.db; commitSnapshotWithMetadata(db, function(err) { if (err) return done(err); db.getSnapshot('testcollection', 'test', null, null, function(err, result) { if (err) return done(err); expect(result.m).equal(null); done(); }); }); }); it('getSnapshot returns metadata when option is true', function(done) { var db = this.db; commitSnapshotWithMetadata(db, function(err) { if (err) return done(err); db.getSnapshot('testcollection', 'test', null, {metadata: true}, function(err, result) { if (err) return done(err); expect(result.m).eql({test: 3}); done(); }); }); }); it('getSnapshot returns metadata when fields is {$submit: true}', function(done) { var db = this.db; commitSnapshotWithMetadata(db, function(err) { if (err) return done(err); db.getSnapshot('testcollection', 'test', {$submit: true}, null, function(err, result) { if (err) return done(err); expect(result.m).eql({test: 3}); done(); }); }); }); }); describe('getSnapshotBulk', function() { it('getSnapshotBulk returns committed and v0 snapshots', function(done) { var data = {x: 5, y: 6}; var op = {v: 0, create: {type: 'json0', data: data}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); db.getSnapshotBulk('testcollection', ['test2', 'test'], null, null, function(err, resultMap) { if (err) return done(err); expect(resultMap).eql({ test: {id: 'test', type: 'http://sharejs.org/types/JSONv0', v: 1, data: data, m: null}, test2: {id: 'test2', type: null, v: 0, data: undefined, m: null} }); done(); }); }); }); it('getSnapshotBulk does not return committed metadata by default', function(done) { var db = this.db; commitSnapshotWithMetadata(db, function(err) { if (err) return done(err); db.getSnapshotBulk('testcollection', ['test2', 'test'], null, null, function(err, resultMap) { if (err) return done(err); expect(resultMap.test.m).equal(null); done(); }); }); }); it('getSnapshotBulk returns metadata when option is true', function(done) { var db = this.db; commitSnapshotWithMetadata(db, function(err) { if (err) return done(err); db.getSnapshotBulk('testcollection', ['test2', 'test'], null, {metadata: true}, function(err, resultMap) { if (err) return done(err); expect(resultMap.test.m).eql({test: 3}); done(); }); }); }); }); describe('getOps', function() { it('getOps returns empty list when there are no ops', function(done) { this.db.getOps('testcollection', 'test', 0, null, null, function(err, ops) { if (err) return done(err); expect(ops).eql([]); done(); }); }); it('getOps returns 1 committed op', function(done) { var op = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); db.getOps('testcollection', 'test', 0, null, null, function(err, ops) { if (err) return done(err); expect(ops).eql([op]); done(); }); }); }); it('getOps returns 2 committed ops', function(done) { var op0 = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}}; var op1 = {v: 1, op: [{p: ['x'], na: 1}]}; var db = this.db; submit(db, 'testcollection', 'test', op0, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test', op1, function(err) { if (err) return done(err); db.getOps('testcollection', 'test', 0, null, null, function(err, ops) { if (err) return done(err); expect(ops).eql([op0, op1]); done(); }); }); }); }); it('getOps returns all ops when from and to are null', function(done) { var op0 = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}}; var op1 = {v: 1, op: [{p: ['x'], na: 1}]}; var db = this.db; submit(db, 'testcollection', 'test', op0, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test', op1, function(err) { if (err) return done(err); db.getOps('testcollection', 'test', null, null, null, function(err, ops) { if (err) return done(err); expect(ops).eql([op0, op1]); done(); }); }); }); }); it('getOps returns from specific op number', function(done) { var op0 = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}}; var op1 = {v: 1, op: [{p: ['x'], na: 1}]}; var db = this.db; submit(db, 'testcollection', 'test', op0, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test', op1, function(err) { if (err) return done(err); db.getOps('testcollection', 'test', 1, null, null, function(err, ops) { if (err) return done(err); expect(ops).eql([op1]); done(); }); }); }); }); it('getOps returns to specific op number', function(done) { var op0 = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}}; var op1 = {v: 1, op: [{p: ['x'], na: 1}]}; var db = this.db; submit(db, 'testcollection', 'test', op0, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test', op1, function(err) { if (err) return done(err); db.getOps('testcollection', 'test', 0, 1, null, function(err, ops) { if (err) return done(err); expect(ops).eql([op0]); done(); }); }); }); }); it('getOps does not return committed metadata by default', function(done) { var op = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}, m: {test: 3}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); db.getOps('testcollection', 'test', null, null, null, function(err, ops) { if (err) return done(err); expect(ops[0].m).eql(undefined); done(); }); }); }); it('getOps returns metadata when option is true', function(done) { var op = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}, m: {test: 3}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); db.getOps('testcollection', 'test', null, null, {metadata: true}, function(err, ops) { if (err) return done(err); expect(ops[0].m).eql({test: 3}); done(); }); }); }); }); describe('getOpsBulk', function() { it('getOpsBulk returns empty map when there are no ops', function(done) { this.db.getOpsBulk('testcollection', {test: 0, test2: 0}, null, null, function(err, opsMap) { if (err) return done(err); expect(opsMap).eql({ test: [], test2: [] }); done(); }); }); it('getOpsBulk returns committed ops', function(done) { var op = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test2', op, function(err) { if (err) return done(err); db.getOpsBulk('testcollection', {test: 0, test2: 0}, null, null, function(err, opsMap) { if (err) return done(err); expect(opsMap).eql({ test: [op], test2: [op] }); done(); }); }); }); }); it('getOpsBulk returns all ops committed from null', function(done) { var op = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test2', op, function(err) { if (err) return done(err); db.getOpsBulk('testcollection', {test: null, test2: null}, null, null, function(err, opsMap) { if (err) return done(err); expect(opsMap).eql({ test: [op], test2: [op] }); done(); }); }); }); }); it('getOpsBulk returns committed ops with specific froms', function(done) { var op0 = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}}; var op1 = {v: 1, op: [{p: ['x'], na: 1}]}; var db = this.db; submit(db, 'testcollection', 'test', op0, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test2', op0, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test', op1, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test2', op1, function(err) { if (err) return done(err); db.getOpsBulk('testcollection', {test: 0, test2: 1}, null, null, function(err, opsMap) { if (err) return done(err); expect(opsMap).eql({ test: [op0, op1], test2: [op1] }); done(); }); }); }); }); }); }); it('getOpsBulk returns committed ops with specific to', function(done) { var op0 = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}}; var op1 = {v: 1, op: [{p: ['x'], na: 1}]}; var db = this.db; submit(db, 'testcollection', 'test', op0, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test2', op0, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test', op1, function(err) { if (err) return done(err); submit(db, 'testcollection', 'test2', op1, function(err) { if (err) return done(err); db.getOpsBulk('testcollection', {test: 1, test2: 0}, {test2: 1}, null, function(err, opsMap) { if (err) return done(err); expect(opsMap).eql({ test: [op1], test2: [op0] }); done(); }); }); }); }); }); }); it('getOpsBulk does not return committed metadata by default', function(done) { var op = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}, m: {test: 3}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); db.getOpsBulk('testcollection', {test: null}, null, null, function(err, opsMap) { if (err) return done(err); expect(opsMap.test[0].m).equal(undefined); done(); }); }); }); it('getOpsBulk returns metadata when option is true', function(done) { var op = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}, m: {test: 3}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); db.getOpsBulk('testcollection', {test: null}, null, {metadata: true}, function(err, opsMap) { if (err) return done(err); expect(opsMap.test[0].m).eql({test: 3}); done(); }); }); }); }); describe('getOpsToSnapshot', function() { it('getOpsToSnapshot returns committed op', function(done) { var op = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); db.getSnapshot('testcollection', 'test', {$submit: true}, null, function(err, snapshot) { if (err) return done(err); db.getOpsToSnapshot('testcollection', 'test', 0, snapshot, null, function(err, ops) { if (err) return done(err); expect(ops).eql([op]); done(); }); }); }); }); it('getOpsToSnapshot does not return committed metadata by default', function(done) { var op = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}, m: {test: 3}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); db.getSnapshot('testcollection', 'test', {$submit: true}, null, function(err, snapshot) { if (err) return done(err); db.getOpsToSnapshot('testcollection', 'test', 0, snapshot, null, function(err, ops) { if (err) return done(err); expect(ops[0].m).equal(undefined); done(); }); }); }); }); it('getOpsToSnapshot returns metadata when option is true', function(done) { var op = {v: 0, create: {type: 'json0', data: {x: 5, y: 6}}, m: {test: 3}}; var db = this.db; submit(db, 'testcollection', 'test', op, function(err) { if (err) return done(err); db.getSnapshot('testcollection', 'test', {$submit: true}, null, function(err, snapshot) { if (err) return done(err); db.getOpsToSnapshot('testcollection', 'test', 0, snapshot, {metadata: true}, function(err, ops) { if (err) return done(err); expect(ops[0].m).eql({test: 3}); done(); }); }); }); }); }); if (options.getQuery) { describe('query', function() { it('query returns data in the collection', function(done) { var snapshot = {v: 1, type: 'json0', data: {x: 5, y: 6}, m: null}; var db = this.db; db.commit('testcollection', 'test', {v: 0, create: {}}, snapshot, null, function(err) { if (err) return done(err); db.query('testcollection', {x: 5}, null, null, function(err, results) { if (err) return done(err); delete results[0].id; expect(results).eql([snapshot]); done(); }); }); }); it('query returns nothing when there is no data', function(done) { this.db.query('testcollection', {x: 5}, null, null, function(err, results) { if (err) return done(err); expect(results).eql([]); done(); }); }); it('query does not return committed metadata by default', function(done) { var db = this.db; commitSnapshotWithMetadata(db, function(err) { if (err) return done(err); db.query('testcollection', {x: 5}, null, null, function(err, results) { if (err) return done(err); expect(results[0].m).equal(null); done(); }); }); }); it('query returns metadata when option is true', function(done) { var db = this.db; commitSnapshotWithMetadata(db, function(err) { if (err) return done(err); db.query('testcollection', {x: 5}, null, {metadata: true}, function(err, results) { if (err) return done(err); expect(results[0].m).eql({test: 3}); done(); }); }); }); }); describe('projections', function() { it('query returns only projected fields', function(done) { if (!this.db.projectsSnapshot) return done(); var snapshot = {type: 'json0', v: 1, data: {x: 5, y: 6}}; var db = this.db; db.commit('testcollection', 'test', {v: 0, create: {}}, snapshot, null, function(err) { if (err) return done(err); db.query('testcollection', {x: 5}, {y: true}, null, function(err, results) { if (err) return done(err); expect(results).eql([{type: 'json0', v: 1, data: {y: 6}, id: 'test'}]); done(); }); }); }); it('query returns no data for matching documents if fields is empty', function(done) { if (!this.db.projectsSnapshot) return done(); var snapshot = {type: 'json0', v: 1, data: {x: 5, y: 6}}; var db = this.db; db.commit('testcollection', 'test', {v: 0, create: {}}, snapshot, null, function(err) { if (err) return done(err); db.query('testcollection', {x: 5}, {}, null, function(err, results) { if (err) return done(err); expect(results).eql([{type: 'json0', v: 1, data: {}, id: 'test'}]); done(); }); }); }); it('query does not return committed metadata by default with projection', function(done) { var db = this.db; commitSnapshotWithMetadata(db, function(err) { if (err) return done(err); db.query('testcollection', {x: 5}, {x: true}, null, function(err, results) { if (err) return done(err); expect(results[0].m).equal(null); done(); }); }); }); it('query returns metadata when option is true with projection', function(done) { var db = this.db; commitSnapshotWithMetadata(db, function(err) { if (err) return done(err); db.query('testcollection', {x: 5}, {x: true}, {metadata: true}, function(err, results) { if (err) return done(err); expect(results[0].m).eql({test: 3}); done(); }); }); }); }); describe('queryPoll', function() { it('returns data in the collection', function(done) { var snapshot = {v: 1, type: 'json0', data: {x: 5, y: 6}}; var db = this.db; db.commit('testcollection', 'test', {v: 0, create: {}}, snapshot, null, function(err) { if (err) return done(err); var dbQuery = options.getQuery({query: {x: 5}}); db.queryPoll('testcollection', dbQuery, null, function(err, ids) { if (err) return done(err); expect(ids).eql(['test']); done(); }); }); }); it('returns nothing when there is no data', function(done) { var dbQuery = options.getQuery({query: {x: 5}}); this.db.queryPoll('testcollection', dbQuery, null, function(err, ids) { if (err) return done(err); expect(ids).eql([]); done(); }); }); }); describe('queryPollDoc', function() { it('returns false when the document does not exist', function(done) { var dbQuery = options.getQuery({query: {}}); if (!this.db.canPollDoc('testcollection', dbQuery)) return done(); var db = this.db; db.queryPollDoc('testcollection', 'doesnotexist', dbQuery, null, function(err, result) { if (err) return done(err); expect(result).equal(false); done(); }); }); it('returns true when the document matches', function(done) { var dbQuery = options.getQuery({query: {x: 5}}); if (!this.db.canPollDoc('testcollection', dbQuery)) return done(); var snapshot = {type: 'json0', v: 1, data: {x: 5, y: 6}}; var db = this.db; db.commit('testcollection', 'test', {v: 0, create: {}}, snapshot, null, function(err) { if (err) return done(err); db.queryPollDoc('testcollection', 'test', dbQuery, null, function(err, result) { if (err) return done(err); expect(result).equal(true); done(); }); }); }); it('returns false when the document does not match', function(done) { var dbQuery = options.getQuery({query: {x: 6}}); if (!this.db.canPollDoc('testcollection', dbQuery)) return done(); var snapshot = {type: 'json0', v: 1, data: {x: 5, y: 6}}; var db = this.db; db.commit('testcollection', 'test', {v: 0, create: {}}, snapshot, null, function(err) { if (err) return done(err); db.queryPollDoc('testcollection', 'test', dbQuery, null, function(err, result) { if (err) return done(err); expect(result).equal(false); done(); }); }); }); }); describe('getQuery', function() { it('getQuery argument order', function(done) { // test that options.getQuery({query: {}, sort: [['foo', 1], ['bar', -1]]}) // sorts by foo first, then bar var snapshots = [ {type: 'json0', id: '0', v: 1, data: {foo: 1, bar: 1}, m: null}, {type: 'json0', id: '1', v: 1, data: {foo: 2, bar: 1}, m: null}, {type: 'json0', id: '2', v: 1, data: {foo: 1, bar: 2}, m: null}, {type: 'json0', id: '3', v: 1, data: {foo: 2, bar: 2}, m: null} ]; var db = this.db; var dbQuery = options.getQuery({query: {}, sort: [['foo', 1], ['bar', -1]]}); async.each(snapshots, function(snapshot, cb) { db.commit('testcollection', snapshot.id, {v: 0, create: {}}, snapshot, null, cb); }, function(err) { if (err) throw err; db.query('testcollection', dbQuery, null, null, function(err, results) { if (err) throw err; expect(results).eql( [snapshots[2], snapshots[0], snapshots[3], snapshots[1]]); done(); }); }); }); }); } }); };