@@ -31,14 +31,18 @@ import (
31
31
32
32
"github.com/google/uuid"
33
33
"github.com/stretchr/testify/assert"
34
+ "github.com/stretchr/testify/mock"
34
35
"github.com/stretchr/testify/require"
35
36
goakt "github.com/tochemey/goakt/v3/actor"
36
37
"github.com/tochemey/goakt/v3/log"
38
+ "go.uber.org/goleak"
37
39
"google.golang.org/protobuf/proto"
40
+ "google.golang.org/protobuf/types/known/anypb"
38
41
39
42
"github.com/tochemey/ego/v3/egopb"
40
43
"github.com/tochemey/ego/v3/eventstream"
41
44
"github.com/tochemey/ego/v3/internal/lib"
45
+ mocks "github.com/tochemey/ego/v3/mocks/persistence"
42
46
testpb "github.com/tochemey/ego/v3/test/data/pb/v3"
43
47
"github.com/tochemey/ego/v3/testkit"
44
48
)
@@ -357,4 +361,170 @@ func TestDurableStateBehavior(t *testing.T) {
357
361
assert .NoError (t , durableStore .Disconnect (ctx ))
358
362
eventStream .Close ()
359
363
})
364
+ t .Run ("with state recovery from state store with no latest state" , func (t * testing.T ) {
365
+ defer goleak .VerifyNone (t )
366
+ ctx := context .TODO ()
367
+ actorSystem , err := goakt .NewActorSystem ("TestActorSystem" ,
368
+ goakt .WithPassivationDisabled (),
369
+ goakt .WithLogger (log .DiscardLogger ),
370
+ goakt .WithActorInitMaxRetries (3 ),
371
+ )
372
+ require .NoError (t , err )
373
+ assert .NotNil (t , actorSystem )
374
+
375
+ // start the actor system
376
+ err = actorSystem .Start (ctx )
377
+ require .NoError (t , err )
378
+
379
+ lib .Pause (time .Second )
380
+
381
+ persistenceID := uuid .NewString ()
382
+ behavior := NewAccountDurableStateBehavior (persistenceID )
383
+
384
+ eventStream := eventstream .New ()
385
+
386
+ durableStore := new (mocks.StateStore )
387
+ durableStore .EXPECT ().Ping (mock .Anything ).Return (nil )
388
+ durableStore .EXPECT ().GetLatestState (mock .Anything , behavior .ID ()).Return (new (egopb.DurableState ), nil )
389
+ durableStore .EXPECT ().WriteState (mock .Anything , mock .AnythingOfType ("*egopb.DurableState" )).Return (nil )
390
+
391
+ persistentActor := newDurableStateActor (behavior , durableStore , eventStream )
392
+ pid , err := actorSystem .Spawn (ctx , behavior .ID (), persistentActor )
393
+ require .NoError (t , err )
394
+ require .NotNil (t , pid )
395
+
396
+ lib .Pause (time .Second )
397
+
398
+ err = actorSystem .Stop (ctx )
399
+ require .NoError (t , err )
400
+
401
+ lib .Pause (time .Second )
402
+
403
+ eventStream .Close ()
404
+ durableStore .AssertExpectations (t )
405
+ })
406
+ t .Run ("with state recovery from state store failure" , func (t * testing.T ) {
407
+ defer goleak .VerifyNone (t )
408
+ ctx := context .TODO ()
409
+ actorSystem , err := goakt .NewActorSystem ("TestActorSystem" ,
410
+ goakt .WithPassivationDisabled (),
411
+ goakt .WithLogger (log .DiscardLogger ),
412
+ goakt .WithActorInitMaxRetries (3 ),
413
+ )
414
+ require .NoError (t , err )
415
+ assert .NotNil (t , actorSystem )
416
+
417
+ // start the actor system
418
+ err = actorSystem .Start (ctx )
419
+ require .NoError (t , err )
420
+
421
+ lib .Pause (time .Second )
422
+
423
+ persistenceID := uuid .NewString ()
424
+ behavior := NewAccountDurableStateBehavior (persistenceID )
425
+
426
+ eventStream := eventstream .New ()
427
+
428
+ durableStore := new (mocks.StateStore )
429
+ durableStore .EXPECT ().Ping (mock .Anything ).Return (nil )
430
+ durableStore .EXPECT ().GetLatestState (mock .Anything , behavior .ID ()).Return (nil , assert .AnError )
431
+
432
+ persistentActor := newDurableStateActor (behavior , durableStore , eventStream )
433
+ pid , err := actorSystem .Spawn (ctx , behavior .ID (), persistentActor )
434
+ require .Error (t , err )
435
+ require .Nil (t , pid )
436
+
437
+ lib .Pause (time .Second )
438
+
439
+ err = actorSystem .Stop (ctx )
440
+ assert .NoError (t , err )
441
+
442
+ lib .Pause (time .Second )
443
+ eventStream .Close ()
444
+ durableStore .AssertExpectations (t )
445
+ })
446
+ t .Run ("with state recovery from state store with initial parsing failure" , func (t * testing.T ) {
447
+ defer goleak .VerifyNone (t )
448
+ ctx := context .TODO ()
449
+ actorSystem , err := goakt .NewActorSystem ("TestActorSystem" ,
450
+ goakt .WithPassivationDisabled (),
451
+ goakt .WithLogger (log .DiscardLogger ),
452
+ goakt .WithActorInitMaxRetries (3 ),
453
+ )
454
+ require .NoError (t , err )
455
+ assert .NotNil (t , actorSystem )
456
+
457
+ // start the actor system
458
+ err = actorSystem .Start (ctx )
459
+ require .NoError (t , err )
460
+
461
+ lib .Pause (time .Second )
462
+
463
+ persistenceID := uuid .NewString ()
464
+ behavior := NewAccountDurableStateBehavior (persistenceID )
465
+
466
+ eventStream := eventstream .New ()
467
+
468
+ latestState := & egopb.DurableState {
469
+ ResultingState : & anypb.Any {
470
+ TypeUrl : "invalid-type-url" ,
471
+ Value : []byte ("invalid-value" ),
472
+ },
473
+ }
474
+ durableStore := new (mocks.StateStore )
475
+ durableStore .EXPECT ().Ping (mock .Anything ).Return (nil )
476
+ durableStore .EXPECT ().GetLatestState (mock .Anything , behavior .ID ()).Return (latestState , nil )
477
+
478
+ persistentActor := newDurableStateActor (behavior , durableStore , eventStream )
479
+ pid , err := actorSystem .Spawn (ctx , behavior .ID (), persistentActor )
480
+ require .Error (t , err )
481
+ require .Nil (t , pid )
482
+
483
+ lib .Pause (time .Second )
484
+
485
+ err = actorSystem .Stop (ctx )
486
+ assert .NoError (t , err )
487
+
488
+ lib .Pause (time .Second )
489
+
490
+ eventStream .Close ()
491
+ durableStore .AssertExpectations (t )
492
+ })
493
+
494
+ t .Run ("with no durable state store" , func (t * testing.T ) {
495
+ defer goleak .VerifyNone (t )
496
+ ctx := context .TODO ()
497
+ actorSystem , err := goakt .NewActorSystem ("TestActorSystem" ,
498
+ goakt .WithPassivationDisabled (),
499
+ goakt .WithLogger (log .DiscardLogger ),
500
+ goakt .WithActorInitMaxRetries (3 ),
501
+ )
502
+ require .NoError (t , err )
503
+ assert .NotNil (t , actorSystem )
504
+
505
+ // start the actor system
506
+ err = actorSystem .Start (ctx )
507
+ require .NoError (t , err )
508
+
509
+ lib .Pause (time .Second )
510
+
511
+ persistenceID := uuid .NewString ()
512
+ behavior := NewAccountDurableStateBehavior (persistenceID )
513
+
514
+ eventStream := eventstream .New ()
515
+
516
+ persistentActor := newDurableStateActor (behavior , nil , eventStream )
517
+ pid , err := actorSystem .Spawn (ctx , behavior .ID (), persistentActor )
518
+ require .Error (t , err )
519
+ require .Nil (t , pid )
520
+
521
+ lib .Pause (time .Second )
522
+
523
+ err = actorSystem .Stop (ctx )
524
+ assert .NoError (t , err )
525
+
526
+ lib .Pause (time .Second )
527
+
528
+ eventStream .Close ()
529
+ })
360
530
}
0 commit comments