UNPKG

dynamoose

Version:

Dynamoose is a modeling tool for Amazon's DynamoDB (inspired by Mongoose)

1,448 lines (1,232 loc) 113 kB
'use strict'; var dynamoose = require('../'); dynamoose.AWS.config.update({ accessKeyId: 'AKID', secretAccessKey: 'SECRET', region: 'us-east-1' }); dynamoose.local(); var should = require('should'); var CatsFixture = require('./fixtures/Cats'); var Cats = {}; var ONE_YEAR = 365*24*60*60; // 1 years in seconds var NINE_YEARS = 9*ONE_YEAR; // 9 years in seconds describe('Model', function (){ this.timeout(15000); before(function(done) { this.timeout(12000); dynamoose.setDefaults({ prefix: 'test-', suffix: '-db' }); Cats = CatsFixture(dynamoose); done(); }); after(function (done) { delete dynamoose.models['test-Cat-db']; done(); }); it('Create simple model', function (done) { this.timeout(12000); Cats.Cat.should.have.property('$__'); Cats.Cat.should.have.property('name'); // Older node doesn't support Function.name changes if (Object.getOwnPropertyDescriptor(Function, 'name').configurable) { Cats.Cat.name.should.eql('Model-test-Cat-db'); } Cats.Cat.$__.name.should.eql('test-Cat-db'); Cats.Cat.$__.options.should.have.property('create', true); var schema = Cats.Cat.$__.schema; should.exist(schema); schema.attributes.id.type.name.should.eql('number'); should(schema.attributes.id.isSet).not.be.ok; should.not.exist(schema.attributes.id.default); should.exist(schema.attributes.id.validator); should(schema.attributes.id.required).not.be.ok; schema.attributes.name.type.name.should.eql('string'); schema.attributes.name.isSet.should.not.be.ok; should.not.exist(schema.attributes.name.default); should.not.exist(schema.attributes.name.validator); should(schema.attributes.name.required).not.be.ok; schema.hashKey.should.equal(schema.attributes.id); // should be same object should.not.exist(schema.rangeKey); var kitten = new Cats.Cat( { id: 1, name: 'Fluffy', vet:{name:'theVet', address:'12 somewhere'}, ears:[{name:'left'}, {name:'right'}], legs: ['front right', 'front left', 'back right', 'back left'], more: {favorites: {food: 'fish'}}, array: [{one: '1'}], validated: 'valid' } ); kitten.id.should.eql(1); kitten.name.should.eql('Fluffy'); var dynamoObj = schema.toDynamo(kitten); dynamoObj.should.eql( { ears: { L: [ { M: { name: { S: 'left' } } }, { M: { name: { S: 'right' } } } ] }, id: { N: '1' }, name: { S: 'Fluffy' }, vet: { M: { address: { S: '12 somewhere' }, name: { S: 'theVet' } } }, legs: { SS: ['front right', 'front left', 'back right', 'back left']}, more: { S: '{"favorites":{"food":"fish"}}' }, array: { S: '[{"one":"1"}]' }, validated: { S: 'valid' } }); kitten.save(done); }); it('Create simple model with range key', function () { Cats.Cat2.should.have.property('name'); // Older node doesn't support Function.name changes if (Object.getOwnPropertyDescriptor(Function, 'name').configurable) { Cats.Cat2.name.should.eql('Model-test-Cat2-db'); } Cats.Cat2.should.have.property('$__'); Cats.Cat2.$__.name.should.eql('test-Cat2-db'); Cats.Cat2.$__.options.should.have.property('create', true); var schema = Cats.Cat2.$__.schema; should.exist(schema); schema.attributes.ownerId.type.name.should.eql('number'); should(schema.attributes.ownerId.isSet).not.be.ok; should.not.exist(schema.attributes.ownerId.default); should.not.exist(schema.attributes.ownerId.validator); should(schema.attributes.ownerId.required).not.be.ok; schema.attributes.name.type.name.should.eql('string'); schema.attributes.name.isSet.should.not.be.ok; should.not.exist(schema.attributes.name.default); should.not.exist(schema.attributes.name.validator); should(schema.attributes.name.required).not.be.ok; schema.hashKey.should.equal(schema.attributes.ownerId); // should be same object schema.rangeKey.should.equal(schema.attributes.name); }); it('Create simple model with unnamed attributes', function (done) { this.timeout(12000); Cats.Cat5.should.have.property('name'); // Older node doesn't support Function.name changes if (Object.getOwnPropertyDescriptor(Function, 'name').configurable) { Cats.Cat5.name.should.eql('Model-test-Cat5-db'); } Cats.Cat5.should.have.property('$__'); Cats.Cat5.$__.name.should.eql('test-Cat5-db'); Cats.Cat5.$__.options.should.have.property('saveUnknown', true); var schema = Cats.Cat5.$__.schema; should.exist(schema); schema.attributes.id.type.name.should.eql('number'); should(schema.attributes.id.isSet).not.be.ok; should.exist(schema.attributes.id.default); should.exist(schema.attributes.id.validator); should(schema.attributes.id.required).not.be.ok; schema.attributes.name.type.name.should.eql('string'); schema.attributes.name.isSet.should.not.be.ok; should.exist(schema.attributes.name.default); should.not.exist(schema.attributes.name.validator); should(schema.attributes.name.required).be.ok; schema.hashKey.should.equal(schema.attributes.id); // should be same object should.not.exist(schema.rangeKey); var kitten = new Cats.Cat5( { id: 2, name: 'Fluffy', owner: 'Someone', unnamedInt: 1, unnamedInt0: 0, unnamedBooleanFalse: false, unnamedBooleanTrue: true, unnamedString: 'unnamed', // Attributes with empty values. DynamoDB won't store empty values // so the return value of toDynamo() should exclude these attributes. unnamedUndefined: undefined, unnamedNull: null, unnamedEmptyString: '', unnamedNumberNaN: NaN, } ); kitten.id.should.eql(2); kitten.name.should.eql('Fluffy'); var dynamoObj = schema.toDynamo(kitten); dynamoObj.should.eql( { id: { N: '2' }, name: { S: 'Fluffy' }, owner: { S: 'Someone' }, unnamedInt: { N: '1' }, unnamedInt0: { N: '0' }, unnamedBooleanFalse: { BOOL: false }, unnamedBooleanTrue: { BOOL: true }, unnamedString: { S: 'unnamed' }, }); kitten.save(done); }); it('Create returnRequest option', function (done) { Cats.ExpiringCat.create({ name: 'Leo' }, {returnRequest: true}) .then(function (request) { request.should.exist; request.TableName.should.eql("test-ExpiringCat-db"); request.Item.name.should.eql({S: "Leo"}); done(); }) .catch(done); }); it('Should support useDocumentTypes and useNativeBooleans being false', function(done) { this.timeout(12000); var kitten = new Cats.Cat10({ id: 2, isHappy: true, parents: ["Max", "Leah"], details: { playful: true, thirsty: false, tired: false } }); kitten.id.should.eql(2); kitten.isHappy.should.eql(true); kitten.parents.should.eql(["Max", "Leah"]); kitten.details.should.eql({ playful: true, thirsty: false, tired: false }); kitten.save(function(err, kitten) { kitten.id.should.eql(2); kitten.isHappy.should.eql(true); kitten.parents.should.eql(["Max", "Leah"]); kitten.details.should.eql({ playful: true, thirsty: false, tired: false }); Cats.Cat10.get(2, function(err, kitten) { kitten.id.should.eql(2); kitten.isHappy.should.eql(true); kitten.parents.should.eql(["Max", "Leah"]); kitten.details.should.eql({ playful: true, thirsty: false, tired: false }); done(); }); }); }); it('Create complex model with unnamed attributes', function (done) { this.timeout(12000); Cats.Cat1.should.have.property('name'); // Older node doesn't support Function.name changes if (Object.getOwnPropertyDescriptor(Function, 'name').configurable) { Cats.Cat1.name.should.eql('Model-test-Cat1-db'); } Cats.Cat1.should.have.property('$__'); Cats.Cat1.$__.name.should.eql('test-Cat1-db'); Cats.Cat1.$__.options.should.have.property('saveUnknown', true); var schema = Cats.Cat1.$__.schema; should.exist(schema); schema.attributes.id.type.name.should.eql('number'); should(schema.attributes.id.isSet).not.be.ok; should.exist(schema.attributes.id.default); should.exist(schema.attributes.id.validator); should(schema.attributes.id.required).not.be.ok; schema.attributes.name.type.name.should.eql('string'); schema.attributes.name.isSet.should.not.be.ok; should.exist(schema.attributes.name.default); should.not.exist(schema.attributes.name.validator); should(schema.attributes.name.required).be.ok; schema.hashKey.should.equal(schema.attributes.id); // should be same object should.not.exist(schema.rangeKey); var kitten = new Cats.Cat1( { id: 2, name: 'Fluffy', owner: 'Someone', children: { "mittens" : { name : "mittens", age: 1 }, "puddles" : { name : "puddles", age: 2 } }, characteristics: ['cute', 'fuzzy'] } ); kitten.id.should.eql(2); kitten.name.should.eql('Fluffy'); var dynamoObj = schema.toDynamo(kitten); dynamoObj.should.eql( { id: {N: '2'}, name: {S: 'Fluffy'}, owner: {S: 'Someone'}, children: { M: { "mittens": {M: {"name": {S: "mittens"}, "age": {N: '1'}}}, "puddles": {M: {"name": {S: "puddles"}, "age": {N: '2'}}} } }, characteristics: {L: [{S: 'cute'}, {S: 'fuzzy'}]} }); kitten.save(done); }); it('Get item for model with unnamed attributes', function (done) { Cats.Cat5.get(2, function(err, model) { should.not.exist(err); should.exist(model); model.should.have.property('id', 2); model.should.have.property('name', 'Fluffy'); model.should.have.property('owner', 'Someone'); model.should.have.property('unnamedInt', 1); model.should.have.property('unnamedString', 'unnamed'); model.should.have.property('$__'); done(); }); }); it('Get item for model', function (done) { Cats.Cat.get(1, function(err, model) { should.not.exist(err); should.exist(model); model.should.have.property('id', 1); model.should.have.property('name', 'Fluffy'); model.should.have.property('vet', { address: '12 somewhere', name: 'theVet' }); model.should.have.property('$__'); done(); }); }); it('Get item for model with falsy keys', function (done) { Cats.Cat8.create({id: 0, age: 0}) .then(function () { return Cats.Cat8.get({id: 0, age: 0}); }) .then(function (falsyCat) { falsyCat.should.have.property('id', 0); falsyCat.should.have.property('age', 0); done(); }) .catch(done); }); it('Get item with invalid key', function (done) { Cats.Cat.get(0, function(err, model) { should.exist(err); err.name.should.equal('ValidationError'); should.not.exist(model); done(); }); }); it('Get and Update corrupted item', function (done) { // create corrupted item var req = dynamoose.ddb().putItem({ Item: { "id": { N: "7" }, "isHappy": { // this is the data corruption S: "tue" } }, ReturnConsumedCapacity: "TOTAL", TableName: Cats.Cat7.$__.table.name }); req.promise().then(function(){ return Cats.Cat7.get(7); }).catch(function(err){ should.exist(err.message); }).then(function(){ return Cats.Cat7.update(7, { name : 'my favorite cat'}); }).catch(function(err){ should.exist(err.message); done(); }); }); it('Get returnRequest option', function (done) { Cats.Cat.get(1, {returnRequest: true}, function(err, request) { should.not.exist(err); should.exist(request); request.TableName.should.eql("test-Cat-db"); request.Key.should.eql({id: {N: '1'}}); done(); }); }); it('Save existing item', function (done) { Cats.Cat.get(1, function(err, model) { should.not.exist(err); should.exist(model); model.name.should.eql('Fluffy'); model.name = 'Bad Cat'; model.vet.name = 'Tough Vet'; model.ears[0].name = 'right'; model.save(function (err) { should.not.exist(err); Cats.Cat.get({id: 1}, {consistent: true}, function(err, badCat) { should.not.exist(err); badCat.name.should.eql('Bad Cat'); badCat.vet.name.should.eql('Tough Vet'); badCat.ears[0].name.should.eql('right'); badCat.ears[1].name.should.eql('right'); done(); }); }); }); }); it('Save existing item without defining updating timestamps', function (done) { var myCat = new Cats.Cat9({ id: 1, name: 'Fluffy', vet:{name:'theVet', address:'12 somewhere'}, ears:[{name:'left'}, {name:'right'}], legs: ['front right', 'front left', 'back right', 'back left'], more: {favorites: {food: 'fish'}}, array: [{one: '1'}], validated: 'valid' }); myCat.save(function(err, theSavedCat1) { var expectedCreatedAt = theSavedCat1.createdAt; var expectedUpdatedAt = theSavedCat1.updatedAt; theSavedCat1.name = "FluffyB"; setTimeout(function() { theSavedCat1.save(function () { Cats.Cat9.get(1, function(err, realCat) { realCat.name.should.eql("FluffyB"); realCat.createdAt.should.eql(expectedCreatedAt); // createdAt should be the same as before realCat.updatedAt.should.not.eql(expectedUpdatedAt); // updatedAt should be different than before done(); }); }); }, 1000); }); }); it('Save existing item with updating timestamps', function (done) { var myCat = new Cats.Cat9({ id: 1, name: 'Fluffy', vet:{name:'theVet', address:'12 somewhere'}, ears:[{name:'left'}, {name:'right'}], legs: ['front right', 'front left', 'back right', 'back left'], more: {favorites: {food: 'fish'}}, array: [{one: '1'}], validated: 'valid' }); myCat.save(function(err, theSavedCat1) { var expectedCreatedAt = theSavedCat1.createdAt; var expectedUpdatedAt = theSavedCat1.updatedAt; myCat.name = "FluffyB"; setTimeout(function() { myCat.save({updateTimestamps: true}, function () { Cats.Cat9.get(1, function(err, realCat) { realCat.name.should.eql("FluffyB"); realCat.createdAt.should.eql(expectedCreatedAt); // createdAt should be the same as before realCat.updatedAt.should.not.eql(expectedUpdatedAt); // updatedAt should be different than before done(); }); }); }, 1000); }); }); it('Save existing item without updating timestamps', function (done) { var myCat = new Cats.Cat9({ id: 1, name: 'Fluffy', vet:{name:'theVet', address:'12 somewhere'}, ears:[{name:'left'}, {name:'right'}], legs: ['front right', 'front left', 'back right', 'back left'], more: {favorites: {food: 'fish'}}, array: [{one: '1'}], validated: 'valid' }); myCat.save(function(err, theSavedCat1) { var expectedCreatedAt = theSavedCat1.createdAt; var expectedUpdatedAt = theSavedCat1.updatedAt; myCat.name = "FluffyB"; setTimeout(function() { myCat.save({updateTimestamps: false}, function () { Cats.Cat9.get(1, function(err, realCat) { realCat.name.should.eql("FluffyB"); realCat.createdAt.should.eql(expectedCreatedAt); // createdAt should be the same as before realCat.updatedAt.should.eql(expectedUpdatedAt); // updatedAt should be the same as before done(); }); }); }, 1000); }); }); it('Save existing item with updating expires', function (done) { var myCat = new Cats.Cat11({ id: 1, name: 'Fluffy', vet:{name:'theVet', address:'12 somewhere'}, ears:[{name:'left'}, {name:'right'}], legs: ['front right', 'front left', 'back right', 'back left'], more: {favorites: {food: 'fish'}}, array: [{one: '1'}], validated: 'valid' }); myCat.save(function(err, theSavedCat1) { var expectedExpires = theSavedCat1.expires; myCat.name = "FluffyB"; setTimeout(function() { myCat.save({updateExpires: true}, function () { Cats.Cat11.get(1, function(err, realCat) { realCat.name.should.eql("FluffyB"); realCat.expires.should.not.eql(expectedExpires); // expires should be different than before done(); }); }); }, 1000); }); }); it('Save existing item without updating expires', function (done) { var myCat = new Cats.Cat11({ id: 2, name: 'Fluffy', vet:{name:'theVet', address:'12 somewhere'}, ears:[{name:'left'}, {name:'right'}], legs: ['front right', 'front left', 'back right', 'back left'], more: {favorites: {food: 'fish'}}, array: [{one: '1'}], validated: 'valid' }); myCat.save(function(err, theSavedCat1) { var expectedExpires = theSavedCat1.expires; myCat.name = "FluffyB"; setTimeout(function() { myCat.save({updateExpires: false}, function () { Cats.Cat11.get(2, function(err, realCat) { realCat.name.should.eql("FluffyB"); realCat.expires.should.eql(expectedExpires); // expires should be the same as before done(); }); }); }, 1000); }); }); it('Save existing item without updating expires (default)', function (done) { var myCat = new Cats.Cat11({ id: 3, name: 'Fluffy', vet:{name:'theVet', address:'12 somewhere'}, ears:[{name:'left'}, {name:'right'}], legs: ['front right', 'front left', 'back right', 'back left'], more: {favorites: {food: 'fish'}}, array: [{one: '1'}], validated: 'valid' }); myCat.save(function(err, theSavedCat1) { var expectedExpires = theSavedCat1.expires; myCat.name = "FluffyB"; setTimeout(function() { myCat.save(function () { Cats.Cat11.get(3, function(err, realCat) { realCat.name.should.eql("FluffyB"); realCat.expires.should.eql(expectedExpires); // expires should be the same as before done(); }); }); }, 1000); }); }); it('Save existing item with a false condition', function (done) { Cats.Cat.get(1, function(err, model) { should.not.exist(err); should.exist(model); model.name.should.eql('Bad Cat'); model.name = 'Whiskers'; model.save({ condition: '#name = :name', conditionNames: { name: 'name' }, conditionValues: { name: 'Muffin' } }, function (err) { should.exist(err); err.code.should.eql('ConditionalCheckFailedException'); Cats.Cat.get({id: 1}, {consistent: true}, function(err, badCat) { should.not.exist(err); badCat.name.should.eql('Bad Cat'); done(); }); }); }); }); it('Save existing item with a true condition', function (done) { Cats.Cat.get(1, function(err, model) { should.not.exist(err); should.exist(model); model.name.should.eql('Bad Cat'); model.name = 'Whiskers'; model.save({ condition: '#name = :name', conditionNames: { name: 'name' }, conditionValues: { name: 'Bad Cat' } }, function (err) { should.not.exist(err); Cats.Cat.get({id: 1}, {consistent: true}, function(err, whiskers) { should.not.exist(err); whiskers.name.should.eql('Whiskers'); done(); }); }); }); }); it('Save with a pre hook', function (done) { var flag = false; Cats.Cat.pre('save', function (next) { flag = true; next(); }); Cats.Cat.get(1, function(err, model) { should.not.exist(err); should.exist(model); model.name.should.eql('Whiskers'); model.name = 'Fluffy'; model.vet.name = 'Nice Guy'; model.save(function (err) { should.not.exist(err); Cats.Cat.get({id: 1}, {consistent: true}, function(err, badCat) { should.not.exist(err); badCat.name.should.eql('Fluffy'); badCat.vet.name.should.eql('Nice Guy'); flag.should.be.true; Cats.Cat.removePre('save'); done(); }); }); }); }); it('Save existing item with an invalid attribute', function (done) { Cats.Cat.get(1, function(err, model) { should.not.exist(err); should.exist(model); model.validated = 'bad'; model.save().catch(function(err) { should.exist(err); err.name.should.equal('ValidationError'); Cats.Cat.get({id: 1}, {consistent: true}, function(err, badCat) { should.not.exist(err); badCat.name.should.eql('Fluffy'); badCat.vet.name.should.eql('Nice Guy'); badCat.ears[0].name.should.eql('right'); badCat.ears[1].name.should.eql('right'); done(); }); }); }); }); it('Deletes item', function (done) { var cat = new Cats.Cat({id: 1}); cat.delete(done); }); it('Deletes item with invalid key', function (done) { var cat = new Cats.Cat({id: 0}); cat.delete(function(err) { should.exist(err); err.name.should.equal('ValidationError'); done(); }); }); it('Delete returnRequest option', function (done) { var cat = new Cats.Cat({id: 1}); cat.delete({returnRequest: true}, function (err, request) { should.not.exist(err); request.should.exist; request.TableName.should.eql("test-Cat-db"); request.Key.should.eql({id: {N: '1'}}); done(); }); }); it('Get missing item', function (done) { Cats.Cat.get(1, function(err, model) { should.not.exist(err); should.not.exist(model); done(); }); }); it('Static Creates new item', function (done) { Cats.Cat.create({id: 666, name: 'Garfield'}, function (err, garfield) { should.not.exist(err); should.exist(garfield); garfield.id.should.eql(666); done(); }); }); it('Static Creates new item with range key', function (done) { Cats.Cat2.create({ownerId: 666, name: 'Garfield'}, function (err, garfield) { should.not.exist(err); should.exist(garfield); garfield.ownerId.should.eql(666); done(); }); }); it('Prevent duplicate create', function (done) { Cats.Cat.create({id: 666, name: 'Garfield'}, function (err, garfield) { should.exist(err); should.not.exist(garfield); done(); }); }); it('Prevent duplicate create with range key', function (done) { Cats.Cat2.create({ownerId: 666, name: 'Garfield'}, function (err, garfield) { should.exist(err); should.not.exist(garfield); done(); }); }); it('Static Creates second item', function (done) { Cats.Cat.create({id: 777, name: 'Catbert'}, function (err, catbert) { should.not.exist(err); should.exist(catbert); catbert.id.should.eql(777); done(); }); }); it('BatchGet items', function (done) { Cats.Cat.batchGet([{id: 666}, {id: 777}], function (err, cats) { cats.length.should.eql(2); done(); }); }); it('BatchGet items for model with falsy keys', function (done) { Cats.Cat8.create({id: 1, age: 0}) .then(function () { return Cats.Cat8.batchGet([{id: 1, age: 0}]); }) .then(function (cats) { cats.length.should.eql(1); cats[0].should.have.property('id', 1); cats[0].should.have.property('age', 0); done(); }) .catch(done); }); it('Static Delete', function (done) { Cats.Cat.delete(666, function (err) { should.not.exist(err); Cats.Cat.get(666, function (err, delCat) { should.not.exist(err); should.not.exist(delCat); Cats.Cat.delete(777, done); }); }); }); it('Should support deletions with validators', function (done) { var cat = new Cats.CatWithGeneratedID({ owner: { name: 'Joe', address: 'Somewhere' }, name: 'Garfield', id: 'Joe_Garfield' }); cat.delete(function (err) { should.not.exist(err); Cats.CatWithGeneratedID.get(cat, function (err, delCat) { should.not.exist(err); should.not.exist(delCat); done(); }); }); }); it('Static Delete with range key', function (done) { Cats.Cat2.delete({ ownerId: 666, name: 'Garfield' }, function (err) { should.not.exist(err); Cats.Cat2.get({ ownerId: 666, name: 'Garfield' }, function (err, delCat) { should.not.exist(err); should.not.exist(delCat); done(); }); }); }); it('Static Creates new item', function (done) { Cats.Cat.create({id: 666, name: 'Garfield'}, function (err, garfield) { should.not.exist(err); should.exist(garfield); garfield.id.should.eql(666); done(); }); }); it('Static Delete with update', function (done) { Cats.Cat.delete(666, { update: true }, function (err, data) { should.not.exist(err); should.exist(data); data.id.should.eql(666); data.name.should.eql('Garfield'); Cats.Cat.get(666, function (err, delCat) { should.not.exist(err); should.not.exist(delCat); done(); }); }); }); it('Static Delete with update failure', function (done) { Cats.Cat.delete(666, { update: true }, function (err) { should.exist(err); err.statusCode.should.eql(400); err.code.should.eql('ConditionalCheckFailedException'); done(); }); }); // See comments on PR #306 for details on why the test below is commented out it('Should enable server side encryption', function() { var Model = dynamoose.model('TestTable', { id: Number, name: String }, { serverSideEncryption: true }); Model.getTableReq().SSESpecification.Enabled.should.be.true; }); it('Server side encryption shouldn\'t be enabled unless specified', function(done) { var Model = dynamoose.model('TestTableB', { id: Number, name: String }); setTimeout(function () { Model.$__.table.describe(function(err, data) { var works = !data.Table.SSEDescription || data.Table.SSEDescription.Status === "DISABLED"; works.should.be.true; done(); }); }, 2000); }); it('Makes model class available inside schema methods', function() { Object.keys(dynamoose.models).should.containEql('test-CatWithMethods-db'); var cat = new Cats.CatWithMethods({id: 1, name: 'Sir Pounce'}); cat.getModel.should.throw(Error); var modelClass = cat.getModel('test-CatWithMethods-db'); modelClass.should.equal(Cats.CatWithMethods); }); describe('Model.update', function (){ before(function (done) { var stray = new Cats.Cat({id: 999, name: 'Tom'}); stray.save(done); }); it('False condition', function (done) { Cats.Cat.update({id: 999}, {name: 'Oliver'}, { condition: '#name = :name', conditionNames: { name: 'name' }, conditionValues: { name: 'Muffin' } }, function (err) { should.exist(err); Cats.Cat.get(999, function (err, tomcat) { should.not.exist(err); should.exist(tomcat); tomcat.id.should.eql(999); tomcat.name.should.eql('Tom'); should.not.exist(tomcat.owner); should.not.exist(tomcat.age); done(); }); }); }); it('True condition', function (done) { Cats.Cat.update({id: 999}, {name: 'Oliver'}, { condition: '#name = :name', conditionNames: { name: 'name' }, conditionValues: { name: 'Tom' } }, function (err, data) { should.not.exist(err); should.exist(data); data.id.should.eql(999); data.name.should.equal('Oliver'); Cats.Cat.get(999, function (err, oliver) { should.not.exist(err); should.exist(oliver); oliver.id.should.eql(999); oliver.name.should.eql('Oliver'); should.not.exist(oliver.owner); should.not.exist(oliver.age); done(); }); }); }); it("If key is null or undefined, will use defaults", function (done) { Cats.Cat3.update(null, {age: 3, name: 'Furrgie'}, function (err, data) { should.not.exist(err); should.exist(data); data.id.should.eql(888); data.name.should.equal('Furrgie'); data.age.should.equal(3); Cats.Cat3.get(888, function (err, furrgie) { should.not.exist(err); should.exist(furrgie); furrgie.id.should.eql(888); furrgie.name.should.eql('Furrgie'); data.age.should.equal(3); Cats.Cat3.update(undefined, {age: 4}, function (err, data) { should.not.exist(err); should.exist(data); data.id.should.eql(888); data.name.should.equal('Furrgie'); data.age.should.equal(4); Cats.Cat3.get(888, function (err, furrgie) { should.not.exist(err); should.exist(furrgie); furrgie.id.should.eql(888); furrgie.name.should.eql('Furrgie'); should.not.exist(furrgie.owner); data.age.should.equal(4); done(); }); }); }); }); }); it("If key is null or undefined and default isn't provided, will throw an error", function (done) { Cats.Cat.update(null, {name: 'Oliver'}, function (err, data) { should.not.exist(data); should.exist(err); done(); }); }); it("If key is a value, will search by that value", function (done) { Cats.Cat3.update(888, {age: 5}, function (err, data) { should.not.exist(err); should.exist(data); data.id.should.eql(888); data.name.should.equal('Furrgie'); data.age.should.equal(5); Cats.Cat3.get(888, function (err, furrgie) { should.not.exist(err); should.exist(furrgie); furrgie.id.should.eql(888); furrgie.name.should.eql('Furrgie'); data.age.should.equal(5); done(); }); }); }); it("Creates an item with required attributes' defaults if createRequired is true", function (done) { Cats.Cat3.update({id: 25}, {age: 3}, {createRequired: true}, function (err, data) { should.not.exist(err); should.exist(data); data.id.should.eql(25); data.name.should.equal('Mittens'); data.age.should.equal(3); Cats.Cat3.get(25, function (err, mittens) { should.not.exist(err); should.exist(mittens); mittens.id.should.eql(25); mittens.name.should.eql('Mittens'); should.not.exist(mittens.owner); data.age.should.equal(3); done(); }); }); }); it("Throws an error when a required attribute has no default and has not been specified in the update if createRequired is true", function (done) { Cats.Cat3.update({id: 25}, {name: 'Rufflestiltskins'}, {createRequired: true}, function (err, data) { should.not.exist(data); should.exist(err); Cats.Cat3.get(25, function (err, mittens) { should.not.exist(err); should.exist(mittens); mittens.id.should.eql(25); mittens.name.should.eql('Mittens'); done(); }); }); }); it('Adds required attributes, even when not specified, if createRequired is true', function (done) { Cats.Cat3.update({id: 45}, {age: 4}, {createRequired: true}, function (err, data) { should.not.exist(err); should.exist(data); data.id.should.eql(45); data.name.should.equal('Mittens'); data.age.should.equal(4); Cats.Cat3.get(45, function (err, mittens) { should.not.exist(err); should.exist(mittens); mittens.id.should.eql(45); mittens.name.should.eql('Mittens'); should.not.exist(mittens.owner); data.age.should.equal(4); done(); }); }); }); it('Does not add required attributes if createRequired is false', function (done) { Cats.Cat3.update({id: 24}, {name: 'Cat-rina'}, {createRequired: false}, function (err, data) { should.not.exist(err); should.exist(data); data.id.should.eql(24); data.name.should.equal('Cat-rina'); should.not.exist(data.age); Cats.Cat3.get(24, function (err, mittens) { should.not.exist(err); should.exist(mittens); mittens.id.should.eql(24); data.name.should.equal('Cat-rina'); should.not.exist(data.age); should.not.exist(mittens.owner); done(); }); }); }); it('If item did not exist and timestamps are desired, createdAt and updatedAt will both be filled in', function (done) { // try a delete beforehand in case the test is run more than once Cats.Cat4.delete({id: 22}, function () { Cats.Cat4.update({id: 22}, {name: 'Twinkles'}, function (err, data) { should.not.exist(err); should.exist(data); should.exist(data.myLittleCreatedAt); should.exist(data.myLittleUpdatedAt); data.id.should.eql(22); data.name.should.equal('Twinkles'); Cats.Cat4.get(22, function (err, twinkles) { should.not.exist(err); should.exist(twinkles); twinkles.id.should.eql(22); twinkles.name.should.equal('Twinkles'); should.exist(twinkles.myLittleCreatedAt); should.exist(twinkles.myLittleUpdatedAt); done(); }); }); }); }); it('UpdatedAt will be updated ', function (done) { // try a delete beforehand in case the test is run more than once Cats.Cat4.delete({id: 22}, function () { Cats.Cat4.update({id: 22}, {name: 'Twinkles'}, function (err, data) { should.not.exist(err); should.exist(data); data.id.should.eql(22); data.name.should.equal('Twinkles'); should.exist(data.myLittleCreatedAt); should.exist(data.myLittleUpdatedAt); // now do another update Cats.Cat4.update({id: 22}, {name: 'Furr-nando'}, function (err, data) { should.not.exist(err); should.exist(data); data.id.should.eql(22); data.name.should.equal('Furr-nando'); data.myLittleUpdatedAt.getTime().should.be.above(data.myLittleCreatedAt.getTime()); Cats.Cat4.get(22, function (err, furrnando) { should.not.exist(err); should.exist(furrnando); furrnando.id.should.eql(22); furrnando.name.should.equal('Furr-nando'); furrnando.myLittleUpdatedAt.getTime().should.be.above(furrnando.myLittleCreatedAt.getTime()); done(); }); }); }); }); }); it('Expires will be updated ', function (done) { Cats.ExpiringCat.create({name: 'Fluffy2'}) .then(function (fluffy) { var max = Math.floor(Date.now() / 1000) + NINE_YEARS; var min = max - 1; should.exist(fluffy); should.exist(fluffy.expires); should.exist(fluffy.expires.getTime); var expiresInSec = Math.floor(fluffy.expires.getTime() / 1000); expiresInSec.should.be.within(min, max); setTimeout(function() { Cats.ExpiringCat.update({name: 'Fluffy2'}, {name: 'Twinkles'}, { updateExpires: true }, function (err, fluffy) { should.not.exist(err); should.exist(fluffy); should.exist(fluffy.expires); should.exist(fluffy.expires.getTime); var expiresInSec2 = Math.floor(fluffy.expires.getTime() / 1000); expiresInSec2.should.be.above(expiresInSec); done(); }); }, 1000); }) .catch(done); }); it('Set expires attribute on save', function (done) { Cats.ExpiringCat.create({name: 'Fluffy'}) .then(function (fluffy) { var max = Math.floor(Date.now() / 1000) + NINE_YEARS; var min = max - 1; should.exist(fluffy); should.exist(fluffy.expires); should.exist(fluffy.expires.getTime); var expiresInSec = Math.floor(fluffy.expires.getTime() / 1000); expiresInSec.should.be.within(min, max); done(); }) .catch(done); }); it('Does not set expires attribute on save if exists', function (done) { Cats.ExpiringCat.create({ name: 'Tigger', expires: new Date(Date.now() + (ONE_YEAR*1000)) }) .then(function (tigger) { var max = Math.floor(Date.now() / 1000) + ONE_YEAR; var min = max - 1; should.exist(tigger); should.exist(tigger.expires); should.exist(tigger.expires.getTime); var expiresInSec = Math.floor(tigger.expires.getTime() / 1000); expiresInSec.should.be.within(min, max); done(); }) .catch(done); }); it('Update expires attribute on save', function (done) { Cats.ExpiringCat.create({ name: 'Leo' }) .then(function (leo) { var max = Math.floor(Date.now() / 1000) + NINE_YEARS; var min = max - 1; var expiresInSec = Math.floor(leo.expires.getTime() / 1000); expiresInSec.should.be.within(min, max); leo.expires = new Date(Date.now() + (ONE_YEAR* 1000)); return leo.save(); }) .then(function (leo) { var max = Math.floor(Date.now() / 1000) + ONE_YEAR; var min = max - 1; var expiresInSec = Math.floor(leo.expires.getTime() / 1000); expiresInSec.should.be.within(min, max); done(); }) .catch(done); }); it('Save returnRequest option', function (done) { Cats.ExpiringCat.create({ name: 'Leo5' }) .then(function (leo) { var max = Math.floor(Date.now() / 1000) + NINE_YEARS; var min = max - 1; var expiresInSec = Math.floor(leo.expires.getTime() / 1000); expiresInSec.should.be.within(min, max); leo.expires = new Date(Date.now() + (ONE_YEAR* 1000)); return leo.save({returnRequest: true}); }) .then(function (request) { request.should.exist; request.TableName.should.eql("test-ExpiringCat-db"); request.Item.name.should.eql({S: "Leo5"}); done(); }) .catch(done); }); it('Should not have an expires property if TTL is set to null', function (done) { Cats.ExpiringCatNull.create({ name: 'Leo12' }) .then(function () { Cats.ExpiringCatNull.get("Leo12").then(function (leo) { should.exist(leo); should.not.exist(leo.expires); done(); }).catch(done); }) .catch(done); }); it('Should not return expired items if returnExpiredItems is false (get)', function (done) { Cats.ExpiringCatNoReturn.create({ name: 'Leo1', expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365)) }) .then(function (leo) { leo.expires = new Date(Date.now() - 5000); return leo.save(); }) .then(function () { Cats.ExpiringCatNoReturn.get("Leo1").then(function (leo) { should.not.exist(leo); done(); }).catch(done); }) .catch(done); }); it('Should return expired items if returnExpiredItems is false and expires is null (get)', function (done) { Cats.ExpiringCatNoReturn.create({ name: 'Leo11', expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365)) }) .then(function (leo) { leo.expires = null; return leo.save(); }) .then(function () { Cats.ExpiringCatNoReturn.get("Leo11").then(function (leo) { should.not.exist(leo.expires); should.exist(leo); done(); }).catch(done); }) .catch(done); }); it('Should return expired items if returnExpiredItems is undefined (get)', function (done) { Cats.ExpiringCatNull.create({ name: 'Leo1', expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365)) }) .then(function (leo) { leo.expires = new Date(Date.now() - 5000); return leo.save(); }) .then(function () { Cats.ExpiringCatNull.get("Leo1").then(function (leo) { should.exist(leo); done(); }).catch(done); }) .catch(done); }); it('Should return expired items if returnExpiredItems is undefined and expires is null (get)', function (done) { Cats.ExpiringCatNull.create({ name: 'Leo11', expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365)) }) .then(function (leo) { leo.expires = null; return leo.save(); }) .then(function () { Cats.ExpiringCatNull.get("Leo11").then(function (leo) { should.not.exist(leo.expires); should.exist(leo); done(); }).catch(done); }) .catch(done); }); it('Should return expired items if returnExpiredItems is true (get)', function (done) { Cats.ExpiringCatReturnTrue.create({ name: 'Leo1', expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365)) }) .then(function (leo) { leo.expires = new Date(Date.now() - 5000); return leo.save(); }) .then(function () { Cats.ExpiringCatReturnTrue.get("Leo1").then(function (leo) { should.exist(leo); done(); }).catch(done); }) .catch(done); }); it('Should return expired items if returnExpiredItems is true and expires is null (get)', function (done) { Cats.ExpiringCatReturnTrue.create({ name: 'Leo11', expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365)) }) .then(function (leo) { leo.expires = null; return leo.save(); }) .then(function () { Cats.ExpiringCatReturnTrue.get("Leo11").then(function (leo) { should.not.exist(leo.expires); should.exist(leo); done(); }).catch(done); }) .catch(done); }); it('Should not return expired items if returnExpiredItems is false (batchGet)', function (done) { Cats.ExpiringCatNoReturn.create({ name: 'Leo2', expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365)) }) .then(function (leo