dynamoose
Version:
Dynamoose is a modeling tool for Amazon's DynamoDB (inspired by Mongoose)
1,448 lines (1,232 loc) • 113 kB
JavaScript
'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