package webhook
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
func TestHungTargetDoesNotStarveOtherTargets(t *testing.T) {
database := newTestDB(t)
const healthy = 5
arrived := make(chan string, healthy)
fast := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
arrived <- r.URL.Path
}))
t.Cleanup(fast.Close)
stop := make(chan struct{})
hung := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
select {
case <-stop:
case <-r.Context().Done():
}
}))
t.Cleanup(hung.Close)
s := New(database, true)
// cleanups run in reverse, and the service has to drain before hung.Close cuts it off
t.Cleanup(func() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
s.Close(ctx)
})
t.Cleanup(func() { close(stop) })
// more than the queue holds, all to a host that never answers
for i := range 300 {
s.dispatch(hookJob(context.Background(), fmt.Sprintf("%s/hung%d", hung.URL, i)))
}
// webhook queues are per hostname, and localhost gets a different one from 127.0.0.1
otherHost := strings.Replace(fast.URL, "127.0.0.1", "localhost", 1)
for i := range healthy {
s.dispatch(hookJob(context.Background(), fmt.Sprintf("%s/ok%d", otherHost, i)))
}
timeout := time.After(5 * time.Second)
for got := range healthy {
select {
case <-arrived:
case <-timeout:
t.Fatalf("%d of %d healthy deliveries arrived within 5s while another target hung", got, healthy)
}
}
}