check / check (push) Waiting to run
A target's settings move from the per-type JSON string in targets.config to columns: url, headers, timeout_seconds, forward_query, archive_expiry and archive_rotation. A column a type does not use stays empty. headers holds the header map as JSON text through GORM's JSON serializer, and archive_expiry holds a time.Duration, 0 meaning never. The forms check each setting once, with the same refusals and messages, and the edit form is filled from the columns, so the per-type config structs, their parsers and the "could not be read" branches are gone. A delivery task carries the six settings in place of the JSON string; tests check that each copy into a task, and back out of one, keeps all six. Model: opus-5-5
164 lines
4.1 KiB
Go
164 lines
4.1 KiB
Go
package delivery_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"sneak.berlin/go/webhooker/internal/database"
|
|
"sneak.berlin/go/webhooker/internal/delivery"
|
|
)
|
|
|
|
// eventQuery is the query string the events in these tests arrived
|
|
// with.
|
|
const eventQuery = "a=1&b=2"
|
|
|
|
// deliverWithQuery sends one event that arrived with eventQuery to an
|
|
// HTTP target at targetURL, with its ForwardQuery setting as given,
|
|
// through the path a received event's delivery takes, and returns the
|
|
// attempt it recorded.
|
|
func deliverWithQuery(
|
|
t *testing.T, targetURL string, forwardQuery bool,
|
|
) database.DeliveryResult {
|
|
t.Helper()
|
|
|
|
s := newISetup(t)
|
|
|
|
event := iSeedEvent(t, s.WebhookDB, s.WebhookID, "{}")
|
|
d := iSeedDelivery(
|
|
t, s.WebhookDB, event.ID, uuid.NewString(),
|
|
database.DeliveryStatusPending,
|
|
)
|
|
task := iTask(
|
|
d, event, s.WebhookID, d.TargetID, "query", targetURL, 1, 1,
|
|
&event.Body,
|
|
)
|
|
task.ForwardQuery = forwardQuery
|
|
task.RawQuery = eventQuery
|
|
|
|
s.Engine.ExportProcessNewTask(context.TODO(), &task)
|
|
|
|
var result database.DeliveryResult
|
|
|
|
require.NoError(t, s.WebhookDB.Where(
|
|
"delivery_id = ?", d.ID,
|
|
).First(&result).Error)
|
|
|
|
return result
|
|
}
|
|
|
|
// TestDeliverHTTP_ForwardQuery proves the URL a delivery is sent to:
|
|
// with the target's setting off, the target URL exactly as configured;
|
|
// with it on, the event's query string appended, joined with "&" to a
|
|
// query string the target URL already has.
|
|
func TestDeliverHTTP_ForwardQuery(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// The target URL's path, without and with a query string of its
|
|
// own.
|
|
const (
|
|
plain = "/in"
|
|
withQuery = "/in?key=k"
|
|
)
|
|
|
|
tests := map[string]struct {
|
|
path string
|
|
forwardQuery bool
|
|
want string
|
|
}{
|
|
"off": {
|
|
path: plain, want: plain,
|
|
},
|
|
"off, the target URL has a query string": {
|
|
path: withQuery, want: withQuery,
|
|
},
|
|
"on": {
|
|
path: plain, forwardQuery: true, want: plain + "?" + eventQuery,
|
|
},
|
|
"on, the target URL has a query string": {
|
|
path: withQuery, forwardQuery: true,
|
|
want: withQuery + "&" + eventQuery,
|
|
},
|
|
}
|
|
|
|
for name, tc := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
received := make(chan string, 1)
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(
|
|
func(w http.ResponseWriter, r *http.Request) {
|
|
received <- r.RequestURI
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
},
|
|
))
|
|
t.Cleanup(ts.Close)
|
|
|
|
result := deliverWithQuery(
|
|
t, ts.URL+tc.path, tc.forwardQuery,
|
|
)
|
|
|
|
assert.True(t, result.Success)
|
|
require.Len(t, received, 1)
|
|
assert.Equal(t, tc.want, <-received)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestDeliverHTTP_ForwardedQueryKeepsTheTargetURLMasked proves the
|
|
// credential in a target URL's own query string stays masked once the
|
|
// event's query string is appended to it: in a response or error that
|
|
// echoes the URL the target was sent, as the event log's Redactor shows
|
|
// it, and in the error a failed connection stores.
|
|
func TestDeliverHTTP_ForwardedQueryKeepsTheTargetURLMasked(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const secret = "s3cr3t"
|
|
|
|
received := make(chan string, 1)
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(
|
|
func(w http.ResponseWriter, r *http.Request) {
|
|
received <- r.RequestURI
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
},
|
|
))
|
|
t.Cleanup(ts.Close)
|
|
|
|
target := &database.Target{
|
|
Type: database.TargetTypeHTTP,
|
|
URL: ts.URL + "/in?token=" + secret,
|
|
ForwardQuery: true,
|
|
MaxAttempts: 1,
|
|
}
|
|
|
|
deliverWithQuery(t, target.URL, target.ForwardQuery)
|
|
require.Len(t, received, 1)
|
|
|
|
sent := <-received
|
|
require.Equal(t, "/in?token="+secret+"&"+eventQuery, sent)
|
|
|
|
redactor := delivery.NewRedactor(target)
|
|
|
|
for _, echoed := range []string{sent, ts.URL + sent} {
|
|
shown := redactor.Redact("rejected " + echoed)
|
|
assert.NotContains(t, shown, secret, echoed)
|
|
assert.Contains(t, shown, delivery.RedactionMarker, echoed)
|
|
}
|
|
|
|
// Nothing listens on port 1.
|
|
failed := deliverWithQuery(
|
|
t, "http://127.0.0.1:1/in?token="+secret, true,
|
|
)
|
|
require.NotEmpty(t, failed.Error)
|
|
assert.NotContains(t, failed.Error, secret)
|
|
assert.NotContains(t, failed.Error, eventQuery)
|
|
}
|