'use strict';
const start = require('./common');
const assert = require('assert');
const random = require('./util').random;
const mongoose = start.mongoose;
const Schema = mongoose.Schema;
const uri = process.env.MONGOOSE_SHARD_TEST_URI;
const redColorEscapeCharacter = '\x1b[31m';
const colorResetEscapeCharacter = '\u001b[39m';
if (!uri) {
console.log([
redColorEscapeCharacter,
'You\'re not testing shards!',
'Please set the MONGOOSE_SHARD_TEST_URI env variable.',
'e.g: `mongodb://127.0.0.1:27017/database',
'Sharding must already be enabled on your database',
colorResetEscapeCharacter
].join('\n'));
return;
}
const schema = new Schema({
name: String,
age: Number,
likes: [String]
}, { shardkey: { name: 1, age: 1 } });
schema.index({ name: 1, age: 1 });
const collection = 'shardperson_' + random();
mongoose.model('ShardPerson', schema, collection);
let version;
let greaterThan20x;
let db;
describe('shard', function() {
before(function(done) {
db = start({ uri: uri });
db.on('error', function(err) {
if (/failed to connect/.test(err)) {
err.message = 'Shard test error: '
+ err.message
+ '\n'
+ ' Are you sure there is a db running at '
+ uri + ' ?'
+ '\n';
}
return done(err);
});
db.on('open', function() {
const P = db.model('ShardPerson', collection);
P.on('index', function() {
const cmd = {};
cmd.shardcollection = db.name + '.' + collection;
cmd.key = P.schema.options.shardkey;
P.db.db.executeDbAdminCommand(cmd, function(err) {
assert.ifError(err);
db.db.admin().serverStatus(function(err, info) {
db.close();
assert.ifError(err);
version = info.version.split('.').map(function(n) {
return parseInt(n, 10);
});
greaterThan20x = version[0] > 2 || version[0] === 2 && version[0] > 0;
done();
});
});
});
});
});
it('can read and write to a shard', function(done) {
const db = start({ uri: uri });
const P = db.model('ShardPerson', collection);
P.create({ name: 'ryu', age: 25, likes: ['street fighting'] }, function(err, ryu) {
assert.ifError(err);
P.findById(ryu._id, function(err, doc) {
db.close();
assert.ifError(err);
assert.equal(doc.id, ryu.id);
done();
});
});
});
it('save() and remove() works with shard keys transparently', function(done) {
const db = start({ uri: uri });
const P = db.model('ShardPerson', collection);
const zangief = new P({ name: 'Zangief', age: 33 });
zangief.save(function(err) {
assert.ifError(err);
assert.equal(zangief.$__.shardval.name, 'Zangief');
assert.equal(zangief.$__.shardval.age, 33);
P.findById(zangief._id, function(err, zang) {
assert.ifError(err);
assert.equal(zang.$__.shardval.name, 'Zangief');
assert.equal(zang.$__.shardval.age, 33);
zang.likes = ['spinning', 'laughing'];
zang.save(function(err) {
assert.ifError(err);
assert.equal(zang.$__.shardval.name, 'Zangief');
assert.equal(zang.$__.shardval.age, 33);
zang.likes.addToSet('winning');
zang.save(function(err) {
assert.ifError(err);
assert.equal(zang.$__.shardval.name, 'Zangief');
assert.equal(zang.$__.shardval.age, 33);
zang.remove(function(err) {
db.close();
assert.ifError(err);
done();
});
});
});
});
});
});
it('inserting to a sharded collection without the full shard key fails', function(done) {
const db = start({ uri: uri });
const P = db.model('ShardPerson', collection);
let pending = 6;
P.create({ name: 'ryu', likes: ['street fighting'] }, function(err) {
assert.ok(err);
assert.ok(err.message);
if (!--pending) {
db.close();
done();
}
});
P.create({ likes: ['street fighting'] }, function(err) {
assert.ok(err);
assert.ok(err.message);
if (!--pending) {
db.close();
done();
}
});
P.create({ name: 'ryu' }, function(err) {
assert.ok(err);
assert.ok(err.message);
if (!--pending) {
db.close();
done();
}
});
P.create({ age: 49 }, function(err) {
assert.ok(err);
assert.ok(err.message);
if (!--pending) {
db.close();
done();
}
});
P.create({ likes: ['street fighting'], age: 8 }, function(err) {
assert.ok(err);
assert.ok(err.message);
if (!--pending) {
db.close();
done();
}
});
const p = new P();
p.save(function(err) {
assert.ok(err);
assert.ok(err.message);
if (!--pending) {
db.close();
done();
}
});
});
it('updating a sharded collection without the full shard key fails', function(done) {
const db = start({ uri: uri });
const P = db.model('ShardPerson', collection);
P.create({ name: 'ken', age: 27 }, function(err, ken) {
assert.ifError(err);
P.update({ name: 'ken' }, { likes: ['kicking', 'punching'] }, function(err) {
assert.ok(/shard key/.test(err.message));
P.update({ _id: ken._id, name: 'ken' }, { likes: ['kicking', 'punching'] }, function(err) {
if (greaterThan20x) {
assert.ok(!err, err);
} else {
assert.ok(/shard key/.test(err.message));
}
P.update({ _id: ken._id, age: 27 }, { likes: ['kicking', 'punching'] }, function(err) {
if (greaterThan20x) {
assert.ok(!err, err);
} else {
assert.ok(/shard key/.test(err.message));
}
P.update({ age: 27 }, { likes: ['kicking', 'punching'] }, function(err) {
db.close();
assert.ok(err);
done();
});
});
});
});
});
});
it('updating shard key values fails', function(done) {
const db = start({ uri: uri });
const P = db.model('ShardPerson', collection);
P.create({ name: 'chun li', age: 19, likes: ['street fighting'] }, function(err, chunli) {
assert.ifError(err);
assert.equal(chunli.$__.shardval.name, 'chun li');
assert.equal(chunli.$__.shardval.age, 19);
chunli.age = 20;
chunli.save(function(err) {
assert.ok(/^After applying the update to the document/.test(err.message));
assert.equal(chunli.$__.shardval.name, 'chun li');
assert.equal(chunli.$__.shardval.age, 19);
P.findById(chunli._id, function(err, chunli) {
assert.ifError(err);
assert.equal(chunli.$__.shardval.name, 'chun li');
assert.equal(chunli.$__.shardval.age, 19);
chunli.name = 'chuuuun liiiii';
chunli.save(function(err) {
db.close();
assert.ok(/^After applying the update to the document/.test(err.message));
done();
});
});
});
});
});
it('allows null shard key values', function(done) {
const db = start({ uri: uri });
const P = db.model('ShardPerson', collection);
P.create({ name: null, age: 27 }, function(err, ken) {
assert.ifError(err);
P.findById(ken, function(err) {
assert.ifError(err);
done();
});
});
});
after(function(done) {
const db = start({ uri: uri });
const P = db.model('ShardPerson', collection);
P.collection.drop(function() {
db.close();
done();
});
});
});