package webhook
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"path/filepath"
"sync"
"testing"
"time"
"github.com/bluesky-social/indigo/atproto/syntax"
"tangled.org/core/spindle/db"
"tangled.org/core/spindle/models"
)
func newTestDB(t *testing.T) *db.DB {
t.Helper()
database, err := db.Make(context.Background(), filepath.Join(t.TempDir(), "spindle.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { database.Close() })
return database
}
func hookJob(ctx context.Context, url string) deliveryJob {
hook := models.Webhook{Id: 1, Url: url, Active: true, Events: []string{"push"}}
return deliveryJob{ctx: ctx, webhook: hook, event: "push", userAgent: "test", payload: []byte("{}")}
}
func TestDispatchWhileClosingDoesNotPanic(t *testing.T) {
const dispatchers = 8
s := New(newTestDB(t), true)
hook := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
t.Cleanup(hook.Close)
ctx := context.Background()
var mu sync.Mutex
var panics []string
closed := make(chan struct{})
var wg sync.WaitGroup
for range dispatchers {
wg.Go(func() {
defer func() {
if p := recover(); p != nil {
mu.Lock()
panics = append(panics, fmt.Sprint(p))
mu.Unlock()
}
}()
for range 300 {
s.dispatch(hookJob(ctx, hook.URL))
}
<-closed
for range 50 {
s.dispatch(hookJob(ctx, hook.URL))
}
})
}
done := make(chan struct{})
go func() {
s.Close(context.Background())
close(done)
}()
select {
case <-done:
case <-time.After(30 * time.Second):
t.Fatal("Close did not return")
}
close(closed)
wg.Wait()
mu.Lock()
defer mu.Unlock()
if len(panics) > 0 {
t.Fatalf("dispatch panicked while the service closed: %d panics, first %q", len(panics), panics[0])
}
}
func TestCloseTwiceIsFine(t *testing.T) {
s := New(newTestDB(t), true)
s.Close(context.Background())
s.Close(context.Background())
}
func TestRedeliverRefusesWhenTheGateSaysPaused(t *testing.T) {
s := New(newTestDB(t), true, WithGate(func(syntax.DID) (bool, error) { return true, nil }))
t.Cleanup(func() { s.Close(context.Background()) })
err := s.Redeliver(context.Background(), models.Webhook{Id: 1, RepoDid: "did:plc:paused", Url: "http://hook.test/x"}, models.WebhookDelivery{RequestBody: "{}"})
if !errors.Is(err, ErrPaused) {
t.Fatalf("err = %v, want ErrPaused", err)
}
}