check / check (push) Waiting to run
A database target's rotation (none, monthly, daily or hourly) puts the UTC period of each event's receive time in its archive file name, so each file holds exactly its period's events. It is on the new webhook page, the add target form and the target edit form, and shown in the target list. Renames move every one of a target's files and move them back if one fails. The sweep prunes every file, one at a time under the target's lock, and deletes a rotated file it leaves empty. Download lists the files, then opens one at a time, oldest first, finding each again under the target's current names, and gives each row its period. The target list names the current file and totals the size of all of them. Model: opus-5-5
186 lines
4.9 KiB
Go
186 lines
4.9 KiB
Go
package handlers_test
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"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, shown string }{
|
|
{expiryNever, expiryNever},
|
|
{"1h", "1 hour"},
|
|
{"12h", "12 hours"},
|
|
{"24h", "1 day"},
|
|
{"720h", "30 days"},
|
|
{"2160h", "90 days"},
|
|
{"8760h", "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.JSONEq(
|
|
t, `{"expiry":"`+choice.value+`"}`, targets[0].Config,
|
|
)
|
|
|
|
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 empty, 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, "",
|
|
)
|
|
|
|
_, 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,
|
|
`{"expiry":"36h"}`,
|
|
)
|
|
|
|
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.JSONEq(
|
|
t, `{"expiry":"36h"}`, storedTarget(t, env, unlisted.ID).Config,
|
|
)
|
|
}
|