UNPKG

dynamite

Version:

promise-based DynamoDB client

438 lines (383 loc) 13.8 kB
// Copyright 2013 The Obvious Corporation. var ConditionBuilder = require('../lib/ConditionBuilder') var utils = require('./utils/testUtils.js') var nodeunitq = require('nodeunitq') var builder = new nodeunitq.Builder(exports) var onError = console.error.bind(console) var tableName = "comments" var rawData = [{"postId": "post1", "column": "@", "title": "This is my post", "content": "And here is some content!", "tags": ['bar', 'foo']}, {"postId": "post1", "column": "/comment/timestamp/002123", "comment": "this is slightly later"}, {"postId": "post1", "column": "/comment/timestamp/010000", "comment": "where am I?"}, {"postId": "post1", "column": "/comment/timestamp/001111", "comment": "HEYYOOOOO"}, {"postId": "post1", "column": "/comment/timestamp/001112", "comment": "what's up?"}, {"postId": "post1", "column": "/canEdit/user/AAA", "userId": "AAA"}] // sorted data for checking the order of returned data var sortedRawData = [] for (var i = 0; i < rawData.length; i++) { sortedRawData[i] = rawData[i] } sortedRawData.sort(function(obj1, obj2) { return obj1.column > obj2.column ? 1 : -1 }) // basic setup for the tests, creating record userA with Index key @ exports.setUp = function (done) { this.db = utils.getMockDatabase() this.client = utils.getMockDatabaseClient() utils.ensureLocalDynamo() utils.createTable(this.db, tableName, "postId", "column") .thenBound(utils.initTable, null, {"db": this.db, "tableName": tableName, "data": rawData}) .fail(onError) .fin(done) } exports.tearDown = function (done) { utils.deleteTable(this.db, tableName) .fin(done) } function checkResults(test, total, offset) { return function (data) { test.equal(data.result.length, total, total + " records should be returned") for (var i = 0; i < data.result.length; i++) { test.deepEqual(data.result[i], sortedRawData[i + offset], "Row should be retrieved in the correct order") } } } // test basic query builder.add(function testBasicQuery(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .execute() .then(checkResults(test, 6, 0)) }) // test basic query with an empty filter builder.add(function testBasicQueryEmptyFilter(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .withFilter(this.client.newConditionBuilder()) .execute() .then(checkResults(test, 6, 0)) }) // test Index key begins with builder.add(function testindexBeginsWith(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBeginsWith('column', '/comment/') .execute() .then(checkResults(test, 4, 1)) }) // test filtering builder.add(function testFilterByComment(test) { var filter = this.client.newConditionBuilder() .filterAttributeBeginsWith("comment", "HEY") return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBeginsWith('column', '/comment/') .withFilter(filter) .execute() .then(checkResults(test, 1, 1)) }) // test filter with limit builder.add(function testFilterWithLimit(test) { var filter = this.client.newConditionBuilder() .filterAttributeBeginsWith("comment", "wh") // The limit parameter is applied before the filter return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBeginsWith('column', '/comment/') .withFilter(filter) .setLimit(2) .execute() .then(checkResults(test, 1, 2)) }) // test Index key between builder.add(function testIndexKeyBetween(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/009999') .execute() .then(checkResults(test, 3, 1)) }) // test Index key less than builder.add(function testIndexKeyLessThan(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexLessThan('column', '/comment/timestamp/001111') .execute() .then(checkResults(test, 1, 0)) }) // test Index key less than equal builder.add(function testIndexKeyLessThanEqual(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexLessThanEqual('column', '/comment/timestamp/001111') .execute() .then(checkResults(test, 2, 0)) }) // test Index key greater than builder.add(function testIndexKeyGreaterThan(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexGreaterThan('column', '/comment/timestamp/001111') .execute() .then(checkResults(test, 4, 2)) }) // test Index key greater than equal builder.add(function testIndexKeyGreaterThanEqual(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexGreaterThanEqual('column', '/comment/timestamp/001111') .execute() .then(checkResults(test, 5, 1)) }) // test Index key equal builder.add(function testIndexKeyEqual(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexEqual('column', '/comment/timestamp/001111') .execute() .then(checkResults(test, 1, 1)) }) // test limit builder.add(function testLimit(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/999999') .setLimit(3) .execute() .then(checkResults(test, 3, 1)) }) // test scan forward builder.add(function testScanForward(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/999999') .execute() .then(checkResults(test, 4, 1)) }) // test cursoring forward builder.add(function testCursorForward(test) { var client = this.client return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/999999') .setLimit(3) .execute() .then(function (data) { return client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/999999') .setStartKey(data.LastEvaluatedKey) .execute() }) .then(function (data) { test.equal(data.result.length, 1, "1 record should be returned") test.equal(data.result[0].comment, "where am I?", "Row comment should be set") }) }) // test cursoring backward builder.add(function testCursorBackward(test) { var client = this.client return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/999999') .setLimit(3) .scanBackward() .execute() .then(function (data) { return client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/999999') .scanBackward() .setStartKey(data.LastEvaluatedKey) .execute() }) .then(function (data) { test.equal(data.result.length, 1, "1 record should be returned") test.equal(data.result[0].comment, "HEYYOOOOO", "Row comment should be set") }) }) // test select attributes builder.add(function testSelectAttributes(test) { var keyOffset = 1 return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/999999') .selectAttributes(['postId', 'comment']) .execute() .then(function (data) { test.equal(data.result.length, 4, "4 records should be returned") for (var i = 0; i < data.result.length; i++) { test.equal(data.result[i].comment, sortedRawData[i + keyOffset].comment, "Row comment should be set") test.equal(data.result[i].column, undefined, 'Column should not be set') } }) }) // test set existence builder.add(function testSetExistence(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexEqual('column', '@') .selectAttributes(['postId', 'tags']) .execute() .then(function (data) { test.deepEqual(data.result[0].tags, ['bar', 'foo'], "post should have tags ['bar', 'foo']") }) }) // test scan backward builder.add(function testScanBackward(test) { var keyOffset = 1 return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/999999') .scanBackward() .execute() .then(function (data) { test.equal(data.result.length, 4, "4 records should be returned") for (var i = 0; i < data.result.length; i++) { test.deepEqual(data.result[i], sortedRawData[(data.result.length - 1 - i) + keyOffset], "Row should be retrieved in the correct order") } }) }) // test count builder.add(function testCount(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/timestamp/002123', '/comment/timestamp/999999') .getCount() .execute() .then(function (data) { test.equal(data.Count, 2, '"2" should be returned') }) }) // test count if it's zero builder.add(function testCountIfZero(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'postDNE') .indexBetween('column', '/comment/timestamp/002123', '/comment/timestamp/999999') .getCount() .execute() .then(function (data) { test.equal(data.Count, 0, '"0" should be returned') }) }) builder.add(function testNext(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/999999') .setLimit(3) .execute() .then(function (data) { test.equal(3, data.Count) test.ok(data.hasNext()) return data.next() }) .then(function (data) { test.equal(1, data.Count) test.ok(!data.hasNext()) return data.next() }) .then(function () { test.fail('Expected error') }) .fail(function (e) { if (e.message !== 'No more results') throw e }) }) builder.add(function testNextWithLimit(test) { return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBetween('column', '/comment/', '/comment/timestamp/999999') .setLimit(2) .execute() .then(function (data) { test.equal(2, data.Count) test.ok(data.hasNext()) return data.next(3) }) .then(function (data) { test.equal(2, data.Count) test.ok(!data.hasNext()) return data.next() }) .then(function () { test.fail('Expected error') }) .fail(function (e) { if (e.message !== 'No more results') throw e }) }) builder.add(function testValidationError(test) { var client = this.client return client.newQueryBuilder('comments') .setHashKey('garbage', 'postId') .execute() .then(function () { test.fail('Expected validation exception') }) .fail(function (e) { if (!client.isValidationError(e)) throw e test.equal('comments', e.table) }) }) builder.add(function testRetryHandler(test) { var client = this.client var calledRetryHandler = 0 return client.newQueryBuilder('comments') .setHashKey('invalid', 'post1') .setRetryHandler(function (method, table, response) { test.equal(response.error.retryable, false) ++calledRetryHandler }) .execute() .then(function () { test.fail('Expected validation to fail!') }) .fail(function () { test.equal(calledRetryHandler, 1) }) }) builder.add(function testKeyConditionExpression(test) { var filter = this.client.newConditionBuilder() .filterAttributeBeginsWith("comment", "HEY") var data = {} ConditionBuilder.populateExpressionField( data, 'KeyConditionExpression', [filter], {}) test.equal('{"#comment":"comment"}', JSON.stringify(data.ExpressionAttributeNames)) test.equal('{":VC2":{"S":"HEY"}}', JSON.stringify(data.ExpressionAttributeValues)) test.equal('begins_with(#comment, :VC2)', data.KeyConditionExpression) test.done() }) builder.add(function testOrConditionExpression(test) { var filter1 = this.client.newConditionBuilder() .filterAttributeBeginsWith("comment", "HEY") var filter2 = this.client.newConditionBuilder() .filterAttributeBeginsWith("comment", "what") return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBeginsWith('column', '/comment/') .withFilter(this.client.orConditions([filter1, filter2])) .execute() .then(checkResults(test, 2, 1)) }) builder.add(function testAndConditionExpression(test) { var filter1 = this.client.newConditionBuilder() .filterAttributeBeginsWith("comment", "HEY") var filter2 = this.client.newConditionBuilder() .filterAttributeEquals("comment", "HEYYOOOOO") return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBeginsWith('column', '/comment/') .withFilter(this.client.andConditions([filter1, filter2])) .execute() .then(checkResults(test, 1, 1)) }) builder.add(function testNotConditionExpression(test) { var filter1 = this.client.newConditionBuilder() .filterAttributeBeginsWith("comment", "HEY") return this.client.newQueryBuilder('comments') .setHashKey('postId', 'post1') .indexBeginsWith('column', '/comment/') .withFilter(this.client.notCondition(filter1)) .execute() .then(checkResults(test, 3, 2)) })