UNPKG

dynamite

Version:

promise-based DynamoDB client

1,035 lines (936 loc) 32.1 kB
// Copyright 2013 The Obvious Corporation var nodeunitq = require('nodeunitq') var builder = new nodeunitq.Builder(exports) var Client = require('../lib/Client') var FakeDynamo = require('../lib/FakeDynamo') var userA = { 'userId': 'userA', 'column': '@', 'age': 29, 'luckyNumbers': [1, 3, 5] } var db, client exports.setUp = function (done) { db = new FakeDynamo() client = new Client({dbClient: db}) var userTable = db.createTable('user') userTable.setHashKey('userId', 'S') userTable.setRangeKey('column', 'S') userTable.setData( JSON.parse(JSON.stringify({userA: {'@': userA}}))) var cookieTable = db.createTable('cookie') cookieTable.setHashKey('cookieId', 'S') cookieTable.setRangeKey('createdAt', 'S') done() } exports.tearDown = function (done) { done() } builder.add(function testConditionalUpdateFails(test) { var conditions = client.newConditionBuilder() .expectAttributeEquals('userId', 'gibberish') return client.newUpdateBuilder('user') .setHashKey('userId', 'gibberish') .setRangeKey('column', '@') .withCondition(conditions) .putAttribute('age', 30) .execute() .then(function () { test.fail('Expected conditional error') }) .fail(function (e) { test.ok(client.isConditionalError(e)) test.ok(!!e.stack) throw e }) .fail(client.throwUnlessConditionalError) }) builder.add(function testConditionalUpdateOk(test) { var conditions = client.newConditionBuilder() .expectAttributeEquals('userId', 'userA') return client.newUpdateBuilder('user') .setHashKey('userId', 'userA') .setRangeKey('column', '@') .withCondition(conditions) .putAttribute('age', 30) .execute() .then(function () { return client.getItem('user') .setHashKey('userId', 'userA') .setRangeKey('column', '@') .execute() }) .then(function (data) { test.equal(data.result.age, 30, 'Age should match 30') }) }) builder.add(function testAddToAttribute(test) { var conditions = client.newConditionBuilder() .expectAttributeEquals('userId', 'userA') return client.newUpdateBuilder('user') .setHashKey('userId', 'userA') .setRangeKey('column', '@') .withCondition(conditions) .addToAttribute('luckyNumbers', [8]) .execute() .then(function () { return client.getItem('user') .setHashKey('userId', 'userA') .setRangeKey('column', '@') .execute() }) .then(function (data) { data.result.luckyNumbers.sort() test.deepEqual(data.result.luckyNumbers, [1, 3, 5, 8]) }) }) builder.add(function testBatchGetDupeKeys(test) { // Real Dynamo throws an exception if a BatchGet has duplicate keys. // Ruby FakeDynamo does not have this validation. return client.newBatchGetBuilder() .requestItems('user', [{'userId': 'userA', 'column': '@'}, {'userId': 'userA', 'column': '@'}]) .execute() .then(function () { test.fail('Expected validation failure') }) .fail(function (e) { if (!/Provided list of item keys contains duplicates/.test(e.message)) { throw e } }) }) builder.add(function testConditionalBuilderMethods(test) { var expected = client.newConditionBuilder() .expectAttributeEquals('userId', 'gibberish') .expectAttributeAbsent('userId2') var actual = client.conditions({userId: 'gibberish', 'userId2': null}) test.deepEqual(expected, actual) test.done() }) builder.add(function testScan(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27}, 2: {userId: 'userA', column: '2', age: 28}, 3: {userId: 'userA', column: '3', age: 29} }, 'userB': { 1: {userId: 'userB', column: '1', age: 29} } }) return client.newScanBuilder('user') .execute() .then(function (data) { var result = data.result test.deepEqual(result[0], {userId: 'userA', column: '1', age: 27}) test.deepEqual(result[1], {userId: 'userA', column: '2', age: 28}) test.deepEqual(result[2], {userId: 'userA', column: '3', age: 29}) test.deepEqual(result[3], {userId: 'userB', column: '1', age: 29}) }) }) builder.add(function testScanWithSimpleFilter(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27, name: 'Ringo'}, 2: {userId: 'userA', column: '4', age: 28, name: 'George'}, 3: {userId: 'userA', column: '3', age: 29, name: 'John'}, 4: {userId: 'userA', column: '2', age: 30, name: 'Paul'} } }) var filter = client.newConditionBuilder() .filterAttributeBeginsWith('name', 'Geo') return client.newScanBuilder('user') .withFilter(filter) .execute() .then(function (data) { test.equal(data.result.length, 1) test.deepEqual(data.result[0], {userId: 'userA', column: '4', age: 28, name: 'George'}) }) }) builder.add(function testScanWithComplexFilter(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27, name: 'Ringo'}, 2: {userId: 'userA', column: '4', age: 28, name: 'George'}, 3: {userId: 'userA', column: '3', age: 29, name: 'John'}, 4: {userId: 'userA', column: '2', age: 30, name: 'Paul'} } }) var filter = client.andConditions([ client.newConditionBuilder().filterAttributeGreaterThan('age', 27), client.newConditionBuilder().filterAttributeLessThan('age', 30) ]) return client.newScanBuilder('user') .withFilter(filter) .execute() .then(function (data) { test.equal(data.result.length, 2) test.deepEqual(data.result[0], {userId: 'userA', column: '4', age: 28, name: 'George'}) test.deepEqual(data.result[1], {userId: 'userA', column: '3', age: 29, name: 'John'}) }) }) builder.add(function testScanWithLimit(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27}, 2: {userId: 'userA', column: '2', age: 28}, 3: {userId: 'userA', column: '3', age: 29} } }) return client.newScanBuilder('user') .setLimit(2) .execute() .then(function (data) { var result = data.result test.deepEqual(result[0], {userId: 'userA', column: '1', age: 27}) test.deepEqual(result[1], {userId: 'userA', column: '2', age: 28}) test.deepEqual(data.LastEvaluatedKey, {userId: 'userA', column: '2'}) }) }) builder.add(function testScanWithStartKey(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27}, 2: {userId: 'userA', column: '2', age: 28}, 3: {userId: 'userA', column: '3', age: 29} }, 'userB': { 1: {userId: 'userB', column: '1', age: 29} } }) return client.newScanBuilder('user') .setStartKey({userId: 'userA', column: '2'}) .execute() .then(function (data) { var result = data.result test.deepEqual(result[0], {userId: 'userA', column: '3', age: 29}) test.deepEqual(result[1], {userId: 'userB', column: '1', age: 29}) }) }) // test querying secondary index using greater than condition builder.add(function testQueryOnSecondaryIndexGreaterThan(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: 3, age: 27}, 2: {userId: 'userA', column: 2, age: 28}, 3: {userId: 'userA', column: 5, age: 3000}, 4: {userId: 'userA', column: 4, age: 29} }, 'userB': { 1: {userId: 'userB', column: '1', age: 29} } }) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThan('age', 28) .execute() .then(function (data) { test.equal(data.result.length, 2, '2 results should be returned') test.equal(data.result[0].age, 29, 'First entry should be 29') test.equal(data.result[1].age, 3000, 'Second entry should be 3000') }) }) // test querying secondary index using less than condition builder.add(function testQueryOnSecondaryIndexLessThan(test) { db.getTable('user').setData({ 'userA': { 0: {userId: 'userA', column: 0, age: 27}, 1: {userId: 'userA', column: 3, age: 26}, 2: {userId: 'userA', column: 2, age: 28}, 3: {userId: 'userA', column: 1, age: 30}, 4: {userId: 'userA', column: 4, age: 29} }, 'userB': { 1: {userId: 'userB', column: '1', age: 29} } }) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexLessThan('age', 29) .scanBackward() .execute() .then(function (data) { test.equal(data.result[0].age, 28, 'First entry should be 28') test.equal(data.result[1].age, 27, 'Second entry should be 27') test.equal(data.result[2].age, 26, 'Third entry should be 26') test.equal(data.result.length, 3, '3 results should be returned') }) }) // test querying secondary index using less than equals condition builder.add(function testQueryOnSecondaryIndexLessThanEquals(test) { db.getTable('user').setData({ 'userA': { 0: {userId: 'userA', column: 0, age: 27}, 1: {userId: 'userA', column: 3, age: 26}, 2: {userId: 'userA', column: 2, age: 28}, 3: {userId: 'userA', column: 1, age: 29}, 4: {userId: 'userA', column: 4, age: 30} }, 'userB': { 1: {userId: 'userB', column: '1', age: 29} } }) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexLessThanEqual('age', 29) .scanBackward() .execute() .then(function (data) { test.equal(data.result[0].age, 29, 'First entry should be 29') test.equal(data.result[1].age, 28, 'Second entry should be 28') test.equal(data.result[2].age, 27, 'Third entry should be 27') test.equal(data.result[3].age, 26, 'Fourth entry should be 26') test.equal(data.result.length, 4, '4 results should be returned') }) }) // test querying secondary index using equals condition builder.add(function testQueryOnSecondaryIndexEquals(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: 3, age: 27}, 2: {userId: 'userA', column: 2, age: 28}, 3: {userId: 'userA', column: 1, age: 29}, 4: {userId: 'userA', column: 4, age: 30} }, 'userB': { 1: {userId: 'userB', column: '1', age: 29} } }) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexEqual('age', 28) .execute() .then(function (data) { test.equal(data.result[0].age, 28, 'Age should match 28') test.equal(data.result.length, 1, '1 result should be returned') }) }) // test querying secondary index using equals condition builder.add(function testQueryOnGlobalSecondaryIndexEquals(test) { db.getTable('user').setGsiDefinitions([ { hash: { name: 'age', type: 'S' }, range: { name: 'userId', type: 'S' } } ]) db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: 3, age: 27}, 2: {userId: 'userA', column: 2, age: 28}, 3: {userId: 'userA', column: 1, age: 29}, 4: {userId: 'userA', column: 4, age: 30} }, 'userB': { 1: {userId: 'userB', column: '1', age: 29} } }) return client.newQueryBuilder('user') .setHashKey('age', 27) .setIndexName('age-userId-index') .indexBeginsWith('userId', 'user') .execute() .then(function (data) { test.equal(data.result[0].age, 27, 'Age should match 28') test.equal(data.result.length, 1, '1 result should be returned') }) }) // test querying secondary index that have repeated column values // this is a test for a regression where fake dynamo may reinsert values // when the keys match again builder.add(function testQueryOnMultipleIndexes(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27}, 2: {userId: 'userA', column: '4', age: 28}, 3: {userId: 'userA', column: '3', age: 29}, 4: {userId: 'userA', column: '2', age: 30}, 5: {userId: 'userA', column: '5', age: 30} }, 'userB': { 1: {userId: 'userB', column: '1', age: 29} } }) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThanEqual('age', 28) .execute() .then(function (data) { test.equal(data.result[0].age, 28, 'Age should match 28') test.equal(data.result[1].age, 29, 'Age should match 29') test.equal(data.result[2].age, 30, 'Age should match 30') test.equal(data.result[3].age, 30, 'Age should match 30') test.equal(data.result.length, 4, '4 results should be returned') }) }) /** * Basic test for Global Secondary Index support * Does not currently support testing for existence of GSIs */ builder.add(function testQueryOnGlobalSecondaryIndexes(test) { db.getTable('user').setGsiDefinitions([ { hash: { name: 'age', type: 'S' }, range: { name: 'height', type: 'N' } } ]) db.getTable('user').setHashKey('userId', 'S') .setData({ 'userA': { 1: {userId: 'userA', column: '3', age: 27, height: 160}, 2: {userId: 'userA', column: '2', age: 28, height: 170}, 3: {userId: 'userA', column: '1', age: 28, height: 180}, 4: {userId: 'userA', column: '4', age: 29, height: 150} }, 'userB': { 1: {userId: 'userB', column: '3', age: 27, height: 200}, 2: {userId: 'userB', column: '2', age: 28, height: 170}, 3: {userId: 'userB', column: '1', age: 28, height: 178}, 4: {userId: 'userB', column: '4', age: 29, height: 190} } }) return client.newQueryBuilder('user') .setHashKey('age', 28) // It is important that the index name has three or more terms (separated by // '-'), it's a DynamoDB index naming convention, and it is how we know that it // is a GSI query .setIndexName('age-height-gsi') .indexGreaterThan('height', 175) .execute() .then(function (data) { // results from userB test.equal(data.result[0].age, 28, 'Age should match 28') test.equal(data.result[0].height, 178, 'Height should match 178') // results from userA test.equal(data.result[1].age, 28, 'Age should match 28') test.equal(data.result[1].height, 180, 'Height should match 180') test.equal(data.result.length, 2, '2 results should be returned') }) }) // Ensure results are sorted by range key, even when there is no condition // on the range key builder.add(function testQueryOnGlobalSecondaryIndexWithoutCondition(test) { db.getTable('user').setGsiDefinitions([ { hash: { name: 'userId', type: 'S' }, range: { name: 'height', type: 'N' } } ]) db.getTable('user') .setHashKey('userId', 'S') .setData({ 'userA': { 0: {userId: 'userA', column: '0', age: 26, height: 188}, 1: {userId: 'userA', column: '1', age: 27, height: 160}, 2: {userId: 'userA', column: '2', age: 28, height: 170}, 3: {userId: 'userA', column: '3', age: 28, height: 180}, 4: {userId: 'userA', column: '4', age: 29, height: 150} } }) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') // It is important that the index name has three or more terms (separated by // '-'), it's a DynamoDB index naming convention, and it is how we know that it // is a GSI query .setIndexName('userId-height-gsi') .scanBackward() .execute() .then(function (data) { var heights = data.result.map(function (r) { return r.height }) test.deepEqual(heights, [188, 180, 170, 160, 150]) }) }) builder.add(function testQueryWithLimit(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '3', age: 27}, 2: {userId: 'userA', column: '2', age: 28}, 3: {userId: 'userA', column: '1', age: 29}, 4: {userId: 'userA', column: '4', age: 30} } }) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThanEqual('age', 28) .setLimit(2) .execute() .then(function (data) { test.equal(data.result[0].age, 28) test.equal(data.result[1].age, 29) test.equal(data.result.length, 2) test.deepEqual(data.LastEvaluatedKey, {userId: 'userA', column: '1'}) return client.newQueryBuilder('user') .setStartKey(data.LastEvaluatedKey) .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThanEqual('age', 28) .execute() }) .then(function (data) { test.equal(data.result[0].age, 30, 'Age should match 30') test.equal(data.result.length, 1, '1 result should be returned') }) }) builder.add(function testQueryWithNext(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '3', age: 27}, 2: {userId: 'userA', column: '2', age: 28}, 3: {userId: 'userA', column: '1', age: 29}, 4: {userId: 'userA', column: '4', age: 30} } }) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThanEqual('age', 25) .scanBackward() .setLimit(3) .execute() .then(function (data) { test.equal(data.result.length, 3) test.equal(data.result[0].age, 30) test.equal(data.result[1].age, 29) test.equal(data.result[2].age, 28) test.ok(data.hasNext()) return data.next() }) .then(function (data) { test.equal(data.result.length, 1) test.equal(data.result[0].age, 27) test.ok(!data.hasNext()) }) }) builder.add(function testQueryWithLimitBackwards(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '3', age: 27}, 2: {userId: 'userA', column: '2', age: 28}, 3: {userId: 'userA', column: '1', age: 29}, 4: {userId: 'userA', column: '4', age: 30} } }) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .scanBackward() .indexLessThan('age', 30) .setLimit(2) .execute() .then(function (data) { test.equal(data.result.length, 2) test.equal(data.result[0].age, 29) test.equal(data.result[1].age, 28) test.deepEqual(data.LastEvaluatedKey, {userId: 'userA', column: '2'}) return client.newQueryBuilder('user') .setStartKey(data.LastEvaluatedKey) .setHashKey('userId', 'userA') .setIndexName('age-index') .indexLessThan('age', 30) .scanBackward() .execute() }) .then(function (data) { test.equal(data.result[0].age, 27) test.equal(data.result.length, 1) }) }) builder.add(function testQueryWithMaxResultSize(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27}, 2: {userId: 'userA', column: '4', age: 28}, 3: {userId: 'userA', column: '3', age: 29}, 4: {userId: 'userA', column: '2', age: 30} } }) db.getTable('user').setMaxResultSetSize(1) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThanEqual('age', 28) .setLimit(2) .execute() .then(function (data) { test.equal(data.result.length, 1, '1 result should be returned') test.equal(data.result[0].age, 28, 'Age should match 28') test.deepEqual(data.LastEvaluatedKey, {userId: 'userA', column: '4'}) }) }) builder.add(function testDescribeTable(test) { return client.describeTable('user') .execute() .then(function (data) { var tableDescription = data.Table var attributes = tableDescription.AttributeDefinitions var keySchema = tableDescription.KeySchema test.ok(tableDescription, 'Table description should exist.') test.equal(attributes.length, 2, 'Table should have 2 attributes in AttributeDefinitions (keys).') test.equal(keySchema.length, 2, 'Table should have 2 attributes in KeySchema (keys).') test.equal(tableDescription.TableName, 'user', 'Table name should be user.') test.equal(tableDescription.TableStatus, 'ACTIVE', 'Table status should be active.') // deep check attributes for (var i = 0; i < attributes.length; i++) { var attribute = attributes[i] if (attribute.AttributeName == 'userId') { test.deepEqual(attribute, {AttributeName: 'userId', AttributeType: 'S'}) } else if (attribute.AttributeName == 'column') { test.deepEqual(attribute, {AttributeName: 'column', AttributeType: 'S'}) } } // deep check key schemas for (i = 0; i < keySchema.length; i++) { var key = keySchema[i] if (key.AttributeName == 'userId') { test.deepEqual(key, {AttributeName: 'userId', KeyType: 'HASH'}) } else if (key.AttributeName == 'column') { test.deepEqual(key, {AttributeName: 'column', KeyType: 'RANGE'}) } } test.expect(9) // make sure the tests in conditionals ran }) }) builder.add(function testQueryFiltering(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27, name: 'Ringo'}, 2: {userId: 'userA', column: '4', age: 28, name: 'George'}, 3: {userId: 'userA', column: '3', age: 29, name: 'John'}, 4: {userId: 'userA', column: '2', age: 30, name: 'Paul'} } }) var filter = client.newConditionBuilder() .filterAttributeBeginsWith('name', 'Geo') return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThanEqual('age', 28) .withFilter(filter) .execute() .then(function (data) { test.deepEqual(data.result[0], {userId: 'userA', column: '4', age: 28, name: 'George'}) test.equal(data.result.length, 1) }) }) builder.add(function testQueryFilterNotNull(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27, name: 'Ringo'}, 2: {userId: 'userA', column: '4', age: 28, name: 'George'}, 3: {userId: 'userA', column: '3', age: 29}, 4: {userId: 'userA', column: '2', age: 30} } }) var filter = client.newConditionBuilder() .filterAttributeNotNull('name') return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThanEqual('age', 28) .withFilter(filter) .execute() .then(function (data) { test.deepEqual(data.result[0], {userId: 'userA', column: '4', age: 28, name: 'George'}) test.equal(data.result.length, 1) }) }) builder.add(function testBooleanQueryFilter(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27, isHappy: true}, 2: {userId: 'userA', column: '4', age: 28, isHappy: false}, 3: {userId: 'userA', column: '3', age: 29, isHappy: true}, 4: {userId: 'userA', column: '2', age: 30} } }) var filter = client.newConditionBuilder() .filterAttributeEquals('isHappy', true) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThanEqual('age', 27) .withFilter(filter) .execute() .then(function (data) { test.equal(data.result.length, 2) test.deepEqual(data.result[0], {userId: 'userA', column: '1', age: 27, isHappy: true}) test.deepEqual(data.result[1], {userId: 'userA', column: '3', age: 29, isHappy: true}) }) }) builder.add(function testQueryFilterWithPartitionKeyThrowsError(test) { var filter = client.newConditionBuilder() .filterAttributeNotEquals('userId', 'Me') return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThanEqual('age', 28) .withFilter(filter) .execute() .then(function () { test.fail('Expected validation exception') }) .fail(function (e) { if (!client.isValidationError(e)) throw e }) }) builder.add(function testQueryFilterWithIndexKeyThrowsError(test) { var filter = client.newConditionBuilder() .filterAttributeNotEquals('age', 1) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .setIndexName('age-index') .indexGreaterThan('age', 15) .withFilter(filter) .execute() .then(function () { test.fail('Expected validation exception') }) .fail(function (e) { if (!client.isValidationError(e)) throw e }) }) builder.add(function testQueryFilterWithRangeKeyThrowsError(test) { var filter = client.newConditionBuilder() .filterAttributeLessThan('createdAt', 30) return client.newQueryBuilder('cookie') .setHashKey('cookieId', 'CookieA') .indexGreaterThan('createdAt', 15) .withFilter(filter) .execute() .then(function () { test.fail('Expected validation exception') }) .fail(function (e) { if (!client.isValidationError(e)) throw e }) }) builder.add(function testQueryFilterOnGsiIndexThrowsError(test) { db.getTable('cookie').setGsiDefinitions([ { hash: { name: 'cookieType', type: 'S' }, range: { name: 'orderedAt', type: 'N' } } ]) var filter = client.newConditionBuilder() .filterAttributeLessThan('orderedAt', 30) return client.newQueryBuilder('cookie') .setHashKey('cookieType', 'Oreo') .setIndexName('cookieType-orderedAt-gsi') .indexGreaterThanEqual('orderedAt', 28) .withFilter(filter) .execute() .then(function () { test.fail('Expected validation exception') }) .fail(function (e) { if (!client.isValidationError(e)) throw e }) }) builder.add(function testQueryFilterOnGsiWithNoRangeWorks(test) { db.getTable('cookie').setGsiDefinitions([ { hash: { name: 'cookieType', type: 'S' } } ]) db.getTable('cookie').setData({ 'cookieA': { 1: {cookieId: 'cookieA', column: '1', createdAt: 1, orderedAt: 27, cookieType: 'Oreo'}, 2: {cookieId: 'cookieA', column: '2', createdAt: 2, orderedAt: 29, cookieType: 'Oreo'} }, 'cookieB': { 1: {cookieId: 'cookieB', column: '1', createdAt: 2, orderedAt: 12, cookieType: 'Snickerdoodle'}, 2: {cookieId: 'cookieB', column: '2', createdAt: 1, orderedAt: 14, cookieType: 'Snickerdoodle'} } }) var filter = client.newConditionBuilder() .filterAttributeLessThan('orderedAt', 28) return client.newQueryBuilder('cookie') .setHashKey('cookieType', 'Oreo') .setIndexName('index-cookieType-gsi') .withFilter(filter) .execute() .then(function (data) { test.equal(data.result.length, 1) test.deepEqual(data.result[0], {cookieId: 'cookieA', column: '1', createdAt: 1, orderedAt: 27, cookieType: 'Oreo'}) }) }) builder.add(function testQueryFilterWithRangeWithIndexDoesNotThrowsError(test) { var filter = client.newConditionBuilder() .filterAttributeLessThan('createdAt', 30) return client.newQueryBuilder('cookie') .setHashKey('cookieId', 'CookieA') .setIndexName('age-index') .indexGreaterThanEqual('age', 28) .withFilter(filter) .execute() .then(function () { test.ok(true) }) .fail(function () { test.fail('Expected not to fail') }) }) builder.add(function testDeleteItem(test) { var conditions = client.newConditionBuilder() .expectAttributeEquals('userId', 'userA') return client.newUpdateBuilder('user') .setHashKey('userId', 'userA') .setRangeKey('column', '@') .withCondition(conditions) .deleteAttribute('age') .execute() .then(function (data) { test.equal(data.result.column, '@') test.equal(data.result.age, undefined) return client.getItem('user') .setHashKey('userId', 'userA') .setRangeKey('column', '@') .execute() }) .then(function (data) { test.equal(data.result.age, undefined) }) }) builder.add(function testPutAttributeNonExisting(test) { return client.newUpdateBuilder('user') .setHashKey('userId', 'userB') .setRangeKey('column', '@') .enableUpsert() .putAttribute('age', 30) .putAttribute('height', 72) .execute() .then(function (data) { test.equal(data.result.age, 30, 'result age should be 30') test.equal(data.result.height, 72, 'result height should be 72') return client.getItem('user') .setHashKey('userId', 'userB') .setRangeKey('column', '@') .execute() }) .then(function (data) { test.equal(data.result.age, 30, 'result age should be 30') test.equal(data.result.height, 72, 'result height should be 72') }) }) builder.add(function testDeleteItemFromSet(test) { var conditions = client.newConditionBuilder() .expectAttributeEquals('userId', 'userA') return client.newUpdateBuilder('user') .setHashKey('userId', 'userA') .setRangeKey('column', '@') .withCondition(conditions) .deleteFromAttribute('luckyNumbers', [3]) .execute() .then(function () { return client.getItem('user') .setHashKey('userId', 'userA') .setRangeKey('column', '@') .execute() }) .then(function (data) { data.result.luckyNumbers.sort() test.deepEqual(data.result.luckyNumbers, [1, 5]) }) }) builder.add(function testLongKey(test) { // Create a string 2^10 chars long. var str = '.' for (var i = 0; i < 10; i++) { str = str + str } return client.getItem('user') .setHashKey('userId', 'userA') .setRangeKey('column', str) .execute() .then(function () { test.fail('Expected validation exception') }) .fail(function (e) { if (!client.isValidationError(e)) throw e }) }) builder.add(function testQueryFilterIn(test) { db.getTable('user').setData({ 'userA': { 1: {userId: 'userA', column: '1', age: 27, name: 'Ringo'}, 2: {userId: 'userA', column: '4', age: 28, name: 'George'}, 3: {userId: 'userA', column: '3', age: 29, name: 'Paul'}, 4: {userId: 'userA', column: '2', age: 30} } }) var filter = client.newConditionBuilder() .filterAttributeIn('name', ['Ringo', 'George']) return client.newQueryBuilder('user') .setHashKey('userId', 'userA') .withFilter(filter) .execute() .then(function (data) { test.deepEqual(['George', 'Ringo'], data.result.map(function (r) { return r.name }).sort()) }) }) builder.add(function testAbsentConditionUpdateSuccess(test) { var conditions = client.newConditionBuilder() .expectAttributeAbsent('userId') return client.newUpdateBuilder('user') .setHashKey('userId', 'userNew') .setRangeKey('column', '@') .withCondition(conditions) .execute() .then(function () { return client.getItem('user') .setHashKey('userId', 'userNew') .setRangeKey('column', '@') .execute() }) .then(function (data) { test.deepEqual({userId: 'userNew', column: '@'}, data.result) }) }) builder.add(function testAbsentConditionUpdateFail(test) { var conditions = client.newConditionBuilder() .expectAttributeAbsent('userId') return client.newUpdateBuilder('user') .setHashKey('userId', 'userA') .setRangeKey('column', '@') .withCondition(conditions) .execute() .then(function () { test.fail('Expected error') }) .fail(client.throwUnlessConditionalError) })