types.map.test.js
'use strict';
/**
* Module dependencies.
*/
const assert = require('assert');
const co = require('co');
const start = require('./common');
const mongoose = start.mongoose;
/**
* Test.
*/
describe('Map', function() {
let db;
before(function() {
db = start();
});
after(function(done) {
db.close(done);
});
it('validation', function() {
let nestedValidateCalls = [];
let validateCalls = [];
const TestSchema = new mongoose.Schema({
v: {
type: Map,
of: {
type: Number,
validate: function(v) {
nestedValidateCalls.push(v);
return v < 4;
}
},
validate: function(v) {
validateCalls.push(v);
return true;
}
}
});
const Test = db.model('MapTest', TestSchema);
return co(function*() {
const doc = yield Test.create({ v: { x: 1 } });
assert.deepEqual(nestedValidateCalls, [1]);
assert.equal(validateCalls.length, 1);
assert.equal(validateCalls[0].get('x'), 1);
assert.ok(doc.v instanceof Map);
let threw = false;
try {
yield Test.create({ v: { notA: 'number' } });
} catch (error) {
threw = true;
assert.ok(!error.errors['v']);
assert.ok(error.errors['v.notA']);
}
assert.ok(threw);
doc.v.set('y', 5);
threw = false;
try {
yield doc.save();
} catch (error) {
threw = true;
assert.ok(!error.errors['v']);
assert.ok(error.errors['v.y']);
}
assert.ok(threw);
});
});
it('deep set', function(done) {
const userSchema = new mongoose.Schema({
socialMediaHandles: {
type: Map,
of: String
}
});
const User = db.model('MapDeepSet', userSchema);
const user = new User({ socialMediaHandles: {} });
user.set('socialMediaHandles.github', 'vkarpov15');
assert.equal(user.socialMediaHandles.get('github'), 'vkarpov15');
assert.equal(user.get('socialMediaHandles.github'), 'vkarpov15');
done();
});
it('query casting', function() {
const TestSchema = new mongoose.Schema({
v: {
type: Map,
of: Number
}
});
const Test = db.model('MapQueryTest', TestSchema);
return co(function*() {
const docs = yield Test.create([
{ v: { n: 1 } },
{ v: { n: 2 } }
]);
let res = yield Test.find({ 'v.n': 1 });
assert.equal(res.length, 1);
res = yield Test.find({ v: { n: 2 } });
assert.equal(res.length, 1);
yield Test.updateOne({ _id: docs[1]._id }, { 'v.n': 3 });
res = yield Test.find({ v: { n: 3 } });
assert.equal(res.length, 1);
let threw = false;
try {
yield Test.updateOne({ _id: docs[1]._id }, { 'v.n': 'not a number' });
} catch (error) {
threw = true;
assert.equal(error.name, 'CastError');
}
assert.ok(threw);
res = yield Test.find({ v: { n: 3 } });
assert.equal(res.length, 1);
});
});
it('defaults', function() {
const TestSchema = new mongoose.Schema({
n: Number,
m: {
type: Map,
of: Number,
default: { bacon: 2, eggs: 6 }
}
});
const Test = db.model('MapDefaultsTest', TestSchema);
return co(function*() {
const doc = new Test({});
assert.ok(doc.m instanceof Map);
assert.deepEqual(Array.from(doc.toObject().m.keys()), ['bacon', 'eggs']);
yield Test.updateOne({}, { n: 1 }, { upsert: true, setDefaultsOnInsert: true });
const saved = yield Test.findOne({ n: 1 });
assert.ok(saved);
assert.deepEqual(Array.from(saved.toObject().m.keys()),
['bacon', 'eggs']);
});
});
it('validation', function() {
const TestSchema = new mongoose.Schema({
ratings: {
type: Map,
of: {
type: Number,
min: 1,
max: 10
}
}
});
const Test = db.model('MapValidationTest', TestSchema);
return co(function*() {
const doc = new Test({ ratings: { github: 11 } });
assert.ok(doc.ratings instanceof Map);
let threw = false;
try {
yield doc.save();
} catch (err) {
threw = true;
assert.ok(err.errors['ratings.github']);
}
assert.ok(threw);
doc.ratings.set('github', 8);
// Shouldn't throw
yield doc.save();
threw = false;
try {
yield Test.updateOne({}, { $set: { 'ratings.github': 11 } }, {
runValidators: true
});
} catch (err) {
threw = true;
assert.ok(err.errors['ratings.github']);
}
assert.ok(threw);
});
});
it('with single nested subdocs', function() {
const TestSchema = new mongoose.Schema({
m: {
type: Map,
of: new mongoose.Schema({ n: Number }, { _id: false, id: false })
}
});
const Test = db.model('MapEmbeddedTest', TestSchema);
return co(function*() {
let doc = new Test({ m: { bacon: { n: 2 } } });
yield doc.save();
assert.ok(doc.m instanceof Map);
assert.deepEqual(doc.toObject().m.get('bacon').toObject(), { n: 2 });
doc.m.get('bacon').n = 4;
yield doc.save();
assert.deepEqual(doc.toObject().m.get('bacon').toObject(), { n: 4 });
doc = yield Test.findById(doc._id);
assert.deepEqual(doc.toObject().m.get('bacon').toObject(), { n: 4 });
});
});
describe('populate', function() {
it('populate individual path', function() {
const UserSchema = new mongoose.Schema({
keys: {
type: Map,
of: {
type: mongoose.Schema.Types.ObjectId,
ref: 'MapPopulateTest'
}
}
});
const KeySchema = new mongoose.Schema({ key: String });
const User = db.model('MapPopulateTest_0', UserSchema);
const Key = db.model('MapPopulateTest', KeySchema);
return co(function*() {
const key = yield Key.create({ key: 'abc123' });
const key2 = yield Key.create({ key: 'key' });
const doc = yield User.create({ keys: { github: key._id } });
const populated = yield User.findById(doc).populate('keys.github');
assert.equal(populated.keys.get('github').key, 'abc123');
populated.keys.set('twitter', key2._id);
yield populated.save();
const rawDoc = yield User.collection.findOne({ _id: doc._id });
assert.deepEqual(rawDoc.keys, { github: key._id, twitter: key2._id });
});
});
it('populate entire map', function() {
const UserSchema = new mongoose.Schema({
apiKeys: {
type: Map,
of: {
type: mongoose.Schema.Types.ObjectId,
ref: 'MapPopulateWildcardTest'
}
}
});
const KeySchema = new mongoose.Schema({ key: String });
const User = db.model('MapPopulateWildcardTest_0', UserSchema);
const Key = db.model('MapPopulateWildcardTest', KeySchema);
return co(function*() {
const key = yield Key.create({ key: 'abc123' });
const key2 = yield Key.create({ key: 'key' });
const doc = yield User.create({ apiKeys: { github: key._id, twitter: key2._id } });
const populated = yield User.findById(doc).populate('apiKeys');
assert.equal(populated.apiKeys.get('github').key, 'abc123');
assert.equal(populated.apiKeys.get('twitter').key, 'key');
});
});
it('populate entire map in doc', function() {
const UserSchema = new mongoose.Schema({
apiKeys: {
type: Map,
of: {
type: mongoose.Schema.Types.ObjectId,
ref: 'MapPopulateMapDocTest'
}
}
});
const KeySchema = new mongoose.Schema({ key: String });
const User = db.model('MapPopulateMapDocTest_0', UserSchema);
const Key = db.model('MapPopulateMapDocTest', KeySchema);
return co(function*() {
const key = yield Key.create({ key: 'abc123' });
const key2 = yield Key.create({ key: 'key' });
const doc = yield User.create({ apiKeys: { github: key._id, twitter: key2._id } });
const _doc = yield User.findById(doc);
yield _doc.populate('apiKeys').execPopulate();
assert.equal(_doc.apiKeys.get('github').key, 'abc123');
assert.equal(_doc.apiKeys.get('twitter').key, 'key');
});
});
it('avoid populating as map if populate on obj (gh-6460)', function() {
const UserSchema = new mongoose.Schema({
apiKeys: {}
});
const KeySchema = new mongoose.Schema({ key: String });
const User = db.model('gh6460_User', UserSchema);
const Key = db.model('gh6460_Key', KeySchema);
return co(function*() {
const key = yield Key.create({ key: 'abc123' });
const key2 = yield Key.create({ key: 'key' });
const doc = yield User.create({ apiKeys: { github: key._id, twitter: key2._id } });
const populated = yield User.findById(doc).populate('apiKeys');
assert.ok(!(populated.apiKeys instanceof Map));
});
});
});
it('discriminators', function() {
const TestSchema = new mongoose.Schema({
n: Number
});
const Test = db.model('MapDiscrimTest', TestSchema);
const Disc = Test.discriminator('MapDiscrimTest_0', new mongoose.Schema({
m: {
type: Map,
of: Number
}
}));
return co(function*() {
const doc = new Disc({ m: { test: 1 } });
assert.ok(doc.m instanceof Map);
assert.deepEqual(Array.from(doc.toObject().m.keys()), ['test']);
yield doc.save();
const fromDb = yield Disc.findOne({ 'm.test': 1 });
assert.ok(fromDb);
assert.equal(fromDb._id.toHexString(), doc._id.toHexString());
});
});
it('embedded discriminators', function() {
const EmployeeSchema = new mongoose.Schema({
name: String
}, { _id: false, id: false });
const DepartmentSchema = new mongoose.Schema({
employees: [EmployeeSchema]
});
DepartmentSchema.path('employees').discriminator('Sales', new mongoose.Schema({
clients: [String]
}, { _id: false, id: false }));
DepartmentSchema.path('employees').discriminator('Engineering', new mongoose.Schema({
apiKeys: {
type: Map,
of: String
}
}, { _id: false, id: false }));
const Department = db.model('MapEmbeddedDiscrimTest', DepartmentSchema);
return co(function*() {
const dept = new Department({
employees: [
{ __t: 'Sales', name: 'E1', clients: ['test1', 'test2'] },
{ __t: 'Engineering', name: 'E2', apiKeys: { github: 'test3' } }
]
});
assert.deepEqual(dept.toObject().employees[0],
{ __t: 'Sales', name: 'E1', clients: ['test1', 'test2'] });
assert.deepEqual(Array.from(dept.toObject().employees[1].apiKeys.values()),
['test3']);
yield dept.save();
let fromDb = yield Department.findOne({ 'employees.apiKeys.github': 'test3' });
assert.ok(fromDb);
dept.employees[1].apiKeys.set('github', 'test4');
yield dept.save();
fromDb = yield Department.findOne({ 'employees.apiKeys.github': 'test4' });
assert.ok(fromDb);
});
});
it('toJSON seralizes map paths (gh-6478)', function() {
const schema = new mongoose.Schema({
str: {
type: Map,
of: String
},
num: {
type: Map,
of: Number
}
});
const Test = db.model('gh6478', schema);
const test = new Test({
str: {
testing: '123'
},
num: {
testing: 456
}
});
assert.deepEqual(test.str.toJSON(), { testing: '123' });
assert.deepEqual(test.num.toJSON(), { testing: 456 });
return co(function*() {
yield test.save();
let found = yield Test.findOne();
assert.deepEqual(found.str.toJSON(), { testing: '123' });
assert.deepEqual(found.num.toJSON(), { testing: 456 });
});
});
});