Files
go-omni/resilience/breaker_test.go

76 lines
1.8 KiB
Go

package resilience
import (
"context"
"errors"
"io"
"net/http"
"strings"
"testing"
"time"
)
type roundTripFunc func(*http.Request) (*http.Response, error)
func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
func TestBreakerProbeAndLateResult(t *testing.T) {
b, _ := New(Config{FailureThreshold: 1, MaxConcurrent: 2, OpenTimeout: time.Second})
now := time.Now()
b.now = func() time.Time { return now }
ctx := context.Background()
first, _ := b.Acquire(ctx)
late, _ := b.Acquire(ctx)
first(false)
late(true)
if b.Snapshot().State != "open" {
t.Fatal(b.Snapshot())
}
if _, err := b.Acquire(ctx); !errors.Is(err, ErrOpen) {
t.Fatal(err)
}
now = now.Add(time.Second)
probe, err := b.Acquire(ctx)
if err != nil {
t.Fatal(err)
}
if _, err = b.Acquire(ctx); !errors.Is(err, ErrOpen) {
t.Fatal(err)
}
probe(true)
probe(false)
if s := b.Snapshot(); s.State != "closed" || s.Active != 0 {
t.Fatal(s)
}
}
func TestTransportHoldsSlotAndReleasesPanic(t *testing.T) {
b, _ := New(Config{MaxConcurrent: 1})
tr := Transport{Breaker: b, Base: roundTripFunc(func(*http.Request) (*http.Response, error) {
return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader("hello"))}, nil
})}
req, _ := http.NewRequest("GET", "http://example.test", nil)
r, err := tr.RoundTrip(req)
if err != nil {
t.Fatal(err)
}
if _, err = tr.RoundTrip(req); !errors.Is(err, ErrBusy) {
t.Fatal(err)
}
io.ReadAll(r.Body)
r.Body.Close()
if b.Snapshot().Active != 0 {
t.Fatal(b.Snapshot())
}
tr.Base = roundTripFunc(func(*http.Request) (*http.Response, error) { panic("fixture") })
func() {
defer func() {
if recover() == nil {
t.Error("panic lost")
}
}()
tr.RoundTrip(req)
}()
if b.Snapshot().Active != 0 {
t.Fatal(b.Snapshot())
}
}