dynogels
Version:
DynamoDB data mapper
1,279 lines (1,051 loc) • 36.4 kB
JavaScript
;
const dynogels = require('../../index');
const chai = require('chai');
const expect = chai.expect;
const async = require('async');
const _ = require('lodash');
const helper = require('../test-helper');
const uuid = require('uuid');
const Joi = require('joi');
chai.should();
let User;
let Tweet;
let Movie;
let DynamicKeyModel;
const internals = {};
internals.userId = n => `userid-${n}`;
internals.loadSeedData = callback => {
callback = callback || _.noop;
async.parallel([
callback => {
async.times(15, (n, next) => {
let roles = ['user'];
if (n % 3 === 0) {
roles = ['admin', 'editor'];
} else if (n % 5 === 0) {
roles = ['qa', 'dev'];
}
User.create({ id: internals.userId(n), email: `test${n}@example.com`, name: `Test ${n % 3}`, age: n + 10, roles: roles }, next);
}, callback);
},
callback => {
async.times(15 * 5, (n, next) => {
const userId = internals.userId(n % 5);
const p = { UserId: userId, content: `I love tweeting, in fact Ive tweeted ${n} times`, num: n };
if (n % 3 === 0) {
p.tag = '#test';
}
return Tweet.create(p, next);
}, callback);
},
callback => {
async.times(10, (n, next) => {
const director = { firstName: 'Steven', lastName: `Spielberg the ${n}`, titles: ['Producer', 'Writer', 'Director'] };
const actors = [
{ firstName: 'Tom', lastName: 'Hanks', titles: ['Producer', 'Actor', 'Soundtrack'] }
];
const tags = [`tag ${n}`];
if (n % 3 === 0) {
actors.push({ firstName: 'Rex', lastName: 'Ryan', titles: ['Actor', 'Head Coach'] });
tags.push('Action');
}
if (n % 5 === 0) {
actors.push({ firstName: 'Tom', lastName: 'Coughlin', titles: ['Writer', 'Head Coach'] });
tags.push('Comedy');
}
Movie.create({ title: `Movie ${n}`, releaseYear: 2001 + n, actors: actors, director: director, tags: tags }, next);
}, callback);
},
], callback);
};
describe('Dynogels Integration Tests', function () {
this.timeout(0);
before(done => {
dynogels.dynamoDriver(helper.realDynamoDB());
User = dynogels.define('dynogels-int-test-user', {
hashKey: 'id',
schema: {
id: Joi.string().default(() => uuid.v4(), 'uuid'),
email: Joi.string().required(),
name: Joi.string().allow(''),
age: Joi.number().min(10),
roles: dynogels.types.stringSet().default(['user']),
acceptedTerms: Joi.boolean().default(false),
things: Joi.array(),
settings: {
nickname: Joi.string(),
notify: Joi.boolean().default(true),
version: Joi.number()
}
}
});
Tweet = dynogels.define('dynogels-int-test-tweet', {
hashKey: 'UserId',
rangeKey: 'TweetID',
schema: {
UserId: Joi.string(),
TweetID: dynogels.types.uuid(),
content: Joi.string(),
num: Joi.number(),
tag: Joi.string(),
PublishedDateTime: Joi.date().default(Date.now, 'now')
},
indexes: [
{ hashKey: 'UserId', rangeKey: 'PublishedDateTime', type: 'local', name: 'PublishedDateTimeIndex' }
]
});
Movie = dynogels.define('dynogels-int-test-movie', {
hashKey: 'title',
timestamps: true,
schema: {
title: Joi.string(),
description: Joi.string(),
releaseYear: Joi.number(),
tags: dynogels.types.stringSet(),
director: Joi.object().keys({
firstName: Joi.string(),
lastName: Joi.string(),
titles: Joi.array()
}),
actors: Joi.array().items(Joi.object().keys({
firstName: Joi.string(),
lastName: Joi.string(),
titles: Joi.array()
}))
}
});
const silentLogger = {
log: () => {},
info: () => {},
warn: () => {},
};
DynamicKeyModel = dynogels.define('dynogels-int-test-dyn-key', {
hashKey: 'id',
schema: Joi.object().keys({
id: Joi.string()
}).unknown(),
log: silentLogger,
});
async.series([
async.apply(dynogels.createTables.bind(dynogels)),
callback => {
const items = [{ fiz: 3, buz: 5, fizbuz: 35 }];
User.create({ id: '123456789', email: 'some@user.com', age: 30, settings: { nickname: 'thedude' }, things: items }, callback);
},
callback => {
User.create({ id: '9999', email: '9999@test.com', age: 99, name: 'Nancy Nine' }, callback);
},
internals.loadSeedData
], done);
});
describe('#create', () => {
it('should create item with hash key', done => {
User.create({
email: 'foo@bar.com',
age: 18,
roles: ['user', 'admin'],
acceptedTerms: true,
settings: {
nickname: 'fooos',
version: 2
}
}, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get()).to.have.keys(['id', 'email', 'age', 'roles', 'acceptedTerms', 'settings']);
return done();
});
});
it('should create item with empty string', done => {
User.create({
email: 'foo2@bar.com',
name: '',
age: 22,
roles: ['user'],
settings: {
nickname: 'foo2',
version: 2
}
}, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get()).to.have.keys(['id', 'email', 'age', 'roles', 'acceptedTerms', 'settings']);
return done();
});
});
it('should return condition exception when using ConditionExpression', done => {
const item = { email: 'test123@test.com', age: 33, roles: ['user'] };
User.create(item, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get('email')).to.eql('test123@test.com');
const params = {};
params.ConditionExpression = '#i <> :x';
params.ExpressionAttributeNames = { '#i': 'id' };
params.ExpressionAttributeValues = { ':x': acc.get('id') };
const item2 = _.merge(item, { id: acc.get('id') });
User.create(item2, params, (error, acc) => {
expect(error).to.exist;
expect(error.code).to.eql('ConditionalCheckFailedException');
expect(acc).to.not.exist;
return done();
});
});
});
it('should return condition exception when using expected shorthand', done => {
const item = { email: 'test444@test.com', age: 33, roles: ['user'] };
User.create(item, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get('email')).to.eql('test444@test.com');
const opts = { expected: { email: 'foo@bar.com' } };
const item2 = _.merge(item, { id: acc.get('id') });
User.create(item2, opts, (error, acc) => {
expect(error).to.exist;
expect(error.code).to.eql('ConditionalCheckFailedException');
expect(acc).to.not.exist;
return done();
});
});
});
it('should return condition exception when using overwrite shorthand', done => {
const item = { email: 'testOverwrite@test.com', age: 20 };
User.create(item, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
const item2 = _.merge(item, { id: acc.get('id') });
User.create(item2, { overwrite: false }, (error, acc) => {
expect(error).to.exist;
expect(error.code).to.eql('ConditionalCheckFailedException');
expect(acc).to.not.exist;
return done();
});
});
});
it('should create item with dynamic keys', done => {
DynamicKeyModel.create({
id: 'rand-1',
name: 'Foo Bar',
children: ['sam', 'steve', 'sarah', 'sally'],
settings: { nickname: 'Tester', info: { color: 'green', age: 19 } }
}, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get()).to.have.keys(['id', 'name', 'children', 'settings']);
return done();
});
});
it('should create multiple items at once', done => {
const item = { email: 'testMulti1@test.com', age: 10 };
const item2 = { email: 'testMulti2@test.com', age: 20 };
const item3 = { email: 'testMulti3@test.com', age: 30 };
User.create([item, item2, item3], (err, accounts) => {
expect(err).to.not.exist;
expect(accounts).to.exist;
expect(accounts).to.have.length(3);
return done();
});
});
});
describe('#get', () => {
it('should get item by hash key', done => {
User.get({ id: '123456789' }, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get()).to.have.keys(['id', 'email', 'age', 'roles', 'acceptedTerms', 'settings', 'things']);
return done();
});
});
it('should get return selected attributes AttributesToGet param', done => {
User.get({ id: '123456789' }, { AttributesToGet: ['email', 'age'] }, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get()).to.have.keys(['email', 'age']);
return done();
});
});
it('should get return selected attributes using ProjectionExpression param', done => {
User.get({ id: '123456789' }, { ProjectionExpression: 'email, age, settings.nickname' }, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get()).to.have.keys(['email', 'age', 'settings']);
expect(acc.get('settings').nickname).to.exist;
return done();
});
});
});
describe('#update', () => {
it('should update item appended role', done => {
User.update({
id: '123456789',
roles: { $add: 'tester' }
}, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get()).to.have.keys(['id', 'email', 'age', 'roles', 'acceptedTerms', 'settings', 'things']);
expect(acc.get('roles').sort()).to.eql(['tester', 'user']);
return done();
});
});
it('should use expected to check that the item exists', (done) => {
User.update(
{
id: '123456789',
email: 'updated_already@exists.com'
},
{
expected: { id: { Exists: true } }
},
(err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.attrs.email).to.eql('updated_already@exists.com');
done();
}
);
});
it('should fail when expected exists check fails', (done) => {
User.update(
{
id: 'does not exist'
},
{ expected: { id: { Exists: true } } },
(err, acc) => {
expect(err).to.exist;
expect(acc).to.not.exist;
done();
}
);
});
it('should remove name attribute from user record when set to empty string', done => {
User.update({ id: '9999', name: '' }, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get()).to.have.keys(['id', 'email', 'age', 'roles', 'acceptedTerms']);
return done();
});
});
it('should update age using expected value', done => {
User.update({ id: '9999', age: 100 }, { expected: { age: 99 } }, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get('age')).to.eql(100);
return done();
});
});
it('should update email using expected that an email already exists', done => {
User.update({ id: '9999', email: 'new9999@test.com' }, { expected: { email: { Exists: true } } }, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get('email')).to.eql('new9999@test.com');
return done();
});
});
it('should remove settings attribute from user record', done => {
User.update({ id: '123456789', settings: null }, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get()).to.have.keys(['id', 'email', 'age', 'roles', 'acceptedTerms', 'things']);
return done();
});
});
it('should update User using updateExpression', done => {
const params = {};
params.UpdateExpression = 'ADD #a :x SET things[0].buz = :y';
params.ConditionExpression = '#a = :current';
params.ExpressionAttributeNames = { '#a': 'age' };
params.ExpressionAttributeValues = { ':x': 1, ':y': 22, ':current': 30 };
User.update({ id: '123456789' }, params, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get('age')).to.equal(31);
expect(acc.get('things')).to.eql([{ fiz: 3, buz: 22, fizbuz: 35 }]);
return done();
});
});
it('should update Movie using updateExpressions', done => {
const params = {};
params.UpdateExpression = 'SET #year = #year + :inc, #dir.titles = list_append(#dir.titles, :title), #act[0].firstName = :firstName ADD tags :tag';
params.ConditionExpression = '#year = :current';
params.ExpressionAttributeNames = {
'#year': 'releaseYear',
'#dir': 'director',
'#act': 'actors'
};
params.ExpressionAttributeValues = {
':inc': 1,
':current': 2001,
':title': ['The Man'],
':firstName': 'Rob',
':tag': dynogels.Set(['Sports', 'Horror'], 'S')
};
Movie.update({ title: 'Movie 0', description: 'This is a description' }, params, (err, mov) => {
expect(err).to.not.exist;
expect(mov.get('description')).to.eql('This is a description');
expect(mov.get('releaseYear')).to.eql(2002);
expect(mov.get('updatedAt')).to.exist;
return done();
});
});
it('should update item with dynamic keys', done => {
DynamicKeyModel.update({
id: 'rand-5',
color: 'green',
settings: { email: 'dynupdate@test.com' }
}, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get()).to.have.keys(['id', 'settings', 'color']);
expect(acc.get()).to.eql({
id: 'rand-5',
color: 'green',
settings: { email: 'dynupdate@test.com' }
});
return done();
});
});
it('should fail to update an attribute not in the schema', (done) => {
User.update({
id: '123456789',
invalidAttribute: 'Invalid Value'
}, (err, account) => {
expect(err).to.exist;
expect(account).to.not.exist;
return done();
});
});
it('should fail to remove a required attribute', (done) => {
User.update({
id: '123456789',
email: null
}, (err, account) => {
expect(err).to.exist;
expect(account).to.not.exist;
done();
});
});
it('should successfully remove an optional attribute', (done) => {
User.update({
id: '123456789',
name: null,
}, (err, acc) => {
expect(err).to.be.null;
expect(acc).to.exist;
done();
});
});
it('should fail for attribute mismatch to schema type', (done) => {
User.update({
id: '123456789',
name: 1
}, (err, account) => {
expect(err).to.exist;
expect(account).to.not.exist;
done();
});
});
it('should fail to use $add for an invalid attribute', (done) => {
User.update({
id: '123456789',
name: { $add: '$addname' }
}, (err, acc) => {
expect(err).to.exist;
expect(acc).to.not.exist;
done();
});
});
});
describe('#getItems', () => {
it('should return 3 items', done => {
User.getItems(['userid-1', 'userid-2', 'userid-3'], (err, accounts) => {
expect(err).to.not.exist;
expect(accounts).to.have.length(3);
return done();
});
});
it('should return 2 items with only selected attributes', done => {
const opts = { AttributesToGet: ['email', 'age'] };
User.getItems(['userid-1', 'userid-2'], opts, (err, accounts) => {
expect(err).to.not.exist;
expect(accounts).to.have.length(2);
_.each(accounts, acc => {
expect(acc.get()).to.have.keys(['email', 'age']);
});
return done();
});
});
});
describe('#query', () => {
it('should return users tweets', done => {
Tweet.query('userid-1').exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get('UserId')).to.eql('userid-1');
});
return done();
});
});
it('should return users tweets with specific attributes', done => {
Tweet.query('userid-1').attributes(['num', 'content']).exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get('UserId')).to.not.exist;
expect(t.get()).to.include.keys('num', 'content');
});
return done();
});
});
it('should return tweets using secondaryIndex', done => {
Tweet.query('userid-1')
.usingIndex('PublishedDateTimeIndex')
.consistentRead(true)
.descending()
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
let prev;
_.each(data.Items, t => {
expect(t.get('UserId')).to.eql('userid-1');
const published = t.get('PublishedDateTime');
if (prev) {
expect(published < prev).to.be.true;
}
prev = published;
});
return done();
});
});
it('should return tweets using secondaryIndex and date object', done => {
const oneMinAgo = new Date(new Date().getTime() - 60 * 1000);
Tweet.query('userid-1')
.usingIndex('PublishedDateTimeIndex')
.where('PublishedDateTime').gt(oneMinAgo)
.descending()
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
let prev;
_.each(data.Items, t => {
expect(t.get('UserId')).to.eql('userid-1');
const published = t.get('PublishedDateTime');
if (prev) {
expect(published < prev).to.be.true;
}
prev = published;
});
return done();
});
});
it('should return tweets that match filters', done => {
Tweet.query('userid-1')
.filter('num').between(4, 8)
.filter('tag').exists()
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get('UserId')).to.eql('userid-1');
expect(t.get('num')).to.be.above(3);
expect(t.get('num')).to.be.below(9);
expect(t.get('tag')).to.exist;
});
return done();
});
});
it('should return tweets that match exists filter', done => {
Tweet.query('userid-1')
.filter('tag').exists()
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get('UserId')).to.eql('userid-1');
expect(t.get('tag')).to.exist;
});
return done();
});
});
it('should return tweets that match IN filter', done => {
Tweet.query('userid-1')
.filter('num').in([4, 6, 8])
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get('UserId')).to.eql('userid-1');
const c = _.includes([4, 6, 8], t.get('num'));
expect(c).to.be.true;
});
return done();
});
});
it('should return tweets that match expression filters', done => {
Tweet.query('userid-1')
.filterExpression('#num BETWEEN :low AND :high AND attribute_exists(#tag)')
.expressionAttributeValues({ ':low': 4, ':high': 8 })
.expressionAttributeNames({ '#num': 'num', '#tag': 'tag' })
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get('UserId')).to.eql('userid-1');
expect(t.get('num')).to.be.above(3);
expect(t.get('num')).to.be.below(9);
expect(t.get('tag')).to.exist;
});
return done();
});
});
it('should return tweets with projection expression', done => {
Tweet.query('userid-1')
.projectionExpression('#con, UserId')
.expressionAttributeNames({ '#con': 'content' })
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get()).to.have.keys(['content', 'UserId']);
});
return done();
});
});
it('should return all tweets from user', done => {
Tweet.query('userid-1').limit(2).loadAll().exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get('UserId')).to.eql('userid-1');
});
return done();
});
});
it('should return movie if directed by Steven Spielberg the 4', done => {
Movie.query('Movie 4').filter('director.firstName').equals('Steven').filter('director.lastName').equals('Spielberg the 4').limit(2).loadAll().exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get('title')).to.eql('Movie 4');
expect(t.get('director').firstName).to.eql('Steven');
expect(t.get('director').lastName).to.eql('Spielberg the 4');
});
return done();
});
});
});
describe('#scan', () => {
it('should return all users', done => {
User.scan().loadAll().exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
return done();
});
});
it('should return all users with limit', done => {
User.scan().limit(2).loadAll().exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
return done();
});
});
it('should return users with specific attributes', done => {
User.scan()
.where('age').gt(18)
.attributes(['email', 'roles', 'age']).exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, u => {
expect(u.get()).to.include.keys('email', 'roles', 'age');
});
return done();
});
});
it('should return 10 users', done => {
User.scan().limit(10).exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length(10);
return done();
});
});
it('should return users older than 18', done => {
User.scan()
.where('age').gt(18)
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, u => {
expect(u.get('age')).to.be.above(18);
});
return done();
});
});
it('should return users matching multiple filters', done => {
User.scan()
.where('age').between(18, 22)
.where('email').beginsWith('test1')
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, u => {
expect(u.get('age')).to.be.within(18, 22);
expect(u.get('email')).to.match(/^test1.*/);
});
return done();
});
});
it('should return users contains admin role', done => {
User.scan()
.where('roles').contains('admin')
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, u => {
expect(u.get('roles')).to.include('admin');
});
return done();
});
});
it('should return users using stream interface', done => {
const stream = User.scan().exec();
let called = false;
stream.on('readable', () => {
called = true;
const data = stream.read();
if (data) {
expect(data.Items).to.have.length.above(0);
}
});
stream.on('end', () => {
expect(called).to.be.true;
return done();
});
});
it('should return users that match expression filters', done => {
User.scan()
.filterExpression('#age BETWEEN :low AND :high AND begins_with(#email, :e)')
.expressionAttributeValues({ ':low': 18, ':high': 22, ':e': 'test1' })
.expressionAttributeNames({ '#age': 'age', '#email': 'email' })
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, u => {
expect(u.get('age')).to.be.within(18, 22);
expect(u.get('email')).to.match(/^test1.*/);
});
return done();
});
});
it('should return users between ages', done => {
User.scan()
.where('age').between(18, 22)
.where('email').beginsWith('test1')
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, u => {
expect(u.get('age')).to.be.within(18, 22);
expect(u.get('email')).to.match(/^test1.*/);
});
return done();
});
});
it('should return users matching IN filter', done => {
User.scan()
.where('age').in([2, 9, 20])
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, u => {
const c = _.includes([2, 9, 20], u.get('age'));
expect(c).to.be.true;
});
return done();
});
});
it('should return users with projection expression', done => {
User.scan()
.projectionExpression('age, email, #roles')
.expressionAttributeNames({ '#roles': 'roles' })
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, u => {
expect(u.get()).to.have.keys(['age', 'email', 'roles']);
});
return done();
});
});
it('should load all users with limit', done => {
User.scan().loadAll().limit(2).exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
return done();
});
});
it('should return users using stream interface and limit', done => {
const stream = User.scan().loadAll().limit(2).exec();
let called = false;
stream.on('readable', () => {
called = true;
const data = stream.read();
if (data) {
expect(data.Items).to.have.length.within(0, 2);
}
});
stream.on('end', () => {
expect(called).to.be.true;
return done();
});
});
it('should load tweets using not null tag clause', done => {
Tweet.scan().where('tag').notNull().exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get('tag')).to.exist;
});
return done();
});
});
it('should return all movies directed by Steven Spielberg the 4', done => {
Movie.scan().where('director.firstName').equals('Steven').where('director.lastName').equals('Spielberg the 4').limit(2).loadAll().exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, t => {
expect(t.get('director').firstName).to.eql('Steven');
expect(t.get('director').lastName).to.eql('Spielberg the 4');
});
return done();
});
});
});
describe('#parallelScan', () => {
it('should return all users', done => {
User.parallelScan(4).exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
return done();
});
});
it('should return users older than 18', done => {
User.parallelScan(4)
.where('age').gt(18)
.exec((err, data) => {
expect(err).to.not.exist;
expect(data.Items).to.have.length.above(0);
_.each(data.Items, u => {
expect(u.get('age')).to.be.above(18);
});
return done();
});
});
it('should return users using stream interface', done => {
const stream = User.parallelScan(4).exec();
let called = false;
stream.on('readable', () => {
called = true;
const data = stream.read();
if (data) {
expect(data.Items).to.have.length.above(0);
}
});
stream.on('end', () => {
expect(called).to.be.true;
return done();
});
});
});
describe('timestamps', () => {
let Model;
let ModelCustomTimestamps;
before(done => {
Model = dynogels.define('dynogels-int-test-timestamp', {
hashKey: 'id',
timestamps: true,
schema: {
id: Joi.string()
}
});
ModelCustomTimestamps = dynogels.define('dynogels-int-test-timestamp-custom', {
hashKey: 'id',
timestamps: true,
createdAt: 'created',
updatedAt: 'updated',
schema: {
id: Joi.string()
}
});
return dynogels.createTables(done);
});
it('should add createdAt param', done => {
Model.create({ id: 'test-1' }, err => {
expect(err).to.not.exist;
Model.get('test-1', (err2, data) => {
expect(err2).to.not.exist;
expect(data.get('id')).to.eql('test-1');
expect(data.get('createdAt')).to.exist;
return done();
});
});
});
it('should add updatedAt param', done => {
Model.update({ id: 'test-2' }, err => {
expect(err).to.not.exist;
Model.get('test-2', (err2, data) => {
expect(err2).to.not.exist;
expect(data.get('id')).to.eql('test-2');
expect(data.get('updatedAt')).to.exist;
return done();
});
});
});
it('should add custom createdAt param', done => {
ModelCustomTimestamps.create({ id: 'test-1' }, err => {
expect(err).to.not.exist;
ModelCustomTimestamps.get('test-1', (err2, data) => {
expect(err2).to.not.exist;
expect(data.get('id')).to.eql('test-1');
expect(data.get('created')).to.exist;
return done();
});
});
});
it('should add custom updatedAt param', done => {
ModelCustomTimestamps.update({ id: 'test-2' }, err => {
expect(err).to.not.exist;
ModelCustomTimestamps.get('test-2', (err2, data) => {
expect(err2).to.not.exist;
expect(data.get('id')).to.eql('test-2');
expect(data.get('updated')).to.exist;
return done();
});
});
});
});
describe('#destroy', () => {
let userId;
beforeEach(done => {
User.create({ email: 'destroy@test.com', age: 20, roles: ['tester'] }, (err, acc) => {
expect(err).to.not.exist;
userId = acc.get('id');
return done();
});
});
it('should destroy item with hash key', done => {
User.destroy({ id: userId }, err => {
expect(err).to.not.exist;
return done();
});
});
it('should destroy item and return old values', done => {
User.destroy({ id: userId }, { ReturnValues: 'ALL_OLD' }, (err, acc) => {
expect(err).to.not.exist;
expect(acc).to.exist;
expect(acc.get('email')).to.eql('destroy@test.com');
return done();
});
});
it('should return condition exception when using ConditionExpression', done => {
const params = {};
params.ConditionExpression = '#i = :x';
params.ExpressionAttributeNames = { '#i': 'id' };
params.ExpressionAttributeValues = { ':x': 'dontexist' };
User.destroy({ id: 'dontexist' }, params, (err, acc) => {
expect(err).to.exist;
expect(err.code).to.eql('ConditionalCheckFailedException');
expect(acc).to.not.exist;
return done();
});
});
it('should return condition exception when using Expected shorthand', done => {
const opts = { expected: { id: 'dontexist' } };
User.destroy({ id: 'dontexist' }, opts, (err, acc) => {
expect(err).to.exist;
expect(err.code).to.eql('ConditionalCheckFailedException');
expect(acc).to.not.exist;
return done();
});
});
});
describe('model methods', () => {
it('#save with passed in attributes', done => {
const t = new Tweet({
UserId: 'tester-1',
content: 'save test tweet',
tag: 'test'
});
t.save(err => {
expect(err).to.not.exist;
return done();
});
});
it('#save without passed in attributes', done => {
const t = new Tweet();
const attrs = { UserId: 'tester-1', content: 'save test tweet', tag: 'test' };
t.set(attrs);
t.save(err => {
expect(err).to.not.exist;
return done();
});
});
it('#save without callback', done => {
const t = new Tweet({
UserId: 'tester-1',
content: 'save test tweet',
tag: 'test'
});
t.save();
return done();
});
it('#update with callback', done => {
Tweet.create({ UserId: 'tester-2', content: 'update test tweet' }, (err, tweet) => {
expect(err).to.not.exist;
tweet.set({ tag: 'update' });
tweet.update(err => {
expect(err).to.not.exist;
expect(tweet.get('tag')).to.eql('update');
return done();
});
});
});
it('#update without callback', done => {
Tweet.create({ UserId: 'tester-2', content: 'update test tweet' }, (err, tweet) => {
expect(err).to.not.exist;
tweet.set({ tag: 'update' });
tweet.update();
return done();
});
});
it('#destroy with callback', done => {
Tweet.create({ UserId: 'tester-2', content: 'update test tweet' }, (err, tweet) => {
expect(err).to.not.exist;
tweet.destroy(err => {
expect(err).to.not.exist;
return done();
});
});
});
it('#destroy without callback', done => {
Tweet.create({ UserId: 'tester-2', content: 'update test tweet' }, (err, tweet) => {
expect(err).to.not.exist;
tweet.destroy();
return done();
});
});
it('#toJSON', done => {
Tweet.create({ UserId: 'tester-2', content: 'update test tweet' }, (err, tweet) => {
expect(err).to.not.exist;
expect(tweet.toJSON()).to.have.keys(['UserId', 'content', 'TweetID', 'PublishedDateTime']);
return done();
});
});
it('#toPlainObject', done => {
Tweet.create({ UserId: 'tester-2', content: 'update test tweet' }, (err, tweet) => {
expect(err).to.not.exist;
expect(tweet.toPlainObject()).to.have.keys(['UserId', 'content', 'TweetID', 'PublishedDateTime']);
return done();
});
});
});
});