package webhooks
import (
"context"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"github.com/vaughan0/go-ini"
sq "github.com/Masterminds/squirrel"
"git.sr.ht/~sircmpwn/core-go/crypto"
"git.sr.ht/~sircmpwn/core-go/database"
)
func init() {
conf, err := ini.Load(strings.NewReader(`
[webhooks]
private-key=ebzsjPaN6E13ln/FeNWly1C92q6bVMVdOnDo1HPl5fc=
[sr.ht]
network-key=tbuG-7Vh44vrDq1L_HKWkHnWrDOtJhEkPKPiauaLeuk=
[test::api]
internal-ipnet=127.0.0.1/24,::1/64`))
if err != nil {
panic(err)
}
crypto.InitCrypto(conf)
}
func TestDelivery(t *testing.T) {
var called bool
srv := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
called = true
assert.Equal(t, r.Method, http.MethodPost)
assert.Equal(t, r.URL.Path, "/webhook")
assert.NotEqual(t, "", r.Header.Get("X-Webhook-Delivery"))
assert.Equal(t, "profile:update", r.Header.Get("X-Webhook-Event"))
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
b, err := ioutil.ReadAll(r.Body)
assert.Nil(t, err)
assert.Equal(t, `{"hello": "world"}`, string(b))
nonce := r.Header.Get("X-Payload-Nonce")
signature := r.Header.Get("X-Payload-Signature")
assert.True(t, crypto.VerifyWebhook(b, nonce, signature))
w.Write([]byte("Thanks!"))
}))
defer srv.Close()
queue := NewLegacyQueue()
q := sq.
Select().
From("user_webhook_subscription").
Where(`user_id = ?`, 42)
queue.Schedule(q, "user", "profile:update", []byte(`{"hello": "world"}`))
db, mock, err := sqlmock.New()
if err != nil {
panic(err)
}
// Lookup phase
mock.ExpectBegin()
mock.ExpectQuery(`SELECT .* FROM user_webhook_subscription`).
WillReturnRows(sqlmock.NewRows([]string{
"id", "created", "url", "events",
}).AddRow(
1337, time.Now().UTC(),
srv.URL + "/webhook",
"profile:update")).
WithArgs(42, sqlmock.AnyArg()) // Any => events LIKE %profile:update%
mock.ExpectCommit()
// Schedule phase
mock.ExpectBegin()
mock.ExpectQuery(`INSERT INTO user_webhook_delivery`).
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(4096))
mock.ExpectCommit()
ctx := database.Context(context.Background(), db)
queue.Queue.Dispatch(ctx)
assert.Nil(t, mock.ExpectationsWereMet())
// Delivery phase
db, mock, err = sqlmock.New()
if err != nil {
panic(err)
}
mock.ExpectBegin()
mock.ExpectExec(`UPDATE user_webhook_delivery`).
WithArgs("Thanks!", 200, sqlmock.AnyArg()) // Any => response headers
mock.ExpectCommit()
ctx = database.Context(context.Background(), db)
queue.Queue.Dispatch(ctx)
assert.Nil(t, mock.ExpectationsWereMet())
assert.True(t, called)
}