Files
go-omni/lifecycle/runtime_test.go

82 lines
2.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 })
}
func TestFinishRetainsDependenciesAfterIncompleteHTTPDrain(t *testing.T) {
r := NewRuntime(context.Background())
var order []string
r.OnStop("worker", func(context.Context) error { order = append(order, "stop"); return nil })
r.OnDrain("worker", func(context.Context) error { order = append(order, "drain"); return nil })
r.OnClose(func() error { t.Error("released dependency while HTTP work remains"); return nil })
fault := errors.New("HTTP shutdown timeout")
err := r.Finish(errors.Join(fault, ErrResourcesRetained))
if !errors.Is(err, fault) || !errors.Is(err, ErrResourcesRetained) {
t.Fatal(err)
}
if !reflect.DeepEqual(order, []string{"stop", "drain"}) {
t.Fatal(order)
}
if r.WorkContext().Err() == nil {
t.Fatal("background work was not canceled")
}
if err = r.Close(); !errors.Is(err, ErrResourcesRetained) {
t.Fatal("deferred Close bypassed retention", err)
}
if err = r.Finish(nil); !errors.Is(err, ErrResourcesRetained) {
t.Fatal("repeat Finish bypassed retention", err)
}
}