check / check (push) Waiting to run
The event log now loads only the 50 newest events, limited in its query, and only the newest starts expanded; the rest start collapsed. Paging is removed rather than capped, since 50 events never need a second page: the Previous and Next links, the `page` query parameter and the page number Replay and Resubmit carried back are gone, and a `?page=` left in an old link shows the 50 newest. A webhook with more than 50 events reads "50 most recent of N events" beside the heading. Comments and a README line that called `page` the only query parameter the service reads now name `next` and `notice`. Model: opus-5-5
211 lines
5.1 KiB
Go
211 lines
5.1 KiB
Go
package handlers_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi"
|
|
"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"
|
|
)
|
|
|
|
// contentTypeJSON is the content type the seeded events in this
|
|
// package carry. Shared across the seed helpers so the literal
|
|
// appears once.
|
|
const contentTypeJSON = "application/json"
|
|
|
|
// seedDeliveredEvent records an event and a delivery for it in
|
|
// the webhook's own database, so the log page has a delivery
|
|
// to render against the target.
|
|
func seedDeliveredEvent(
|
|
t *testing.T,
|
|
dbMgr *database.WebhookDBManager,
|
|
webhookID, targetID string,
|
|
) {
|
|
t.Helper()
|
|
|
|
webhookDB, err := dbMgr.GetDB(webhookID)
|
|
require.NoError(t, err)
|
|
|
|
event := &database.Event{
|
|
WebhookID: webhookID,
|
|
Method: http.MethodPost,
|
|
Body: `{"test":true}`,
|
|
ContentType: contentTypeJSON,
|
|
}
|
|
|
|
require.NoError(t, webhookDB.Omit(
|
|
clause.Associations,
|
|
).Create(event).Error)
|
|
|
|
dlv := &database.Delivery{
|
|
EventID: event.ID,
|
|
TargetID: targetID,
|
|
Status: database.DeliveryStatusDelivered,
|
|
}
|
|
|
|
require.NoError(t, webhookDB.Omit(
|
|
clause.Associations,
|
|
).Create(dlv).Error)
|
|
}
|
|
|
|
// renderSourceLogsPage runs the real event log handler for a
|
|
// webhook and returns the rendered HTML.
|
|
func renderSourceLogsPage(
|
|
t *testing.T,
|
|
h *handlers.Handlers,
|
|
sess *session.Session,
|
|
webhookID string,
|
|
) string {
|
|
t.Helper()
|
|
|
|
return renderSourceLogsPageWithQuery(
|
|
t, h, sess, webhookID, "",
|
|
)
|
|
}
|
|
|
|
// renderSourceLogsPageWithQuery is renderSourceLogsPage over a
|
|
// caller-supplied query string, for the page state a redirect back to
|
|
// the log carries in one.
|
|
func renderSourceLogsPageWithQuery(
|
|
t *testing.T,
|
|
h *handlers.Handlers,
|
|
sess *session.Session,
|
|
webhookID, query string,
|
|
) string {
|
|
t.Helper()
|
|
|
|
req := httptest.NewRequestWithContext(
|
|
context.Background(),
|
|
http.MethodGet,
|
|
"/hook/"+webhookID+"/events"+query,
|
|
nil,
|
|
)
|
|
|
|
for _, c := range authenticatedCookies(
|
|
t, sess, deleteTestUserID, deleteTestUsername,
|
|
) {
|
|
req.AddCookie(c)
|
|
}
|
|
|
|
rctx := chi.NewRouteContext()
|
|
rctx.URLParams.Add(paramSourceID, webhookID)
|
|
|
|
req = req.WithContext(
|
|
context.WithValue(
|
|
req.Context(), chi.RouteCtxKey, rctx,
|
|
),
|
|
)
|
|
|
|
w := httptest.NewRecorder()
|
|
h.HandleSourceLogs().ServeHTTP(w, req)
|
|
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
|
|
return w.Body.String()
|
|
}
|
|
|
|
// TestHandleSourceLogs_MasksSlackWebhookURL proves the event
|
|
// log page is handed a display-safe projection of each target
|
|
// rather than the stored row, so the credential cannot be
|
|
// rendered from its template data.
|
|
func TestHandleSourceLogs_MasksSlackWebhookURL(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)
|
|
tgt := seedConfiguredTarget(
|
|
t, db, wh.ID,
|
|
database.TargetTypeSlack,
|
|
`{"webhookUrl":"`+slackWebhookURL+`"}`,
|
|
)
|
|
|
|
seedDeliveredEvent(t, dbMgr, wh.ID, tgt.ID)
|
|
|
|
body := renderSourceLogsPage(t, h, sess, wh.ID)
|
|
|
|
assert.NotContains(t, body, slackSecretPath)
|
|
assert.NotContains(t, body, "T00000000")
|
|
assert.NotContains(t, body, "B00000000")
|
|
assert.NotContains(
|
|
t, body, "XXXXXXXXXXXXXXXXXXXXXXXX",
|
|
)
|
|
assert.NotContains(t, body, "webhookUrl")
|
|
|
|
// The page still identifies the delivery's target.
|
|
assert.Contains(t, body, tgt.Name)
|
|
assert.Contains(t, body, "delivered")
|
|
}
|
|
|
|
// TestHandleSourceLogs_ShowsFiftyNewestEvents proves the event log
|
|
// holds the 50 newest events, newest first, and not one more, and says
|
|
// how many events there are in all.
|
|
func TestHandleSourceLogs_ShowsFiftyNewestEvents(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
f := newRecentEventsFixture(t)
|
|
base := time.Now().Add(-time.Hour)
|
|
|
|
for i := range 51 {
|
|
f.event(
|
|
t, fmt.Sprintf("application/x-log-%02d", i), "{}",
|
|
base.Add(time.Duration(i)*time.Second),
|
|
)
|
|
}
|
|
|
|
body := renderSourceLogsPage(t, f.h, f.sess, f.webhook.ID)
|
|
|
|
assert.Equal(t, 50, strings.Count(body, `role="button"`))
|
|
assert.NotContains(t, body, "application/x-log-00")
|
|
assert.Contains(t, body, "application/x-log-01")
|
|
assert.Less(
|
|
t,
|
|
strings.Index(body, "application/x-log-50"),
|
|
strings.Index(body, "application/x-log-49"),
|
|
)
|
|
assert.Contains(t, body, "50 most recent of 51 events")
|
|
}
|
|
|
|
// TestHandleSourceLogs_OnlyNewestStartsExpanded proves that of the
|
|
// events in the log only the newest starts expanded.
|
|
func TestHandleSourceLogs_OnlyNewestStartsExpanded(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
f := newRecentEventsFixture(t)
|
|
now := time.Now()
|
|
|
|
f.event(t, "application/x-older", "{}", now.Add(-time.Minute))
|
|
f.event(t, "application/x-newer", "{}", now)
|
|
|
|
body := renderSourceLogsPage(t, f.h, f.sess, f.webhook.ID)
|
|
|
|
assert.Equal(t, 1, strings.Count(body, " data-open>"))
|
|
|
|
open := strings.Index(body, " data-open>")
|
|
newer := strings.Index(body, "application/x-newer")
|
|
older := strings.Index(body, "application/x-older")
|
|
|
|
assert.Less(t, open, newer, "the newest event is not the open one")
|
|
assert.Less(t, newer, older)
|
|
}
|