<commit_before>var async = require('async');
var expect = require('expect.js');
var Backend = require('../lib/backend');
var ot = require('../lib/ot');

module.exports = function(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() {
      this.backend.close();
    });

    require('./client/projections')();
    require('./client/query-subscribe')();
    require('./client/query')();
    require('./client/submit')();
    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}, 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, 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, function(err, opsOut) {
            if (err) return done(err);
            db.getSnapshot('testcollection', 'foo', 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);
        });
      });
    });

    describe('getSnapshot', function() {
      it('getSnapshot returns v0 snapshot', function(done) {
        this.db.getSnapshot('testcollection', 'test', null, function(err, result) {
          if (err) return done(err);
          expect(result).eql({id: 'test', type: null, v: 0, data: undefined});
          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, succeeded) {
          if (err) return done(err);
          db.getSnapshot('testcollection', 'test', null, function(err, result) {
            if (err) return done(err);
            expect(result).eql({id: 'test', type: 'http://sharejs.org/types/JSONv0', v: 1, data: data});
            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, succeeded) {
          if (err) return done(err);
          db.getSnapshotBulk('testcollection', ['test2', 'test'], 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},
              test2: {id: 'test2', type: null, v: 0, data: undefined}
            });
            done();
          });
        });
      });
    });

    describe('getOps', function() {
      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, succeeded) {
          if (err) return done(err);
          db.getOps('testcollection', 'test', 0, 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, succeeded) {
          if (err) return done(err);
          submit(db, 'testcollection', 'test', op1, function(err, succeeded) {
            if (err) return done(err);
            db.getOps('testcollection', 'test', 0, 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, succeeded) {
          if (err) return done(err);
          submit(db, 'testcollection', 'test', op1, function(err, succeeded) {
            if (err) return done(err);
            db.getOps('testcollection', 'test', 1, 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, succeeded) {
          if (err) return done(err);
          submit(db, 'testcollection', 'test', op1, function(err, succeeded) {
            if (err) return done(err);
            db.getOps('testcollection', 'test', 0, 1, function(err, ops) {
              if (err) return done(err);
              expect(ops).eql([op0]);
              done();
            });
          });
        });
      });
    });

    describe('getOpsBulk', function() {
      it('getOpsBulk returns committed ops', function(done) {<commit_msg>add tests of db.getOps when there are no ops<commit_after>var async = require('async');
var expect = require('expect.js');
var Backend = require('../lib/backend');
var ot = require('../lib/ot');

module.exports = function(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() {
      this.backend.close();
    });

    require('./client/projections')();
    require('./client/query-subscribe')();
    require('./client/query')();
    require('./client/submit')();
    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}, 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, 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, function(err, opsOut) {
            if (err) return done(err);
            db.getSnapshot('testcollection', 'foo', 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);
        });
      });
    });

    describe('getSnapshot', function() {
      it('getSnapshot returns v0 snapshot', function(done) {
        this.db.getSnapshot('testcollection', 'test', null, function(err, result) {
          if (err) return done(err);
          expect(result).eql({id: 'test', type: null, v: 0, data: undefined});
          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, succeeded) {
          if (err) return done(err);
          db.getSnapshot('testcollection', 'test', null, function(err, result) {
            if (err) return done(err);
            expect(result).eql({id: 'test', type: 'http://sharejs.org/types/JSONv0', v: 1, data: data});
            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, succeeded) {
          if (err) return done(err);
          db.getSnapshotBulk('testcollection', ['test2', 'test'], 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},
              test2: {id: 'test2', type: null, v: 0, data: undefined}
            });
            done();
          });
        });
      });
    });

    describe('getOps', function() {
      it('getOps returns empty list when there are no ops', function(done) {
        this.db.getOps('testcollection', 'test', 0, 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, succeeded) {
          if (err) return done(err);
          db.getOps('testcollection', 'test', 0, 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, succeeded) {
          if (err) return done(err);
          submit(db, 'testcollection', 'test', op1, function(err, succeeded) {
            if (err) return done(err);
            db.getOps('testcollection', 'test', 0, 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, succeeded) {
          if (err) return done(err);
          submit(db, 'testcollection', 'test', op1, function(err, succeeded) {
            if (err) return done(err);
            db.getOps('testcollection', 'test', 1, 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, succeeded) {
          if (err) return done(err);
          submit(db, 'testcollection', 'test', op1, function(err, succeeded) {
            if (err) return done(err);
            db.getOps('testcollection', 'test', 0, 1, function(err, ops) {
              if (err) return done(err);
              expect(ops).eql([op0]);
              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, function(err, opsMap) {
          if (err) return done(err);
          expect(opsMap).eql({
            test: [],
            test2: []
          });
          done();
        });
      });

      it('getOpsBulk returns committed ops', function(done) {
{'fraction_non_alphanumeric': 0.16706573300203298, 'fraction_numerical': 0.01179128077704992, 'mean_word_length': 1.9668945181611044, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1004030', 'n_tokens_mistral': 6977, 'n_tokens_neox': 6747, 'n_words': 1860}