package lifecycle import ( "context" "errors" "fmt" "sync" "testing" ) func TestShutdownRetainsFirstFailure(t *testing.T) { parent, signal := context.WithCancel(context.Background()) c := New(parent) signal() if c.Err() != nil { t.Fatal("signal treated as failure") } first := errors.New("dependency stopped") c.RequestShutdown(first) var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done(); c.RequestShutdown(errors.New("later")); c.RequestShutdown(nil); _ = c.Err() }() } wg.Wait() if !errors.Is(c.Err(), first) || c.Context().Err() == nil { t.Fatal(c.Err()) } } func TestShutdownBeforeServingAndParentCauses(t *testing.T) { fatal := errors.New("initialization fault") c := New(nil) c.RequestShutdown(fatal) if c.Context().Err() == nil || !errors.Is(Cause(c.Context()), fatal) || !errors.Is(c.Err(), fatal) { t.Fatal("lost early shutdown") } c.RequestShutdown(nil) if !errors.Is(c.Err(), fatal) { t.Fatal("cleanup erased failure") } parent, cancel := context.WithCancelCause(context.Background()) child := New(parent) cancel(fatal) if !errors.Is(child.Err(), fatal) { t.Fatal("lost parent failure") } normal := New(nil) normal.RequestShutdown(nil) if normal.Err() != nil { t.Fatal(normal.Err()) } } func TestCancellationCompatibleCauseAndExplicitFailure(t *testing.T) { cause := fmt.Errorf("stop: %w", context.Canceled) ctx, cancel := context.WithCancelCause(context.Background()) cancel(cause) if Cause(ctx) != nil { t.Fatal("cancellation-compatible stop treated as a fault") } c := New(nil) c.RequestShutdown(cause) if !errors.Is(c.Err(), cause) { t.Fatal("explicit dependency failure suppressed") } }