├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package.json └── test ├── custom_names_only_test.js ├── custom_names_types_options_test.js ├── custom_names_types_test.js ├── index_find_update_test.js ├── index_test.js ├── index_update_test.js ├── sub_document_test.js └── touch_test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .idea/ 4 | *.iml 5 | *.iws 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: "node_js" 2 | node_js: 3 | - "6" 4 | - "5" 5 | - "4" 6 | services: 7 | - mongodb 8 | - elasticsearch 9 | before_script: 10 | - sleep 10 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | copyright (c) 2012 Nicholas Penree 2 | Original work: copyright (c) 2012 Brian Noguchi 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mongoose Timestamps Plugin 2 | ========================== 3 | 4 | [![Build Status](https://travis-ci.org/drudge/mongoose-timestamp.svg?branch=master)](https://travis-ci.org/drudge/mongoose-timestamp) 5 | [![Dependency Status](https://david-dm.org/drudge/mongoose-timestamp.svg)](https://david-dm.org/drudge/mongoose-timestamp) 6 | [![devDependency Status](https://david-dm.org/drudge/mongoose-timestamp/dev-status.svg)](https://david-dm.org/drudge/mongoose-timestamp#info=devDependencies) 7 | [![Downloads Monthly](https://img.shields.io/npm/dm/mongoose-timestamp.svg)](https://www.npmjs.com/package/mongoose-timestamp) 8 | [![Downloads Total](https://img.shields.io/npm/dt/mongoose-timestamp.svg)](https://www.npmjs.com/package/mongoose-timestamp) 9 | 10 | Simple plugin for [Mongoose](https://github.com/LearnBoost/mongoose) which adds `createdAt` and `updatedAt` date attributes 11 | that get auto-assigned to the most recent create/update timestamp. 12 | 13 | ## Installation 14 | 15 | `npm install mongoose-timestamp` 16 | 17 | ## Usage 18 | 19 | ```javascript 20 | var timestamps = require('mongoose-timestamp'); 21 | var UserSchema = new Schema({ 22 | username: String 23 | }); 24 | UserSchema.plugin(timestamps); 25 | mongoose.model('User', UserSchema); 26 | var User = mongoose.model('User', UserSchema) 27 | ``` 28 | The User model will now have `createdAt` and `updatedAt` properties, which get 29 | automatically generated and updated when you save your document. 30 | 31 | ```javascript 32 | var user = new User({username: 'Prince'}); 33 | user.save(function (err) { 34 | console.log(user.createdAt); // Should be approximately now 35 | console.log(user.createdAt === user.updatedAt); // true 36 | // Wait 1 second and then update the user 37 | setTimeout( function () { 38 | user.username = 'Symbol'; 39 | user.save( function (err) { 40 | console.log(user.updatedAt); // Should be approximately createdAt + 1 second 41 | console.log(user.createdAt < user.updatedAt); // true 42 | }); 43 | }, 1000); 44 | }); 45 | ``` 46 | #### findOneAndModify (mongoose >= 4.0.1) 47 | 48 | Mongoose 4.0.1 added support for findOneAndModify hooks. You must the mongoose promise exec for the hooks to work as mongoose uses mquery when a callback is passed and the hook system is bypassed. 49 | 50 | ```javascript 51 | User.findOneAndUpdate({username: 'Prince'}, { password: 'goatcheese' }, { new: true, upsert: true }) 52 | .exec(function (err, updated) { 53 | console.log(user.updatedAt); // Should be approximately createdAt + 1 second 54 | console.log(user.createdAt < user.updatedAt); // true 55 | }); 56 | ``` 57 | 58 | You can specify custom property names by passing them in as options like this: 59 | 60 | ```javascript 61 | mongoose.plugin(timestamps, { 62 | createdAt: 'created_at', 63 | updatedAt: 'updated_at' 64 | }); 65 | ``` 66 | 67 | Any model's updatedAt attribute can be updated to the current time using `touch()`. 68 | 69 | ## License 70 | 71 | (The MIT License) 72 | 73 | Copyright (c) 2012-2016 Nicholas Penree <nick@penree.com> 74 | 75 | Based on [mongoose-types](https://github.com/bnoguchi/mongoose-types): Copyright (c) 2012 [Brian Noguchi](https://github.com/bnoguchi) 76 | 77 | Permission is hereby granted, free of charge, to any person obtaining 78 | a copy of this software and associated documentation files (the 79 | 'Software'), to deal in the Software without restriction, including 80 | without limitation the rights to use, copy, modify, merge, publish, 81 | distribute, sublicense, and/or sell copies of the Software, and to 82 | permit persons to whom the Software is furnished to do so, subject to 83 | the following conditions: 84 | 85 | The above copyright notice and this permission notice shall be 86 | included in all copies or substantial portions of the Software. 87 | 88 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 91 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 92 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 93 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 94 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 95 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Mongoose Timestamps Plugin 3 | * Copyright(c) 2012 Nicholas Penree 4 | * Original work Copyright(c) 2012 Brian Noguchi 5 | * MIT Licensed 6 | */ 7 | 8 | var defaults = require('defaults'); 9 | 10 | function timestampsPlugin(schema, options) { 11 | var updatedAt = 'updatedAt'; 12 | var createdAt = 'createdAt'; 13 | var updatedAtOpts = Date; 14 | var createdAtOpts = Date; 15 | var dataObj = {}; 16 | 17 | if (typeof options === 'object') { 18 | if (typeof options.updatedAt === 'string') { 19 | updatedAt = options.updatedAt; 20 | } else if (typeof options.updatedAt === 'object') { 21 | updatedAtOpts = defaults(options.updatedAt, { 22 | name: updatedAt, 23 | type: Date 24 | }); 25 | updatedAt = updatedAtOpts.name; 26 | } 27 | 28 | if (typeof options.createdAt === 'string') { 29 | createdAt = options.createdAt; 30 | } else if (typeof options.createdAt === 'object') { 31 | createdAtOpts = defaults(options.createdAt, { 32 | name: createdAt, 33 | type: Date 34 | }); 35 | createdAt = createdAtOpts.name; 36 | } 37 | } 38 | 39 | if (!schema.path(updatedAt) && updatedAt) { 40 | dataObj[updatedAt] = updatedAtOpts; 41 | } 42 | 43 | if (schema.path(createdAt)) { 44 | if (!schema.path(updatedAt) && updatedAt) { 45 | schema.add(dataObj); 46 | } 47 | if (schema.virtual(createdAt).get) { 48 | schema.virtual(createdAt) 49 | .get( function () { 50 | if (this["_" + createdAt]) return this["_" + createdAt]; 51 | return this["_" + createdAt] = this._id.getTimestamp(); 52 | }); 53 | } 54 | schema.pre('save', function(next) { 55 | if (this.isNew) { 56 | var newDate = new Date; 57 | if (createdAt) this[createdAt] = newDate; 58 | if (updatedAt) this[updatedAt] = newDate; 59 | } else if (this.isModified() && updatedAt) { 60 | this[updatedAt] = new Date; 61 | } 62 | next(); 63 | }); 64 | 65 | } else { 66 | if (createdAt) { 67 | dataObj[createdAt] = createdAtOpts; 68 | } 69 | if (dataObj[createdAt] || dataObj[updatedAt]) { 70 | schema.add(dataObj); 71 | } 72 | schema.pre('save', function(next) { 73 | if (!this[createdAt]) { 74 | var newDate = new Date; 75 | if (createdAt) this[createdAt] = newDate; 76 | if (updatedAt) this[updatedAt] = newDate; 77 | } else if (this.isModified() && updatedAt) { 78 | this[updatedAt] = new Date; 79 | } 80 | next(); 81 | }); 82 | } 83 | 84 | schema.pre('findOneAndUpdate', function(next) { 85 | if (this.op === 'findOneAndUpdate') { 86 | var newDate = new Date; 87 | this._update = this._update || {}; 88 | if (createdAt) { 89 | if (this._update[createdAt]) { 90 | delete this._update[createdAt]; 91 | } 92 | 93 | this._update['$setOnInsert'] = this._update['$setOnInsert'] || {}; 94 | this._update['$setOnInsert'][createdAt] = newDate; 95 | } 96 | if (updatedAt) { 97 | this._update[updatedAt] = newDate; 98 | } 99 | } 100 | next(); 101 | }); 102 | 103 | schema.pre('update', function(next) { 104 | if (this.op === 'update') { 105 | var newDate = new Date; 106 | this._update = this._update || {}; 107 | if (createdAt) { 108 | if (this._update[createdAt]) { 109 | delete this._update[createdAt]; 110 | } 111 | 112 | this._update['$setOnInsert'] = this._update['$setOnInsert'] || {}; 113 | this._update['$setOnInsert'][createdAt] = newDate; 114 | } 115 | if (updatedAt) { 116 | this._update[updatedAt] = newDate; 117 | } 118 | } 119 | next(); 120 | }); 121 | 122 | if(!schema.methods.hasOwnProperty('touch') && updatedAt) 123 | schema.methods.touch = function(callback){ 124 | this[updatedAt] = new Date; 125 | this.save(callback); 126 | } 127 | 128 | } 129 | 130 | module.exports = timestampsPlugin; 131 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mongoose-timestamp", 3 | "description": "Mongoose plugin that adds createdAt and updatedAt auto-assigned date properties", 4 | "version": "0.6.0", 5 | "author": "Nicholas Penree ", 6 | "keywords": [ 7 | "mongodb", 8 | "mongoose", 9 | "plugin", 10 | "timestamps", 11 | "createdAt", 12 | "updatedAt" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/drudge/mongoose-timestamp" 17 | }, 18 | "devDependencies": { 19 | "mocha": "~2.5.3", 20 | "mongoosastic": "^4.0.2", 21 | "mongoose": "~4.5.3", 22 | "request": "^2.72.0", 23 | "should": "~9.0.2" 24 | }, 25 | "scripts": { 26 | "test": "mocha -u bdd -R spec -c ./test/*test.js" 27 | }, 28 | "license": "MIT", 29 | "engine": { 30 | "node": ">= 0.6" 31 | }, 32 | "dependencies": { 33 | "defaults": "^1.0.3" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/custom_names_only_test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @list dependencies 4 | **/ 5 | 6 | var mocha = require('mocha'); 7 | var should = require('should'); 8 | var mongoose = require('mongoose'); 9 | var Schema = mongoose.Schema; 10 | var timestamps = require('../'); 11 | 12 | mongoose = mongoose.createConnection('mongodb://localhost/mongoose_timestamps') 13 | mongoose.on('error', function (err) { 14 | console.error('MongoDB error: ' + err.message); 15 | console.error('Make sure a mongoDB server is running and accessible by this application') 16 | }); 17 | 18 | var opts = {createdAt: 'customNameCreatedAt', updatedAt: 'customNameUpdatedAt'}; 19 | 20 | var CustomizedNameOnlyTimeCopSchema = new Schema({ 21 | email: String 22 | }); 23 | CustomizedNameOnlyTimeCopSchema.plugin(timestamps, opts); 24 | 25 | var CustomizedNameOnlyTimeCop = mongoose.model('CustomizedNameOnlyTimeCop', CustomizedNameOnlyTimeCopSchema); 26 | 27 | describe('timestamps custom names only', function() { 28 | it('should have the updatedAt field named "' + opts.updatedAt + '" with type "Date"', function(done) { 29 | var customCop = new CustomizedNameOnlyTimeCop({email: 'example@example.com'}); 30 | customCop.save(function (err) { 31 | customCop.should.have.property(opts.updatedAt); 32 | customCop[opts.updatedAt].should.be.a.Date; 33 | done(); 34 | }); 35 | }); 36 | 37 | it('should have the createdAt field named "' + opts.createdAt + '" with type "Date"', function(done) { 38 | var customCop = new CustomizedNameOnlyTimeCop({email: 'example@example.com'}); 39 | customCop.save(function (err) { 40 | customCop.should.have.property(opts.createdAt); 41 | customCop[opts.createdAt].should.be.a.Date; 42 | done(); 43 | }); 44 | }); 45 | 46 | it('should be set to the same value on creation', function(done) { 47 | var cop = new CustomizedNameOnlyTimeCop({ email: 'brian@brian.com' }); 48 | cop.save( function (err) { 49 | cop[opts.createdAt].should.equal(cop[opts.updatedAt]); 50 | done(); 51 | }); 52 | }); 53 | 54 | it('should have updatedAt greater than createdAt upon updating', function(done) { 55 | CustomizedNameOnlyTimeCop.findOne({email: 'brian@brian.com'}, function (err, found) { 56 | found.email = 'jeanclaude@vandamme.com'; 57 | setTimeout( function () { 58 | found.save( function (err, updated) { 59 | updated[opts.updatedAt].should.be.above(updated[opts.createdAt]); 60 | done(); 61 | }); 62 | }, 1000); 63 | }); 64 | }); 65 | 66 | after(function() { 67 | mongoose.close(); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/custom_names_types_options_test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @list dependencies 4 | **/ 5 | 6 | var mocha = require('mocha'); 7 | var should = require('should'); 8 | var mongoose = require('mongoose'); 9 | var Schema = mongoose.Schema; 10 | var timestamps = require('../'); 11 | var request = require('request'); 12 | var mongoosastic = require('mongoosastic'); 13 | 14 | mongoose = mongoose.createConnection('mongodb://localhost/mongoose_timestamps') 15 | mongoose.on('error', function (err) { 16 | console.error('MongoDB error: ' + err.message); 17 | console.error('Make sure a mongoDB server is running and accessible by this application') 18 | }); 19 | 20 | var opts = { 21 | createdAt: { 22 | name: 'customNameCreatedAt', 23 | type: String, 24 | index: true 25 | }, 26 | updatedAt: { 27 | name: 'customNameUpdatedAt', 28 | type: String, 29 | es_indexed: true 30 | } 31 | }; 32 | 33 | var CustomizedTypeOptionsTimeCopSchema = new Schema({ 34 | email: String 35 | }); 36 | 37 | CustomizedTypeOptionsTimeCopSchema.plugin(timestamps, opts); 38 | CustomizedTypeOptionsTimeCopSchema.plugin(mongoosastic); 39 | 40 | var CustomizedTypeOptionsTimeCop = mongoose.model('CustomizedTypeOptionsTimeCop', CustomizedTypeOptionsTimeCopSchema); 41 | 42 | describe('timestamps custom names and types with options', function() { 43 | it('should create an index when passed in the createdAt options', function(done) { 44 | CustomizedTypeOptionsTimeCop.collection.getIndexes(function(err, res) { 45 | var idx = res[opts.createdAt.name + '_1']; 46 | idx.should.be.an.Array().with.length(1); 47 | var comp = idx[0]; 48 | comp.should.be.an.Array().with.length(2); 49 | comp[0].should.equal(opts.createdAt.name); 50 | done(); 51 | }); 52 | }); 53 | 54 | it('should create an elastic search index when passed es_indexed = true', function(done) { 55 | var checkElastic = function() { 56 | request({ 57 | url: 'http://127.0.0.1:9200/customizedtypeoptionstimecops', 58 | json: true 59 | }, function(err, res, body) { 60 | if (err) return done(err); 61 | body.should.have.a.property('customizedtypeoptionstimecops'); 62 | body.customizedtypeoptionstimecops.should.have.a.property('mappings'); 63 | body.customizedtypeoptionstimecops.mappings.should.have.a.property('customizedtypeoptionstimecop'); 64 | body.customizedtypeoptionstimecops.mappings.customizedtypeoptionstimecop.should.have.a.property('properties'); 65 | body.customizedtypeoptionstimecops.mappings.customizedtypeoptionstimecop.properties.should.have.a.property(opts.updatedAt.name); 66 | done(); 67 | }); 68 | }; 69 | var customCop = new CustomizedTypeOptionsTimeCop({email: 'example@example.com'}); 70 | customCop.save(function() { 71 | customCop.on('es-indexed', checkElastic); 72 | }); 73 | }); 74 | 75 | after(function() { 76 | mongoose.close(); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /test/custom_names_types_test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @list dependencies 4 | **/ 5 | 6 | var mocha = require('mocha'); 7 | var should = require('should'); 8 | var mongoose = require('mongoose'); 9 | var Schema = mongoose.Schema; 10 | var timestamps = require('../'); 11 | 12 | mongoose = mongoose.createConnection('mongodb://localhost/mongoose_timestamps') 13 | mongoose.on('error', function (err) { 14 | console.error('MongoDB error: ' + err.message); 15 | console.error('Make sure a mongoDB server is running and accessible by this application') 16 | }); 17 | 18 | var opts = { 19 | createdAt: { 20 | name: 'customNameCreatedAt', 21 | type: String 22 | }, 23 | updatedAt: { 24 | name: 'customNameUpdatedAt', 25 | type: String 26 | } 27 | }; 28 | 29 | var CustomizedNameAndTypesTimeCopSchema = new Schema({ 30 | email: String 31 | }); 32 | CustomizedNameAndTypesTimeCopSchema.plugin(timestamps, opts); 33 | var CustomizedNameAndTypesTimeCop = mongoose.model('CustomizedNameAndTypesTimeCop', CustomizedNameAndTypesTimeCopSchema); 34 | 35 | describe('timestamps custom names and types', function() { 36 | it('should have the updatedAt field named "' + opts.updatedAt.name + '" with type "String"', function(done) { 37 | var customCop = new CustomizedNameAndTypesTimeCop({email: 'example@example.com'}); 38 | customCop.save(function (err) { 39 | customCop.should.have.property(opts.updatedAt.name); 40 | customCop[opts.updatedAt.name].should.be.a[opts.updatedAt.type]; 41 | done(); 42 | }); 43 | }); 44 | 45 | it('should have the createdAt field named "' + opts.createdAt.name + '" with type "String"', function(done) { 46 | var customCop = new CustomizedNameAndTypesTimeCop({email: 'example@example.com'}); 47 | customCop.save(function (err) { 48 | customCop.should.have.property(opts.createdAt.name); 49 | customCop[opts.createdAt.name].should.be.a[opts.createdAt.type]; 50 | done(); 51 | }); 52 | }); 53 | 54 | it('should be set to the same value on creation', function(done) { 55 | var cop = new CustomizedNameAndTypesTimeCop({ email: 'brian@brian.com' }); 56 | cop.save( function (err) { 57 | cop[opts.createdAt.name].should.equal(cop[opts.updatedAt.name]); 58 | done(); 59 | }); 60 | }); 61 | 62 | it('should have updatedAt greater than createdAt upon updating', function(done) { 63 | CustomizedNameAndTypesTimeCop.findOne({email: 'brian@brian.com'}, function (err, found) { 64 | found.email = 'jeanclaude@vandamme.com'; 65 | setTimeout( function () { 66 | found.save( function (err, updated) { 67 | updated[opts.updatedAt.name].should.be.above(updated[opts.createdAt.name]); 68 | done(); 69 | }); 70 | }, 1000); 71 | }); 72 | }); 73 | 74 | after(function() { 75 | mongoose.close(); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /test/index_find_update_test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @list dependencies 4 | **/ 5 | 6 | var mocha = require('mocha'); 7 | var should = require('should'); 8 | var mongoose = require('mongoose'); 9 | var Schema = mongoose.Schema; 10 | var timestamps = require('../'); 11 | 12 | mongoose = mongoose.createConnection('mongodb://localhost/mongoose_timestamps') 13 | mongoose.on('error', function (err) { 14 | console.error('MongoDB error: ' + err.message); 15 | console.error('Make sure a mongoDB server is running and accessible by this application') 16 | }); 17 | 18 | var TimeCopSchema = new Schema({ 19 | email: String, 20 | nemesis: { type: String, default: 'brian' } 21 | }); 22 | TimeCopSchema.plugin(timestamps); 23 | var TimeCop = mongoose.model('TimeCop', TimeCopSchema); 24 | 25 | describe('findOneAndUpdate', function() { 26 | it('should have updatedAt greater than or equal to createdAt upon updating', function(done) { 27 | TimeCop.findOneAndUpdate({email: 'stewie@familyguysmatter.com'}, { nemesis: 'lois' }, { new: true, upsert: true }) 28 | .exec(function (err, updated) { 29 | updated.updatedAt.should.not.be.below(updated.createdAt); 30 | done(); 31 | }); 32 | }) 33 | 34 | after(function() { 35 | mongoose.close(); 36 | }); 37 | }) 38 | -------------------------------------------------------------------------------- /test/index_test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @list dependencies 4 | **/ 5 | 6 | var mocha = require('mocha'); 7 | var should = require('should'); 8 | var mongoose = require('mongoose'); 9 | var Schema = mongoose.Schema; 10 | var timestamps = require('../'); 11 | 12 | mongoose = mongoose.createConnection('mongodb://localhost/mongoose_timestamps') 13 | mongoose.on('error', function (err) { 14 | console.error('MongoDB error: ' + err.message); 15 | console.error('Make sure a mongoDB server is running and accessible by this application') 16 | }); 17 | 18 | var TimeCopSchema = new Schema({ 19 | email: String, 20 | nemesis: String 21 | }); 22 | TimeCopSchema.plugin(timestamps); 23 | var TimeCop = mongoose.model('TimeCop', TimeCopSchema); 24 | 25 | describe('timestamps', function() { 26 | it('should be set to the same value on creation', function(done) { 27 | var cop = new TimeCop({ email: 'brian@brian.com' }); 28 | cop.save( function (err) { 29 | cop.createdAt.should.equal(cop.updatedAt); 30 | done(); 31 | }); 32 | }) 33 | 34 | it('should have updatedAt greater than createdAt upon updating', function(done) { 35 | TimeCop.findOne({email: 'brian@brian.com'}, function (err, found) { 36 | found.email = 'jeanclaude@vandamme.com'; 37 | setTimeout( function () { 38 | found.save( function (err, updated) { 39 | updated.updatedAt.should.be.above(updated.createdAt); 40 | done(); 41 | }); 42 | }, 1000); 43 | }); 44 | }) 45 | 46 | after(function() { 47 | mongoose.close(); 48 | }); 49 | }) 50 | -------------------------------------------------------------------------------- /test/index_update_test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @list dependencies 4 | **/ 5 | 6 | var mocha = require('mocha'); 7 | var should = require('should'); 8 | var mongoose = require('mongoose'); 9 | var Schema = mongoose.Schema; 10 | var timestamps = require('../'); 11 | 12 | mongoose = mongoose.createConnection('mongodb://localhost/mongoose_timestamps') 13 | mongoose.on('error', function (err) { 14 | console.error('MongoDB error: ' + err.message); 15 | console.error('Make sure a mongoDB server is running and accessible by this application') 16 | }); 17 | 18 | var TimeCopSchema = new Schema({ 19 | email: String, 20 | nemesis: { type: String, default: 'brian' } 21 | }); 22 | TimeCopSchema.plugin(timestamps); 23 | var TimeCop = mongoose.model('TimeCop', TimeCopSchema); 24 | 25 | describe('update', function() { 26 | it('should have updatedAt greater than or equal to createdAt upon updating', function(done) { 27 | TimeCop.update({email: 'stewie@familyguysmatter.com'}, { $set: { nemesis: 'dave' } }, { upsert: false }) 28 | .exec(function (err, updated) { 29 | TimeCop.findOne({email: 'stewie@familyguysmatter.com'}, function(err, update) { 30 | 31 | update.updatedAt.should.not.be.below(updated.createdAt); 32 | 33 | done(); 34 | }); 35 | }); 36 | }) 37 | 38 | after(function() { 39 | mongoose.close(); 40 | }); 41 | }) 42 | -------------------------------------------------------------------------------- /test/sub_document_test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @list dependencies 4 | **/ 5 | 6 | var mocha = require('mocha'); 7 | var should = require('should'); 8 | var mongoose = require('mongoose'); 9 | var Schema = mongoose.Schema; 10 | var timestamps = require('../'); 11 | 12 | mongoose.Promise = global.Promise || mongoose.Promise; 13 | 14 | mongoose = mongoose.createConnection('mongodb://localhost/mongoose_timestamps') 15 | mongoose.on('error', function (err) { 16 | console.error('MongoDB error: ' + err.message); 17 | console.error('Make sure a mongoDB server is running and accessible by this application') 18 | }); 19 | 20 | var SubDocumentSchema = new Schema({ 21 | message: String 22 | }); 23 | SubDocumentSchema.plugin(timestamps); 24 | 25 | var TimeCopWithSubDocsSchema = new Schema({ 26 | email: String, 27 | subDocs: [SubDocumentSchema] 28 | }); 29 | TimeCopWithSubDocsSchema.plugin(timestamps); 30 | var TimeCopWithSubDocs = mongoose.model('TimeCopWithSubDocs', TimeCopWithSubDocsSchema); 31 | 32 | describe('sub document timestamps', function() { 33 | before(function (done) { 34 | TimeCopWithSubDocs.collection.remove(done); 35 | }); 36 | it('should be set to the same value on creation', function(done) { 37 | var cop = new TimeCopWithSubDocs({ email: 'brian@brian.com' }); 38 | cop.subDocs.push(cop.subDocs.create({message: 'Message from the future'})); 39 | cop.subDocs.push(cop.subDocs.create({message: 'Don\'t trust Fielding'})); 40 | cop.save( function (err) { 41 | if (err) return done(err); 42 | cop.subDocs.forEach(function (subDoc) { 43 | subDoc.createdAt.should.eql(subDoc.updatedAt); 44 | }); 45 | done(); 46 | }); 47 | }); 48 | 49 | it('should not have updatedAt change if parent was updated but not sub document', function(done) { 50 | TimeCopWithSubDocs.findOne({email: 'brian@brian.com'}, function (err, found) { 51 | found.email = 'jeanclaude@vandamme.com'; 52 | setTimeout( function () { 53 | found.save( function (err, updated) { 54 | updated.updatedAt.should.be.above(updated.createdAt); 55 | updated.subDocs.forEach(function (subDoc) { 56 | subDoc.createdAt.should.eql(subDoc.updatedAt); 57 | }); 58 | done(); 59 | }); 60 | }, 1000); 61 | }); 62 | }); 63 | 64 | it('should have updatedAt greater than createdAt if sub document was updated', function(done) { 65 | TimeCopWithSubDocs.findOne({email: 'jeanclaude@vandamme.com'}, function (err, found) { 66 | found.subDocs[1].message = 'Don\'t trust McComb'; 67 | var lastUpdated = found.updatedAt; 68 | setTimeout( function () { 69 | found.save( function (err, updated) { 70 | updated.updatedAt.should.be.above(lastUpdated); 71 | var subDocs = updated.subDocs; 72 | subDocs[0].updatedAt.should.eql(subDocs[0].createdAt); 73 | subDocs[1].updatedAt.should.be.above(subDocs[1].createdAt); 74 | done(); 75 | }); 76 | }, 1000); 77 | }); 78 | }); 79 | 80 | after(function() { 81 | mongoose.close(); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /test/touch_test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @list dependencies 4 | **/ 5 | 6 | var mocha = require('mocha'); 7 | var should = require('should'); 8 | var mongoose = require('mongoose'); 9 | var Schema = mongoose.Schema; 10 | var timestamps = require('../'); 11 | 12 | mongoose = mongoose.createConnection('mongodb://localhost/mongoose_timestamps') 13 | mongoose.on('error', function (err) { 14 | console.error('MongoDB error: ' + err.message); 15 | console.error('Make sure a mongoDB server is running and accessible by this application') 16 | }); 17 | 18 | var UserSchema = new Schema({ 19 | email: String 20 | }) 21 | 22 | UserSchema.plugin(timestamps) 23 | var User = mongoose.model('User', UserSchema) 24 | 25 | describe('User Schema', function(){ 26 | it('should have the method touch', function(){ 27 | UserSchema.methods.hasOwnProperty('touch').should.equal(true) 28 | }) 29 | }) 30 | 31 | describe('timestamp',function(){ 32 | it('should be updated on calling the method touch', function(done){ 33 | var user = new User({email: "tyrion.lannister@westeros.com"}) 34 | var past = undefined 35 | user.save(function (err) { 36 | past = user.updatedAt 37 | }); 38 | setTimeout(function(){ 39 | user.touch(function(){ 40 | user.updatedAt.should.above(past) 41 | done() 42 | }) 43 | },1500) 44 | }) 45 | 46 | after(function() { 47 | mongoose.close(); 48 | }); 49 | }) 50 | 51 | 52 | --------------------------------------------------------------------------------