'use strict'; var PouchDB = require('./pouchdb'); var should = require('chai').should(); var testUtils = require('./test.utils.js'); var adapters = ['local']; adapters.forEach(function (adapter) { describe('test.all_docs.js-' + adapter, function () { var dbs = {}; beforeEach(function (done) { dbs = {name: testUtils.adapterUrl(adapter, 'testdb')}; testUtils.cleanup([dbs.name], done); }); afterEach(function (done) { testUtils.cleanup([dbs.name], done); }); var origDocs = [ {_id: '0', a: 1, b: 1}, {_id: '3', a: 4, b: 16}, {_id: '1', a: 2, b: 4}, {_id: '2', a: 3, b: 9} ]; it('Testing all docs', function (done) { var db = new PouchDB(dbs.name); testUtils.writeDocs(db, JSON.parse(JSON.stringify(origDocs)), function () { db.allDocs(function (err, result) { should.not.exist(err); var rows = result.rows; result.total_rows.should.equal(4, 'correct number of results'); for (var i = 0; i < rows.length; i++) { rows[i].id.should.be.at.least('0'); rows[i].id.should.be.at.most('4'); } db.allDocs({ startkey: '2', include_docs: true }, function (err, all) { all.rows.should.have .length(2, 'correct number when opts.startkey set'); all.rows[0].id.should .equal('2', 'correct docs when opts.startkey set'); var opts = { startkey: 'org.couchdb.user:', endkey: 'org.couchdb.user;' }; db.allDocs(opts, function (err, raw) { raw.rows.should.have.length(0, 'raw collation'); var ids = ['0', '3', '1', '2']; db.changes().on('complete', function (changes) { // order of changes is not guaranteed in a // clustered changes feed changes.results.forEach(function (row) { ids.should.include(row.id, 'seq order'); }); db.changes({ descending: true }).on('complete', function (changes) { // again, order is not guaranteed so // unsure if this is a useful test ids = ['2', '1', '3', '0']; changes.results.forEach(function (row) { ids.should.include(row.id, 'descending=true'); }); done(); }).on('error', done); }).on('error', done); }); }); }); }); }); it('Testing allDocs opts.keys', function () { var db = new PouchDB(dbs.name); function keyFunc(doc) { return doc.key; } var keys; return db.bulkDocs(origDocs).then(function () { keys = ['3', '1']; return db.allDocs({keys: keys}); }).then(function (result) { result.rows.map(keyFunc).should.deep.equal(keys); keys = ['2', '0', '1000']; return db.allDocs({ keys: keys }); }).then(function (result) { result.rows.map(keyFunc).should.deep.equal(keys); result.rows[2].error.should.equal('not_found'); return db.allDocs({ keys: keys, descending: true }); }).then(function (result) { result.rows.map(keyFunc).should.deep.equal(['1000', '0', '2']); result.rows[0].error.should.equal('not_found'); return db.allDocs({ keys: keys, startkey: 'a' }); }).then(function () { throw new Error('expected an error'); }, function (err) { should.exist(err); return db.allDocs({ keys: keys, endkey: 'a' }); }).then(function () { throw new Error('expected an error'); }, function (err) { should.exist(err); return db.allDocs({keys: []}); }).then(function (result) { result.rows.should.have.length(0); return db.get('2'); }).then(function (doc) { return db.remove(doc); }).then(function () { return db.allDocs({ keys: keys, include_docs: true }); }).then(function (result) { result.rows.map(keyFunc).should.deep.equal(keys); }); }); it('Testing allDocs opts.keys with skip', function () { var db = new PouchDB(dbs.name); return db.bulkDocs(origDocs).then(function () { return db.allDocs({ keys: ['3', '1'], skip: 1 }); }).then(function (res) { res.total_rows.should.equal(4); res.rows.should.have.length(1); res.rows[0].id.should.equal('1'); }); }); it('Testing allDocs invalid opts.keys', function () { var db = new PouchDB(dbs.name); return db.allDocs({keys: 1234}).then(function() { throw 'should not be here'; }).catch(function(err) { should.exist(err); }); }); it('Testing deleting in changes', function (done) { var db = new PouchDB(dbs.name); db.info(function (err, info) { var update_seq = info.update_seq; testUtils.writeDocs(db, JSON.parse(JSON.stringify(origDocs)), function () { db.get('1', function (err, doc) { db.remove(doc, function (err, deleted) { should.exist(deleted.ok); db.changes({ since: update_seq }).on('complete', function (changes) { var deleted_ids = changes.results.map(function (c) { if (c.deleted) { return c.id; } }); deleted_ids.should.include('1'); done(); }).on('error', done); }); }); }); }); }); it('Testing updating in changes', function (done) { var db = new PouchDB(dbs.name); db.info(function (err, info) { var update_seq = info.update_seq; testUtils.writeDocs(db, JSON.parse(JSON.stringify(origDocs)), function () { db.get('3', function (err, doc) { doc.updated = 'totally'; db.put(doc, function () { db.changes({ since: update_seq }).on('complete', function (changes) { var ids = changes.results.map(function (c) { return c.id; }); ids.should.include('3'); done(); }).on('error', done); }); }); }); }); }); it('Testing include docs', function (done) { var db = new PouchDB(dbs.name); testUtils.writeDocs(db, JSON.parse(JSON.stringify(origDocs)), function () { db.changes({ include_docs: true }).on('complete', function (changes) { changes.results.forEach(function (row) { if (row.id === '0') { row.doc.a.should.equal(1); } }); done(); }).on('error', done); }); }); it('Testing conflicts', function (done) { var db = new PouchDB(dbs.name); testUtils.writeDocs(db, JSON.parse(JSON.stringify(origDocs)), function () { // add conflicts var conflictDoc1 = { _id: '3', _rev: '2-aa01552213fafa022e6167113ed01087', value: 'X' }; var conflictDoc2 = { _id: '3', _rev: '2-ff01552213fafa022e6167113ed01087', value: 'Z' }; db.put(conflictDoc1, { new_edits: false }, function () { db.put(conflictDoc2, { new_edits: false }, function () { db.get('3', function (err, winRev) { winRev._rev.should.equal(conflictDoc2._rev); db.changes({ include_docs: true, conflicts: true, style: 'all_docs' }).on('complete', function (changes) { changes.results.map(function (x) { return x.id; }).sort() .should.deep.equal(['0', '1', '2', '3'], 'all ids are in _changes'); var result = changes.results.filter(function (row) { return row.id === '3'; })[0]; result.changes.should.have .length(3, 'correct number of changes'); result.doc._rev.should.equal(conflictDoc2._rev); result.doc._id.should.equal('3', 'correct doc id'); winRev._rev.should.equal(result.doc._rev); result.doc._conflicts.should.be.instanceof(Array); result.doc._conflicts.should.have.length(2); conflictDoc1._rev.should.equal(result.doc._conflicts[0]); db.allDocs({ include_docs: true, conflicts: true }, function (err, res) { var row = res.rows[3]; res.rows.should.have.length(4, 'correct number of changes'); row.key.should.equal('3', 'correct key'); row.id.should.equal('3', 'correct id'); row.value.rev.should.equal(winRev._rev, 'correct rev'); row.doc._rev.should.equal(winRev._rev, 'correct rev'); row.doc._id.should.equal('3', 'correct order'); row.doc._conflicts.should.be.instanceof(Array); row.doc._conflicts.should.have.length(2); conflictDoc1._rev.should .equal(res.rows[3].doc._conflicts[0]); done(); }); }).on('error', done); }); }); }); }); }); it('test basic collation', function (done) { var db = new PouchDB(dbs.name); var docs = { docs: [ {_id: 'z', foo: 'z'}, {_id: 'a', foo: 'a'} ] }; db.bulkDocs(docs, function () { db.allDocs({ startkey: 'z', endkey: 'z' }, function (err, result) { result.rows.should.have.length(1, 'Exclude a result'); done(); }); }); }); it('3883 start_key end_key aliases', function () { var db = new PouchDB(dbs.name); var docs = [{_id: 'a', foo: 'a'}, {_id: 'z', foo: 'z'}]; return db.bulkDocs(docs).then(function() { return db.allDocs({start_key: 'z', end_key: 'z'}); }).then(function (result) { result.rows.should.have.length(1, 'Exclude a result'); }); }); it('test total_rows with a variety of criteria', function (done) { var db = new PouchDB(dbs.name); var docs = [ {_id : '0'}, {_id : '1'}, {_id : '2'}, {_id : '3'}, {_id : '4'}, {_id : '5'}, {_id : '6'}, {_id : '7'}, {_id : '8'}, {_id : '9'} ]; db.bulkDocs({docs : docs}).then(function (res) { docs[3]._deleted = true; docs[7]._deleted = true; docs[3]._rev = res[3].rev; docs[7]._rev = res[7].rev; return db.remove(docs[3]); }).then(function () { return db.remove(docs[7]); }).then(function () { return db.allDocs(); }).then(function (res) { res.rows.should.have.length(8, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '5'}); }).then(function (res) { res.rows.should.have.length(4, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '5', skip : 2, limit : 10}); }).then(function (res) { res.rows.should.have.length(2, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '5', limit : 0}); }).then(function (res) { res.rows.should.have .length(0, 'correctly return rows, startkey w/ limit=0'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({keys : ['5'], limit : 0}); }).then(function (res) { res.rows.should.have .length(0, 'correctly return rows, keys w/ limit=0'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({limit : 0}); }).then(function (res) { res.rows.should.have.length(0, 'correctly return rows, limit=0'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '5', descending : true, skip : 1}); }).then(function (res) { res.rows.should.have.length(4, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '5', endkey : 'z'}); }).then(function (res) { res.rows.should.have.length(4, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '5', endkey : '5'}); }).then(function (res) { res.rows.should.have.length(1, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '5', endkey : '4'}); }).then(function (res) { res.rows.should.have.length(0, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '5', endkey : '4', descending : true}); }).then(function (res) { res.rows.should.have.length(2, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '3', endkey : '7', descending : false}); }).then(function (res) { res.rows.should.have.length(3, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '7', endkey : '3', descending : true}); }).then(function (res) { res.rows.should.have.length(3, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({startkey : '', endkey : '0'}); }).then(function (res) { res.rows.should.have.length(1, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({keys : ['0', '1', '3']}); }).then(function (res) { res.rows.should.have.length(3, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({keys : ['0', '1', '0', '2', '1', '1']}); }).then(function (res) { res.rows.should.have.length(6, 'correctly return rows'); res.rows.map(function (row) { return row.key; }).should.deep.equal( ['0', '1', '0', '2', '1', '1']); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({keys : []}); }).then(function (res) { res.rows.should.have.length(0, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({keys : ['7']}); }).then(function (res) { res.rows.should.have.length(1, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({key : '3'}); }).then(function (res) { res.rows.should.have.length(0, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({key : '2'}); }).then(function (res) { res.rows.should.have.length(1, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); return db.allDocs({key : 'z'}); }).then(function (res) { res.rows.should.have.length(0, 'correctly return rows'); res.total_rows.should.equal(8, 'correctly return total_rows'); done(); }, done); }); it('test total_rows with both skip and limit', function (done) { var db = new PouchDB(dbs.name); var docs = { docs: [ {_id: "w", foo: "w"}, {_id: "x", foo: "x"}, {_id: "y", foo: "y"}, {_id: "z", foo: "z"} ] }; db.bulkDocs(docs, function () { db.allDocs({ startkey: 'x', limit: 1, skip : 1}, function (err, res) { res.total_rows.should.equal(4, 'Accurately return total_rows count'); res.rows.should.have.length(1, 'Correctly limit the returned rows'); res.rows[0].id.should.equal('y', 'Correctly skip 1 doc'); db.get('x', function (err, xDoc) { db.remove(xDoc, function () { db.allDocs({ startkey: 'w', limit: 2, skip : 1}, function (err, res) { res.total_rows.should .equal(3, 'Accurately return total_rows count after delete'); res.rows.should.have .length(2, 'Correctly limit the returned rows after delete'); res.rows[0].id.should .equal('y', 'Correctly skip 1 doc after delete'); done(); }); }); }); }); }); }); it('test limit option and total_rows', function (done) { var db = new PouchDB(dbs.name); var docs = { docs: [ {_id: 'z', foo: 'z'}, {_id: 'a', foo: 'a'} ] }; db.bulkDocs(docs, function () { db.allDocs({ startkey: 'a', limit: 1 }, function (err, res) { res.total_rows.should.equal(2, 'Accurately return total_rows count'); res.rows.should.have.length(1, 'Correctly limit the returned rows.'); done(); }); }); }); it('test escaped startkey/endkey', function (done) { var db = new PouchDB(dbs.name); var id1 = '"weird id!" a'; var id2 = '"weird id!" z'; var docs = { docs: [ { _id: id1, foo: 'a' }, { _id: id2, foo: 'z' } ] }; db.bulkDocs(docs, function () { db.allDocs({ startkey: id1, endkey: id2 }, function (err, res) { res.total_rows.should.equal(2, 'Accurately return total_rows count'); done(); }); }); }); it('test "key" option', function (done) { var db = new PouchDB(dbs.name); db.bulkDocs({ docs: [ { _id: '0' }, { _id: '1' }, { _id: '2' } ] }, function (err) { should.not.exist(err); db.allDocs({ key: '1' }, function (err, res) { res.rows.should.have.length(1, 'key option returned 1 doc'); db.allDocs({ key: '1', keys: [ '1', '2' ] }, function (err) { should.exist(err); db.allDocs({ key: '1', startkey: '1' }, function (err) { should.not.exist(err); db.allDocs({ key: '1', endkey: '1' }, function (err) { should.not.exist(err); // when mixing key/startkey or key/endkey, the results // are very weird and probably undefined, so don't go beyond // verifying that there's no error done(); }); }); }); }); }); }); it('test inclusive_end=false', function (done) { new PouchDB(dbs.name).then(function (db) { var docs = [ { _id: '1' }, { _id: '2' }, { _id: '3' }, { _id: '4' } ]; return db.bulkDocs({docs: docs}).then(function () { return db.allDocs({inclusive_end: false, endkey: '2'}); }).then(function (res) { res.rows.should.have.length(1); return db.allDocs({inclusive_end: false, endkey: '1'}); }).then(function (res) { res.rows.should.have.length(0); return db.allDocs({inclusive_end: false, endkey: '1', startkey: '0'}); }).then(function (res) { res.rows.should.have.length(0); return db.allDocs({inclusive_end: false, endkey: '5'}); }).then(function (res) { res.rows.should.have.length(4); return db.allDocs({inclusive_end: false, endkey: '4'}); }).then(function (res) { res.rows.should.have.length(3); return db.allDocs({inclusive_end: false, endkey: '4', startkey: '3'}); }).then(function (res) { res.rows.should.have.length(1); return db.allDocs({inclusive_end: false, endkey: '1', descending: true}); }).then(function (res) { res.rows.should.have.length(3); return db.allDocs({inclusive_end: true, endkey: '4'}); }).then(function (res) { res.rows.should.have.length(4); return db.allDocs({ descending: true, startkey: '3', endkey: '2', inclusive_end: false }); }); }).then(function (res) { res.rows.should.have.length(1); }).then(function () { done(); }, function (err) { done(err); }); }); it.skip('#3082 test wrong num results returned', function () { var db = new PouchDB(dbs.name); var docs = []; for (var i = 0; i < 1000; i++) { docs.push({}); } var lastkey; var allkeys = []; function paginate() { var opts = {include_doc: true, limit: 100}; if (lastkey) { opts.startkey = lastkey; opts.skip = 1; } return db.allDocs(opts).then(function (res) { if (!res.rows.length) { return; } if (lastkey) { res.rows[0].key.should.be.above(lastkey); } res.rows.should.have.length(100); lastkey = res.rows.pop().key; allkeys.push(lastkey); return paginate(); }); } return db.bulkDocs(docs).then(function () { return paginate().then(function () { // try running all queries at once to try to isolate race condition return PouchDB.utils.Promise.all(allkeys.map(function (key) { return db.allDocs({ limit: 100, include_docs: true, startkey: key, skip: 1 }).then(function (res) { if (!res.rows.length) { return; } res.rows[0].key.should.be.above(key); res.rows.should.have.length(100); }); })); }); }); }); it('test empty db', function (done) { return new PouchDB(dbs.name).then(function (db) { return db.allDocs().then(function (res) { res.rows.should.have.length(0); res.total_rows.should.equal(0); done(); }); }); }); it('test after db close', function (done) { return new PouchDB(dbs.name).then(function (db) { return db.close().then(function () { return db.allDocs().catch(function (err) { err.message.should.equal('database is closed'); done(); }); }); }); }); if (adapter === 'local') { // chrome doesn't like \u0000 in URLs it('test unicode ids and revs', function (done) { var db = new PouchDB(dbs.name); var id = 'baz\u0000'; var rev; return db.put({_id: id}).then(function (res) { rev = res.rev; }).then(function () { return db.get(id); }).then(function (doc) { doc._id.should.equal(id); doc._rev.should.equal(rev); return db.allDocs({keys: [id]}); }).then(function (res) { res.rows.should.have.length(1); res.rows[0].value.rev.should.equal(rev); }).then(done, done); }); } }); });