deno.land / x / mongoose@6.7.5 / test / timestamps.test.js
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072'use strict';
const start = require('./common');
const assert = require('assert');
const mongoose = start.mongoose;const Schema = mongoose.Schema;
describe('timestamps', function() { let db;
before(function() { db = start(); });
after(async function() { await db.close(); });
beforeEach(() => db.deleteModel(/.*/)); afterEach(() => require('./util').clearTestData(db)); afterEach(() => require('./util').stopRemainingOps(db));
// These tests use schemas only, no database connection needed. describe('schema options', function() { it('should have createdAt and updatedAt fields', function(done) { const TestSchema = new Schema({ name: String }, { timestamps: true });
assert.ok(TestSchema.path('createdAt')); assert.ok(TestSchema.path('updatedAt')); done(); });
it('should have createdAt and updatedAt fields', function(done) { const TestSchema = new Schema({ name: String });
TestSchema.set('timestamps', true);
assert.ok(TestSchema.path('createdAt')); assert.ok(TestSchema.path('updatedAt')); done(); });
it('should have created and updatedAt fields', function(done) { const TestSchema = new Schema({ name: String }, { timestamps: { createdAt: 'created' } });
assert.ok(TestSchema.path('created')); assert.ok(TestSchema.path('updatedAt')); done(); });
it('should have created and updatedAt fields', function(done) { const TestSchema = new Schema({ name: String });
TestSchema.set('timestamps', { createdAt: 'created' });
assert.ok(TestSchema.path('created')); assert.ok(TestSchema.path('updatedAt')); done(); });
it('should have created and updated fields', function(done) { const TestSchema = new Schema({ name: String }, { timestamps: { createdAt: 'created', updatedAt: 'updated' } });
assert.ok(TestSchema.path('created')); assert.ok(TestSchema.path('updated')); done(); });
it('should have just createdAt if updatedAt set to falsy', function(done) { const TestSchema = new Schema({ name: String });
TestSchema.set('timestamps', { updatedAt: false });
assert.ok(TestSchema.path('createdAt')); assert.ok(!TestSchema.path('updatedAt')); done(); });
it('should have created and updated fields', function(done) { const TestSchema = new Schema({ name: String });
TestSchema.set('timestamps', { createdAt: 'created', updatedAt: 'updated' });
assert.ok(TestSchema.path('created')); assert.ok(TestSchema.path('updated')); done(); });
it('TTL index with timestamps (gh-5656)', function() { const testSchema = new Schema({ foo: String, updatedAt: { type: Date, expires: '2h' } }, { timestamps: true });
const indexes = testSchema.indexes(); assert.deepEqual(indexes, [ [{ updatedAt: 1 }, { background: true, expireAfterSeconds: 7200 }] ]); }); });
it('does not override timestamp params defined in schema (gh-4868)', function(done) { const startTime = Date.now(); const schema = new mongoose.Schema({ createdAt: { type: Date, select: false }, updatedAt: { type: Date, select: true }, name: String }, { timestamps: true }); const M = db.model('Test', schema);
M.create({ name: 'Test' }, function(error, doc) { assert.ifError(error); M.findOne({ _id: doc._id }, function(error, doc) { assert.ifError(error); assert.ok(!doc.createdAt); assert.ok(doc.updatedAt); assert.ok(doc.updatedAt.valueOf() >= startTime); done(); }); }); });
it('updatedAt without createdAt (gh-5598)', function(done) { const startTime = Date.now(); const schema = new mongoose.Schema({ name: String }, { timestamps: { createdAt: null, updatedAt: true } }); const M = db.model('Test', schema);
M.create({ name: 'Test' }, function(error, doc) { assert.ifError(error); M.findOne({ _id: doc._id }, function(error, doc) { assert.ifError(error); assert.ok(!doc.createdAt); assert.ok(doc.updatedAt); assert.ok(doc.updatedAt.valueOf() >= startTime); done(); }); }); });
it('updatedAt without createdAt for nested (gh-5598)', function(done) { const startTime = Date.now(); const schema = new mongoose.Schema({ name: String }, { timestamps: { createdAt: null, updatedAt: true } }); const parentSchema = new mongoose.Schema({ child: schema }); const M = db.model('Test', parentSchema);
M.create({ child: { name: 'test' } }, function(error, doc) { assert.ifError(error); M.findOne({ _id: doc._id }, function(error, doc) { assert.ifError(error); assert.ok(!doc.child.createdAt); assert.ok(doc.child.updatedAt); assert.ok(doc.child.updatedAt.valueOf() >= startTime); done(); }); }); });
it('nested paths (gh-4503)', function(done) { const startTime = Date.now(); const schema = new mongoose.Schema({ name: String }, { timestamps: { createdAt: 'ts.c', updatedAt: 'ts.a' } }); const M = db.model('Test', schema);
M.create({ name: 'Test' }, function(error, doc) { assert.ifError(error); M.findOne({ _id: doc._id }, function(error, doc) { assert.ifError(error); assert.ok(doc.ts.c); assert.ok(doc.ts.c.valueOf() >= startTime); assert.ok(doc.ts.a); assert.ok(doc.ts.a.valueOf() >= startTime); done(); }); }); });
it('does not override nested timestamp params defined in schema (gh-4868)', function(done) { const startTime = Date.now(); const schema = new mongoose.Schema({ ts: { createdAt: { type: Date, select: false }, updatedAt: { type: Date, select: true } }, name: String }, { timestamps: { createdAt: 'ts.createdAt', updatedAt: 'ts.updatedAt' } }); const M = db.model('Test', schema);
M.create({ name: 'Test' }, function(error, doc) { assert.ifError(error); M.findOne({ _id: doc._id }, function(error, doc) { assert.ifError(error); assert.ok(!doc.ts.createdAt); assert.ok(doc.ts.updatedAt); assert.ok(doc.ts.updatedAt.valueOf() >= startTime); done(); }); }); });
it('does not override timestamps in nested schema (gh-4868)', function(done) { const startTime = Date.now(); const tsSchema = new mongoose.Schema({ createdAt: { type: Date, select: false }, updatedAt: { type: Date, select: true } }); const schema = new mongoose.Schema({ ts: tsSchema, name: String }, { timestamps: { createdAt: 'ts.createdAt', updatedAt: 'ts.updatedAt' } }); const M = db.model('Test', schema);
M.create({ name: 'Test' }, function(error, doc) { assert.ifError(error); M.findOne({ _id: doc._id }, function(error, doc) { assert.ifError(error); assert.ok(!doc.ts.createdAt); assert.ok(doc.ts.updatedAt); assert.ok(doc.ts.updatedAt.valueOf() >= startTime); done(); }); }); });
it('no timestamps added when parent/child timestamps explicitly false (gh-7202)', function(done) { const subSchema = new Schema({}, { timestamps: false }); const schema = new Schema({ sub: subSchema }, { timestamps: false });
const Test = db.model('Test', schema); const test = new Test({ sub: {} });
test.save((err, saved) => { assert.ifError(err); assert.strictEqual(saved.createdAt, undefined); assert.strictEqual(saved.updatedAt, undefined); assert.strictEqual(saved.sub.createdAt, undefined); assert.strictEqual(saved.sub.updatedAt, undefined); done(); }); });
it('avoids calling createdAt getters when setting updatedAt (gh-7496)', function() { const modelSchema = new Schema({ createdAt: { type: Date, get: (date) => date && date.valueOf() / 1000 }, updatedAt: { type: Date, get: (date) => date && date.valueOf() / 1000 } }, { timestamps: true });
const Model = db.model('Test', modelSchema);
const start = new Date(); return Model.create({}).then(doc => { assert.ok(doc._doc.createdAt.valueOf() >= start.valueOf()); assert.ok(doc._doc.updatedAt.valueOf() >= start.valueOf()); }); });
it('handles custom statics that conflict with built-in functions (gh-7698)', function() { const schema = new mongoose.Schema({ name: String }, { timestamps: true });
let called = 0; schema.statics.updateOne = function() { ++called; return mongoose.Model.updateOne.apply(this, arguments); }; const M = db.model('Test', schema);
const startTime = Date.now(); return M.deleteMany({}). then(() => M.updateOne({}, { name: 'foo' }, { upsert: true })). then(() => assert.equal(called, 1)). then(() => M.findOne()). then(doc => assert.ok(doc.createdAt.valueOf() >= startTime)); });
it('timestamps handle reusing child schemas (gh-7712)', async function() { const childSchema = new mongoose.Schema({ name: String }, { timestamps: true }); const M1 = db.model('Test', new mongoose.Schema({ child: childSchema })); const M2 = db.model('Test1', new mongoose.Schema({ children: [childSchema] }));
let startTime = null; let doc = await M1.create({ child: { name: 'foo' } }); assert.ok(doc.child.updatedAt); await new Promise(resolve => setTimeout(resolve, 25)); startTime = Date.now();
doc = await M1.findOneAndUpdate({}, { $set: { 'child.name': 'bar' } }, { new: true }); assert.ok(doc.child.updatedAt.valueOf() >= startTime, `Timestamp not updated: ${doc.child.updatedAt}`);
doc = await M2.create({ children: [{ name: 'foo' }] }); assert.ok(doc.children[0].updatedAt); await new Promise(resolve => setTimeout(resolve, 25)); startTime = Date.now();
doc = await M2.findOneAndUpdate({ 'children.name': 'foo' }, { $set: { 'children.$.name': 'bar' } }, { new: true }); assert.ok(doc.children[0].updatedAt.valueOf() >= startTime, `Timestamp not updated: ${doc.children[0].updatedAt}`); });
it('respects timestamps: false in child schema (gh-8007)', async function() { const sub = Schema({ name: String }, { timestamps: false, _id: false }); const schema = Schema({ data: sub });
const Model = db.model('Test', schema);
let res = await Model.create({ data: {} });
await Model.bulkWrite([ { updateOne: { filter: { _id: res._id }, update: { 'data.name': 'foo' } } } ]);
res = await Model.findOne({}).lean(); assert.deepEqual(res.data, { name: 'foo' }); });
it('updates updatedAt when calling update without $set (gh-4768)', async function() { const Model = db.model('Test', Schema({ name: String }, { timestamps: true }));
let doc = await Model.create({ name: 'test1' }); const start = doc.updatedAt;
await delay(50); doc = await Model.findOneAndUpdate({}, doc.toObject(), { new: true }); assert.ok(doc.updatedAt > start, `${doc.updatedAt} >= ${start}`); });
it('updates updatedAt when calling update on subchild', async function() { const subchildschema = new mongoose.Schema({ name: String }, { timestamps: true }); const schema = new mongoose.Schema({ name: String, subchild: subchildschema }, { timestamps: true }); const parentSchema = new mongoose.Schema({ child: schema }, { timestamps: true });
const Model = db.model('Test', parentSchema);
let doc = await Model.create({ name: 'test', child: { name: 'child', subchild: { name: 'subchild' } } }); assert.ok(doc.child.updatedAt); const startTime = doc.createdAt; await new Promise(resolve => setTimeout(resolve), 25);
doc = await Model.findOneAndUpdate({}, { $set: { 'child.subchild.name': 'subChildUpdated' } }, { new: true });
assert.ok(doc.updatedAt.valueOf() > startTime, `Parent Timestamp not updated: ${doc.updatedAt}`); assert.ok(doc.child.updatedAt.valueOf() > startTime, `Child Timestamp not updated: ${doc.updatedAt}`); assert.ok(doc.child.subchild.updatedAt.valueOf() > startTime, `SubChild Timestamp not updated: ${doc.updatedAt}`); });
it('sets timestamps on deeply nested docs on upsert (gh-8894)', function() { const JournalSchema = Schema({ message: String }, { timestamps: true }); const ProductSchema = Schema({ name: String, journal: [JournalSchema], lastJournal: JournalSchema }, { timestamps: true }); const schema = Schema({ products: [ProductSchema] }, { timestamps: true }); const Order = db.model('Order', schema);
const update = { products: [{ name: 'ASUS Vivobook Pro', journal: [{ message: 'out of stock' }], lastJournal: { message: 'out of stock' } }] };
return Order.findOneAndUpdate({}, update, { upsert: true, new: true }). then(doc => { assert.ok(doc.products[0].journal[0].createdAt); assert.ok(doc.products[0].journal[0].updatedAt);
assert.ok(doc.products[0].lastJournal.createdAt); assert.ok(doc.products[0].lastJournal.updatedAt); }); });
it('sets timestamps on bulk write without `$set` (gh-9268)', async function() { const NestedSchema = new Schema({ name: String }, { timestamps: true, _id: false }); const TestSchema = new Schema({ nestedDoc: NestedSchema }); const Test = db.model('Test', TestSchema);
await Test.create({ nestedDoc: { name: 'test' } }); const doc = await Test.findOne().lean();
await delay(10); await Test.bulkWrite([ { updateOne: { filter: {}, update: { 'nestedDoc.name': 'test2' } } } ]);
const newDoc = await Test.findById(doc).lean(); assert.ok(newDoc.nestedDoc.updatedAt > doc.nestedDoc.updatedAt); });
it('works with property named "set" (gh-9428)', function() { const schema = new Schema({ set: String }, { timestamps: true }); const Model = db.model('Test', schema);
return Model.create({ set: 'test' }).then(doc => assert.ok(doc.createdAt)); });
it('should not override createdAt when not selected (gh-4340)', function(done) { const TestSchema = new Schema({ name: String }, { timestamps: true });
const Test = db.model('Test', TestSchema);
Test.create({ name: 'hello' }, function(err, doc) { // Let’s save the dates to compare later. const createdAt = doc.createdAt; const updatedAt = doc.updatedAt;
assert.ok(doc.createdAt);
Test.findById(doc._id, { name: true }, function(err, doc) { // The dates shouldn’t be selected here. assert.ok(!doc.createdAt); assert.ok(!doc.updatedAt);
doc.name = 'world';
doc.save(function(err, doc) { // Let’s save the new updatedAt date as it should have changed. const newUpdatedAt = doc.updatedAt;
assert.ok(!doc.createdAt); assert.ok(doc.updatedAt);
Test.findById(doc._id, function(err, doc) { // Let’s make sure that everything is working again by // comparing the dates with the ones we saved. assert.equal(doc.createdAt.valueOf(), createdAt.valueOf()); assert.notEqual(doc.updatedAt.valueOf(), updatedAt.valueOf()); assert.equal(doc.updatedAt.valueOf(), newUpdatedAt.valueOf());
done(); }); }); }); }); });
describe('auto update createdAt and updatedAt when create/save/update document', function() { let CatSchema; let Cat;
beforeEach(function() { CatSchema = new Schema({ name: String, hobby: String }, { timestamps: true }); Cat = db.model('Cat', CatSchema); return Cat.deleteMany({}).then(() => Cat.create({ name: 'newcat' })); });
it('should have fields when create', function(done) { const cat = new Cat({ name: 'newcat' }); cat.save(function(err, doc) { assert.ok(doc.createdAt); assert.ok(doc.updatedAt); assert.ok(doc.createdAt.getTime() === doc.updatedAt.getTime()); done(); }); });
it('sets timestamps on findOneAndUpdate', function(done) { Cat.findOneAndUpdate({ name: 'notexistname' }, { $set: {} }, { upsert: true, new: true }, function(err, doc) { assert.ok(doc.createdAt); assert.ok(doc.updatedAt); assert.ok(doc.createdAt.getTime() === doc.updatedAt.getTime()); done(); }); });
it('sets timestamps on findOneAndReplace (gh-9951)', function() { return Cat.findOneAndReplace({ name: 'notexistname' }, {}, { upsert: true, new: true }).then(doc => { assert.ok(doc.createdAt); assert.ok(doc.updatedAt); assert.ok(doc.createdAt.getTime() === doc.updatedAt.getTime()); }); });
it('should change updatedAt when save', function(done) { Cat.findOne({ name: 'newcat' }, function(err, doc) { const old = doc.updatedAt;
doc.hobby = 'coding';
doc.save(function(err, doc) { assert.ok(doc.updatedAt.getTime() > old.getTime()); done(); }); }); });
it('should not change updatedAt when save with no modifications', function(done) { Cat.findOne({ name: 'newcat' }, function(err, doc) { const old = doc.updatedAt;
doc.save(function(err, doc) { assert.ok(doc.updatedAt.getTime() === old.getTime()); done(); }); }); });
it('can skip with timestamps: false (gh-7357)', async function() { const cat = await Cat.findOne();
const old = cat.updatedAt;
await delay(10);
cat.hobby = 'fishing';
await cat.save({ timestamps: false });
assert.strictEqual(cat.updatedAt, old); });
it('can skip with `$timestamps(false)` (gh-12117)', async function() { const cat = await Cat.findOne(); const old = cat.updatedAt;
await delay(10);
cat.hobby = 'fishing';
cat.$timestamps(false); await cat.save();
assert.strictEqual(cat.updatedAt, old); });
it('should change updatedAt when findOneAndUpdate', function(done) { Cat.create({ name: 'test123' }, function(err) { assert.ifError(err); Cat.findOne({ name: 'test123' }, function(err, doc) { const old = doc.updatedAt; Cat.findOneAndUpdate({ name: 'test123' }, { $set: { hobby: 'fish' } }, { new: true }, function(err, doc) { assert.ok(doc.updatedAt.getTime() > old.getTime()); done(); }); }); }); });
it('insertMany with createdAt off (gh-6381)', async function() { const CatSchema = new Schema({ name: String, createdAt: { type: Date, default: function() { return new Date('2013-06-01'); } } }, { timestamps: { createdAt: false, updatedAt: true } });
const Cat = db.model('Test', CatSchema);
const d = new Date('2011-06-01');
await Cat.deleteMany({}); await Cat.insertMany([{ name: 'a' }, { name: 'b', createdAt: d }]);
const cats = await Cat.find().sort('name');
assert.equal(cats[0].createdAt.valueOf(), new Date('2013-06-01').valueOf()); assert.equal(cats[1].createdAt.valueOf(), new Date('2011-06-01').valueOf()); });
it('should have fields when update', function(done) { Cat.findOne({ name: 'newcat' }, function(err, doc) { const old = doc.updatedAt; Cat.update({ name: 'newcat' }, { $set: { hobby: 'fish' } }, function() { Cat.findOne({ name: 'newcat' }, function(err, doc) { assert.ok(doc.updatedAt.getTime() > old.getTime()); done(); }); }); }); });
it('should change updatedAt when updateOne', function(done) { Cat.findOne({ name: 'newcat' }, function(err, doc) { const old = doc.updatedAt; Cat.updateOne({ name: 'newcat' }, { $set: { hobby: 'fish' } }, function() { Cat.findOne({ name: 'newcat' }, function(err, doc) { assert.ok(doc.updatedAt.getTime() > old.getTime()); done(); }); }); }); });
it('should change updatedAt when updateMany', function(done) { Cat.findOne({ name: 'newcat' }, function(err, doc) { const old = doc.updatedAt; Cat.updateMany({ name: 'newcat' }, { $set: { hobby: 'fish' } }, function() { Cat.findOne({ name: 'newcat' }, function(err, doc) { assert.ok(doc.updatedAt.getTime() > old.getTime()); done(); }); }); }); });
it('nested docs (gh-4049)', function(done) { const GroupSchema = new Schema({ cats: [CatSchema] });
const Group = db.model('Test', GroupSchema); const now = Date.now(); Group.create({ cats: [{ name: 'Garfield' }] }, function(error, group) { assert.ifError(error); assert.ok(group.cats[0].createdAt); assert.ok(group.cats[0].createdAt.getTime() >= now); done(); }); });
it('nested docs with push (gh-4049)', function(done) { const GroupSchema = new Schema({ cats: [CatSchema] });
const Group = db.model('Test', GroupSchema); const now = Date.now(); Group.create({ cats: [{ name: 'Garfield' }] }, function(error, group) { assert.ifError(error); group.cats.push({ name: 'Keanu' }); group.save(function(error) { assert.ifError(error); Group.findById(group._id, function(error, group) { assert.ifError(error); assert.ok(group.cats[1].createdAt); assert.ok(group.cats[1].createdAt.getTime() > now); done(); }); }); }); }); });
it('timestamps with number types (gh-3957)', async function() { const schema = Schema({ createdAt: Number, updatedAt: Number, name: String }, { timestamps: true }); const Model = db.model('Test', schema); const start = Date.now();
const doc = await Model.create({ name: 'test' });
assert.equal(typeof doc.createdAt, 'number'); assert.equal(typeof doc.updatedAt, 'number'); assert.ok(doc.createdAt >= start); assert.ok(doc.updatedAt >= start); });
it('timestamps with custom timestamp (gh-3957)', async function() { const schema = Schema({ createdAt: Number, updatedAt: Number, name: String }, { timestamps: { currentTime: () => 42 } }); const Model = db.model('Test', schema);
const doc = await Model.create({ name: 'test' });
assert.equal(typeof doc.createdAt, 'number'); assert.equal(typeof doc.updatedAt, 'number'); assert.equal(doc.createdAt, 42); assert.equal(doc.updatedAt, 42); });
it('timestamps with custom timestamp using getter method (gh-3957)', async function() { const now = Date.now(); const schema = Schema({ createdAt: { type: Schema.Types.Number, get: (createdAt) => new Date(createdAt * 1000) }, updatedAt: { type: Schema.Types.Number, get: (updatedAt) => new Date(updatedAt * 1000) }, name: String }, { timestamps: { currentTime: () => Math.floor(now / 1000) } }); const Model = db.model('Test', schema); const doc = await Model.create({ name: 'test' });
assert.equal(typeof doc.createdAt, 'object'); assert.equal(typeof doc.updatedAt, 'object'); assert.equal(Math.floor(doc.createdAt.getTime() / 1000), Math.floor(now / 1000)); assert.equal(Math.floor(doc.updatedAt.getTime() / 1000), Math.floor(now / 1000)); });
it('shouldnt bump updatedAt in single nested subdocs that are not modified (gh-9357)', async function() { const nestedSchema = Schema({ nestedA: { type: String }, nestedB: { type: String } }, { timestamps: true }); const parentSchema = Schema({ content: { a: nestedSchema, b: nestedSchema, c: String } });
const Parent = db.model('Test', parentSchema);
await Parent.deleteMany({});
const _id = await Parent.create({ content: { a: { nestedA: 'a' }, b: { nestedB: 'b' } } }).then(doc => doc._id);
const doc = await Parent.findById(_id);
const ts = doc.content.b.updatedAt; doc.content.a.nestedA = 'b'; await delay(10); await doc.save();
const fromDb = await Parent.findById(_id); assert.strictEqual(fromDb.content.b.updatedAt.valueOf(), ts.valueOf()); });
it('bumps updatedAt with mixed $set (gh-9357)', async function() { const nestedSchema = Schema({ nestedA: { type: String }, nestedB: { type: String } }, { timestamps: true }); const parentSchema = Schema({ content: { a: nestedSchema, b: nestedSchema, c: String } });
const Parent = db.model('Test', parentSchema);
await Parent.deleteMany({});
const doc = await Parent.create({ content: { a: { nestedA: 'a' }, b: { nestedB: 'b' } } }); const ts = doc.content.b.updatedAt;
await delay(10); const fromDb = await Parent.findOneAndUpdate({}, { 'content.c': 'value', $set: { 'content.a.nestedA': 'value' } }, { new: true });
assert.ok(fromDb.content.a.updatedAt.valueOf() > ts.valueOf()); });
it('makes createdAt immutable by default (gh-10139)', async function() { const schema = Schema({ name: String }, { timestamps: true }); const Model = db.model('Time', schema); const doc = await Model.create({ name: 'test' }); const test = doc.createdAt; doc.createdAt = new Date(); assert.equal(test, doc.createdAt); });
it('sets createdAt when using $push/$addToSet on path with positional operator (gh-10447)', async function() { const userSchema = new Schema({ email: String }, { timestamps: true }); const eventSchema = new Schema({ users: [userSchema], message: String }, { timestamps: true });
const churchSchema = new Schema({ events: [eventSchema] }, { timestamps: true });
const Church = db.model('Test', churchSchema);
await Church.create({ events: [{ churchId: 1, message: 'test', users: [{ churchId: 1, email: 'test@google.com' }] }] });
const church = await Church.findOneAndUpdate({ events: { $elemMatch: { users: { $not: { $elemMatch: { email: 'test2@google.com' } } } } } }, { $addToSet: { 'events.$.users': { churchId: 1, email: 'test2@google.com' } } }, { new: true });
assert.equal(church.events.length, 1); assert.equal(church.events[0].users.length, 2); assert.equal(church.events[0].users[1].email, 'test2@google.com'); assert.ok(church.events[0].users[1].createdAt); });
it('sets createdAt when creating new single nested subdoc (gh-11603)', async function() { const childSchema = new mongoose.Schema( { name: String }, { timestamps: { createdAt: 'created', updatedAt: false }, _id: false } );
const testSchema = new mongoose.Schema({ child: childSchema, age: Number });
const Test = db.model('Test', testSchema);
await Test.create({ age: 10 }); await Test.findOneAndUpdate( { child: { $exists: false } }, { $set: { child: { name: 'Update Creation' } } } );
let updatedParent = await Test.findOne({ age: 10 }); assert.ok(updatedParent.child.created instanceof Date);
await Test.create({ age: 12 }); const parentToChange = await Test.findOne({ child: { $exists: false } });
parentToChange.child = { name: 'Save Creation' }; await parentToChange.save();
updatedParent = await Test.findOne({ age: 12 }); assert.ok(updatedParent.child.created instanceof Date); const date = updatedParent.child.created;
updatedParent.child.name = 'test update'; await updatedParent.save();
updatedParent = await Test.findOne({ age: 12 }); assert.ok(updatedParent.child.created instanceof Date); assert.strictEqual(updatedParent.child.created.valueOf(), date.valueOf()); });
it('sets timestamps on sub-schema if parent schema does not have timestamps: true (gh-12119)', async function() { // `timestamps` option set to true on deepest sub document const ConditionSchema = new mongoose.Schema({ kind: String, amount: Number }, { timestamps: true });
// no `timestamps` option defined const ProfileSchema = new mongoose.Schema({ conditions: [ConditionSchema] });
const UserSchema = new mongoose.Schema({ name: String, profile: { type: ProfileSchema } }, { timestamps: true });
const User = db.model('User', UserSchema);
const res = await User.findOneAndUpdate( { name: 'test' }, { $set: { profile: { conditions: [{ kind: 'price', amount: 10 }] } } }, { upsert: true, returnDocument: 'after' } );
assert.ok(res.profile.conditions[0].createdAt); assert.ok(res.profile.conditions[0].updatedAt); });
it('works with insertMany() and embedded discriminators (gh-12150)', async function() { const AssetSchema = new Schema({ url: String, size: String }, { timestamps: true }); const HeaderSectionSchema = new Schema({ title: String, image: AssetSchema });
// Abstract section const BaseSectionSchema = new Schema({ isVisible: Boolean }, { discriminatorKey: 'kind' });
// Main Schema const PageSchema = new Schema({ sections: [BaseSectionSchema] // Same error without the array "sections: BaseSectionSchema" }, { timestamps: true });
const sections = PageSchema.path('sections'); sections.discriminator('header', HeaderSectionSchema);
const Test = db.model('Test', PageSchema);
await Test.insertMany([{ sections: { isVisible: true, kind: 'header', title: 'h1' } }]);
const doc = await Test.findOne(); assert.equal(doc.sections.length, 1); assert.equal(doc.sections[0].title, 'h1'); });
it('findOneAndUpdate creates subdocuments with timestamps in correct order (gh-12475)', async function() { const testSchema = new Schema( { uuid: String, addresses: [new Schema({ location: String }, { timestamps: true })] }, { timestamps: true } );
const Test = db.model('Test', testSchema);
const item = new Test({ uuid: '123', addresses: [{ location: 'earth' }] }); await item.save();
const newItem = await Test.findOneAndUpdate({ uuid: '123' }, { uuid: '456', $push: { addresses: { location: 'earth' } } }, { upsert: true, new: true, runValidators: true });
for (const address of newItem.addresses) { const keys = Object.keys(address.toObject()); assert.deepStrictEqual(keys, ['location', '_id', 'createdAt', 'updatedAt']); } });});
async function delay(ms) { await new Promise((resolve) => setTimeout(resolve, ms));}
Version Info