1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147 |
- ###
- Copyright (c) 2014 Ramesh Nair (hiddentao.com)
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use,
- copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following
- conditions:
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- ###
- squel = require "../dist/squel-basic"
- {_, testCreator, assert, expect, should} = require './testbase'
- test = testCreator()
- test['Version number'] =
- assert.same squel.VERSION, require('../package.json').version
- test['Default flavour'] =
- assert.isNull squel.flavour
- test['internal methods'] =
- '_getObjectClassName': ->
- s = 'a string'
- b = new Object()
- c = new Error()
- d = 1
- assert.same squel.cls._getObjectClassName(0), undefined
- assert.same squel.cls._getObjectClassName(true), 'Boolean'
- assert.same squel.cls._getObjectClassName(1.2), 'Number'
- assert.same squel.cls._getObjectClassName('a string'), 'String'
- assert.same squel.cls._getObjectClassName(new Object), 'Object'
- assert.same squel.cls._getObjectClassName(new Error), 'Error'
- test['Cloneable base class'] =
- '>> clone()': ->
- class Child extends squel.cls.Cloneable
- constructor: ->
- @a = 1
- @b = 2.2
- @c = true
- @d = 'str'
- @e = [1]
- @f = { a: 1 }
- child = new Child()
- copy = child.clone()
- assert.instanceOf copy, Child
- child.a = 2
- child.b = 3.2
- child.c = false
- child.d = 'str2'
- child.e.push(2)
- child.f.b = 1
- assert.same copy.a, 1
- assert.same copy.b, 2.2
- assert.same copy.c, true
- assert.same copy.d, 'str'
- assert.same copy.e, [1]
- assert.same copy.f, { a: 1 }
- test['Default query builder options'] =
- 'default options': ->
- assert.same {
- autoQuoteTableNames: false
- autoQuoteFieldNames: false
- autoQuoteAliasNames: true
- useAsForTableAliasNames: false
- nameQuoteCharacter: '`'
- tableAliasQuoteCharacter: '`'
- fieldAliasQuoteCharacter: '"'
- valueHandlers: []
- parameterCharacter: '?'
- numberedParameters: false
- numberedParametersPrefix: '$'
- numberedParametersStartAt: 1
- replaceSingleQuotes: false
- singleQuoteReplacement: '\'\''
- separator: ' '
- stringFormatter: null
- }, squel.cls.DefaultQueryBuilderOptions
- test['Register global custom value handler'] =
- 'beforeEach': ->
- @originalHandlers = [].concat(squel.cls.globalValueHandlers)
- squel.cls.globalValueHandlers = []
- 'afterEach': ->
- squel.cls.globalValueHandlers = @originalHandlers
- 'default': ->
- handler = -> 'test'
- squel.registerValueHandler(Date, handler)
- squel.registerValueHandler(Object, handler)
- squel.registerValueHandler('boolean', handler)
- assert.same 3, squel.cls.globalValueHandlers.length
- assert.same { type: Date, handler: handler }, squel.cls.globalValueHandlers[0]
- assert.same { type: Object, handler: handler }, squel.cls.globalValueHandlers[1]
- assert.same { type: 'boolean', handler: handler }, squel.cls.globalValueHandlers[2]
- 'type should be class constructor': ->
- assert.throws (-> squel.registerValueHandler 1, null), "type must be a class constructor or string"
- 'handler should be function': ->
- class MyClass
- assert.throws (-> squel.registerValueHandler MyClass, 1), 'handler must be a function'
- 'overrides existing handler': ->
- handler = -> 'test'
- handler2 = -> 'test2'
- squel.registerValueHandler(Date, handler)
- squel.registerValueHandler(Date, handler2)
- assert.same 1, squel.cls.globalValueHandlers.length
- assert.same { type: Date, handler: handler2 }, squel.cls.globalValueHandlers[0]
- test['str()'] =
- constructor: ->
- f = squel.str('GETDATE(?)', 12, 23)
- assert.ok (f instanceof squel.cls.FunctionBlock)
- assert.same 'GETDATE(?)', f._strings[0]
- assert.same [12, 23], f._values[0]
- 'custom value handler':
- beforeEach: ->
- @inst = squel.str('G(?,?)', 12, 23, 65)
-
- handlerConfig = _.find squel.cls.globalValueHandlers, (hc) ->
- hc.type is squel.cls.FunctionBlock
- @handler = handlerConfig.handler
- toString: ->
- assert.same @inst.toString(), @handler(@inst)
- toParam: ->
- assert.same @inst.toParam(), @handler(@inst, true)
- test['Load an SQL flavour'] =
- beforeEach: ->
- @flavoursBackup = squel.flavours
- squel.flavours = {}
- afterEach: ->
- squel.flavours = @flavoursBackup
- 'invalid flavour': ->
- assert.throws (-> squel.useFlavour 'test'), 'Flavour not available: test'
- 'flavour reference should be a function': ->
- squel.flavours['test'] = 'blah'
- assert.throws (-> squel.useFlavour 'test'), 'Flavour not available: test'
- 'flavour setup function gets executed': ->
- squel.flavours['test'] = test.mocker.spy()
- ret = squel.useFlavour 'test'
- assert.ok squel.flavours['test'].calledOnce
- assert.ok !!ret.select()
- 'can switch flavours': ->
- squel.flavours['test'] = test.mocker.spy( (s) ->
- s.cls.dummy = 1
- )
- squel.flavours['test2'] = test.mocker.spy( (s) ->
- s.cls.dummy2 = 2
- )
- ret = squel.useFlavour 'test'
- assert.same ret.cls.dummy, 1
- ret = squel.useFlavour 'test2'
- assert.same ret.cls.dummy, undefined
- assert.same ret.cls.dummy2, 2
- ret = squel.useFlavour()
- assert.same ret.cls.dummy, undefined
- assert.same ret.cls.dummy2, undefined
- 'can get current flavour': ->
- flavour = 'test'
- squel.flavours[flavour] = test.mocker.spy()
- ret = squel.useFlavour flavour
- assert.same ret.flavour, flavour
- test['Builder base class'] =
- beforeEach: ->
- @cls = squel.cls.BaseBuilder
- @inst = new @cls
- @originalHandlers = [].concat(squel.cls.globalValueHandlers)
- afterEach: ->
- squel.cls.globalValueHandlers = @originalHandlers
- 'instanceof Cloneable': ->
- assert.instanceOf @inst, squel.cls.Cloneable
- 'constructor':
- 'default options': ->
- assert.same squel.cls.DefaultQueryBuilderOptions, @inst.options
- 'overridden options': ->
- @inst = new @cls
- dummy1: 'str'
- dummy2: 12.3
- usingValuePlaceholders: true
- dummy3: true,
- globalValueHandlers: [1]
- expectedOptions = _.extend {}, squel.cls.DefaultQueryBuilderOptions,
- dummy1: 'str'
- dummy2: 12.3
- usingValuePlaceholders: true
- dummy3: true
- globalValueHandlers: [1]
- assert.same expectedOptions, @inst.options
- 'registerValueHandler':
- 'afterEach': ->
- squel.cls.globalValueHandlers = []
- 'default': ->
- handler = -> 'test'
- @inst.registerValueHandler(Date, handler)
- @inst.registerValueHandler(Object, handler)
- @inst.registerValueHandler('number', handler)
- assert.same 3, @inst.options.valueHandlers.length
- assert.same { type: Date, handler: handler }, @inst.options.valueHandlers[0]
- assert.same { type: Object, handler: handler }, @inst.options.valueHandlers[1]
- assert.same { type: 'number', handler: handler }, @inst.options.valueHandlers[2]
- 'type should be class constructor': ->
- assert.throws (=> @inst.registerValueHandler 1, null), "type must be a class constructor or string"
- 'handler should be function': ->
- class MyClass
- assert.throws (=> @inst.registerValueHandler MyClass, 1), 'handler must be a function'
- 'returns instance for chainability': ->
- handler = -> 'test'
- assert.same @inst, @inst.registerValueHandler(Date, handler)
- 'overrides existing handler': ->
- handler = -> 'test'
- handler2 = -> 'test2'
- @inst.registerValueHandler(Date, handler)
- @inst.registerValueHandler(Date, handler2)
- assert.same 1, @inst.options.valueHandlers.length
- assert.same { type: Date, handler: handler2 }, @inst.options.valueHandlers[0]
- 'does not touch global value handlers list': ->
- oldGlobalHandlers = squel.cls.globalValueHandlers
- handler = -> 'test'
- @inst.registerValueHandler(Date, handler)
- assert.same oldGlobalHandlers, squel.cls.globalValueHandlers
- '_sanitizeExpression':
- 'if Expression':
- 'empty expression': ->
- e = squel.expr()
- assert.same e, @inst._sanitizeExpression(e)
- 'non-empty expression': ->
- e = squel.expr().and("s.name <> 'Fred'")
- assert.same e, @inst._sanitizeExpression(e)
- 'if string': ->
- s = 'BLA BLA'
- assert.same 'BLA BLA', @inst._sanitizeExpression(s)
- 'if neither Expression nor String': ->
- testFn = => @inst._sanitizeExpression(1)
- assert.throws testFn, 'expression must be a string or Expression instance'
- '_sanitizeName':
- beforeEach: ->
- test.mocker.spy @inst, '_sanitizeName'
- 'if string': ->
- assert.same 'bla', @inst._sanitizeName('bla')
- 'if boolean': ->
- assert.throws (=> @inst._sanitizeName(true, 'bla')), 'bla must be a string'
- 'if integer': ->
- assert.throws (=> @inst._sanitizeName(1)), 'undefined must be a string'
- 'if float': ->
- assert.throws (=> @inst._sanitizeName(1.2, 'meh')), 'meh must be a string'
- 'if array': ->
- assert.throws (=> @inst._sanitizeName([1], 'yes')), 'yes must be a string'
- 'if object': ->
- assert.throws (=> @inst._sanitizeName(new Object, 'yes')), 'yes must be a string'
- 'if null': ->
- assert.throws (=> @inst._sanitizeName(null, 'no')), 'no must be a string'
- 'if undefined': ->
- assert.throws (=> @inst._sanitizeName(undefined, 'no')), 'no must be a string'
- '_sanitizeField':
- 'default': ->
- test.mocker.spy @inst, '_sanitizeName'
- assert.same 'abc', @inst._sanitizeField('abc')
- assert.ok @inst._sanitizeName.calledWithExactly 'abc', 'field name'
- 'QueryBuilder': ->
- s = squel.select().from('scores').field('MAX(score)')
- assert.same s, @inst._sanitizeField(s)
- '_sanitizeBaseBuilder':
- 'is not base builder': ->
- assert.throws (=> @inst._sanitizeBaseBuilder(null)), 'must be a BaseBuilder instance'
- 'is a query builder': ->
- qry = squel.select()
- assert.same qry, @inst._sanitizeBaseBuilder(qry)
- '_sanitizeTable':
- 'default': ->
- test.mocker.spy @inst, '_sanitizeName'
- assert.same 'abc', @inst._sanitizeTable('abc')
- assert.ok @inst._sanitizeName.calledWithExactly 'abc', 'table'
- 'not a string': ->
- assert.throws (=> @inst._sanitizeTable(null)), 'table name must be a string or a query builder'
- 'query builder': ->
- select = squel.select()
- assert.same select, @inst._sanitizeTable(select, true)
- '_sanitizeFieldAlias': ->
- 'default': ->
- test.mocker.spy @inst, '_sanitizeName'
- @inst._sanitizeFieldAlias('abc')
- assert.ok @inst._sanitizeName.calledWithExactly 'abc', 'field alias'
- '_sanitizeTableAlias': ->
- 'default': ->
- test.mocker.spy @inst, '_sanitizeName'
- @inst._sanitizeTableAlias('abc')
- assert.ok @inst._sanitizeName.calledWithExactly 'abc', 'table alias'
- '_sanitizeLimitOffset':
- 'undefined': ->
- assert.throws (=> @inst._sanitizeLimitOffset()), 'limit/offset must be >= 0'
- 'null': ->
- assert.throws (=> @inst._sanitizeLimitOffset null), 'limit/offset must be >= 0'
- 'float': ->
- assert.same 1, @inst._sanitizeLimitOffset 1.2
- 'boolean': ->
- assert.throws (=> @inst._sanitizeLimitOffset false), 'limit/offset must be >= 0'
- 'string': ->
- assert.same 2, @inst._sanitizeLimitOffset '2'
- 'array': ->
- assert.same 3, @inst._sanitizeLimitOffset [3]
- 'object': ->
- assert.throws (=> @inst._sanitizeLimitOffset(new Object)), 'limit/offset must be >= 0'
- 'number >= 0': ->
- assert.same 0, @inst._sanitizeLimitOffset 0
- assert.same 1, @inst._sanitizeLimitOffset 1
- 'number < 0': ->
- assert.throws (=> @inst._sanitizeLimitOffset(-1)), 'limit/offset must be >= 0'
- '_sanitizeValue':
- beforeEach: ->
- test.mocker.spy @inst, '_sanitizeValue'
- afterEach: ->
- squel.cls.globalValueHandlers = []
- 'if string': ->
- assert.same 'bla', @inst._sanitizeValue('bla')
- 'if boolean': ->
- assert.same true, @inst._sanitizeValue(true)
- assert.same false, @inst._sanitizeValue(false)
- 'if integer': ->
- assert.same -1, @inst._sanitizeValue(-1)
- assert.same 0, @inst._sanitizeValue(0)
- assert.same 1, @inst._sanitizeValue(1)
- 'if float': ->
- assert.same -1.2, @inst._sanitizeValue(-1.2)
- assert.same 1.2, @inst._sanitizeValue(1.2)
- 'if array': ->
- assert.throws (=> @inst._sanitizeValue([1])), 'field value must be a string, number, boolean, null or one of the registered custom value types'
- 'if object': ->
- assert.throws (=> @inst._sanitizeValue(new Object)), 'field value must be a string, number, boolean, null or one of the registered custom value types'
- 'if null': ->
- assert.same null, @inst._sanitizeValue(null)
- 'if BaseBuilder': ->
- s = squel.select()
- assert.same s, @inst._sanitizeValue(s)
- 'if undefined': ->
- assert.throws (=> @inst._sanitizeValue(undefined)), 'field value must be a string, number, boolean, null or one of the registered custom value types'
- 'custom handlers':
- 'global': ->
- squel.registerValueHandler(Date, _.identity)
- date = new Date
- assert.same date, @inst._sanitizeValue(date)
- 'instance': ->
- @inst.registerValueHandler(Date, _.identity)
- date = new Date
- assert.same date, @inst._sanitizeValue(date)
- '_escapeValue': ->
- @inst.options.replaceSingleQuotes = false
- assert.same "te'st", @inst._escapeValue("te'st")
- @inst.options.replaceSingleQuotes = true
- assert.same "te''st", @inst._escapeValue("te'st")
- @inst.options.singleQuoteReplacement = '--'
- assert.same "te--st", @inst._escapeValue("te'st")
- '_formatTableName':
- 'default': ->
- assert.same 'abc', @inst._formatTableName('abc')
- 'auto quote names':
- beforeEach: ->
- @inst.options.autoQuoteTableNames = true
- 'default quote character': ->
- assert.same '`abc`', @inst._formatTableName('abc')
- 'custom quote character': ->
- @inst.options.nameQuoteCharacter = '|'
- assert.same '|abc|', @inst._formatTableName('abc')
- '_formatTableAlias':
- 'default': ->
- assert.same '`abc`', @inst._formatTableAlias('abc')
- 'custom quote character': ->
- @inst.options.tableAliasQuoteCharacter = '~'
- assert.same '~abc~', @inst._formatTableAlias('abc')
- 'auto quote alias names is OFF': ->
- @inst.options.autoQuoteAliasNames = false
- assert.same 'abc', @inst._formatTableAlias('abc')
- 'AS is turned ON': ->
- @inst.options.autoQuoteAliasNames = false
- @inst.options.useAsForTableAliasNames = true
- assert.same 'AS abc', @inst._formatTableAlias('abc')
- '_formatFieldAlias':
- default: ->
- assert.same '"abc"', @inst._formatFieldAlias('abc')
- 'custom quote character': ->
- @inst.options.fieldAliasQuoteCharacter = '~'
- assert.same '~abc~', @inst._formatFieldAlias('abc')
- 'auto quote alias names is OFF': ->
- @inst.options.autoQuoteAliasNames = false
- assert.same 'abc', @inst._formatFieldAlias('abc')
- '_formatFieldName':
- default: ->
- assert.same 'abc', @inst._formatFieldName('abc')
- 'auto quote names':
- beforeEach: ->
- @inst.options.autoQuoteFieldNames = true
- 'default quote character': ->
- assert.same '`abc`.`def`', @inst._formatFieldName('abc.def')
- 'do not quote *': ->
- assert.same '`abc`.*', @inst._formatFieldName('abc.*')
- 'custom quote character': ->
- @inst.options.nameQuoteCharacter = '|'
- assert.same '|abc|.|def|', @inst._formatFieldName('abc.def')
- 'ignore periods when quoting': ->
- assert.same '`abc.def`', @inst._formatFieldName('abc.def', ignorePeriodsForFieldNameQuotes: true)
- '_formatCustomValue':
- 'not a custom value type': ->
- assert.same null, @inst._formatCustomValue(null)
- assert.same 'abc', @inst._formatCustomValue('abc')
- assert.same 12, @inst._formatCustomValue(12)
- assert.same 1.2, @inst._formatCustomValue(1.2)
- assert.same true, @inst._formatCustomValue(true)
- assert.same false, @inst._formatCustomValue(false)
- 'custom value type':
- 'global': ->
- class MyClass
- myObj = new MyClass
- squel.registerValueHandler MyClass, () -> 3.14
- squel.registerValueHandler 'boolean', (v) -> 'a' + v
- assert.same 3.14, @inst._formatCustomValue(myObj)
- assert.same 'atrue', @inst._formatCustomValue(true)
- 'instance': ->
- class MyClass
- myObj = new MyClass
- @inst.registerValueHandler MyClass, () -> 3.14
- @inst.registerValueHandler 'number', (v) -> v + 'a'
- assert.same 3.14, @inst._formatCustomValue(myObj)
- assert.same '5.2a', @inst._formatCustomValue(5.2)
- 'instance handler takes precedence over global': ->
- @inst.registerValueHandler Date, (d) -> 'hello'
- squel.registerValueHandler Date, (d) -> 'goodbye'
- assert.same "hello", @inst._formatCustomValue(new Date)
- @inst = new @cls
- valueHandlers: []
- assert.same "goodbye", @inst._formatCustomValue(new Date)
- 'whether to format for parameterized output': ->
- @inst.registerValueHandler Date, (d, asParam) ->
- return if asParam then 'foo' else 'bar'
- val = new Date()
- assert.same 'foo', @inst._formatCustomValue(val, true)
- assert.same 'bar', @inst._formatCustomValue(val)
-
- '_formatValueForParamArray':
- 'Query builder': ->
- s = squel.select().from('table')
- assert.same s, @inst._formatValueForParamArray(s)
- 'else calls _formatCustomValue': ->
- spy = test.mocker.stub @inst, '_formatCustomValue', (v, asParam) ->
- 'test' + (if asParam then 'foo' else 'bar')
- assert.same 'testfoo', @inst._formatValueForParamArray(null)
- assert.same 'testfoo', @inst._formatValueForParamArray('abc')
- assert.same 'testfoo', @inst._formatValueForParamArray(12)
- assert.same 'testfoo', @inst._formatValueForParamArray(1.2)
- assert.same 'testfoo', @inst._formatValueForParamArray(true)
- assert.same 'testfoo', @inst._formatValueForParamArray(false)
- assert.same 6, spy.callCount
- 'Array - recursively calls itself on each element': ->
- spy = test.mocker.spy @inst, '_formatValueForParamArray'
- v = [ squel.select().from('table'), 1.2 ]
- res = @inst._formatValueForParamArray(v)
- assert.same v, res
- assert.same 3, spy.callCount
- assert.ok spy.calledWith v[0]
- assert.ok spy.calledWith v[1]
- '_formatValueForQueryString':
- 'null': ->
- assert.same 'NULL', @inst._formatValueForQueryString(null)
- 'boolean': ->
- assert.same 'TRUE', @inst._formatValueForQueryString(true)
- assert.same 'FALSE', @inst._formatValueForQueryString(false)
- 'integer': ->
- assert.same 12, @inst._formatValueForQueryString(12)
- 'float': ->
- assert.same 1.2, @inst._formatValueForQueryString(1.2)
- 'string':
- 'have string formatter function': ->
- @inst.options.stringFormatter = (str) -> "N(#{str})"
- assert.same "N(test)", @inst._formatValueForQueryString('test')
- 'default': ->
- escapedValue = undefined
- test.mocker.stub @inst, '_escapeValue', (str) -> escapedValue or str
- assert.same "'test'", @inst._formatValueForQueryString('test')
- assert.same "'test'", @inst._formatValueForQueryString('test')
- assert.ok @inst._escapeValue.calledWithExactly('test')
- escapedValue = 'blah'
- assert.same "'blah'", @inst._formatValueForQueryString('test')
- 'dont quote': ->
- escapedValue = undefined
- test.mocker.stub @inst, '_escapeValue', (str) -> escapedValue or str
- assert.same "test", @inst._formatValueForQueryString('test', dontQuote: true )
- assert.ok @inst._escapeValue.notCalled
- 'Array - recursively calls itself on each element': ->
- spy = test.mocker.spy @inst, '_formatValueForQueryString'
- expected = "('test', 123, TRUE, 1.2, NULL)"
- assert.same expected, @inst._formatValueForQueryString([ 'test', 123, true, 1.2, null ])
- assert.same 6, spy.callCount
- assert.ok spy.calledWith 'test'
- assert.ok spy.calledWith 123
- assert.ok spy.calledWith true
- assert.ok spy.calledWith 1.2
- assert.ok spy.calledWith null
- 'BaseBuilder': ->
- spy = test.mocker.stub @inst, '_applyNestingFormatting', (v) => "{{#{v}}}"
- s = squel.select().from('table')
- assert.same '{{SELECT * FROM table}}', @inst._formatValueForQueryString(s)
- 'checks to see if it is custom value type first': ->
- test.mocker.stub @inst, '_formatCustomValue', (val, asParam) ->
- 12 + (if asParam then 25 else 65)
- test.mocker.stub @inst, '_applyNestingFormatting', (v) -> "{#{v}}"
- assert.same '{77}', @inst._formatValueForQueryString(123)
- '_applyNestingFormatting':
- default: ->
- assert.same '(77)', @inst._applyNestingFormatting('77')
- assert.same '((77)', @inst._applyNestingFormatting('(77')
- assert.same '(77))', @inst._applyNestingFormatting('77)')
- assert.same '(77)', @inst._applyNestingFormatting('(77)')
- 'no nesting': ->
- assert.same '77', @inst._applyNestingFormatting('77', false)
- '_buildString':
- 'empty': ->
- assert.same @inst._buildString('', []), {
- text: '',
- values: [],
- }
- 'no params':
- 'non-parameterized': ->
- assert.same @inst._buildString('abc = 3', []), {
- text: 'abc = 3',
- values: []
- }
- 'parameterized': ->
- assert.same @inst._buildString('abc = 3', [], { buildParameterized: true }), {
- text: 'abc = 3',
- values: []
- }
- 'non-array':
- 'non-parameterized': ->
- assert.same @inst._buildString('a = ? ? ? ?', [2, 'abc', false, null]), {
- text: 'a = 2 \'abc\' FALSE NULL',
- values: []
- }
- 'parameterized': ->
- assert.same @inst._buildString('a = ? ? ? ?', [2, 'abc', false, null], { buildParameterized: true }), {
- text: 'a = ? ? ? ?',
- values: [2, 'abc', false, null]
- }
- 'array': ->
- 'non-parameterized': ->
- assert.same @inst._buildString('a = ?', [[1,2,3]]), {
- text: 'a = (1, 2, 3)',
- values: [],
- }
- 'parameterized': ->
- assert.same @inst._buildString('a = ?', [[1,2,3]], { buildParameterized: true }), {
- text: 'a = (?, ?, ?)',
- values: [1, 2, 3]
- }
- 'nested builder': ->
- beforeEach:
- @s = squel.select().from('master').where('b = ?', 5)
- 'non-parameterized': ->
- assert.same @inst._buildString('a = ?', [@s]), {
- text: 'a = (SELECT * FROM master WHERE (b = ?))',
- values: [5]
- }
- 'parameterized': ->
- assert.same @inst._buildString('a = ?', [@s], { buildParameterized: true }), {
- text: 'a = (SELECT * FROM master WHERE (b = ?))',
- values: [5]
- }
- 'return nested output':
- 'non-parameterized': ->
- assert.same @inst._buildString('a = ?', [3], { nested: true }), {
- text: '(a = 3)',
- values: []
- }
- 'parameterized': ->
- assert.same @inst._buildString('a = ?', [3], { buildParameterized: true, nested: true }), {
- text: '(a = ?)',
- values: [3]
- }
- 'string formatting options': ->
- options =
- formattingOptions:
- dontQuote: true
- assert.same @inst._buildString('a = ?', ['NOW()'], options), {
- text: 'a = NOW()',
- values: []
- }
- 'custom parameter character': ->
- beforeEach: ->
- @inst.options.parameterCharacter = '@@'
- 'non-parameterized': ->
- assert.same @inst._buildString('a = @@', [[1,2,3]]), {
- text: 'a = (1, 2, 3)',
- values: [],
- }
- 'parameterized': ->
- assert.same @inst._buildString('a = @@', [[1,2,3]]), {
- text: 'a = (@@, @@, @@)',
- values: [1,2,3],
- }
- '_buildManyStrings':
- 'empty': ->
- assert.same @inst._buildManyStrings([], []), {
- text: '',
- values: [],
- }
- 'simple':
- beforeEach: ->
- @strings = [
- 'a = ?',
- 'b IN ? AND c = ?'
- ]
- @values = [
- ['elephant'],
- [[1,2,3], 4]
- ]
- 'non-parameterized': ->
- assert.same @inst._buildManyStrings(@strings, @values), {
- text: 'a = \'elephant\' b IN (1, 2, 3) AND c = 4',
- values: [],
- }
- 'parameterized': ->
- assert.same @inst._buildManyStrings(@strings, @values, { buildParameterized: true }), {
- text: 'a = ? b IN (?, ?, ?) AND c = ?',
- values: ['elephant', 1, 2, 3, 4],
- }
- 'return nested': ->
- 'non-parameterized': ->
- assert.same @inst._buildManyStrings(['a = ?', 'b = ?'], [[1], [2]], { nested: true }), {
- text: '(a = 1 b = 2)',
- values: [],
- }
- 'parameterized': ->
- assert.same @inst._buildManyStrings(['a = ?', 'b = ?'], [[1], [2]], { buildParameterized: true, nested: true }), {
- text: '(a = ? b = ?)',
- values: [1, 2],
- }
- 'custom separator': ->
- 'non-parameterized': ->
- @inst.options.separator = '|'
- assert.same @inst._buildManyStrings(['a = ?', 'b = ?'], [[1], [2]]), {
- text: '(a = 1|b = 2)',
- values: [],
- }
- 'parameterized': ->
- assert.same @inst._buildManyStrings(['a = ?', 'b = ?'], [[1], [2]], { buildParameterized: true}), {
- text: '(a = ?|b = ?)',
- values: [1, 2],
- }
- 'toParam': ->
- spy = test.mocker.stub @inst, '_toParamString', ->
- {
- text: 'dummy'
- values: [1]
- }
- options = {test: 2}
- assert.same @inst.toParam(options), {
- text: 'dummy'
- values: [1]
- }
- spy.should.have.been.calledOnce
- assert.same spy.getCall(0).args[0].test, 2
- assert.same spy.getCall(0).args[0].buildParameterized, true
- 'toString': ->
- spy = test.mocker.stub @inst, '_toParamString', ->
- {
- text: 'dummy'
- values: [1]
- }
- options = {test: 2}
- assert.same @inst.toString(options), 'dummy'
- spy.should.have.been.calledOnce
- assert.same spy.getCall(0).args[0], options
- test['QueryBuilder base class'] =
- beforeEach: ->
- @cls = squel.cls.QueryBuilder
- @inst = new @cls
- 'instanceof base builder': ->
- assert.instanceOf @inst, squel.cls.BaseBuilder
- 'constructor':
- 'default options': ->
- assert.same squel.cls.DefaultQueryBuilderOptions, @inst.options
- 'overridden options': ->
- @inst = new @cls
- dummy1: 'str'
- dummy2: 12.3
- usingValuePlaceholders: true
- dummy3: true
- expectedOptions = _.extend {}, squel.cls.DefaultQueryBuilderOptions,
- dummy1: 'str'
- dummy2: 12.3
- usingValuePlaceholders: true
- dummy3: true
- assert.same expectedOptions, @inst.options
- 'default blocks - none': ->
- assert.same [], @inst.blocks
- 'blocks passed in':
- 'exposes block methods': ->
- limitExposedMethodsSpy = test.mocker.spy(squel.cls.LimitBlock.prototype, 'exposedMethods');
- distinctExposedMethodsSpy = test.mocker.spy(squel.cls.DistinctBlock.prototype, 'exposedMethods');
- limitSpy = test.mocker.spy(squel.cls.LimitBlock.prototype, 'limit')
- distinctSpy = test.mocker.spy(squel.cls.DistinctBlock.prototype, 'distinct')
- blocks = [
- new squel.cls.LimitBlock(),
- new squel.cls.DistinctBlock()
- ]
- @inst = new @cls({}, blocks)
- assert.ok limitExposedMethodsSpy.calledOnce
- assert.ok distinctExposedMethodsSpy.calledOnce
- assert.typeOf @inst.distinct, 'function'
- assert.typeOf @inst.limit, 'function'
- assert.same @inst, @inst.limit(2)
- assert.ok limitSpy.calledOnce
- assert.ok limitSpy.calledOn(blocks[0])
- assert.same @inst, @inst.distinct()
- assert.ok distinctSpy.calledOnce
- assert.ok distinctSpy.calledOn(blocks[1])
- 'cannot expose the same method twice': ->
- blocks = [
- new squel.cls.DistinctBlock(),
- new squel.cls.DistinctBlock()
- ]
- try
- @inst = new @cls({}, blocks)
- throw new Error 'should not reach here'
- catch err
- assert.same 'Error: Builder already has a builder method called: distinct', err.toString()
- 'updateOptions()':
- 'updates query builder options': ->
- oldOptions = _.extend({}, @inst.options)
- @inst.updateOptions
- updated: false
- expected = _.extend oldOptions,
- updated: false
- assert.same expected, @inst.options
- 'updates building block options': ->
- @inst.blocks = [
- new squel.cls.Block()
- ]
- oldOptions = _.extend({}, @inst.blocks[0].options)
- @inst.updateOptions
- updated: false
- expected = _.extend oldOptions,
- updated: false
- assert.same expected, @inst.blocks[0].options
- 'toString()':
- 'returns empty if no blocks': ->
- assert.same '', @inst.toString()
- 'skips empty block strings': ->
- @inst.blocks = [
- new squel.cls.StringBlock({}, ''),
- ]
- assert.same '', @inst.toString()
- 'returns final query string': ->
- i = 1
- toStringSpy = test.mocker.stub squel.cls.StringBlock.prototype, '_toParamString', ->
- {
- text: "ret#{++i}"
- values: []
- }
- @inst.blocks = [
- new squel.cls.StringBlock({}, 'STR1'),
- new squel.cls.StringBlock({}, 'STR2'),
- new squel.cls.StringBlock({}, 'STR3')
- ]
- assert.same 'ret2 ret3 ret4', @inst.toString()
- assert.ok toStringSpy.calledThrice
- assert.ok toStringSpy.calledOn(@inst.blocks[0])
- assert.ok toStringSpy.calledOn(@inst.blocks[1])
- assert.ok toStringSpy.calledOn(@inst.blocks[2])
- 'toParam()':
- 'returns empty if no blocks': ->
- assert.same { text: '', values: [] }, @inst.toParam()
- 'skips empty block strings': ->
- @inst.blocks = [
- new squel.cls.StringBlock({}, ''),
- ]
- assert.same { text: '', values: [] }, @inst.toParam()
- 'returns final query string': ->
- @inst.blocks = [
- new squel.cls.StringBlock({}, 'STR1'),
- new squel.cls.StringBlock({}, 'STR2'),
- new squel.cls.StringBlock({}, 'STR3')
- ]
- i = 1
- toStringSpy = test.mocker.stub squel.cls.StringBlock.prototype, '_toParamString', ->
- {
- text: "ret#{++i}"
- values: []
- }
- assert.same { text: 'ret2 ret3 ret4', values: [] }, @inst.toParam()
- assert.ok toStringSpy.calledThrice
- assert.ok toStringSpy.calledOn(@inst.blocks[0])
- assert.ok toStringSpy.calledOn(@inst.blocks[1])
- assert.ok toStringSpy.calledOn(@inst.blocks[2])
- 'returns query with unnumbered parameters': ->
- @inst.blocks = [
- new squel.cls.WhereBlock({}),
- ]
- @inst.blocks[0]._toParamString = test.mocker.spy -> {
- text: 'a = ? AND b in (?, ?)',
- values: [1, 2, 3]
- }
- assert.same { text: 'a = ? AND b in (?, ?)', values: [1, 2, 3]}, @inst.toParam()
- 'returns query with numbered parameters': ->
- @inst = new @cls
- numberedParameters: true
- @inst.blocks = [
- new squel.cls.WhereBlock({}),
- ]
- test.mocker.stub squel.cls.WhereBlock.prototype, '_toParamString', -> {
- text: 'a = ? AND b in (?, ?)', values: [1, 2, 3]
- }
- assert.same @inst.toParam(), { text: 'a = $1 AND b in ($2, $3)', values: [1, 2, 3]}
- 'returns query with numbered parameters and custom prefix': ->
- @inst = new @cls
- numberedParameters: true
- numberedParametersPrefix: '&%'
- @inst.blocks = [
- new squel.cls.WhereBlock({}),
- ]
- test.mocker.stub squel.cls.WhereBlock.prototype, '_toParamString', -> {
- text: 'a = ? AND b in (?, ?)', values: [1, 2, 3]
- }
- assert.same @inst.toParam(), { text: 'a = &%1 AND b in (&%2, &%3)', values: [1, 2, 3]}
- 'cloning':
- 'blocks get cloned properly': ->
- blockCloneSpy = test.mocker.spy(squel.cls.StringBlock.prototype, 'clone')
- @inst.blocks = [
- new squel.cls.StringBlock({}, 'TEST')
- ]
- newinst = @inst.clone()
- @inst.blocks[0].str = 'TEST2'
- assert.same 'TEST', newinst.blocks[0].toString()
- 'registerValueHandler':
- 'beforEach': ->
- @originalHandlers = [].concat(squel.cls.globalValueHandlers)
- 'afterEach': ->
- squel.cls.globalValueHandlers = @originalHandlers
- 'calls through to base class method': ->
- baseBuilderSpy = test.mocker.spy(squel.cls.BaseBuilder.prototype, 'registerValueHandler')
- handler = -> 'test'
- @inst.registerValueHandler(Date, handler)
- @inst.registerValueHandler('number', handler)
- assert.ok baseBuilderSpy.calledTwice
- assert.ok baseBuilderSpy.calledOn(@inst)
- 'returns instance for chainability': ->
- handler = -> 'test'
- assert.same @inst, @inst.registerValueHandler(Date, handler)
- 'calls through to blocks': ->
- @inst.blocks = [
- new squel.cls.StringBlock({}, ''),
- ]
- baseBuilderSpy = test.mocker.spy(@inst.blocks[0], 'registerValueHandler')
- handler = -> 'test'
- @inst.registerValueHandler(Date, handler)
- assert.ok baseBuilderSpy.calledOnce
- assert.ok baseBuilderSpy.calledOn(@inst.blocks[0])
- 'get block':
- 'valid': ->
- block = new squel.cls.FunctionBlock()
- @inst.blocks.push(block)
- assert.same block, @inst.getBlock(squel.cls.FunctionBlock)
- 'invalid': ->
- assert.throws (-> @inst.getBlock(squel.cls.FunctionBlock) )
- module?.exports[require('path').basename(__filename)] = test
|