UNPKG

criterion

Version:

criterion allows you to work with (build, combine, reuse, ...) SQL-where-conditions ('x = 5 AND y IS NOT NULL'...) as data (goodbye string-concatenation) and compile them to SQL: it has a succinct mongodb-like query-language, a simple and elegant function

818 lines (640 loc) 24.9 kB
criterion = C = require '../src/criterion' escape = (x) -> '"' + x + '"' module.exports = 'successfully making criteria': 'x = 7': (test) -> SQL = 'x = ?' ESCAPED = '"x" = ?' PARAMS = [7] lang1 = criterion {x: 7} lang2 = criterion {x: {$eq: 7}} raw = criterion SQL, PARAMS... dsl = C.eq(C.escape('x'), 7) dslReversed = C.eq(7, C.escape('x')) test.equal lang1.sql(), SQL test.equal lang1.sql(escape), ESCAPED test.deepEqual lang1.params(), PARAMS test.equal lang2.sql(), SQL test.equal lang2.sql(escape), ESCAPED test.deepEqual lang2.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.equal dslReversed.sql(), '? = x' test.done() '!=': (test) -> SQL = 'x != ?' ESCAPED = '"x" != ?' PARAMS = ['a'] lang = criterion {x: {$ne: 'a'}} raw = criterion SQL, PARAMS... dsl = C.ne(C.escape('x'), 'a') test.equal lang.sql(), SQL test.equal lang.sql(escape), ESCAPED test.deepEqual lang.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.done() '< AND <=': (test) -> SQL = '(x < ?) AND (y <= ?)' ESCAPED = '("x" < ?) AND ("y" <= ?)' PARAMS = [3, 4] lang = criterion {x: {$lt: 3}, y: {$lte: 4}} raw = criterion SQL, PARAMS... dsl = C.and( C.lt(C.escape('x'), 3) C.lte(C.escape('y'), 4) ) combined = criterion(x: {$lt: 3}) .and(y: {$lte: 4}) test.equal lang.sql(), SQL test.equal lang.sql(escape), ESCAPED test.deepEqual lang.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.equal combined.sql(), SQL test.equal combined.sql(escape), ESCAPED test.deepEqual combined.params(), PARAMS test.done() '> AND >=': (test) -> SQL = '(x > ?) AND (y >= ?)' ESCAPED = '("x" > ?) AND ("y" >= ?)' PARAMS = [5, 6] lang = criterion {x: {$gt: 5}, y: {$gte: 6}} raw = criterion SQL, PARAMS... dsl = C.and( C.gt(C.escape('x'), 5) C.gte(C.escape('y'), 6) ) test.equal lang.sql(), SQL test.equal lang.sql(escape), ESCAPED test.deepEqual lang.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.done() 'x IS NULL': (test) -> SQL = 'x IS NULL' ESCAPED = '"x" IS NULL' PARAMS = [] lang = criterion {x: {$null: true}} raw = criterion SQL dsl = C.null(C.escape('x')) test.equal lang.sql(), SQL test.equal lang.sql(escape), ESCAPED test.deepEqual lang.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.done() 'x IS NOT NULL': (test) -> SQL = 'x IS NOT NULL' ESCAPED = '"x" IS NOT NULL' PARAMS = [] lang = criterion {x: {$null: false}} raw = criterion SQL dsl = C.null(C.escape('x'), false) test.equal lang.sql(), SQL test.equal lang.sql(escape), ESCAPED test.deepEqual lang.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.done() 'x IN (?, ?, ?)': (test) -> SQL = 'x IN (?, ?, ?)' ESCAPED = '"x" IN (?, ?, ?)' PARAMS = [1, 2, 3] lang = criterion {x: [1, 2, 3]} langLong = criterion {x: {$in: [1, 2, 3]}} raw1 = criterion SQL, 1, 2, 3 raw2 = criterion 'x IN (?)', [1, 2, 3] dsl = C.in(C.escape('x'), [1, 2, 3]) test.equal lang.sql(), SQL test.equal lang.sql(escape), ESCAPED test.deepEqual lang.params(), PARAMS test.equal langLong.sql(), SQL test.equal langLong.sql(escape), ESCAPED test.deepEqual langLong.params(), PARAMS test.equal raw1.sql(), SQL test.equal raw1.sql(escape), SQL test.deepEqual raw1.params(), PARAMS test.equal raw2.sql(), SQL test.equal raw2.sql(escape), SQL test.deepEqual raw2.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.done() 'NOT IN': (test) -> SQL = 'x NOT IN (?, ?, ?)' ESCAPED = '"x" NOT IN (?, ?, ?)' PARAMS = [1, 2, 3] lang = criterion {x: {$nin: [1, 2, 3]}} raw = criterion SQL, PARAMS... dsl = C.nin(C.escape('x'), [1, 2, 3]) test.equal lang.sql(), SQL test.equal lang.sql(escape), ESCAPED test.deepEqual lang.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.done() 'AND': (test) -> SQL = '(x = ?) AND (y = ?) AND (z = ?) AND a = ?' ESCAPED = '("x" = ?) AND ("y" = ?) AND ("z" = ?) AND a = ?' PARAMS = [7, 'foo', 2.5, 6] lang1 = criterion {x: 7, y: 'foo'}, {z: 2.5}, [criterion('a = ?', 6)] lang2 = criterion [{x: 7, y: 'foo'}, {z: 2.5}, criterion('a = ?', 6)] lang3 = criterion {$and: [{x: 7, y: 'foo'}, {z: 2.5}, criterion('a = ?', 6)]} lang4 = criterion {x: 7, y: 'foo', $and: [{z: 2.5}, criterion('a = ?', 6)]} raw = criterion SQL, PARAMS... dsl1 = C.and( C.eq(C.escape('x'), 7) C.eq(C.escape('y'), 'foo') C.eq(C.escape('z'), 2.5) C('a = ?', 6) ) dsl2 = C.and( C.eq(C.escape('x'), 7) C.eq(C.escape('y'), 'foo') C.and( C.eq(C.escape('z'), 2.5) C('a = ?', 6) ) ) dsl3 = C( C.eq(C.escape('x'), 7) C.eq(C.escape('y'), 'foo') C( C.eq(C.escape('z'), 2.5) C('a = ?', 6) ) ) test.equal lang1.sql(), SQL test.equal lang1.sql(escape), ESCAPED test.deepEqual lang1.params(), PARAMS test.equal lang2.sql(), SQL test.equal lang2.sql(escape), ESCAPED test.deepEqual lang2.params(), PARAMS test.equal lang3.sql(), SQL test.equal lang3.sql(escape), ESCAPED test.deepEqual lang3.params(), PARAMS test.equal lang4.sql(), SQL test.equal lang4.sql(escape), ESCAPED test.deepEqual lang4.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl1.sql(), SQL test.equal dsl1.sql(escape), ESCAPED test.deepEqual dsl1.params(), PARAMS test.equal dsl2.sql(), SQL test.equal dsl2.sql(escape), ESCAPED test.deepEqual dsl2.params(), PARAMS test.equal dsl3.sql(), SQL test.equal dsl3.sql(escape), ESCAPED test.deepEqual dsl3.params(), PARAMS test.done() 'OR': (test) -> SQL = '(x = ?) OR (y = ?) OR (z = ?) OR a = ?' ESCAPED = '("x" = ?) OR ("y" = ?) OR ("z" = ?) OR a = ?' PARAMS = [7, 'foo', 2.5, 6] lang1 = criterion {$or: [{x: 7}, {y: 'foo'}, {z: 2.5}, criterion('a = ?', 6)]} lang2 = criterion {$or: {x: 7, y: 'foo', $or: [{z: 2.5}, criterion('a = ?', 6)]}} raw = criterion SQL, PARAMS... dsl1 = C.or( C.eq(C.escape('x'), 7) C.eq(C.escape('y'), 'foo') C.eq(C.escape('z'), 2.5) C('a = ?', 6) ) dsl2 = C.or( C.eq(C.escape('x'), 7) C.eq(C.escape('y'), 'foo') C.or( C.eq(C.escape('z'), 2.5) C('a = ?', 6) ) ) test.equal lang1.sql(), SQL test.equal lang1.sql(escape), ESCAPED test.deepEqual lang1.params(), PARAMS test.equal lang2.sql(), SQL test.equal lang2.sql(escape), ESCAPED test.deepEqual lang2.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl1.sql(), SQL test.equal dsl1.sql(escape), ESCAPED test.deepEqual dsl1.params(), PARAMS test.equal dsl2.sql(), SQL test.equal dsl2.sql(escape), ESCAPED test.deepEqual dsl2.params(), PARAMS test.done() 'NOT': (test) -> SQL = 'NOT ((x > ?) AND (y >= ?))' ESCAPED = 'NOT (("x" > ?) AND ("y" >= ?))' PARAMS = [3, 4] lang = criterion {$not: {x: {$gt: 3}, y: {$gte: 4}}} raw = criterion SQL, PARAMS... dsl = C.not( C.and( C.gt(C.escape('x'), 3) C.gte(C.escape('y'), 4) ) ) test.equal lang.sql(), SQL test.equal lang.sql(escape), ESCAPED test.deepEqual lang.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.done() 'OR inside AND (is wrapped in parentheses)': (test) -> SQL = '(username = ?) AND (password = ?) AND ((active = ?) OR (active IS NULL))' ESCAPED = '("username" = ?) AND ("password" = ?) AND (("active" = ?) OR ("active" IS NULL))' PARAMS = ['user', 'hash', 1] lang = criterion username: 'user' password: 'hash' $or: [{active: 1}, {active: {$null: true}}] raw = criterion SQL, PARAMS... dsl = C.and( C.eq(C.escape('username'), 'user') C.eq(C.escape('password'), 'hash') C.or( C.eq(C.escape('active'), 1) C.null(C.escape('active')) ) ) test.equal lang.sql(), SQL test.equal lang.sql(escape), ESCAPED test.deepEqual lang.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.done() 'AND, OR and NOT can be deeply nested': (test) -> SQL = '(alpha = ?) OR ((charlie != ?) AND (NOT (((delta = ?) OR (delta IS NULL)) AND (echo = ?)))) OR ((echo < ?) AND ((golf = ?) OR (NOT (lima != ?)))) OR (foxtrot = ?) OR (NOT (alpha = ? OR (NOT (echo < ?)) OR (alpha < ?) OR (bravo = ?) OR ((alpha = ?) AND (bravo = ?)))) OR (bravo = ?)' ESCAPED = '("alpha" = ?) OR (("charlie" != ?) AND (NOT ((("delta" = ?) OR ("delta" IS NULL)) AND ("echo" = ?)))) OR (("echo" < ?) AND (("golf" = ?) OR (NOT ("lima" != ?)))) OR ("foxtrot" = ?) OR (NOT (alpha = ? OR (NOT ("echo" < ?)) OR ("alpha" < ?) OR ("bravo" = ?) OR (("alpha" = ?) AND ("bravo" = ?)))) OR ("bravo" = ?)' PARAMS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] lang = criterion { $or: { alpha: 1 $and: { charlie: {$ne: 2} $not: { $and: { $or: [ {delta: 3}, {delta: {$null: true}} ] echo: 4 } } } $or: [ [ {echo: {$lt: 5}} {$or: { golf: 6 $not: {lima: {$ne: 7}} }} ] {foxtrot: 8} ] $not: { $or: [ criterion('alpha = ?', 9) {$not: {echo: {$lt: 10}}} {alpha: {$lt: 11}} {bravo: 12} [ {alpha: 13} {bravo: 14} ] ] } bravo: 15 } } raw = criterion SQL, PARAMS... dsl = C.or( C.eq(C.escape('alpha'), 1) C.and( C.ne(C.escape('charlie'), 2) C.not( C.and( C.or( C.eq(C.escape('delta'), 3) C.null(C.escape('delta')) ) C.eq(C.escape('echo'), 4) ) ) ) C.or( C.and( C.lt(C.escape('echo'), 5) C.or( C.eq(C.escape('golf'), 6) C.not( C.ne(C.escape('lima'), 7) ) ) ) C.eq(C.escape('foxtrot'), 8) ) C.not( C.or( C('alpha = ?', 9) C.not( C.lt(C.escape('echo'), 10) ) C.lt(C.escape('alpha'), 11) C.eq(C.escape('bravo'), 12) C( C.eq(C.escape('alpha'), 13) C.eq(C.escape('bravo'), 14) ) ) ) C.eq(C.escape('bravo'), 15) ) test.equal lang.sql(), SQL test.equal lang.sql(escape), ESCAPED test.deepEqual lang.params(), PARAMS test.equal raw.sql(), SQL test.equal raw.sql(escape), SQL test.deepEqual raw.params(), PARAMS test.equal dsl.sql(), SQL test.equal dsl.sql(escape), ESCAPED test.deepEqual dsl.params(), PARAMS test.done() 'IN and $nin': (test) -> subquery = sql: (escape) -> "SELECT #{escape 'id'} FROM \"user\" WHERE #{escape 'is_active'}" params: -> [] subqueryWithParams = sql: (escape) -> "SELECT #{escape 'id'} FROM \"user\" WHERE #{escape 'is_active'} = ?" params: -> [true] inWithoutParams = criterion {x: {$in: subquery}} test.equal inWithoutParams.sql(), 'x IN (SELECT id FROM "user" WHERE is_active)' test.equal inWithoutParams.sql(escape), '"x" IN (SELECT "id" FROM "user" WHERE "is_active")' test.deepEqual inWithoutParams.params(), [] inWithParams = criterion {x: {$in: subqueryWithParams}} test.equal inWithParams.sql(), 'x IN (SELECT id FROM "user" WHERE is_active = ?)' test.equal inWithParams.sql(escape), '"x" IN (SELECT "id" FROM "user" WHERE "is_active" = ?)' test.deepEqual inWithParams.params(), [true] ninWithoutParams = criterion {x: {$nin: subquery}} test.equal ninWithoutParams.sql(), 'x NOT IN (SELECT id FROM "user" WHERE is_active)' test.equal ninWithoutParams.sql(escape), '"x" NOT IN (SELECT "id" FROM "user" WHERE "is_active")' test.deepEqual ninWithoutParams.params(), [] ninWithParams = criterion {x: {$nin: subqueryWithParams}} test.equal ninWithParams.sql(), 'x NOT IN (SELECT id FROM "user" WHERE is_active = ?)' test.equal ninWithParams.sql(escape), '"x" NOT IN (SELECT "id" FROM "user" WHERE "is_active" = ?)' test.deepEqual ninWithParams.params(), [true] test.done() '$exists': (test) -> subquery = sql: (escape) -> "SELECT * FROM \"user\" WHERE #{escape 'is_active'}" params: -> [] subqueryWithParams = sql: (escape) -> "SELECT * FROM \"user\" WHERE #{escape 'is_active'} = ?" params: -> [true] existsWithoutParams = criterion {id: 7, $exists: subquery} test.equal existsWithoutParams.sql(), '(id = ?) AND (EXISTS (SELECT * FROM "user" WHERE is_active))' test.equal existsWithoutParams.sql(escape), '("id" = ?) AND (EXISTS (SELECT * FROM "user" WHERE "is_active"))' test.deepEqual existsWithoutParams.params(), [7] existsWithParams = criterion {id: 7, $exists: subqueryWithParams} test.equal existsWithParams.sql(), '(id = ?) AND (EXISTS (SELECT * FROM "user" WHERE is_active = ?))' test.equal existsWithParams.sql(escape), '("id" = ?) AND (EXISTS (SELECT * FROM "user" WHERE "is_active" = ?))' test.deepEqual existsWithParams.params(), [7, true] test.done() '$any, $neAny, $ltAny, $lteAny, $gtAny, $gteAny, $all, $neAll, $ltAll, $lteAll, $gtAll, $gteAll': (test) -> subquery = sql: (escape) -> "SELECT * FROM #{escape "user"}" params: -> [] subqueryWithParams = sql: (escape) -> "SELECT * FROM #{escape "user"} WHERE #{escape "id"} = ?" params: -> [7] anyWithoutParams = criterion {x: {$any: subquery}} test.equal anyWithoutParams.sql(), 'x = ANY (SELECT * FROM user)' test.equal anyWithoutParams.sql(escape), '"x" = ANY (SELECT * FROM "user")' test.deepEqual anyWithoutParams.params(), [] anyWithParams = criterion {x: {$any: subqueryWithParams}, y: 6} test.equal anyWithParams.sql(), '(x = ANY (SELECT * FROM user WHERE id = ?)) AND (y = ?)' test.equal anyWithParams.sql(escape), '("x" = ANY (SELECT * FROM "user" WHERE "id" = ?)) AND ("y" = ?)' test.deepEqual anyWithParams.params(), [7, 6] # since all other subqueries follow the same code path # we omit testing with params and escaping for them test.equal criterion({x: {$neAny: subquery}}).sql(), 'x != ANY (SELECT * FROM user)' test.equal criterion({x: {$ltAny: subquery}}).sql(), 'x < ANY (SELECT * FROM user)' test.equal criterion({x: {$lteAny: subquery}}).sql(), 'x <= ANY (SELECT * FROM user)' test.equal criterion({x: {$gtAny: subquery}}).sql(), 'x > ANY (SELECT * FROM user)' test.equal criterion({x: {$gteAny: subquery}}).sql(), 'x >= ANY (SELECT * FROM user)' test.equal criterion({x: {$all: subquery}}).sql(), 'x = ALL (SELECT * FROM user)' test.equal criterion({x: {$neAll: subquery}}).sql(), 'x != ALL (SELECT * FROM user)' test.equal criterion({x: {$ltAll: subquery}}).sql(), 'x < ALL (SELECT * FROM user)' test.equal criterion({x: {$lteAll: subquery}}).sql(), 'x <= ALL (SELECT * FROM user)' test.equal criterion({x: {$gtAll: subquery}}).sql(), 'x > ALL (SELECT * FROM user)' test.equal criterion({x: {$gteAll: subquery}}).sql(), 'x >= ALL (SELECT * FROM user)' test.done() 'row-wise comparison': (test) -> subquery = sql: (escape) -> "SELECT #{escape 'created_at'} FROM #{escape 'message'} WHERE #{escape 'id'} = ?" params: -> [1] c = criterion {is_active: true, created_at: {$lte: subquery}} test.equal c.sql(), '(is_active = ?) AND (created_at <= (SELECT created_at FROM message WHERE id = ?))' test.equal c.sql(escape), '("is_active" = ?) AND ("created_at" <= (SELECT "created_at" FROM "message" WHERE "id" = ?))' test.deepEqual c.params(), [true, 1] test.done() 'with one param and one array': (test) -> c = criterion 'x = ? AND y IN (?)', 7, [8,9,10] test.equal c.sql(), 'x = ? AND y IN (?, ?, ?)' test.deepEqual c.params(), [7, 8, 9, 10] test.done() 'with two params and array': (test) -> c = criterion 'x = ? AND y = ? AND z IN (?)', 7, 8, [9,10,11] test.equal c.sql(), 'x = ? AND y = ? AND z IN (?, ?, ?)' test.deepEqual c.params(), [7, 8, 9, 10, 11] test.done() 'with two params and two arrays': (test) -> c = criterion 'x = ? AND y = ? AND z IN (?) AND (a && ARRAY[?])', 7, 8, [9,10,11], [12,13,14] test.equal c.sql(), 'x = ? AND y = ? AND z IN (?, ?, ?) AND (a && ARRAY[?, ?, ?])' test.deepEqual c.params(), [7, 8, 9, 10, 11, 12, 13, 14] test.done() 'equality with criterion argument': (test) -> c = criterion {x: criterion('crypt(?, gen_salt(?, ?))', 'password', 'bf', 4)} test.equal c.sql(), 'x = crypt(?, gen_salt(?, ?))' test.equal c.sql(escape), '"x" = crypt(?, gen_salt(?, ?))' test.deepEqual c.params(), ['password', 'bf', 4] test.done() '$ne with criterion argument': (test) -> c = criterion {x: {$ne: criterion('crypt(?, gen_salt(?, ?))', 'password', 'bf', 4)}} test.equal c.sql(), 'x != crypt(?, gen_salt(?, ?))' test.equal c.sql(escape), '"x" != crypt(?, gen_salt(?, ?))' test.deepEqual c.params(), ['password', 'bf', 4] test.done() '$lt with criterion argument': (test) -> c = criterion {x: {$lt: criterion('NOW()')}} test.equal c.sql(), 'x < NOW()' test.equal c.sql(escape), '"x" < NOW()' test.deepEqual c.params(), [] test.done() 'successfully manipulating criteria': 'and': (test) -> fst = criterion {x: 7, y: 'foo'} snd = criterion 'z = ?', true fstAndSnd = fst.and snd test.equal fstAndSnd.sql(), '(x = ?) AND (y = ?) AND z = ?' test.deepEqual fstAndSnd.params(), [7, 'foo', true] test.done() 'or': (test) -> fst = criterion {x: 7, y: 'foo'} snd = criterion 'z = ?', true sndOrFst = snd.or fst test.equal sndOrFst.sql(), 'z = ? OR ((x = ?) AND (y = ?))' test.deepEqual sndOrFst.params(), [true, 7, 'foo'] test.done() 'not': (test) -> c = criterion {x: 7, y: 'foo'} test.equal c.not().sql(), 'NOT ((x = ?) AND (y = ?))' test.deepEqual c.not().params(), [7, 'foo'] test.done() 'double negation is removed': (test) -> c = criterion {x: 7, y: 'foo'} test.equal c.not().not().sql(), '(x = ?) AND (y = ?)' test.deepEqual c.not().not().params(), [7, 'foo'] test.equal c.not().not().not().sql(), 'NOT ((x = ?) AND (y = ?))' test.deepEqual c.not().not().not().params(), [7, 'foo'] test.done() 'errors when making criteria': 'not string or object': (test) -> try criterion 6 catch e test.equal e.message, 'string or object expected as first argument but number given' test.done() 'empty object': (test) -> try criterion {} catch e test.equal e.message, 'empty condition-object' try criterion [{}] catch e test.equal e.message, 'empty condition-object' test.done() 'empty array param': (test) -> try criterion 'b < ? AND a IN(?) AND c < ?', 6, [], 7 catch e test.equal e.message, 'params[1] is an empty array' test.done() 'null value with modifier': (test) -> try criterion {x: {$lt: null}} catch e test.equal e.message, 'value undefined or null for key x and modifier key $lt' test.done() 'null value without modifier': (test) -> try criterion {x: null} catch e test.equal e.message, 'value undefined or null for key x' test.done() 'in with empty array': (test) -> try criterion {x: []} catch e test.equal e.message, '`in` with empty array as right operand' test.done() '$nin with empty array': (test) -> try criterion {x: {$nin: []}} catch e test.equal e.message, '`nin` with empty array as right operand' test.done() '$any with array': (test) -> try criterion {x: {$any: [1]}} catch e test.equal e.message, "`any` doesn't support array as right operand. only `in` and `nin` do!" test.done() '$any with number': (test) -> try criterion {x: {$any: 6}} catch e test.equal e.message, "`any` requires right operand that implements sql-fragment interface" test.done() 'unknown modifier': (test) -> try criterion {x: {$not: 6}} catch e test.equal e.message, 'unknown modifier key $not' try criterion {x: {$foo: 6}} catch e test.equal e.message, 'unknown modifier key $foo' test.done() '$exists without sql-fragment': (test) -> try criterion {$exists: 6} catch e test.equal e.message, "`exists` operand must implement sql-fragment interface" test.done() '$in without array or sql-fragment': (test) -> try criterion({x: {$in: 6}}) catch e test.equal e.message, "`in` requires right operand that is an array or implements sql-fragment interface" test.done() '$nin without array or sql-fragment': (test) -> try criterion({x: {$nin: 6}}) catch e test.equal e.message, "`nin` requires right operand that is an array or implements sql-fragment interface" test.done()