Files
gpt-load/internal/app/lifecycle_integration_test.go

157 lines
4.5 KiB
Go

package app
import (
"context"
"errors"
"net/http"
"sync"
"testing"
"time"
"gpt-load/internal/httplifecycle"
"gpt-load/internal/storage"
"github.com/gin-gonic/gin"
)
func TestAppStopWaitsForTrackedControlHandler(t *testing.T) {
db, err := storage.Open(":memory:")
if err != nil {
t.Fatalf("storage.Open() error = %v", err)
}
lifecycle := httplifecycle.NewCoordinator()
engine, err := NewEngineWithLifecycle(lifecycle)
if err != nil {
t.Fatalf("NewEngineWithLifecycle() error = %v", err)
}
entered := make(chan struct{})
release := make(chan struct{})
var releaseOnce sync.Once
engine.GET("/blocking", func(c *gin.Context) {
close(entered)
<-release
c.Status(http.StatusNoContent)
})
application := NewApp(AppParams{
Engine: engine,
Config: testConfig(t),
DB: db,
StartupBootstrap: startupBootstrapFunc(noopStartupBootstrap),
RuntimeState: runtimeStateLoaderFunc(func(context.Context) error { return nil }),
ControlRuntime: newControlRuntimeFake(nil, false),
RequestLogs: newRequestLogRuntimeFake(nil, nil),
Lifecycle: lifecycle,
})
t.Cleanup(func() { releaseOnce.Do(func() { close(release) }) })
if err := application.Start(); err != nil {
t.Fatalf("Start() error = %v", err)
}
requestDone := make(chan error, 1)
go func() {
response, requestErr := http.Get("http://" + application.Address() + "/blocking")
if requestErr == nil {
defer response.Body.Close()
if response.StatusCode != http.StatusNoContent {
requestErr = errors.New("unexpected blocking response status")
}
}
requestDone <- requestErr
}()
select {
case <-entered:
case <-time.After(time.Second):
t.Fatal("blocking handler was not entered")
}
stopDone := make(chan error, 1)
go func() { stopDone <- application.Stop(context.Background()) }()
select {
case err := <-stopDone:
t.Fatalf("Stop() returned before tracked handler released: %v", err)
case <-time.After(50 * time.Millisecond):
}
releaseOnce.Do(func() { close(release) })
if err := <-requestDone; err != nil {
t.Fatalf("blocking request error = %v", err)
}
if err := <-stopDone; err != nil {
t.Fatalf("Stop() error = %v", err)
}
}
func TestAppStopCancelsTrackedControlHandlerAndStartsExecutionShutdown(t *testing.T) {
db, err := storage.Open(":memory:")
if err != nil {
t.Fatalf("storage.Open() error = %v", err)
}
lifecycle := httplifecycle.NewCoordinator()
engine, err := NewEngineWithLifecycle(lifecycle)
if err != nil {
t.Fatalf("NewEngineWithLifecycle() error = %v", err)
}
entered := make(chan struct{})
canceled := make(chan struct{})
shutdown := make(chan struct{})
var canceledOnce sync.Once
var shutdownOnce sync.Once
engine.GET("/blocking", func(c *gin.Context) {
close(entered)
<-c.Request.Context().Done()
canceledOnce.Do(func() { close(canceled) })
c.Status(http.StatusNoContent)
})
executionRuntime := &executionRuntimeFake{
shutdownFunc: func() { shutdownOnce.Do(func() { close(shutdown) }) },
}
application := NewApp(AppParams{
Engine: engine,
Config: testConfig(t),
DB: db,
StartupBootstrap: startupBootstrapFunc(noopStartupBootstrap),
RuntimeState: runtimeStateLoaderFunc(func(context.Context) error { return nil }),
ControlRuntime: newControlRuntimeFake(nil, false),
RequestLogs: newRequestLogRuntimeFake(nil, nil),
Lifecycle: lifecycle,
ExecutionRuntime: executionRuntime,
})
if err := application.Start(); err != nil {
t.Fatalf("Start() error = %v", err)
}
requestDone := make(chan error, 1)
go func() {
response, requestErr := http.Get("http://" + application.Address() + "/blocking")
if requestErr == nil {
defer response.Body.Close()
if response.StatusCode != http.StatusNoContent {
requestErr = errors.New("unexpected blocking response status")
}
}
requestDone <- requestErr
}()
select {
case <-entered:
case <-time.After(time.Second):
t.Fatal("blocking handler was not entered")
}
stopDone := make(chan error, 1)
go func() { stopDone <- application.Stop(context.Background()) }()
select {
case <-canceled:
case <-time.After(time.Second):
t.Fatal("management handler context was not canceled during shutdown")
}
select {
case <-shutdown:
case <-time.After(time.Second):
t.Fatal("execution runtime shutdown did not start promptly")
}
if err := <-requestDone; err != nil {
t.Fatalf("blocking request error = %v", err)
}
if err := <-stopDone; err != nil {
t.Fatalf("Stop() error = %v", err)
}
}