http-add-on/interceptor/handler/probe_test.go

251 lines
4.6 KiB
Go

package handler
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"time"
"github.com/go-logr/logr/funcr"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/kedacore/http-add-on/pkg/util"
)
var _ = Describe("ProbeHandler", func() {
Context("New", func() {
It("returns new object with expected fields", func() {
var (
ctx = context.Background()
ret = errors.New("test error")
)
var b bool
healthCheckers := []util.HealthChecker{
util.HealthCheckerFunc(func(_ context.Context) error {
b = true
return ret
}),
}
ph := NewProbe(healthCheckers)
Expect(ph).NotTo(BeNil())
h := ph.healthy.Load()
Expect(h).To(BeFalse())
hcs := ph.healthCheckers
Expect(hcs).To(HaveLen(1))
hc := hcs[0]
Expect(hc).NotTo(BeNil())
err := hc.HealthCheck(ctx)
Expect(err).To(MatchError(ret))
Expect(b).To(BeTrue())
})
})
Context("ServeHTTP", func() {
const (
host = "keda.sh"
path = "/README"
)
var (
w *httptest.ResponseRecorder
r *http.Request
)
BeforeEach(func() {
w = httptest.NewRecorder()
r = httptest.NewRequest(http.MethodGet, path, nil)
r.Host = host
})
When("healthy", func() {
It("serves 200", func() {
var (
sc = http.StatusOK
st = http.StatusText(sc)
)
var ph Probe
ph.healthy.Store(true)
ph.ServeHTTP(w, r)
Expect(w.Code).To(Equal(sc))
Expect(w.Body.String()).To(Equal(st))
})
})
When("unhealthy", func() {
It("serves 503", func() {
var (
sc = http.StatusServiceUnavailable
st = http.StatusText(sc)
)
var ph Probe
ph.healthy.Store(false)
ph.ServeHTTP(w, r)
Expect(w.Code).To(Equal(sc))
Expect(w.Body.String()).To(Equal(st))
})
})
})
Context("Start", func() {
It("returns when context is done", func() {
ctx, cancel := context.WithCancel(context.Background())
cancel()
var ph Probe
err := util.WithTimeout(time.Second, func() error {
ph.Start(ctx)
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("invokes check every second", func() {
const (
n = 10
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var i int
ph := Probe{
healthCheckers: []util.HealthChecker{
util.HealthCheckerFunc(func(_ context.Context) error {
i++
return nil
}),
},
}
go ph.Start(ctx)
time.Sleep(n * time.Second)
Expect(i).To(BeNumerically("~", n, 2))
})
})
Context("check", func() {
When("all health checks succeed", func() {
It("sets healthy to true", func() {
var (
ctx = context.Background()
)
var bs []bool
ph := Probe{
healthCheckers: []util.HealthChecker{
util.HealthCheckerFunc(func(_ context.Context) error {
bs = append(bs, true)
return nil
}),
util.HealthCheckerFunc(func(_ context.Context) error {
bs = append(bs, true)
return nil
}),
util.HealthCheckerFunc(func(_ context.Context) error {
bs = append(bs, true)
return nil
}),
},
}
ph.check(ctx)
healthCheckersLen := len(ph.healthCheckers)
Expect(bs).To(HaveLen(healthCheckersLen))
healthy := ph.healthy.Load()
Expect(healthy).To(BeTrue())
})
})
When("a health check fail", func() {
It("sets healthy to false", func() {
var (
ctx = context.Background()
)
ph := Probe{
healthCheckers: []util.HealthChecker{
util.HealthCheckerFunc(func(_ context.Context) error {
return nil
}),
util.HealthCheckerFunc(func(_ context.Context) error {
return context.Canceled
}),
util.HealthCheckerFunc(func(_ context.Context) error {
return nil
}),
},
}
ph.healthy.Store(true)
ph.check(ctx)
healthy := ph.healthy.Load()
Expect(healthy).To(BeFalse())
})
It("logs the returned error", func() {
const (
msg = "health check function failed"
)
var (
ctx = context.Background()
ret = errors.New("test error")
)
var b bool
ctx = util.ContextWithLogger(ctx, funcr.NewJSON(
func(obj string) {
var m map[string]interface{}
err := json.Unmarshal([]byte(obj), &m)
Expect(err).NotTo(HaveOccurred())
Expect(m).To(HaveKeyWithValue("msg", msg))
Expect(m).To(HaveKeyWithValue("error", ret.Error()))
b = true
},
funcr.Options{},
))
ph := Probe{
healthCheckers: []util.HealthChecker{
util.HealthCheckerFunc(func(_ context.Context) error {
return ret
}),
},
}
ph.check(ctx)
Expect(b).To(BeTrue())
})
})
})
})