Browse files

Tests for base classes updated.

Todo: tests for newly refactored query builders.

Todo: tests to ensure that customised query builders and custom blocks work fine.
  • Loading branch information...
1 parent cd5475f commit fcca5cc4c6431b76c38806a11d342f23775c1e0d @hiddentao committed Mar 26, 2013
Showing with 89 additions and 310 deletions.
  1. +15 −13 src/squel.coffee
  2. +74 −297 test/baseclasses.test.coffee
View
28 src/squel.coffee
@@ -88,10 +88,10 @@ class cls.BaseBuilder extends cls.Cloneable
t = typeof condition
c = @_getObjectClassName(condition)
- if 'cls.Expression' isnt c and "string" isnt t
- throw new Error "condition must be a string or cls.Expression instance"
+ if 'Expression' isnt c and "string" isnt t
+ throw new Error "condition must be a string or Expression instance"
# If it's an expression builder instance then convert it to string form.
- if 'cls.Expression' is t or 'cls.Expression' is c
+ if 'Expression' is t or 'Expression' is c
condition = condition.toString()
condition
@@ -682,14 +682,16 @@ class cls.QueryBuilder extends cls.BaseBuilder
# Constructor
#
# blocks - array of cls.BaseBuilderBlock instances to build the query with.
- constructor: (blocks) ->
- @blocks = blocks
+ constructor: (options, blocks) ->
+ super options
+
+ @blocks = blocks or []
# Copy exposed methods into myself
for block in @blocks
for methodName, methodBody of block.exposedMethods()
if @[methodName]?
- throw new Error _getObjectClassName(@) + "already has a builder method called #{methodName}"
+ throw new Error "#{@_getObjectClassName(@)} already has a builder method called: #{methodName}"
( (name, body) =>
@[name] = =>
@@ -698,9 +700,9 @@ class cls.QueryBuilder extends cls.BaseBuilder
)(methodName, methodBody)
- # Get the final fully constructed query string.
- toString: =>
- (block.buildStr(@) for block in @blocks).join(' ')
+ # Get the final fully constructed query string.
+ toString: =>
+ (block.buildStr(@) for block in @blocks).join(' ')
@@ -723,7 +725,7 @@ class cls.Select extends cls.QueryBuilder
new cls.OffsetBlock(options)
]
- super blocks
+ super options, blocks
@@ -740,7 +742,7 @@ class cls.Update extends cls.QueryBuilder
new cls.LimitBlock(options)
]
- super blocks
+ super options, blocks
@@ -758,7 +760,7 @@ class cls.Delete extends cls.QueryBuilder
new cls.LimitBlock(options),
]
- super blocks
+ super options, blocks
@@ -774,7 +776,7 @@ class cls.Insert extends cls.BaseBuilder
new cls.InsertIntoFieldBlock(options)
]
- super blocks
+ super options, blocks
View
371 test/baseclasses.test.coffee
@@ -32,7 +32,7 @@ test = testCreator()
test['Cloneable base class'] =
'>> clone()': ->
- class Child extends squel.Cloneable
+ class Child extends squel.classes.Cloneable
constructor: ->
@a = 1
@b = 2.2
@@ -73,19 +73,20 @@ test['Default query builder options'] =
-test['QueryBuilder base class'] =
+test['Builder base class'] =
beforeEach: ->
- @inst = new squel.QueryBuilder()
+ @cls = squel.classes.BaseBuilder
+ @inst = new @cls
'instanceof Cloneable': ->
- assert.instanceOf @inst, squel.Cloneable
+ assert.instanceOf @inst, squel.classes.Cloneable
'constructor':
'default options': ->
assert.same squel.classes.DefaultQueryBuilderOptions, @inst.options
'overridden options': ->
- @inst = new squel.QueryBuilder
+ @inst = new @cls
dummy1: 'str'
dummy2: 12.3
usingValuePlaceholders: true
@@ -297,326 +298,102 @@ test['QueryBuilder base class'] =
-test['WhereOrderLimit base class'] =
- beforeEach: ->
- @inst = new squel.WhereOrderLimit()
-
- 'instanceof QueryBuilder': ->
- assert.instanceOf @inst, squel.QueryBuilder
-
- 'default field values': ->
- assert.same [], @inst.wheres
- assert.same [], @inst.orders
- assert.same null, @inst.limits
-
- '>> where()':
- beforeEach: ->
- test.mocker.spy(@inst, '_sanitizeCondition')
-
- 'with empty string': ->
- assert.same @inst, @inst.where("")
-
- assert.ok @inst._sanitizeCondition.calledWithExactly ""
- assert.same [], @inst.wheres
-
- 'with Expression': ->
- e = squel.expr().or('a = 5')
-
- assert.same @inst, @inst.where(e)
-
- assert.ok @inst._sanitizeCondition.calledWithExactly e
- assert.same [e.toString()], @inst.wheres
-
- 'with non-empty string':
- beforeEach: ->
- @ret = @inst.where("a")
-
- 'updates internal state': ->
- assert.same @ret, @inst
- assert.ok @inst._sanitizeCondition.calledWithExactly "a"
- assert.same ['a'], @inst.wheres
-
- 'with non-empty string again':
- beforeEach: ->
- @ret = @inst.where("b")
-
- 'adds to internal state': ->
- assert.ok @inst._sanitizeCondition.calledWithExactly "b"
- assert.same ['a', 'b'], @inst.wheres
-
-
- '>> order()':
- beforeEach: ->
- test.mocker.spy(@inst, '_sanitizeField')
-
- 'args empty': ->
- assert.throws (=> @inst.order()), 'field name must be a string'
-
- assert.ok @inst._sanitizeField.calledWithExactly undefined
- assert.same [], @inst.orders
-
- 'args (field)':
- beforeEach: ->
- @ret = @inst.order("field")
-
- 'updates internal state': ->
- assert.same @ret, @inst
-
- assert.ok @inst._sanitizeField.calledWithExactly 'field'
- assert.same [ { field: 'field', dir: 'ASC' } ], @inst.orders
-
- 'args (field, true)':
- beforeEach: ->
- @ret = @inst.order("field", true)
-
- 'updates internal state': ->
- assert.same @ret, @inst
-
- assert.ok @inst._sanitizeField.calledWithExactly 'field'
- assert.same [ { field: 'field', dir: 'ASC' } ], @inst.orders
-
- '>> args (field2, false)':
- beforeEach: ->
- @ret = @inst.order("field2", false)
-
- 'adds to internal state': ->
- assert.same @ret, @inst
-
- assert.ok @inst._sanitizeField.calledWithExactly 'field2'
- assert.same [ { field: 'field', dir: 'ASC' }, { field: 'field2', dir: 'DESC' } ], @inst.orders
-
-
- '>> limit()':
- beforeEach: ->
- test.mocker.spy(@inst, '_sanitizeLimitOffset')
-
- 'args empty': ->
- assert.throws (=> @inst.limit()), 'limit/offset must be >=0'
-
- assert.ok @inst._sanitizeLimitOffset.calledWithExactly undefined
- assert.same null, @inst.limits
-
- 'args (0)':
- beforeEach: ->
- @ret = @inst.limit(0)
-
- 'updates internal state': ->
- assert.same @ret, @inst
-
- assert.ok @inst._sanitizeLimitOffset.calledWithExactly 0
- assert.same 0, @inst.limits
-
- '>> args (2)':
- beforeEach: ->
- @ret = @inst.limit(2)
-
- 'updates internal state': ->
- assert.same @ret, @inst
-
- assert.ok @inst._sanitizeLimitOffset.calledWithExactly 2
- assert.same 2, @inst.limits
- '>> _whereString()':
- 'no clauses': ->
- @inst.wheres = []
- assert.same @inst._whereString(), ""
-
- '1 clause': ->
- @inst.wheres = ['a']
- assert.same @inst._whereString(), " WHERE (a)"
-
- '>1 clauses': ->
- @inst.wheres = ['a', 'b']
- assert.same @inst._whereString(), " WHERE (a) AND (b)"
-
+test['QueryBuilder base class'] =
+ beforeEach: ->
+ @cls = squel.classes.QueryBuilder
+ @inst = new @cls
- '>> _orderString()':
- 'no clauses': ->
- @inst.orders = []
- assert.same @inst._orderString(), ""
+ 'instanceof base builder': ->
+ assert.instanceOf @inst, squel.classes.BaseBuilder
- '1 clause': ->
- @inst.orders = [{ field: 'a', dir: 'ASC' }]
- assert.same @inst._orderString(), " ORDER BY a ASC"
+ 'constructor':
+ 'default options': ->
+ assert.same squel.classes.DefaultQueryBuilderOptions, @inst.options
- '>1 clauses': ->
- @inst.orders = [{ field: 'a', dir: 'ASC' }, { field: 'b', dir: 'DESC' }]
- assert.same @inst._orderString(), " ORDER BY a ASC, b DESC"
+ 'overridden options': ->
+ @inst = new @cls
+ dummy1: 'str'
+ dummy2: 12.3
+ usingValuePlaceholders: true
+ dummy3: true
+ expectedOptions = _.extend {}, squel.classes.DefaultQueryBuilderOptions,
+ dummy1: 'str'
+ dummy2: 12.3
+ usingValuePlaceholders: true
+ dummy3: true
- '>> _limitString()':
- 'not set': ->
- @inst.limits = null
- assert.same @inst._limitString(), ""
+ assert.same expectedOptions, @inst.options
- 'set': ->
- @inst.limits = 2
- assert.same @inst._limitString(), " LIMIT 2"
+ 'default blocks - none': ->
+ assert.same [], @inst.blocks
+ 'blocks passed in':
+ 'exposes block methods': ->
+ limitExposedMethodsSpy = test.mocker.spy(squel.classes.LimitBlock.prototype, 'exposedMethods');
+ distinctExposedMethodsSpy = test.mocker.spy(squel.classes.DistinctBlock.prototype, 'exposedMethods');
+ limitSpy = test.mocker.spy(squel.classes.LimitBlock.prototype, 'limit')
+ distinctSpy = test.mocker.spy(squel.classes.DistinctBlock.prototype, 'distinct')
+ blocks = [
+ new squel.classes.LimitBlock(),
+ new squel.classes.DistinctBlock()
+ ]
-test['JoinWhereOrderLimit base class'] =
- beforeEach: ->
- @inst = new squel.JoinWhereOrderLimit()
+ @inst = new @cls({}, blocks)
- 'instanceof WhereOrderLimit': ->
- assert.instanceOf @inst, squel.JoinWhereOrderLimit
+ assert.ok limitExposedMethodsSpy.calledOnce
+ assert.ok distinctExposedMethodsSpy.calledOnce
- 'default field values': ->
- assert.same [], @inst.joins
+ assert.typeOf @inst.distinct, 'function'
+ assert.typeOf @inst.limit, 'function'
- '>> join()':
- beforeEach: ->
- test.mocker.spy(@inst, '_sanitizeTable')
- test.mocker.spy(@inst, '_sanitizeAlias')
- test.mocker.spy(@inst, '_sanitizeCondition')
+ @inst.limit(2)
+ assert.ok limitSpy.calledOnce
+ assert.ok limitSpy.calledOn(blocks[0])
- 'args: ()': ->
- assert.throws (=> @inst.join()), 'table name must be a string'
- assert.ok @inst._sanitizeTable.calledWithExactly(undefined)
+ @inst.distinct()
+ assert.ok distinctSpy.calledOnce
+ assert.ok distinctSpy.calledOn(blocks[1])
- 'args: (table)':
- beforeEach: ->
- @ret = @inst.join('table')
-
- 'update internal state': ->
- assert.same @ret, @inst
- assert.same @inst.joins, [
- {
- type: 'INNER'
- table: 'table'
- alias: undefined
- condition: undefined
- }
- ]
- assert.ok @inst._sanitizeTable.calledWithExactly('table')
- assert.ok @inst._sanitizeAlias.notCalled
- assert.ok @inst._sanitizeCondition.notCalled
-
- '>> args(table2)': ->
- assert.same @inst.join('table2'), @inst
- assert.same @inst.joins, [
- {
- type: 'INNER'
- table: 'table'
- alias: undefined
- condition: undefined
- }
- {
- type: 'INNER'
- table: 'table2'
- alias: undefined
- condition: undefined
- }
+ 'cannot expose the same method twice': ->
+ blocks = [
+ new squel.classes.DistinctBlock(),
+ new squel.classes.DistinctBlock()
]
- 'args: (table, alias)': ->
- @inst.join('table', 'alias')
-
- assert.same @inst.joins, [
- {
- type: 'INNER'
- table: 'table'
- alias: 'alias'
- condition: undefined
- }
- ]
+ try
+ @inst = new @cls({}, blocks)
+ throw new Error 'should not reach here'
+ catch err
+ assert.same 'Error: QueryBuilder already has a builder method called: distinct', err.toString()
- assert.ok @inst._sanitizeTable.calledWithExactly('table')
- assert.ok @inst._sanitizeAlias.calledWithExactly('alias')
- assert.ok @inst._sanitizeCondition.notCalled
- 'args: (table, alias, condition)': ->
- @inst.join('table', 'alias', 'condition')
+ 'toString()':
+ 'returns empty if no blocks': ->
+ assert.same '', @inst.toString()
- assert.same @inst.joins, [
- {
- type: 'INNER'
- table: 'table'
- alias: 'alias'
- condition: 'condition'
- }
+ 'returns final query string': ->
+ @inst.blocks = [
+ new squel.classes.StringBlock({}, 'STR1'),
+ new squel.classes.StringBlock({}, 'STR2'),
+ new squel.classes.StringBlock({}, 'STR3')
]
- assert.ok @inst._sanitizeTable.calledWithExactly('table')
- assert.ok @inst._sanitizeAlias.calledWithExactly('alias')
- assert.ok @inst._sanitizeCondition.calledWithExactly('condition')
-
- 'args: (table, alias, condition, OUTER)': ->
- @inst.join('table', 'alias', 'condition', 'OUTER')
-
- assert.same @inst.joins, [
- type: 'OUTER'
- table: 'table'
- alias: 'alias'
- condition: 'condition'
- ]
-
- '>> left_join()':
- beforeEach: -> test.mocker.spy(@inst, 'join')
-
- 'args (table)': ->
- assert.same @inst.left_join('table'), @inst
- assert.ok @inst.join.calledWithExactly('table', null, null, 'LEFT')
-
- 'args (table, alias)': ->
- assert.same @inst.left_join('table', 'alias'), @inst
- assert.ok @inst.join.calledWithExactly('table', 'alias', null, 'LEFT')
-
- 'args (table, alias, condition)': ->
- assert.same @inst.left_join('table', 'alias', 'condition'), @inst
- assert.ok @inst.join.calledWithExactly('table', 'alias', 'condition', 'LEFT')
-
-
- '>> right_join()':
- beforeEach: -> test.mocker.spy(@inst, 'join')
-
- 'args (table)': ->
- assert.same @inst.right_join('table'), @inst
- assert.ok @inst.join.calledWithExactly('table', null, null, 'RIGHT')
-
- 'args (table, alias)': ->
- assert.same @inst.right_join('table', 'alias'), @inst
- assert.ok @inst.join.calledWithExactly('table', 'alias', null, 'RIGHT')
-
- 'args (table, alias, condition)': ->
- assert.same @inst.right_join('table', 'alias', 'condition'), @inst
- assert.ok @inst.join.calledWithExactly('table', 'alias', 'condition', 'RIGHT')
-
-
- '>> outer_join()':
- beforeEach: -> test.mocker.spy(@inst, 'join')
-
- 'args (table)': ->
- assert.same @inst.outer_join('table'), @inst
- assert.ok @inst.join.calledWithExactly('table', null, null, 'OUTER')
-
- 'args (table, alias)': ->
- assert.same @inst.outer_join('table', 'alias'), @inst
- assert.ok @inst.join.calledWithExactly('table', 'alias', null, 'OUTER')
-
- 'args (table, alias, condition)': ->
- assert.same @inst.outer_join('table', 'alias', 'condition'), @inst
- assert.ok @inst.join.calledWithExactly('table', 'alias', 'condition', 'OUTER')
-
+ i = 1
+ buildStrSpy = test.mocker.stub squel.classes.StringBlock.prototype, 'buildStr', -> "ret#{++i}"
- '>> _joinString()':
- beforeEach: -> @inst.joins = []
+ assert.same 'ret2 ret3 ret4', @inst.toString()
- 'no joins': ->
- assert.same @inst._joinString(), ""
+ assert.ok buildStrSpy.calledThrice
+ assert.ok buildStrSpy.calledOn(@inst.blocks[0])
+ assert.ok buildStrSpy.calledOn(@inst.blocks[1])
+ assert.ok buildStrSpy.calledOn(@inst.blocks[2])
- '1 join': ->
- @inst.left_join('table')
- assert.same @inst._joinString(), " LEFT JOIN table"
- '>1 joins': ->
- @inst.left_join('table')
- @inst.right_join('table2', 'a2')
- @inst.join('table3', null, 'c3')
- assert.same @inst._joinString(), " LEFT JOIN table RIGHT JOIN table2 `a2` INNER JOIN table3 ON (c3)"

0 comments on commit fcca5cc

Please sign in to comment.