var _ = require('underscore'); var when = require('when'); var Builder = require('../../lib/builder').Builder; var Common = require('../../lib/common').Common; var Raw = require('../../lib/raw').Raw; describe('Builder', function () { var builder; beforeEach(function() { builder = new Builder({ query: function(obj) { return when.resolve(obj); }, grammar: require('../../clients/server/mysql/grammar').grammar }); }); describe('constructor', function () { it('accepts the current Knex instance, attaching the client, and grammar', function() { var grammar = {some: 'grammar'}; var client = {some: 'db'}; var knex = {client: client, grammar: grammar}; var reset = sinon.stub(Builder.prototype, 'reset'); var builder = new Builder(knex); expect(builder.knex).to.eql(knex); expect(builder.client).to.eql(client); expect(builder.grammar).to.eql(grammar); reset.should.have.been.calledOnce; reset.restore(); }); }); describe('from', function() { it('sets the tableName', function() { expect(builder.table).to.be.empty; var result = builder.from('myTable'); expect(builder.table).to.equal('myTable'); expect(result).to.equal(builder); }); it('returns the tableName when no arguments are passed', function() { expect(builder.table).to.be.empty; var result = builder.from('myTable'); expect(builder.from()).to.equal('myTable'); }); }); describe('column', function() { it('adds a value to the columns array', function() { expect(builder.columns).to.have.length(0); builder.column('myColumn'); expect(builder.columns).to.have.length(1); }); }); describe('distinct', function() { it('sets the isDistinct flag to true', function() { builder.distinct('distinctCol'); expect(builder.columns[0]).to.equal('distinctCol'); }); it('adds the column to the columns array', function() { builder.distinct('distinctCol'); expect(builder.flags.distinct).to.be.true; }); }); describe('toSql', function() { it('sets the type to "select" if not otherwise set', function() { expect(builder.type).not.to.exist; builder.toSql(); expect(builder.type).to.equal('select'); }); it('compiles based on the type, passing the current context', function(done) { var compileSelect = builder.grammar.compileSelect; builder.grammar.compileSelect = function(ctx) { expect(ctx).to.eql(builder); builder.grammar.compileSelect = compileSelect; done(); }; builder.toSql(); }); }); describe('toString', function() { it('should properly escape the query string', function() { var output = "select * from `items` outer join `users` on `items`.`id` = `users`.`id` where `id` = 'User' or `id` = (SELECT id from otheritems)"; expect(builder .from('items') .join('users', function() { this.on('items.id', '=', 'users.id'); this.type('outer'); }) .where('id', '=', 'User') .orWhere('id', '=', new Raw({}).query('(SELECT id from otheritems)')).toString()).to.equal(output); }); }); describe('clone', function() { it('should keep the correct type when cloning the instance', function() { var cloned = builder.insert({a: 'value'}).into('tableName').clone(); expect(cloned.type).to.equal('insert'); }); }); describe('reset', function() { }); describe('join', function() { var JoinClause = require('../../lib/builder/joinclause').JoinClause; it('accepts the joining table, the first column, operator, second column, and (optional) type', function() { expect(builder.joins).to.have.length(0); builder.from('users'); builder.join('accounts', 'users.id', '=', 'accounts.id'); expect(builder.joins).to.have.length(1); expect(builder.joins[0]).to.be.an.instanceOf(JoinClause); expect(builder.toString()).to.equal("select * from `users` inner join `accounts` on `users`.`id` = `accounts`.`id`"); }); it('accepts a different join type as the fifth parameter', function() { builder.from('users'); builder.join('accounts', 'users.id', '=', 'accounts.id', 'left outer'); expect(builder.toString()).to.equal("select * from `users` left outer join `accounts` on `users`.`id` = `accounts`.`id`"); }); it('may take a function as the second argument, for a grouped join', function() { builder.join('accounts', function(join) { expect(builder.joins).to.have.length(0); expect(this).to.be.an.instanceOf(JoinClause); expect(join).to.be.an.instanceOf(JoinClause); }); }); }); describe('where', function() { describe('where', function() { it('should allow a function as the first argument, for a grouped where clause', function() { builder.where('id', '=', 1); expect(builder.wheres).to.have.length(1); expect(builder.bindings).to.have.length(1); var subWhere = function(qb) { expect(this).to.equal(qb); expect(this).to.be.an.instanceOf(Builder); this.where({id: 3}).orWhere('id', 4); }; builder.where(subWhere); expect(builder.bindings).to.have.length(3); expect(builder.wheres).to.have.length(2); expect(builder.from('test').toString()).to.equal("select * from `test` where `id` = 1 and (`id` = 3 or `id` = 4)"); }); it('should allow a raw instance as the first argument, which will add a whereRaw clause', function() { builder.where(new Raw({}).query('id > ?', 2)); expect(builder.wheres).to.have.length(1); expect(builder.bindings).to.have.length(1); expect(builder.bindings[0]).to.equal(2); }); it('should allow an object as the first argument, which will assume to be a k/v pair of where "="', function() { builder.where({id: 2, name: 'Test'}); expect(builder.wheres).to.have.length(2); expect(builder.bindings).to.have.length(2); }); it('should assume that if the second argument is not an operator, it should be an "="', function() { builder.where('id', 2); expect(builder.wheres).to.have.length(1); expect(builder.bindings[0]).to.equal(2); }); it('should accept a function as the "value", for a sub select', function() { builder.where('id', '=', function(qb) { expect(this).to.equal(qb); expect(this).to.be.an.instanceOf(Builder); this.select('account_id').from('names').where('names.id', '>', 1).orWhere(function() { this.where('names.first_name', 'like', 'Tim%').andWhere('names.id', '>', 10); }); }); expect(builder.bindings).to.have.length(3); expect(builder.toString()).to.equal("select * where `id` = (select `account_id` from `names` where `names`.`id` > 1 or (`names`.`first_name` like 'Tim%' and `names`.`id` > 10))"); }); it('should not do whereNull on where("foo", "<>", null) #76', function() { var query = builder.where('foo', '<>', null); expect(query.toString()).to.equal('select * where `foo` <> NULL'); }); it('should expand where("foo", "!=") to - where id = "!="', function() { var query = builder.where('foo', '!='); expect(query.toString()).to.equal("select * where `foo` = '!='"); }); }); describe('whereRaw', function() { it('is called by orWhereRaw, passing the sql, bindings, and "or"', function() { }); }); describe('whereExists', function() { }); }); describe('groupBy', function() { }); describe('orderBy', function() { }); describe('union', function() { }); describe('having', function() { }); describe('offset / limit', function() { }); describe('aggregate', function() { }); describe('select', function() { it('should throw if a table has not been specified when compiling'); }); describe('insert', function() { it('sets the type to insert', function() { builder.insert(); expect(builder.type).to.equal('insert'); }); it('sets the values to be insert', function() { var spy = sinon.spy(builder, 'prepValues'); builder.insert({key: 1, key2: 2}); spy.should.have.been.calledOnce; }); it('sorts the values in arrays', function() { builder.insert([{key1: 'tim', key2: 'test'}, {key2: 'test2', key1: null}]); expect(_.map(builder.values, function(val) { return _.pluck(val, '0'); })).to.eql([['key1', 'key2'], ['key1', 'key2']]); }); it('does not mutate the values being inserted', function() { var x = [{a: 1}, {b: 2}]; var y = [{a: 1}, {b: 2}]; builder.insert(x); expect(x).to.eql(y); }); it('takes a second argument to set the isReturning, using the returning method', function() { expect(builder.flags.returning).to.be.empty; builder.insert({'insert': 'val'}, 'user_id'); expect(builder.flags.returning).to.equal('user_id'); }); }); describe('update', function() { it('sorts the values in arrays', function() { builder.update({key1: 'tim', key2: 'test'}); expect(_.pluck(builder.values, '0')).to.eql(['key1', 'key2']); expect(builder.bindings).to.have.length(2); }); }); describe('delete', function() { }); describe('truncate', function() { }); });