mirror of
https://github.com/hanzo-fi/go-libs.git
synced 2026-10-30 20:53:44 +00:00
140 lines
3.3 KiB
Go
140 lines
3.3 KiB
Go
package health_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/fx"
|
|
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/fx/servicefx"
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/service/health"
|
|
)
|
|
|
|
func TestHealthController(t *testing.T) {
|
|
t.Parallel()
|
|
type testCase struct {
|
|
name string
|
|
healthChecksProvider []any
|
|
expectedStatus int
|
|
expectedResult map[string]string
|
|
}
|
|
|
|
var tests = []testCase{
|
|
{
|
|
name: "all-ok",
|
|
healthChecksProvider: []any{
|
|
func() health.NamedCheck {
|
|
return health.NewNamedCheck("test1", health.CheckFn(func(ctx context.Context) error {
|
|
return nil
|
|
}))
|
|
},
|
|
func() health.NamedCheck {
|
|
return health.NewNamedCheck("test2", health.CheckFn(func(ctx context.Context) error {
|
|
return nil
|
|
}))
|
|
},
|
|
},
|
|
expectedStatus: http.StatusOK,
|
|
expectedResult: map[string]string{
|
|
"test1": "OK",
|
|
"test2": "OK",
|
|
},
|
|
},
|
|
{
|
|
name: "one-failing",
|
|
healthChecksProvider: []any{
|
|
func() health.NamedCheck {
|
|
return health.NewNamedCheck("test1", health.CheckFn(func(ctx context.Context) error {
|
|
return nil
|
|
}))
|
|
},
|
|
func() health.NamedCheck {
|
|
return health.NewNamedCheck("test2", health.CheckFn(func(ctx context.Context) error {
|
|
return errors.New("failure")
|
|
}))
|
|
},
|
|
},
|
|
expectedStatus: http.StatusInternalServerError,
|
|
expectedResult: map[string]string{
|
|
"test1": "OK",
|
|
"test2": "failure",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
options := make([]fx.Option, 0)
|
|
options = append(options, servicefx.HealthModule(), fx.NopLogger)
|
|
for _, p := range tc.healthChecksProvider {
|
|
options = append(options, servicefx.ProvideHealthCheck(p))
|
|
}
|
|
options = append(options, fx.Invoke(func(ctrl *health.HealthController) {
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/_health", nil)
|
|
ctrl.Check(rec, req)
|
|
require.Equal(t, tc.expectedStatus, rec.Result().StatusCode)
|
|
|
|
ret := make(map[string]string)
|
|
require.NoError(t, json.NewDecoder(rec.Result().Body).Decode(&ret))
|
|
require.Equal(t, tc.expectedResult, ret)
|
|
}))
|
|
app := fx.New(options...)
|
|
require.NoError(t, app.Err())
|
|
}
|
|
}
|
|
|
|
func TestHealthControllerReturnsWhenContextCanceledDuringHungCheck(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
started := make(chan struct{})
|
|
release := make(chan struct{})
|
|
ctrl := health.NewHealthController(
|
|
health.NewNamedCheck("hung", health.CheckFn(func(ctx context.Context) error {
|
|
close(started)
|
|
<-release
|
|
return nil
|
|
})),
|
|
)
|
|
t.Cleanup(func() {
|
|
close(release)
|
|
})
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
req := httptest.NewRequest(http.MethodGet, "/_health", nil).WithContext(ctx)
|
|
rec := httptest.NewRecorder()
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
ctrl.Check(rec, req)
|
|
close(done)
|
|
}()
|
|
|
|
select {
|
|
case <-started:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("health check did not start")
|
|
}
|
|
|
|
cancel()
|
|
|
|
select {
|
|
case <-done:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("health controller did not return after request context cancellation")
|
|
}
|
|
|
|
require.Equal(t, http.StatusInternalServerError, rec.Result().StatusCode)
|
|
|
|
ret := make(map[string]string)
|
|
require.NoError(t, json.NewDecoder(rec.Result().Body).Decode(&ret))
|
|
require.Equal(t, map[string]string{
|
|
"hung": context.Canceled.Error(),
|
|
}, ret)
|
|
}
|