package lifecycle_test import ( "context" "errors" "fmt" "io" "log/slog" "os" "syscall" "testing" "time" "go.auvem.com/corestack/lifecycle" ) func TestRealFlow(t *testing.T) { for _, failure := range []bool{false, true} { t.Run(fmt.Sprint(failure), func(t *testing.T) { signals := make(chan os.Signal, 2) app, err := lifecycle.New(lifecycle.Config{Logger: slog.New(slog.NewTextHandler(io.Discard, nil)), Signals: signals, ShutdownTimeout: time.Second}) if err != nil { t.Fatal(err) } ready := make(chan struct{}, 3) release := make(chan struct{}) t.Cleanup(func() { select { case <-release: default: close(release) } }) failed := errors.New("service failed") for _, name := range []string{"daemon", "muxer"} { err = app.Register(lifecycle.Module{Name: name, Setup: func(ctx context.Context) error { if name == "daemon" { return app.Go(ctx, "queue", func(ctx context.Context) error { ready <- struct{}{}; <-ctx.Done(); <-release; return ctx.Err() }) } return nil }, Run: func(ctx context.Context) error { ready <- struct{}{} if name == "muxer" && failure { <-release return failed } <-ctx.Done() return ctx.Err() }, Teardown: func(context.Context) error { return nil }}) if err != nil { t.Fatal(err) } } done := make(chan error, 1) go func() { done <- app.Run(context.Background()) }() for range 3 { select { case <-ready: case <-time.After(3 * time.Second): t.Fatal("services did not start") } } if !failure { signals <- syscall.SIGTERM } close(release) select { case err = <-done: if failure && !errors.Is(err, failed) || !failure && err != nil { t.Fatalf("Run: %v", err) } case <-time.After(2 * time.Second): t.Fatal("Run blocked") } }) } } func Example() { signals := make(chan os.Signal, 2) app, err := lifecycle.New(lifecycle.Config{Logger: slog.New(slog.NewTextHandler(io.Discard, nil)), Signals: signals}) if err != nil { panic(err) } ready := make(chan struct{}, 2) for _, name := range []string{"daemon", "muxer"} { err := app.Register(lifecycle.Module{ Name: name, Setup: func(ctx context.Context) error { return app.Go(ctx, name+" worker", func(ctx context.Context) error { <-ctx.Done(); return ctx.Err() }) }, Run: func(ctx context.Context) error { ready <- struct{}{}; <-ctx.Done(); return ctx.Err() }, Teardown: func(context.Context) error { return nil }, }) if err != nil { panic(err) } } go func() { <-ready; <-ready; signals <- syscall.SIGTERM }() fmt.Println(app.Run(context.Background())) // Output: }