Files
webhooker/internal/handlers/target_list_test.go
T
clawbot 9305af4f85
check / check (push) Successful in 3m17s
Show each target's delivered and failed deliveries in the target list (closes #372)
The target list showed nothing about how a target's deliveries were going. Each target now shows Delivered and Failed, each in total and in the last 24 hours. The totals are the per-target running totals kept for the statistics pane, so retention does not reduce them; the 24-hour figures are one count over the deliveries' final-status index, for all of the webhook's targets at once. Pending and retrying deliveries count in neither. If the event database cannot be read, each row says so instead of showing zeros. The archive details and Download button on database targets are kept.

Model: opus-5-5
2026-10-02 23:47:16 +02:00

175 lines
5.6 KiB
Go

package handlers_test
import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sneak.berlin/go/webhooker/internal/database"
"sneak.berlin/go/webhooker/internal/delivery"
"sneak.berlin/go/webhooker/internal/handlers"
"sneak.berlin/go/webhooker/internal/logger"
"sneak.berlin/go/webhooker/internal/session"
)
// TestHandleSourceDetail_ShowsArchiveFile proves a database target's
// row names its archive file and says "not created yet" before the
// first write, adds the file's size and last write once it has one row,
// and says "not created yet" again once the file has been moved away.
// A target of another type shows no archive file.
func TestHandleSourceDetail_ShowsArchiveFile(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 := seedWebhook(t, db)
archive := seedTarget(t, db, wh.ID, database.TargetTypeDatabase)
seedTarget(t, db, wh.ID, database.TargetTypeLog)
path := delivery.ArchivePath(dbMgr, wh, archive)
body := renderSourceDetailPage(t, h, sess, wh.ID)
assert.Equal(t, 1, strings.Count(body, "Archive File:"))
assert.Contains(t, body, filepath.Base(path))
assert.Contains(t, body, "not created yet")
assert.NotContains(t, body, "Archive Size:")
seedArchive(t, path, 1, 100)
file, err := os.Stat(path)
require.NoError(t, err)
body = renderSourceDetailPage(t, h, sess, wh.ID)
assert.Contains(t, body, filepath.Base(path))
assert.NotContains(t, body, "not created yet")
assert.Regexp(t,
`Archive Size:</span>\s*<span>[1-9][0-9.]* [kM]?B</span>`, body,
)
assert.Contains(t, body,
`title="`+file.ModTime().UTC().Format(time.DateTime)+` UTC"`,
)
require.NoError(t, os.Rename(path, filepath.Join(t.TempDir(), "moved.db")))
body = renderSourceDetailPage(t, h, sess, wh.ID)
assert.Contains(t, body, filepath.Base(path))
assert.Contains(t, body, "not created yet")
assert.NotContains(t, body, "Archive Size:")
}
// targetList returns the text of the targets section in a rendered
// webhook page, from its heading to the next heading, with the markup
// taken out and each run of space made one space. Each target's row
// then reads as its name, type, state and buttons, followed by the
// lines below them.
func targetList(t *testing.T, page string) string {
t.Helper()
_, list, found := strings.Cut(page, ">Targets</h2>")
require.True(t, found, "the page has no targets section")
list, _, _ = strings.Cut(list, "<h2")
list = regexp.MustCompile(`<[^>]*>`).ReplaceAllString(list, " ")
return strings.Join(strings.Fields(list), " ")
}
// TestHandleSourceDetail_ShowsTargetDeliveries checks each target row's
// delivered and failed deliveries, in total and in the last 24 hours,
// for the history seedStatsHistory builds, before and after the real
// retention reaper removes the oldest event. The http target has one
// delivered, one of them in the last 24 hours, and three failed, one of
// them in the last 24 hours and one of them the oldest event's, which
// retention removes without changing the total. The active log target
// has two failed, both in the last 24 hours, and its pending and
// retrying deliveries count in neither. The four inactive log targets
// have none.
func TestHandleSourceDetail_ShowsTargetDeliveries(t *testing.T) {
t.Parallel()
var (
h *handlers.Handlers
sess *session.Session
db *database.Database
dbMgr *database.WebhookDBManager
log *logger.Logger
)
app := newTestApp(t, &h, &sess, &db, &dbMgr, &log)
app.RequireStart()
t.Cleanup(app.RequireStop)
hist := seedStatsHistory(t, h, sess, db, dbMgr)
const (
httpRow = "Delivered: 1 in total, 1 in the last 24 hours " +
"Failed: 3 in total, 1 in the last 24 hours"
activeLogRow = "t-log log Active Edit Deactivate Delete " +
"Delivered: 0 in total, 0 in the last 24 hours " +
"Failed: 2 in total, 2 in the last 24 hours"
inactiveLogRow = "t-log log Inactive Edit Activate Delete " +
"Delivered: 0 in total, 0 in the last 24 hours " +
"Failed: 0 in total, 0 in the last 24 hours"
)
list := targetList(t, renderSourceDetailPage(t, h, sess, hist.webhook.ID))
assert.Equal(t, 1, strings.Count(list, httpRow))
assert.Equal(t, 1, strings.Count(list, activeLogRow))
assert.Equal(t, 4, strings.Count(list, inactiveLogRow))
statsPrune(t, db, dbMgr, log, hist.webhookDB)
list = targetList(t, renderSourceDetailPage(t, h, sess, hist.webhook.ID))
assert.Equal(t, 1, strings.Count(list, httpRow))
assert.Equal(t, 1, strings.Count(list, activeLogRow))
assert.Equal(t, 4, strings.Count(list, inactiveLogRow))
}
// TestHandleSourceDetail_TargetDeliveriesUnreadable checks that when the
// webhook's event database cannot be read, each target's row says so
// instead of showing zeros.
func TestHandleSourceDetail_TargetDeliveriesUnreadable(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 := seedWebhook(t, db)
seedTarget(t, db, wh.ID, database.TargetTypeLog)
webhookDB, err := dbMgr.GetDB(wh.ID)
require.NoError(t, err)
require.NoError(t,
webhookDB.Migrator().DropTable(&database.TargetTotals{}))
list := targetList(t, renderSourceDetailPage(t, h, sess, wh.ID))
assert.Contains(t, list, "t-log log Active Edit Deactivate Delete "+
"The delivery counts could not be read.")
assert.NotContains(t, list, "Delivered:")
}