@@ -362,110 +362,46 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="orm" {
362362
363363 });
364364
365- describe ( " duplicate() re-fires expression-form defaults — fresh per-instance evaluation" , function (){
365+ describe ( " duplicate() — deep-copy semantic, no re-fire (option 6 contract)" , function (){
366+ // Core duplicate is a deep copy of state, not a re-construction. Hibernate (which
367+ // needs fresh per-entity evaluation) handles its template→entity re-firing at the
368+ // extension layer. See test/functions/Duplicate.cfc for the broader user-mutation
369+ // preservation contract.
366370
367- it ( " duplicate fires fresh #now () # — dup gets a later timestamp than src after sleep" , function (){
368- // Contract: duplicate re-fires expression-form defaults so each duplicate
369- // gets a fresh per-instance evaluation. The natural reading of
370- // default="#now()#" is "the time this instance came into existence" — and
371- // duplicates ARE new instances coming into existence.
372- var src = new LDEV6303 .ExpressionDefaultsCfc ();
373- sleep ( 1100 );
374- var dup = duplicate ( src );
375-
376- expect ( dateCompare ( dup .getNowDef (), src .getNowDef (), " s" ) ).toBeGT ( 0 ,
377- " duplicate must re-evaluate now() — dup's timestamp should be later than src's"
378- );
379- });
380-
381- it ( " duplicate fires fresh #createUUID () # — dup's UUID differs from src's" , function (){
382- var src = new LDEV6303 .ExpressionDefaultsCfc ();
383- var dup = duplicate ( src );
384-
385- expect ( dup .getUuidDef () ).notToBe ( src .getUuidDef (),
386- " duplicate must re-evaluate createUUID() — dup gets a fresh UUID"
387- );
388- // sanity: both look like UUIDs
389- expect ( len ( dup .getUuidDef () ) ).toBeGT ( 0 );
390- expect ( len ( src .getUuidDef () ) ).toBeGT ( 0 );
391- });
392-
393- it ( " duplicate fires fresh deterministic expression (eval gives same value but separate computation)" , function (){
394- // Deterministic expressions naturally give the same value either way.
395- // This test documents that they continue to work — fresh eval of
396- // repeatString('x',5) gives "xxxxx" same as src's value.
371+ it ( " deterministic expression-form value round-trips through duplicate as deep copy" , function (){
397372 var src = new LDEV6303 .ExpressionDefaultsCfc ();
398373 var dup = duplicate ( src );
399374 expect ( dup .getExpressionDef () ).toBe ( " xxxxx" );
400375 expect ( dup .getExpressionDef () ).toBe ( src .getExpressionDef () );
401376 });
402377
403- it ( " duplicate gives dup its own array — mutating dup does not leak to src" , function (){
404- // Mutable expression-form defaults (#[]#, #{}#) must produce a fresh
405- // container per duplicate. Without re-fire, a shallow-copy duplicate
406- // would share the array reference with src — silent leak.
378+ it ( " deep duplicate gives dup its own array — mutating dup does not leak to src" , function (){
379+ // Even without re-fire, deep duplicate must give dup a fresh array reference
380+ // (Duplicator.duplicate handles container deep-copy).
407381 var src = new LDEV6303 .ExpressionDefaultsCfc ();
408382 var dup = duplicate ( src );
409383
410384 arrayAppend ( dup .getFreshArrayDef (), " added-to-dup" );
411385
412386 expect ( arrayLen ( src .getFreshArrayDef () ) ).toBe ( 0 ,
413- " src's array must be unaffected by mutation on dup's array (separate references via re-fire) "
387+ " src's array must be unaffected by mutation on dup's deep-copy array "
414388 );
415389 expect ( arrayLen ( dup .getFreshArrayDef () ) ).toBe ( 1 );
416390 });
417391
418- it ( " duplicate(src, false) — shallow duplicate also re-fires expression-form (Hibernate path)" , function (){
419- // Hibernate's CFCInstantiator uses cfc.duplicate(false) to create
420- // entity instances. The shallow flag must NOT prevent re-fire of
421- // expression-form defaults — otherwise every entity from a cached
422- // template gets the template's frozen UUID/now/[]/{}.
423- var src = new LDEV6303 .ExpressionDefaultsCfc ();
424- var dup = duplicate ( src , false );
425-
426- expect ( dup .getUuidDef () ).notToBe ( src .getUuidDef (),
427- " duplicate(src, false) must re-evaluate expression-form defaults too"
428- );
429-
430- // Mutation on dup's struct must not leak to src
431- var s = dup .getNowStructDef ();
432- s .n = 999 ;
433-
434- expect ( src .getNowStructDef ().n ).toBe ( 1 ,
435- " src's struct must be a separate reference from dup's (no shared-ref leak via shallow duplicate)"
436- );
437- });
438-
439- it ( " explicit assignment after duplicate still wins — user values aren't clobbered by re-fire" , function (){
440- // Re-fire seeds dup's scope at duplicate-time. Subsequent explicit
441- // assignment by the user must override the re-fire'd value.
392+ it ( " explicit assignment after duplicate wins over the deep-copied value" , function (){
442393 var src = new LDEV6303 .ExpressionDefaultsCfc ();
443394 var dup = duplicate ( src );
444395
445396 var freshTs = now ();
446397 dup .setNowDef ( freshTs );
447398
448- expect ( dateCompare ( dup .getNowDef (), freshTs , " s" ) ).toBe ( 0 ,
449- " explicit setNowDef on dup must win over re-fire'd value"
450- );
399+ expect ( dateCompare ( dup .getNowDef (), freshTs , " s" ) ).toBe ( 0 );
451400 expect ( dateCompare ( src .getNowDef (), freshTs , " s" ) ).toBeLTE ( 0 ,
452401 " src's nowDef must be unchanged by mutation on the duplicate"
453402 );
454403 });
455404
456- it ( " inherited expression-form default re-fires on the child duplicate" , function (){
457- // Child class instance, inheriting a #now()# default from parent.
458- // duplicate of the child must re-fire the parent's expression-form
459- // default — inheritance walk in _duplicate.
460- var src = new LDEV6303 .ChildExprDefCfc ();
461- sleep ( 1100 );
462- var dup = duplicate ( src );
463-
464- expect ( dateCompare ( dup .getParentNowDef (), src .getParentNowDef (), " s" ) ).toBeGT ( 0 ,
465- " inherited #now () # default must re-fire on the child's duplicate"
466- );
467- });
468-
469405 });
470406
471407 describe ( " serializeJson shape — programmatic discriminability for consumers" , function (){
0 commit comments