wip unit tests & refactoring

This commit is contained in:
Tim Griesser 2013-09-05 16:36:49 -04:00
parent 2bf076f15a
commit faa2497ef1
38 changed files with 596 additions and 220 deletions

View File

@ -1,20 +0,0 @@
(function(define) {
"use strict";
define(function(require, exports) {
var Helpers = require('../../lib/helpers').Helpers;
var Client = function(name, options) {};
Client.prototype = {};
Client.extend = Helpers.extend;
exports.Client = Client;
});
})(
typeof define === 'function' && define.amd ? define : function (factory) { factory(require, exports); }
);

View File

@ -10,12 +10,10 @@ define(function(require, exports) {
var Grammar = require('./grammar').Grammar;
var SchemaGrammar = require('./schemagrammar').SchemaGrammar;
var Client = require('./client').Client;
var Helpers = require('../../lib/helpers').Helpers;
var ClientBase = require('../base').ClientBase;
var Helpers = require('../../lib/helpers').Helpers;
var Sqlite3 = Client.extend({
});
var Sqlite3 = ClientBase.extend({});
// Extends the standard sql grammar.
Sqlite3.grammar = _.defaults({

View File

@ -91,8 +91,9 @@ exports.protoProps = {
});
},
finishTransaction: function(type, trans, dfd, msg) {
finishTransaction: function(type, transaction, msg) {
var ctx = this;
var dfd = transaction.dfd;
nodefn.call(trans.connection.query.bind(trans.connection), type + ';', []).then(function(resp) {
if (type === 'commit') dfd.resolve(msg || resp);
if (type === 'rollback') dfd.reject(msg || resp);

View File

@ -21,8 +21,8 @@ _.extend(MysqlClient.prototype, base.protoProps, {
// acquire a connection, and then dispose of it when we're done.
query: function(builder) {
var emptyConnection = !builder._connection;
var debug = this.debug || builder._debug;
var instance = this;
var debug = this.debug || builder._debug;
var client = this;
return when((builder._connection || this.getConnection()))
.tap(this.checkSchema(builder))
.then(function(conn) {
@ -64,7 +64,7 @@ _.extend(MysqlClient.prototype, base.protoProps, {
// Empty the connection after we run the query, unless one was specifically
// set (in the case of transactions, etc).
return dfd.promise.ensure(function() {
if (emptyConnection) instance.pool.release(conn);
if (emptyConnection) client.pool.release(conn);
});
}).otherwise(function(err) {
throw new Error(err.toString() + ': ' + builder.sql);

46
knex.js
View File

@ -16,9 +16,11 @@ define(function(require, exports, module) {
var Raw = require('./lib/raw').Raw;
var Transaction = require('./lib/transaction').Transaction;
var Builder = require('./lib/builder').Builder;
var Interface = require('./lib/builder/interface').Interface;
var Schema = require('./lib/schema').Schema;
var ClientBase = require('./clients/clientbase').ClientBase;
// var Interface = require('./lib/builder/interface').Interface;
var Schema = require('./lib/schemabuilder').SchemaBuilder;
var SchemaInterface = require('./lib/schemainterface').SchemaInterface;
var ClientBase = require('./clients/base').ClientBase;
// The `Knex` module, taking either a fully initialized
// database client, or a configuration to initialize one. This is something
@ -61,12 +63,12 @@ define(function(require, exports, module) {
// `Knex.select('*').from('tableName').then(...`
// `Knex.insert(values).into('tableName').then(...`
// `Knex.update(values).then(...`
_.each(Interface, function(val, key) {
instance[key] = function() {
var builder = new Builder(instance);
return builder[key].apply(builder, arguments);
};
});
// _.each(Interface, function(val, key) {
// instance[key] = function() {
// var builder = new Builder(instance);
// return builder[key].apply(builder, arguments);
// };
// });
// Attach each of the `Schema` "interface" methods directly onto to `Knex.Schema` namespace:
// `Knex.Schema.table('tableName', function() {...`
@ -76,22 +78,21 @@ define(function(require, exports, module) {
instance.schema[key] = function() {
var schemaBuilder = new SchemaBuilder(instance);
schemaBuilder.table = _.first(arguments);
return schemaBuilder[key].apply(schemaBuilder, _.rest(arguments));
return SchemaInterface[key].apply(schemaBuilder, _.rest(arguments));
};
});
// Attach each of the `Migrate` "interface" methods directly on to
_.each(MigrateInterface, function(val, key) {
instance.migrate[key] = function() {
var migrateBuilder = new MigrateBuilder(instance);
return MigrateBuilder[key].apply(migrateBuilder, arguments);
};
});
// _.each(MigrateInterface, function(val, key) {
// instance.migrate[key] = function() {
// var migrateBuilder = new MigrateBuilder(instance);
// return MigrateBuilder[key].apply(migrateBuilder, arguments);
// };
// });
// Method to run a new `Raw` query on the current client.
instance.raw = function() {
var raw = new Raw(instance);
return raw.query.apply(raw, arguments);
instance.raw = function(sql, bindings) {
return new Raw(instance).query(sql, bindings);
};
// Keep a reference to the current client.
@ -102,8 +103,7 @@ define(function(require, exports, module) {
// Runs a new transaction, taking a container and
instance.transaction = function(container) {
var transaction = new Transaction(instance);
return transaction.run(container);
return new Transaction(instance).run(container);
};
// Return the new Knex instance.
@ -125,6 +125,10 @@ define(function(require, exports, module) {
// finally, export the `Knex` object for node and the browser.
module.exports = Knex;
Knex.initialize = function(config) {
return Knex(config);
};
});
})(

View File

@ -11,19 +11,15 @@ define(function(require, exports) {
var Common = require('./common').Common;
var Raw = require('./raw').Raw;
var Helpers = require('./helpers').Helpers;
var JoinClause = require('./joinclause').JoinClause;
var JoinClause = require('./builder/joinclause').JoinClause;
var array = [];
var push = array.push;
var Builder = function(client) {
if (client instanceof Builder) {
this.client = client.client;
this.grammar = this.client.grammar;
} else {
this.client = client;
this.grammar = client.grammar;
}
var Builder = function(instance) {
this.knex = instance;
this.client = instance.client;
this.grammar = instance.grammar;
this.reset();
};
@ -64,7 +60,7 @@ define(function(require, exports) {
// Clones the current query builder, including any
// pieces that have been set thus far.
clone: function() {
var item = new Builder(this);
var item = new Builder(this.knex);
item.table = this.table;
var items = [
'isDistinct', 'joins', 'wheres', 'orders',
@ -143,8 +139,8 @@ define(function(require, exports) {
},
// Alias to `where`, for internal builder consistency.
andWhere: function() {
return this.where.apply(this, arguments);
andWhere: function(column, operator, value) {
return this.where(column, operator, value, 'and');
},
// Adds an `or where` clause to the query.
@ -167,7 +163,7 @@ define(function(require, exports) {
// Adds a `where exists` clause to the query.
whereExists: function(callback, bool, type) {
var query = new Builder(this);
var query = new Builder(this.knex);
callback.call(query, query);
this.wheres.push({
type: (type || 'Exists'),
@ -385,7 +381,7 @@ define(function(require, exports) {
for (var i = 0, l = obj.length; i < l; i++) {
bindings[i] = obj[i][1];
}
this.bindings = bindings.concat(this.bindings || []);
this.bindings = bindings.concat(this.bindings);
this.values = obj;
return this._setType('update');
},
@ -425,7 +421,7 @@ define(function(require, exports) {
// Helper for compiling any advanced `where in` queries.
_whereInSub: function(column, callback, bool, condition) {
condition += 'Sub';
var query = new Builder(this);
var query = new Builder(this.knex);
callback.call(query, query);
this.wheres.push({type: condition, column: column, query: query, bool: bool});
push.apply(this.bindings, query.bindings);
@ -434,8 +430,7 @@ define(function(require, exports) {
// Helper for compiling any advanced `where` queries.
_whereNested: function(callback, bool) {
var query = new Builder(this);
query.table = this.table;
var query = new Builder(this.knex);
callback.call(query, query);
this.wheres.push({type: 'Nested', query: query, bool: bool});
push.apply(this.bindings, query.bindings);
@ -444,7 +439,7 @@ define(function(require, exports) {
// Helper for compiling any of the `where` advanced queries.
_whereSub: function(column, operator, callback, bool) {
var query = new Builder(this);
var query = new Builder(this.knex);
callback.call(query, query);
this.wheres.push({
type: 'Sub',
@ -466,14 +461,12 @@ define(function(require, exports) {
// Helper for the incrementing/decrementing queries.
_counter: function(column, amount, symbol) {
var sql = {};
sql[column] = new Raw('' + this.grammar.wrap(column) + ' ' + (symbol || '+') + ' ' + amount);
return this.update(sql);
return this.update({column: this.knex.raw('' + this.grammar.wrap(column) + ' ' + (symbol || '+') + ' ' + amount)});
},
// Helper for compiling any `union` queries.
_union: function(callback, bool) {
var query = new Builder(this);
var query = new Builder(this.knex);
callback.call(query, query);
this.unions.push({query: query, all: bool});
push.apply(this.bindings, query.bindings);

View File

@ -8,8 +8,8 @@ define(function(require, exports) {
var JoinClause = function(type, table) {
this.clauses = [];
this.type = type;
this.table = table;
this.type = type;
this.table = table;
};
JoinClause.prototype = {

0
lib/chainable/column.js Normal file
View File

0
lib/chainable/foreign.js Normal file
View File

View File

@ -16,12 +16,10 @@ define(function(require, exports) {
// used to generate the sql in one form or another.
exports.Common = {
_debug: false,
_promise: null,
isDebugging: false,
debug: function() {
this._debug = true;
this.isDebugging = true;
return this;
},
@ -30,22 +28,28 @@ define(function(require, exports) {
// if called more than once. Any unhandled errors will be thrown
// after the last block.
exec: function(callback) {
this._promise || (this._promise = this.runQuery());
this._promise || (this._promise = this.client.query(this));
return this._promise.then(function(resp) {
if (callback) callback(null, resp);
}, function(err) {
if (callback) callback(err, null);
}).then(null, function(err) {
}).otherwise(function(err) {
setTimeout(function() { throw err; }, 0);
});
},
// The promise interface for the query builder.
then: function(onFulfilled, onRejected) {
this._promise || (this._promise = this.runQuery());
this._promise || (this._promise = this.client.query(this));
return this._promise.then(onFulfilled, onRejected);
},
// Passthrough to the convenient `tap` mechanism of when.js
tap: function(handler) {
this._promise = this._promise || this.client.query(this);
return this._promise.tap(handler);
},
// Returns an array of query strings filled out with the
// correct values based on bindings, etc. Useful for debugging.
toString: function() {
@ -63,13 +67,13 @@ define(function(require, exports) {
// Explicitly sets the connection.
connection: function(connection) {
this._connection = connection;
this.usingConnection = connection;
return this;
},
// The connection the current query is being run on, optionally
// specified by the `connection` method.
_connection: false,
usingConnection: false,
// Sets the "type" of the current query, so we can potentially place
// `select`, `update`, `del`, etc. anywhere in the query statement
@ -97,29 +101,33 @@ define(function(require, exports) {
},
// Runs the query on the current builder instance and returns a promise.
runQuery: function() {
if (this.transaction) {
if (!this.transaction.connection) {
throw new Error('The transaction has already completed.');
}
this._connection = this.transaction.connection;
}
// runQuery: function() {
// this.client.query(this);
// Prep the SQL associated with the this.
this.sql = this.toSql();
this.bindings = this._cleanBindings();
if (!_.isArray(this.sql)) this.sql = [this.sql];
// if (this.transaction) {
// if (!this.transaction.connection) {
// throw new Error('The transaction has already completed.');
// }
// this.usingConnection = this.transaction.connection;
// }
var chain;
for (var i = 0, l = this.sql.length; i < l; i++) {
if (chain) {
chain.then(Helpers.multiQuery(this, i, chain));
} else {
chain = Helpers.multiQuery(this, i);
}
}
return chain;
},
// // Prep the SQL associated with the this.
// this.sql = this.toSql();
// this.bindings = this._cleanBindings();
// if (!_.isArray(this.sql)) this.sql = [this.sql];
// ;
// var chain;
// for (var i = 0, l = this.sql.length; i < l; i++) {
// if (chain) {
// chain.then(Helpers.multiQuery(this, i, chain));
// } else {
// chain = Helpers.multiQuery(this, i);
// }
// }
// return chain;
// },
// Sets the current Builder connection to that of the
// the currently running transaction

View File

@ -21,16 +21,6 @@ define(function(require, exports) {
});
},
// Sets up a multi-query to be executed with serial promises.
multiQuery: function(builder, i, chain) {
if (chain) {
return function() {
return Helpers.multiQuery(builder, i);
};
}
return builder.client.query(_.extend({currentIndex: i}, builder, {sql: builder.sql[i]}));
},
// The standard Backbone.js `extend` method.
extend: function(protoProps, staticProps) {
var parent = this;

View File

@ -11,8 +11,8 @@ var sequence = require('when/sequence');
// The new migration we're performing.
// Takes a `config` object, which has the name
// of the current migration (`main` if not otherwise specified)
var Migrate = function(Instance) {
this.Knex = Instance;
var Migrate = function(instance) {
this.knex = instance;
_.bindAll(this, 'currentVersion', 'createMigrationTable', '_migrationData');
};

View File

@ -7,15 +7,23 @@ define(function(require, exports) {
var _ = require('underscore');
var Common = require('./common').Common;
var Raw = function(sql, bindings) {
this.bindings = (!_.isArray(bindings) ? (bindings ? [bindings] : []) : bindings);
this.sql = sql;
var Raw = function(instance) {
this.knex = instance;
};
_.extend(Raw.prototype, {
_.extend(Raw.prototype, Common, {
_source: 'Raw',
// Set the sql and the bindings associated with the query, returning
// the current raw object.
query: function(sql, bindings) {
this.bindings = _.isArray(bindings) ? bindings :
bindings ? [bindings] : [];
this.sql = sql;
return this;
},
// Returns the raw sql for the query.
toSql: function() {
return this.sql;
@ -28,5 +36,5 @@ define(function(require, exports) {
});
})(
typeof define === 'function' && define.amd ? define : function (factory) { factory(require, exports, module); }
typeof define === 'function' && define.amd ? define : function (factory) { factory(require, exports); }
);

View File

@ -6,12 +6,13 @@ define(function(require, exports) {
var _ = require('underscore');
var Raw = require('./raw').Raw;
var Common = require('./common').Common;
var Helpers = require('./helpers').Helpers;
var SchemaBuilder = function(table) {
this.table = table;
var SchemaBuilder = function(instance) {
this.knex = instance;
this.client = instance.client;
this.grammar = instance.schemaGrammar;
this.columns = [];
this.commands = [];
this.bindings = [];
@ -22,9 +23,8 @@ define(function(require, exports) {
_source: 'SchemaBuilder',
instance: function() {
var builder = new SchemaBuilder(this.table);
builder.client = this.client;
builder.grammar = this.grammar;
var builder = new SchemaBuilder(this.knex);
builder.table = this.table;
return builder;
},

View File

@ -8,27 +8,35 @@ define(function(require, exports) {
var _ = require('underscore');
var Transaction = function(instance) {
this.knex = instance;
this.client = instance.client;
};
Transaction.prototype.run = function(container) {
Transaction.prototype = {
var client = this.knex.client;
// Passed a `container` function, this method runs the current
// transaction, returning a promise.
run: function(container) {
return this.client.startTransaction()
.then(this.getContainerObject)
.then(this.initiateDeferred(container));
},
return client.startTransaction().then(function(connection) {
getContainerObject: function(connection) {
// Initiate a deferred object, so we know when the
// transaction completes or fails, we know what to do.
var dfd = when.defer();
// The client we need to call `finishTransaction` on.
var client = this.client;
// The object passed around inside the transaction container.
var containerObj = {
commit: function(val) {
client.finishTransaction('commit', this, dfd, val);
commit: function(message) {
client.finishTransaction('commit', this, message);
},
rollback: function(err) {
client.finishTransaction('rollback', this, dfd, err);
rollback: function(error) {
client.finishTransaction('rollback', this, error);
},
// "rollback to"?
connection: connection
};
@ -36,12 +44,27 @@ define(function(require, exports) {
// Ensure the transacting object methods are bound with the correct context.
_.bindAll(containerObj, 'commit', 'rollback');
// Call the container with the transaction
// commit & rollback objects.
container(containerObj);
},
initiateDeferred: function(container) {
return function(containerObj) {
// Initiate a deferred object, so we know when the
// transaction completes or fails, we know what to do.
var dfd = containerObj.dfd = when.defer();
// Call the container with the transaction
// commit & rollback objects.
container(containerObj);
// Return the promise for the entire transaction.
return dfd.promise;
};
}
return dfd.promise;
});
};
exports.Transaction = Transaction;

View File

@ -7,14 +7,19 @@
"test": "test"
},
"devDependencies": {
"mocha": "~1.12.0",
"objectdump": "~0.3.0",
"mysql": "~2.0.0-alpha7",
"pg": "~2.3.1",
"sqlite3": "~2.1.7",
"node-uuid": "~1.4.0",
"grunt": "~0.4.1",
"grunt-release": "~0.5.1"
"grunt-release": "~0.5.1",
"sinon": "~1.7.3",
"chai": "~1.7.2",
"mocha-as-promised": "~1.4.0",
"sinon-chai": "~2.4.0",
"mocha": "~1.12.1",
"chai-as-promised": "~3.3.1"
},
"dependencies": {
"when": "~2.3.0",

View File

@ -1,71 +1,17 @@
var equal = require('assert').equal;
var When = require('when');
var _ = require('underscore');
var Knex = require('../knex');
var conn = require(process.env.KNEX_TEST || './shared/config');
require("mocha-as-promised")();
// The output goes here.
exports.output = {};
global.sinon = require("sinon");
var pool = {
afterCreate: function(conn, done) {
equal(_.has(conn, '__cid'), true);
done();
},
beforeDestroy: function(conn, done) {
equal(_.has(conn, '__cid'), true);
done();
}
};
var chai = global.chai = require("chai");
chai.use(require("chai-as-promised"));
chai.use(require("sinon-chai"));
chai.should();
Knex.Initialize({
client: 'mysql',
connection: conn.mysql,
pool: _.extend({}, pool, {
afterCreate: function(conn, done) {
conn.query("SET sql_mode='TRADITIONAL';", [], function(err) {
done(err);
});
}
})
});
var Sqlite3 = Knex.Initialize('sqlite3', {
client: 'sqlite3',
connection: conn.sqlite3,
pool: pool
});
var Postgres = Knex.Initialize('postgres', {
client: 'postgres',
connection: conn.postgres,
pool: pool
});
global._ = require('underscore');
global.when = require('when');
global.expect = chai.expect;
global.AssertionError = chai.AssertionError;
global.Assertion = chai.Assertion;
global.assert = chai.assert;
var regularThen = Knex.Builder.prototype.then;
var then = function(success, error) {
var ctx = this;
var bindings = ctx._cleanBindings();
var chain = regularThen.call(this, function(resp) {
return {
object: resp,
string: {
sql: ctx.sql,
bindings: bindings
}
};
});
return chain.then(success, error);
};
describe('Knex', function() {
_.each(['Builder', 'SchemaBuilder', 'Raw'], function(item) {
Knex[item].prototype.then = then;
Postgres[item].prototype.then = then;
Sqlite3[item].prototype.then = then;
});
require('./regular')(Knex, 'mysql');
require('./regular')(Postgres, 'postgres');
require('./regular')(Sqlite3, 'sqlite3');
});
require('./unit/builder')();

View File

@ -0,0 +1,64 @@
var equal = require('assert').equal;
var When = require('when');
var _ = require('underscore');
var Knex = require('../knex');
var conn = require(process.env.KNEX_TEST || './shared/config');
// The output goes here.
exports.output = {};
var pool = {
afterCreate: function(conn, done) {
equal(_.has(conn, '__cid'), true);
done();
},
beforeDestroy: function(conn, done) {
equal(_.has(conn, '__cid'), true);
done();
}
};
var MySQL = Knex.initialize({
client: 'mysql',
connection: conn.mysql,
pool: _.extend({}, pool, {
afterCreate: function(conn, done) {
conn.query("SET sql_mode='TRADITIONAL';", [], function(err) {
done(err);
});
}
})
});
var SQLite3 = Knex.initialize('sqlite3', {
client: 'sqlite3',
connection: conn.sqlite3,
pool: pool
});
var PostgreSQL = Knex.initialize('postgres', {
client: 'postgres',
connection: conn.postgres,
pool: pool
});
var regularThen = MySQL.builder.prototype.then;
var then = function(success, error) {
var ctx = this;
var bindings = ctx._cleanBindings();
var chain = regularThen.call(this, function(resp) {
return {
object: resp,
string: {
sql: ctx.sql,
bindings: bindings
}
};
});
return chain.then(success, error);
};
describe('Knex', function() {
require('./regular')(MySQL, 'mysql');
require('./regular')(PostgreSQL, 'postgres');
require('./regular')(SQLite3, 'sqlite3');
});

View File

@ -1,12 +1,269 @@
module.exports = function(client) {
module.exports = function() {
var Builder = require('../../lib/builder');
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 () {
it('should be a constructor, accepting the current Knex client');
var builder;
beforeEach(function() {
builder = new Builder({
query: function(obj) {
return when.resolve(obj);
},
grammar: require('../../clients/server/mysql').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();
});
it('has the Common methods mixed-in to the Builder.prototype', function() {
_.each(Common, function(val, key) {
expect(Builder.prototype[key]).to.equal(val);
});
});
});
describe('from', function() {
it('sets the tableName', function() {
expect(builder.table).not.to.exist;
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).not.to.exist;
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.isDistinct).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('clone', function() {
});
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))");
});
var arg1 = {}, arg2 = {}, arg3 = {}, arg4 = {};
it('is called by andWhere, passing the first three arguments and an "and"', function() {
sinon.mock(builder).expects('where').withExactArgs(arg1, arg2, arg3, 'and');
builder.andWhere(arg1, arg2, arg3, 'error');
});
it('is called by orWhere, passing the first three arguments and an "or"', function() {
sinon.mock(builder).expects('where').withExactArgs(arg1, arg2, arg3, 'or');
builder.orWhere(arg1, arg2, arg3, 'error');
});
});
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() {
});
describe('update', function() {
});
describe('delete', function() {
});
describe('truncate', function() {
});
});

66
test/unit/common.js Normal file
View File

@ -0,0 +1,66 @@
module.exports = function(client) {
var Common = require('../../lib/common').Common;
var CommonStub = function(knex) {
this.knex = knex;
this.client = client;
this.grammar = client.grammar;
};
CommonStub.prototype = Common;
describe('Common', function () {
var common;
beforeEach(function() {
common = new CommonStub();
});
describe('debug', function () {
it('should set the flag for the isDebugging property of the object');
});
describe('exec', function() {
it('should accept a callback, which provides an err / success');
it('should rethrow any error in the handler');
});
describe('then', function() {
it('should accept two functions, which are passed along to the promise handler');
});
describe('tap', function () {
it('is essentially a passthrough to the `tap` method of when.js');
});
describe('toString', function() {
it('turns the current query into a string value, based on the current client');
});
describe('connection', function() {
it('should set the usingConnection property of the current object');
});
describe('transacting', function () {
it('should set the transaction object');
});
});
};

0
test/unit/knex.js Normal file
View File

15
test/unit/raw.js Normal file
View File

@ -0,0 +1,15 @@
module.exports = function(client) {
var Raw = require('../../lib/raw').Raw;
describe('Raw', function () {
it('has the Common methods mixed-in to the Builder.prototype', function() {
_.each(Common, function(val, key) {
expect(Builder.prototype[key]).to.equal(val);
});
});
});
};

View File

@ -1,5 +0,0 @@
var Sqlite3 = require('../../clients/base/sqlite3').Sqlite3;
describe('Sqlite3', function () {
});

23
test/unit/transaction.js Normal file
View File

@ -0,0 +1,23 @@
module.exports = function(client) {
var Transaction = require('../../lib/transaction').Transaction;
describe('Transaction', function () {
describe('Constructor', function() {
it('saves the current client off the knex instance', function() {
var knex = {client: {name: 'mysql'}};
var transaction = new Transaction(knex);
assert(transaction.client).should.eql(knex.client);
});
});
});
};