Store events and create deliveries in the delivery engine (closes #544)
check / check (push) Waiting to run
check / check (push) Waiting to run
The receiver, resubmit and replay handlers now read and check the request and make one call. Engine.StoreEvent stores an event with a pending delivery for each active target and the totals in one transaction, then queues them; Engine.CreateReplay does the same for one replay delivery. The handlers take *delivery.Engine, so the Notifier interface and its bridge are gone, and the engine's notify is no longer exported. Archives and CircuitBreakers stay because the handler tests fake them. The tests that read a queued task moved to the engine's tests; the handler tests use an engine that is never started. Model: opus-5-5
This commit is contained in:
@@ -10,7 +10,6 @@ import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/google/uuid"
|
||||
@@ -19,7 +18,6 @@ import (
|
||||
"gorm.io/gorm/clause"
|
||||
"sneak.berlin/go/webhooker/internal/config"
|
||||
"sneak.berlin/go/webhooker/internal/database"
|
||||
"sneak.berlin/go/webhooker/internal/delivery"
|
||||
"sneak.berlin/go/webhooker/internal/handlers"
|
||||
"sneak.berlin/go/webhooker/internal/middleware"
|
||||
"sneak.berlin/go/webhooker/internal/middleware/middlewaretest"
|
||||
@@ -209,50 +207,19 @@ func receivedLine(t *testing.T, buf *bytes.Buffer) map[string]any {
|
||||
return found[0]
|
||||
}
|
||||
|
||||
// targetWithEverySetting returns target settings with all six set, the
|
||||
// archive ones included, so a task that drops one of them differs from
|
||||
// the target.
|
||||
func targetWithEverySetting() database.Target {
|
||||
return database.Target{
|
||||
URL: replayTargetURL,
|
||||
Headers: map[string]string{"X-Api-Key": "k"},
|
||||
TimeoutSeconds: 7,
|
||||
ForwardQuery: true,
|
||||
ArchiveExpiry: time.Hour,
|
||||
ArchiveRotation: "daily",
|
||||
}
|
||||
}
|
||||
|
||||
// assertTaskCarriesTargetSettings proves a delivery task carries every
|
||||
// one of the target's settings. They are copied into the task one field
|
||||
// at a time, and a field left out is sent empty without any error.
|
||||
func assertTaskCarriesTargetSettings(
|
||||
t *testing.T, task delivery.Task, target *database.Target,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
assert.Equal(t, target.ID, task.TargetID)
|
||||
assert.Equal(t, target.URL, task.TargetURL)
|
||||
assert.Equal(t, target.Headers, task.TargetHeaders)
|
||||
assert.Equal(t, target.TimeoutSeconds, task.TimeoutSeconds)
|
||||
assert.Equal(t, target.ForwardQuery, task.ForwardQuery)
|
||||
assert.Equal(t, target.ArchiveExpiry, task.ArchiveExpiry)
|
||||
assert.Equal(t, target.ArchiveRotation, task.ArchiveRotation)
|
||||
}
|
||||
|
||||
// TestHandleWebhook_TaskCarriesEveryTargetSetting proves the task a
|
||||
// received event hands the delivery engine carries every one of the
|
||||
// target's settings.
|
||||
func TestHandleWebhook_TaskCarriesEveryTargetSetting(t *testing.T) {
|
||||
// TestHandleWebhook_StoresTheEvent proves an accepted request is
|
||||
// answered with the receiver's 200 and body, and that the event is
|
||||
// stored with a pending delivery to the webhook's active target.
|
||||
func TestHandleWebhook_StoresTheEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
h *handlers.Handlers
|
||||
db *database.Database
|
||||
notif *recordingNotifier
|
||||
dbMgr *database.WebhookDBManager
|
||||
)
|
||||
|
||||
app := newTestApp(t, &h, &db, ¬if)
|
||||
app := newTestApp(t, &h, &db, &dbMgr)
|
||||
app.RequireStart()
|
||||
|
||||
t.Cleanup(app.RequireStop)
|
||||
@@ -261,7 +228,7 @@ func TestHandleWebhook_TaskCarriesEveryTargetSetting(t *testing.T) {
|
||||
entrypoint := seedEntrypoint(t, db, webhook.ID)
|
||||
target := seedConfiguredTarget(
|
||||
t, db, webhook.ID, database.TargetTypeHTTP,
|
||||
targetWithEverySetting(),
|
||||
database.Target{URL: replayTargetURL},
|
||||
)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
@@ -271,8 +238,55 @@ func TestHandleWebhook_TaskCarriesEveryTargetSetting(t *testing.T) {
|
||||
))
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
assert.JSONEq(t, `{"status":"ok"}`, w.Body.String())
|
||||
|
||||
tasks := notif.Tasks()
|
||||
require.Len(t, tasks, 1)
|
||||
assertTaskCarriesTargetSettings(t, tasks[0], target)
|
||||
webhookDB, err := dbMgr.GetDB(webhook.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
events := listEvents(t, webhookDB)
|
||||
require.Len(t, events, 1)
|
||||
assert.Equal(t, entrypoint.ID, events[0].EntrypointID)
|
||||
|
||||
deliveries := listDeliveries(t, webhookDB, events[0].ID)
|
||||
require.Len(t, deliveries, 1)
|
||||
assert.Equal(t, target.ID, deliveries[0].TargetID)
|
||||
assert.Equal(t, database.DeliveryStatusPending, deliveries[0].Status)
|
||||
}
|
||||
|
||||
// TestHandleWebhook_FailedStoreAnswersPlainError proves that an event
|
||||
// the engine cannot store is answered with the receiver's plain-text
|
||||
// 500, and that nothing of it is left stored.
|
||||
func TestHandleWebhook_FailedStoreAnswersPlainError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
h *handlers.Handlers
|
||||
db *database.Database
|
||||
dbMgr *database.WebhookDBManager
|
||||
)
|
||||
|
||||
app := newTestApp(t, &h, &db, &dbMgr)
|
||||
app.RequireStart()
|
||||
|
||||
t.Cleanup(app.RequireStop)
|
||||
|
||||
webhook := seedWebhook(t, db)
|
||||
entrypoint := seedEntrypoint(t, db, webhook.ID)
|
||||
|
||||
// Without its event totals table the webhook's database refuses
|
||||
// the totals write, which comes after the event row.
|
||||
webhookDB, err := dbMgr.GetDB(webhook.ID)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t,
|
||||
webhookDB.Migrator().DropTable(&database.EventTotals{}))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
h.HandleWebhook().ServeHTTP(w, postRequest(
|
||||
"/h/"+entrypoint.Path, nil,
|
||||
map[string]string{"uuid": entrypoint.Path},
|
||||
))
|
||||
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
assert.Equal(t, "Internal server error\n", w.Body.String())
|
||||
assert.Empty(t, listEvents(t, webhookDB))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user