Files
go-omni/lifecycle/runtime_test.go

57 lines
1.4 KiB
Go

package lifecycle
import (
"context"
"errors"
"reflect"
"strings"
"sync"
"testing"
)
func TestRuntimeRollbackAndConcurrentClose(t *testing.T) {
r := NewRuntime(context.Background())
var order []int
failure, cleanup := errors.New("initialization failed"), errors.New("cleanup failed")
r.OnClose(func() error { order = append(order, 1); return cleanup })
r.OnClose(func() error { order = append(order, 2); panic("broken dependency") })
r.OnClose(func() error { order = append(order, 3); return nil })
var wg sync.WaitGroup
for i := 0; i < 20; i++ {
wg.Add(1)
go func() {
defer wg.Done()
if err := r.Close(); !errors.Is(err, cleanup) {
t.Error(err)
}
}()
}
wg.Wait()
if !reflect.DeepEqual(order, []int{3, 2, 1}) {
t.Fatal(order)
}
err := r.Finish(failure)
if !errors.Is(err, failure) || !errors.Is(err, cleanup) || !strings.Contains(err.Error(), "cleanup panic") {
t.Fatal(err)
}
if r.Context().Err() == nil {
t.Fatal("runtime context was not stopped")
}
}
func TestRuntimeRetainsMonitorFaultDuringCleanup(t *testing.T) {
r := NewRuntime(context.Background())
fault := errors.New("monitor failed")
r.OnClose(func() error { r.RequestShutdown(fault); return nil })
r.RequestShutdown(nil)
if err := r.Finish(nil); !errors.Is(err, fault) {
t.Fatal(err)
}
defer func() {
if recover() == nil {
t.Fatal("late ownership transfer accepted")
}
}()
r.OnClose(func() error { return nil })
}