check / check (push) Successful in 4m23s
The webhook page and everything under it move from /source/ID to /hook/ID, the list and new-webhook form to /hooks and /hooks/new, and the event log from .../logs to /hook/ID/events, body download included. Entrypoint URLs move from /webhook/UUID to /h/UUID, and the webhook page shows only that form. The old paths are gone. Links, redirects, form actions, tests, comments and the README follow. Go identifiers and template file names are unchanged. A new route test posts to the entrypoint URL the webhook page shows and checks that the receiver rate limit applies to it. Model: opus-5-5
156 lines
3.6 KiB
Go
156 lines
3.6 KiB
Go
package handlers_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"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")
|
|
}
|