github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/actor/supervision_test.go (about)

     1  package actor
     2  
     3  import (
     4  	"reflect"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  )
     9  
    10  type actorWithSupervisor struct {
    11  	wg *sync.WaitGroup
    12  }
    13  
    14  func (a *actorWithSupervisor) Receive(ctx Context) {
    15  	switch ctx.Message().(type) {
    16  	case *Started:
    17  		child := ctx.Spawn(PropsFromProducer(func() Actor { return &failingChildActor{} }))
    18  		ctx.Send(child, "Fail!")
    19  	}
    20  }
    21  
    22  func (a *actorWithSupervisor) HandleFailure(*ActorSystem, Supervisor, *PID, *RestartStatistics, interface{}, interface{}) {
    23  	a.wg.Done()
    24  }
    25  
    26  type failingChildActor struct{}
    27  
    28  func (a *failingChildActor) Receive(ctx Context) {
    29  	switch ctx.Message().(type) {
    30  	case string:
    31  		panic("Oh noes!")
    32  	}
    33  }
    34  
    35  func TestActorWithOwnSupervisorCanHandleFailure(t *testing.T) {
    36  	wg := &sync.WaitGroup{}
    37  	wg.Add(1)
    38  	props := PropsFromProducer(func() Actor { return &actorWithSupervisor{wg: wg} })
    39  	rootContext.Spawn(props)
    40  	wg.Wait()
    41  }
    42  
    43  func NewObserver() (func(ReceiverFunc) ReceiverFunc, *Expector) {
    44  	c := make(chan interface{})
    45  	e := &Expector{C: c}
    46  	f := func(next ReceiverFunc) ReceiverFunc {
    47  		fn := func(context ReceiverContext, env *MessageEnvelope) {
    48  			message := env.Message
    49  			c <- message
    50  			next(context, env)
    51  		}
    52  
    53  		return fn
    54  	}
    55  	return f, e
    56  }
    57  
    58  type Expector struct {
    59  	C <-chan interface{}
    60  }
    61  
    62  func (e *Expector) ExpectMsg(expected interface{}, t *testing.T) {
    63  	actual := <-e.C
    64  	if actual == expected {
    65  	} else {
    66  
    67  		at := reflect.TypeOf(actual)
    68  		et := reflect.TypeOf(expected)
    69  		t.Errorf("Expected %v:%v, got %v:%v", et, expected, at, actual)
    70  	}
    71  }
    72  
    73  func (e *Expector) ExpectNoMsg(t *testing.T) {
    74  	select {
    75  	case actual := <-e.C:
    76  		at := reflect.TypeOf(actual)
    77  		t.Errorf("Expected no message got %v:%v", at, actual)
    78  	case <-time.After(time.Second * 1):
    79  		// pass
    80  	}
    81  }
    82  
    83  func TestActorStopsAfterXRestarts(t *testing.T) {
    84  	m, e := NewObserver()
    85  	props := PropsFromProducer(func() Actor { return &failingChildActor{} }, WithReceiverMiddleware(m))
    86  	child := rootContext.Spawn(props)
    87  	fail := "fail!"
    88  
    89  	e.ExpectMsg(startedMessage, t)
    90  
    91  	// root supervisor allows 10 restarts
    92  	for i := 0; i < 10; i++ {
    93  		rootContext.Send(child, fail)
    94  		e.ExpectMsg(fail, t)
    95  		e.ExpectMsg(restartingMessage, t)
    96  		e.ExpectMsg(startedMessage, t)
    97  	}
    98  	rootContext.Send(child, fail)
    99  	e.ExpectMsg(fail, t)
   100  	// the 11th time should cause a termination
   101  	e.ExpectMsg(stoppingMessage, t)
   102  }