check / check (push) Successful in 3m23s
Each entrypoint in the webhook page's entrypoint list now shows when its last event arrived (relative, with the full UTC time on hover), or "never", and how many events arrived through it within the webhook's retention period. Both come from one query per page, grouped by entrypoint, over a new events index on entrypoint_id, deleted_at and created_at, so the page reads only the index entries it counts. Pre-1.0: the index goes into the schema in place. A test checks the database's plan for the statement as the code builds it; another shows two entrypoints with different traffic each with their own figures, an event older than retention left out, and an unused entrypoint reading "never". Model: opus-5-5
107 lines
2.9 KiB
Go
107 lines
2.9 KiB
Go
package handlers_test
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/gorm/clause"
|
|
"sneak.berlin/go/webhooker/internal/database"
|
|
"sneak.berlin/go/webhooker/internal/handlers"
|
|
"sneak.berlin/go/webhooker/internal/session"
|
|
)
|
|
|
|
// TestHandleSourceDetail_ShowsEntrypointEvents proves each entrypoint
|
|
// on the webhook page shows its own figures: how many events arrived
|
|
// through it within the webhook's retention period, leaving out one
|
|
// older than that, and when the newest arrived, or "never" for an
|
|
// entrypoint with none.
|
|
func TestHandleSourceDetail_ShowsEntrypointEvents(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
h *handlers.Handlers
|
|
sess *session.Session
|
|
db *database.Database
|
|
dbMgr *database.WebhookDBManager
|
|
)
|
|
|
|
app := newTestApp(t, &h, &sess, &db, &dbMgr)
|
|
app.RequireStart()
|
|
|
|
t.Cleanup(app.RequireStop)
|
|
|
|
wh := &database.Webhook{
|
|
UserID: deleteTestUserID, Name: "figures", RetentionDays: 7,
|
|
}
|
|
require.NoError(t, db.DB().Omit(clause.Associations).Create(wh).Error)
|
|
|
|
webhookDB, err := dbMgr.GetDB(wh.ID)
|
|
require.NoError(t, err)
|
|
|
|
entrypoint := func() *database.Entrypoint {
|
|
ep := &database.Entrypoint{
|
|
WebhookID: wh.ID, Path: uuid.New().String(), Active: true,
|
|
}
|
|
require.NoError(t,
|
|
db.DB().Omit(clause.Associations).Create(ep).Error)
|
|
|
|
return ep
|
|
}
|
|
|
|
event := func(ep *database.Entrypoint, age time.Duration) time.Time {
|
|
e := &database.Event{
|
|
WebhookID: wh.ID,
|
|
EntrypointID: ep.ID,
|
|
Method: http.MethodPost,
|
|
}
|
|
e.CreatedAt = time.Now().Add(-age)
|
|
require.NoError(t,
|
|
webhookDB.Omit(clause.Associations).Create(e).Error)
|
|
|
|
return e.CreatedAt
|
|
}
|
|
|
|
busy, quiet, unused := entrypoint(), entrypoint(), entrypoint()
|
|
|
|
event(busy, 8*24*time.Hour) // older than the 7 days kept
|
|
event(busy, 3*time.Hour)
|
|
busyLast := event(busy, time.Hour)
|
|
quietLast := event(quiet, 2*24*time.Hour)
|
|
|
|
body := renderSourceDetailPage(t, h, sess, wh.ID)
|
|
|
|
// rowOf returns the part of the page from an entrypoint's URL to
|
|
// the next entrypoint's, which holds its figures.
|
|
rowOf := func(ep *database.Entrypoint) string {
|
|
_, row, found := strings.Cut(body, `id="entrypoint-url-`+ep.ID+`"`)
|
|
require.True(t, found)
|
|
|
|
row, _, _ = strings.Cut(row, `id="entrypoint-url-`)
|
|
|
|
return row
|
|
}
|
|
|
|
lastEvent := func(at time.Time) string {
|
|
return `Last Event:</span>\s*<span title="` +
|
|
at.UTC().Format(time.DateTime) + ` UTC">[^<]+ ago</span>`
|
|
}
|
|
|
|
count := func(n int) string {
|
|
return `Events Within Retention:</span>\s*<span>` +
|
|
strconv.Itoa(n) + `</span>`
|
|
}
|
|
|
|
assert.Regexp(t, lastEvent(busyLast), rowOf(busy))
|
|
assert.Regexp(t, count(2), rowOf(busy))
|
|
assert.Regexp(t, lastEvent(quietLast), rowOf(quiet))
|
|
assert.Regexp(t, count(1), rowOf(quiet))
|
|
assert.Regexp(t, `Last Event:</span>\s*<span>never</span>`, rowOf(unused))
|
|
assert.Regexp(t, count(0), rowOf(unused))
|
|
}
|