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, so nothing that reads a target parses it. 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. Model: opus-5-5
190 lines
5.1 KiB
Go
190 lines
5.1 KiB
Go
package handlers_test
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"sneak.berlin/go/webhooker/internal/database"
|
|
)
|
|
|
|
// expiryNever is the archive expiry that keeps archived events
|
|
// forever.
|
|
const expiryNever = "never"
|
|
|
|
// matched returns what the one group of pattern matched in page, at
|
|
// each match.
|
|
func matched(pattern, page string) []string {
|
|
matches := regexp.MustCompile(pattern).FindAllStringSubmatch(page, -1)
|
|
groups := make([]string, 0, len(matches))
|
|
|
|
for _, m := range matches {
|
|
groups = append(groups, m[1])
|
|
}
|
|
|
|
return groups
|
|
}
|
|
|
|
// expiryShown returns the archive expiries the webhook page's target
|
|
// list shows.
|
|
func expiryShown(
|
|
t *testing.T, env *sourceTestEnv, webhookID string,
|
|
) []string {
|
|
t.Helper()
|
|
|
|
w := httptest.NewRecorder()
|
|
env.handlers.HandleSourceDetail().ServeHTTP(w, getRequest(
|
|
t, "/hook/"+webhookID, env.cookies,
|
|
map[string]string{sourceIDParam: webhookID},
|
|
))
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
|
|
return matched(
|
|
`Archive expiry:</span>\s*<span>([^<]*)</span>`, w.Body.String(),
|
|
)
|
|
}
|
|
|
|
// expirySelected returns the target edit page and the expiries its
|
|
// expiry select starts on.
|
|
func expirySelected(
|
|
t *testing.T, env *sourceTestEnv, webhookID, targetID string,
|
|
) (string, []string) {
|
|
t.Helper()
|
|
|
|
page := targetEditPage(t, env, webhookID, targetID)
|
|
|
|
return page, selectedIn(page, "expiry")
|
|
}
|
|
|
|
// targetEditPage returns a target's edit page.
|
|
func targetEditPage(
|
|
t *testing.T, env *sourceTestEnv, webhookID, targetID string,
|
|
) string {
|
|
t.Helper()
|
|
|
|
w := serveTarget(
|
|
env, http.MethodGet,
|
|
"/hook/"+webhookID+"/targets/"+targetID+"/edit", nil,
|
|
)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
|
|
return w.Body.String()
|
|
}
|
|
|
|
// selectedIn returns the values the select named name on page starts
|
|
// on.
|
|
func selectedIn(page, name string) []string {
|
|
_, rest, _ := strings.Cut(page, `<select id="`+name+`" name="`+name+`"`)
|
|
options, _, _ := strings.Cut(rest, "</select>")
|
|
|
|
return matched(`<option value="([^"]*)" selected>`, options)
|
|
}
|
|
|
|
// TestArchiveExpiryChoices adds a database target with each archive
|
|
// expiry the forms offer, and checks that it is stored as chosen,
|
|
// shown in plain units in the target list, and that the target edit
|
|
// form starts on it.
|
|
func TestArchiveExpiryChoices(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
env := setupSourceTest(t)
|
|
|
|
choices := []struct {
|
|
value string
|
|
stored time.Duration
|
|
shown string
|
|
}{
|
|
{expiryNever, 0, expiryNever},
|
|
{"1h", time.Hour, "1 hour"},
|
|
{"12h", 12 * time.Hour, "12 hours"},
|
|
{"24h", 24 * time.Hour, "1 day"},
|
|
{"720h", 720 * time.Hour, "30 days"},
|
|
{"2160h", 2160 * time.Hour, "90 days"},
|
|
{"8760h", 8760 * time.Hour, "365 days"},
|
|
}
|
|
|
|
for _, choice := range choices {
|
|
t.Run(choice.value, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
webhook := seedWebhookWithRetention(t, env.db, 30)
|
|
|
|
form := url.Values{}
|
|
form.Set("name", "archive")
|
|
form.Set("type", string(database.TargetTypeDatabase))
|
|
form.Set("expiry", choice.value)
|
|
|
|
w := serveTarget(
|
|
env, http.MethodPost, "/hook/"+webhook.ID+"/targets", form,
|
|
)
|
|
require.Equal(t, http.StatusSeeOther, w.Code, w.Body.String())
|
|
|
|
targets := targetsForWebhook(t, env.db, webhook.ID)
|
|
require.Len(t, targets, 1)
|
|
assert.Equal(t, choice.stored, targets[0].ArchiveExpiry)
|
|
|
|
assert.Equal(
|
|
t, []string{choice.shown},
|
|
expiryShown(t, env, webhook.ID),
|
|
)
|
|
|
|
_, selected := expirySelected(t, env, webhook.ID, targets[0].ID)
|
|
assert.Equal(t, []string{choice.value}, selected)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestArchiveExpiryEditStartsOnStoredValue checks the edit form of a
|
|
// database target whose stored expiry is 0, which selects never,
|
|
// and of one whose expiry is not one of the choices, which is listed
|
|
// first as its own selected entry and saved unchanged.
|
|
func TestArchiveExpiryEditStartsOnStoredValue(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
env := setupSourceTest(t)
|
|
|
|
webhook := seedWebhookWithRetention(t, env.db, 30)
|
|
empty := seedConfiguredTarget(
|
|
t, env.db, webhook.ID, database.TargetTypeDatabase,
|
|
database.Target{},
|
|
)
|
|
|
|
_, selected := expirySelected(t, env, webhook.ID, empty.ID)
|
|
assert.Equal(t, []string{expiryNever}, selected)
|
|
|
|
webhook = seedWebhookWithRetention(t, env.db, 30)
|
|
unlisted := seedConfiguredTarget(
|
|
t, env.db, webhook.ID, database.TargetTypeDatabase,
|
|
database.Target{ArchiveExpiry: 36 * time.Hour},
|
|
)
|
|
|
|
assert.Equal(t, []string{"36 hours"}, expiryShown(t, env, webhook.ID))
|
|
|
|
page, selected := expirySelected(t, env, webhook.ID, unlisted.ID)
|
|
assert.Equal(t, []string{"36h"}, selected)
|
|
assert.Regexp(
|
|
t,
|
|
`<select id="expiry" name="expiry" class="input">\s*`+
|
|
`<option value="36h" selected>36h</option>\s*`+
|
|
`<option value="never">never</option>`,
|
|
page,
|
|
)
|
|
assert.Contains(t, page, `<option value="8760h">365d</option>`)
|
|
|
|
form := url.Values{}
|
|
form.Set("name", unlisted.Name)
|
|
form.Set("expiry", "36h")
|
|
|
|
w := submitTargetEdit(env, webhook.ID, unlisted.ID, form)
|
|
require.Equal(t, http.StatusSeeOther, w.Code, w.Body.String())
|
|
assert.Equal(
|
|
t, 36*time.Hour, storedTarget(t, env, unlisted.ID).ArchiveExpiry,
|
|
)
|
|
}
|