@@ -395,6 +395,69 @@ func TestHistory_DefaultReturnsNil(t *testing.T) {
395395 }
396396}
397397
398+ // ---------------------------------------------------------------------------
399+ // 5b. Restore trace mode: WithRestoreFullTrace / WithRestoreUnboundedHistory.
400+ // ---------------------------------------------------------------------------
401+
402+ // TestRestore_TraceModeOptIn asserts a restored instance is lite by default (a
403+ // subsequent Fire omits rich fields and does not retain new traces), that
404+ // WithRestoreFullTrace restores full per-step traces without continued retention,
405+ // and that WithRestoreUnboundedHistory restores full traces AND keeps retaining
406+ // them — the mode the durable runner relies on for byte-identical replay. (Restore
407+ // always seeds history from the snapshot's traces, so the distinguishing behavior
408+ // is whether later Fires are rich and whether they keep accumulating.)
409+ func TestRestore_TraceModeOptIn (t * testing.T ) {
410+ m := buildToggleMachine ()
411+ ctx := context .Background ()
412+
413+ src := m .Cast (nil , state.WithInitialState [toggleState ](toggleA ), state .WithUnboundedHistory [toggleState ]())
414+ src .Fire (ctx , toggleGo )
415+ snap := src .Snapshot ()
416+
417+ // Default restore: lite Fire (no rich fields) and no further retention.
418+ lite , err := m .Restore (snap )
419+ if err != nil {
420+ t .Fatalf ("Restore (default): %v" , err )
421+ }
422+ base := len (lite .History ())
423+ r := lite .Fire (ctx , toggleGo )
424+ if len (r .Trace .EnteredStates ) != 0 || len (r .Trace .EffectsEmitted ) != 0 {
425+ t .Errorf ("default restore should fire lite, got rich fields: %+v" , r .Trace )
426+ }
427+ if got := len (lite .History ()); got != base {
428+ t .Errorf ("default restore should not retain new traces: %d -> %d" , base , got )
429+ }
430+
431+ // WithRestoreFullTrace: rich Fire, but still no continued retention.
432+ full , err := m .Restore (snap , state .WithRestoreFullTrace [toggleState ]())
433+ if err != nil {
434+ t .Fatalf ("Restore (full trace): %v" , err )
435+ }
436+ fbase := len (full .History ())
437+ rf := full .Fire (ctx , toggleGo )
438+ if len (rf .Trace .EnteredStates ) == 0 || len (rf .Trace .EffectsEmitted ) == 0 {
439+ t .Errorf ("WithRestoreFullTrace should populate rich fields, got %+v" , rf .Trace )
440+ }
441+ if got := len (full .History ()); got != fbase {
442+ t .Errorf ("WithRestoreFullTrace should not retain new traces: %d -> %d" , fbase , got )
443+ }
444+
445+ // WithRestoreUnboundedHistory: rich Fire AND continued unbounded retention.
446+ dur , err := m .Restore (snap , state .WithRestoreUnboundedHistory [toggleState ]())
447+ if err != nil {
448+ t .Fatalf ("Restore (unbounded): %v" , err )
449+ }
450+ dbase := len (dur .History ())
451+ dur .Fire (ctx , toggleGo )
452+ dur .Fire (ctx , toggleGo )
453+ if got := len (dur .History ()); got != dbase + 2 {
454+ t .Errorf ("WithRestoreUnboundedHistory should keep retaining: %d -> %d (want +2)" , dbase , got )
455+ }
456+ if h := dur .History (); len (h [len (h )- 1 ].EnteredStates ) == 0 {
457+ t .Errorf ("retained trace under unbounded restore should be full, got %+v" , h [len (h )- 1 ])
458+ }
459+ }
460+
398461// ---------------------------------------------------------------------------
399462// 6. Label cache: m.label correctness.
400463// ---------------------------------------------------------------------------
0 commit comments