103 lines
2.6 KiB
Go
103 lines
2.6 KiB
Go
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: <nil>
|
|
}
|