@@ -12,6 +12,7 @@ const ArraySubdocument = require('../lib/types/arraySubdocument');
12
12
const Query = require ( '../lib/query' ) ;
13
13
const assert = require ( 'assert' ) ;
14
14
const idGetter = require ( '../lib/helpers/schema/idGetter' ) ;
15
+ const sinon = require ( 'sinon' ) ;
15
16
const util = require ( './util' ) ;
16
17
const utils = require ( '../lib/utils' ) ;
17
18
@@ -14296,6 +14297,47 @@ describe('document', function() {
14296
14297
14297
14298
delete mongoose . Schema . Types . CustomType ;
14298
14299
} ) ;
14300
+
14301
+ it ( 'handles undoReset() on deep recursive subdocuments (gh-15255)' , async function ( ) {
14302
+ const RecursiveSchema = new mongoose . Schema ( { } ) ;
14303
+
14304
+ const s = [ RecursiveSchema ] ;
14305
+ RecursiveSchema . path ( 'nested' , s ) ;
14306
+
14307
+ const generateRecursiveDocument = ( depth , curr = 0 ) => {
14308
+ return {
14309
+ name : `Document of depth ${ curr } ` ,
14310
+ nested : depth > 0 ? new Array ( 2 ) . fill ( ) . map ( ( ) => generateRecursiveDocument ( depth - 1 , curr + 1 ) ) : [ ] ,
14311
+ __v : 5
14312
+ } ;
14313
+ } ;
14314
+ const TestModel = db . model ( 'Test' , RecursiveSchema ) ;
14315
+ const data = generateRecursiveDocument ( 10 ) ;
14316
+ const doc = new TestModel ( data ) ;
14317
+ await doc . save ( ) ;
14318
+
14319
+ sinon . spy ( Document . prototype , '$__undoReset' ) ;
14320
+
14321
+ try {
14322
+ const d = await TestModel . findById ( doc . _id ) ;
14323
+ d . increment ( ) ;
14324
+ d . data = 'asd' ;
14325
+ // Force a version error by updating the document directly
14326
+ await TestModel . collection . updateOne ( { _id : doc . _id } , { $inc : { __v : 1 } } ) ;
14327
+ const err = await d . save ( ) . then ( ( ) => null , err => err ) ;
14328
+ assert . ok ( err ) ;
14329
+ assert . equal ( err . name , 'VersionError' ) ;
14330
+ // `$__undoReset()` should be called 1x per subdoc, plus 1x for top-level doc. Without fix for gh-15255,
14331
+ // this would fail because `$__undoReset()` is called nearly 700k times for only 2046 subdocs
14332
+ assert . strictEqual ( Document . prototype . $__undoReset . getCalls ( ) . length , d . $getAllSubdocs ( ) . length + 1 ) ;
14333
+ assert . ok ( Document . prototype . $__undoReset . getCalls ( ) . find ( call => call . thisValue === d ) , 'top level doc was not reset' ) ;
14334
+ for ( const subdoc of d . $getAllSubdocs ( ) ) {
14335
+ assert . ok ( Document . prototype . $__undoReset . getCalls ( ) . find ( call => call . thisValue === subdoc ) , `${ subdoc . name } was not reset` ) ;
14336
+ }
14337
+ } finally {
14338
+ sinon . restore ( ) ;
14339
+ }
14340
+ } ) ;
14299
14341
} ) ;
14300
14342
14301
14343
describe ( 'Check if instance function that is supplied in schema option is available' , function ( ) {
0 commit comments