Use one name for each thing the UI shows (closes #399)
check / check (push) Successful in 3m15s
check / check (push) Successful in 3m15s
The database target type is called an archive on its badge, in the add target form's type list and on its edit page, and its settings read "Archive expiry" and "Archive rotation" everywhere, the new webhook page included. The retry field is labelled "Delivery attempts", with its help text and error messages to match, on both target forms and in the target list. The navbar says "Sign out" and the sign-in page "Sign in". The resubmit notice says "webhook". The stored values (`database`, `max_retries`) and their meaning are unchanged. Model: opus-5-5
This commit is contained in:
@@ -45,7 +45,7 @@ func expiryShown(
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
return matched(
|
||||
`Archive Expiry:</span>\s*<span>([^<]*)</span>`, w.Body.String(),
|
||||
`Archive expiry:</span>\s*<span>([^<]*)</span>`, w.Body.String(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ func rotationShown(
|
||||
t.Helper()
|
||||
|
||||
return matched(
|
||||
`Archive Rotation:</span>\s*<span>([^<]*)</span>`,
|
||||
`Archive rotation:</span>\s*<span>([^<]*)</span>`,
|
||||
renderedPage(t, env, webhookID),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ func (h *Handlers) rejectLogin(
|
||||
)))
|
||||
h.renderLoginError(
|
||||
w, r,
|
||||
"Too many failed login attempts. Please try again later.",
|
||||
"Too many failed sign-in attempts. Please try again later.",
|
||||
http.StatusTooManyRequests,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func noticeFor(r *http.Request) *notice {
|
||||
},
|
||||
resubmitNoTargets: {
|
||||
Text: "Resubmitted: a new event was created, but this " +
|
||||
"source has no active targets, so nothing was queued.",
|
||||
"webhook has no active targets, so nothing was queued.",
|
||||
},
|
||||
}[noticeCode(r.URL.Query().Get(noticeParam))]
|
||||
if !ok {
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestEveryPageRendersItsOwnTitle(t *testing.T) {
|
||||
data map[string]any
|
||||
title string
|
||||
}{
|
||||
{"login.html", map[string]any{}, "Login - Webhooker"},
|
||||
{"login.html", map[string]any{}, "Sign in - Webhooker"},
|
||||
{"profile.html", map[string]any{}, "Profile - Webhooker"},
|
||||
{"settings.html", map[string]any{}, "Settings - Webhooker"},
|
||||
{"sources_list.html", map[string]any{}, "Webhooks - Webhooker"},
|
||||
|
||||
@@ -233,8 +233,13 @@ func TestHandleSourceDetail_RendersNamedTargetFields(
|
||||
assert.Contains(t, body, "1 configured")
|
||||
assert.NotContains(t, body, "sekrit")
|
||||
|
||||
assert.Contains(t, body, "Archive Expiry")
|
||||
assert.Contains(t, body, "30 days")
|
||||
// The database type is called an archive: on its badge, in the
|
||||
// add target form's type list and in its settings.
|
||||
list := targetList(t, body)
|
||||
assert.Contains(t, list, "t-database archive Active")
|
||||
assert.Contains(t, list, "Archive expiry: 30 days")
|
||||
assert.Contains(t, list, "Archive rotation: none")
|
||||
assert.Contains(t, body, `<option value="database">Archive</option>`)
|
||||
|
||||
// An unknown type gets the neutral placeholder, never the
|
||||
// stored blob.
|
||||
|
||||
@@ -1749,7 +1749,7 @@ func (h *Handlers) setTargetFromForm(
|
||||
// cannot destroy the count a target is delivering with.
|
||||
maxRetries, err := parseMaxRetries(in.MaxRetries, target.MaxRetries)
|
||||
if err != nil {
|
||||
return "Invalid max retries: " + retriesErrorMessage(err), nil
|
||||
return "Invalid delivery attempts: " + retriesErrorMessage(err), nil
|
||||
}
|
||||
|
||||
target.Name = in.Name
|
||||
|
||||
@@ -418,6 +418,30 @@ func TestHandleTargetEdit_PrefillsTheStoredValuesUnmasked(
|
||||
assert.Contains(t, page, "original-name")
|
||||
}
|
||||
|
||||
// TestHandleTargetEdit_CallsTheDatabaseTypeArchive pins the names the
|
||||
// edit page of a database target gives its type and its settings to
|
||||
// the ones its badge and the add target form use.
|
||||
func TestHandleTargetEdit_CallsTheDatabaseTypeArchive(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
env := setupSourceTest(t)
|
||||
webhook := seedWebhookWithRetention(t, env.db, 30)
|
||||
target := seedTarget(t, env.db, webhook.ID, database.TargetTypeDatabase)
|
||||
|
||||
w := serveTarget(
|
||||
env, http.MethodGet,
|
||||
"/hook/"+webhook.ID+"/targets/"+target.ID+"/edit",
|
||||
nil,
|
||||
)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
page := w.Body.String()
|
||||
|
||||
assert.Contains(t, page, "Type: archive.")
|
||||
assert.Contains(t, page, `class="label">Archive expiry</label>`)
|
||||
assert.Contains(t, page, `class="label">Archive rotation</label>`)
|
||||
}
|
||||
|
||||
// TestHandleTargetEditSubmit_Rejects covers every submission that
|
||||
// must not reach storage.
|
||||
//
|
||||
@@ -588,7 +612,7 @@ func TestHandleTargetEditSubmit_RefusedFormComesBack(t *testing.T) {
|
||||
{
|
||||
database.TargetTypeSlack,
|
||||
"name=edited&url=" + editOriginalURL + "&max_retries=25",
|
||||
"Invalid max retries",
|
||||
"Invalid delivery attempts",
|
||||
},
|
||||
{
|
||||
database.TargetTypeDatabase,
|
||||
|
||||
@@ -25,14 +25,14 @@ var (
|
||||
// errRetriesInvalid signals a max_retries form value that is not
|
||||
// a non-negative whole number.
|
||||
errRetriesInvalid = errors.New(
|
||||
"retries must be a whole number of attempts",
|
||||
"must be a whole number",
|
||||
)
|
||||
|
||||
// errRetriesTooLarge signals a max_retries form value that is a
|
||||
// whole number but above maxTargetRetries. It is distinguished
|
||||
// from errRetriesInvalid so the message can name the ceiling
|
||||
// instead of implying the input was not a number.
|
||||
errRetriesTooLarge = errors.New("retries out of range")
|
||||
errRetriesTooLarge = errors.New("out of range")
|
||||
)
|
||||
|
||||
// parseMaxRetries interprets a max_retries form value.
|
||||
@@ -82,7 +82,7 @@ func retriesErrorMessage(err error) string {
|
||||
if errors.Is(err, errRetriesTooLarge) {
|
||||
return errRetriesTooLarge.Error() +
|
||||
": at most " + strconv.Itoa(maxTargetRetries) +
|
||||
" retries"
|
||||
" attempts"
|
||||
}
|
||||
|
||||
return errRetriesInvalid.Error() +
|
||||
|
||||
@@ -354,15 +354,14 @@ func TestEntrypointCopyButtonIsProgressiveEnhancement(t *testing.T) {
|
||||
// and target_http gives up once the attempt number reaches
|
||||
// max_retries), and 0 is special-cased to a single fire-and-forget
|
||||
// attempt with no circuit breaker.
|
||||
const maxRetriesHelp = "This is the total number of delivery attempts, " +
|
||||
"not retries on top of the first: a value of 3 makes three attempts " +
|
||||
"in all. 0 means a single attempt with no retries and no circuit " +
|
||||
"breaker."
|
||||
const maxRetriesHelp = "How many times each delivery is attempted in " +
|
||||
"all, the first attempt included. 0 means a single attempt with no " +
|
||||
"retries and no circuit breaker."
|
||||
|
||||
// TestTargetFormMaxRetriesCopyMatchesBehaviour pins the max_retries
|
||||
// help text on both the create form (the add-target form on the webhook
|
||||
// detail page) and the edit form, so the copy cannot drift back to
|
||||
// calling the number a retry count.
|
||||
// label, "Delivery attempts", and help text on both the create form
|
||||
// (the add-target form on the webhook detail page) and the edit form,
|
||||
// so the copy cannot drift back to calling the number a retry count.
|
||||
func TestTargetFormMaxRetriesCopyMatchesBehaviour(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -393,6 +392,7 @@ func TestTargetFormMaxRetriesCopyMatchesBehaviour(t *testing.T) {
|
||||
},
|
||||
)
|
||||
|
||||
assert.Contains(t, createBody, "Delivery attempts:</label>")
|
||||
assert.Contains(
|
||||
t, createBody, maxRetriesHelp,
|
||||
"the add-target form must explain max_retries as total attempts",
|
||||
@@ -419,8 +419,33 @@ func TestTargetFormMaxRetriesCopyMatchesBehaviour(t *testing.T) {
|
||||
},
|
||||
)
|
||||
|
||||
assert.Contains(t, editBody, `class="label">Delivery attempts</label>`)
|
||||
assert.Contains(
|
||||
t, editBody, maxRetriesHelp,
|
||||
"the target edit form must explain max_retries as total attempts",
|
||||
)
|
||||
}
|
||||
|
||||
// TestCreateFormCallsTheDatabaseTargetAnArchive pins the names the new
|
||||
// webhook page gives the database target its Archive checkbox creates,
|
||||
// and that target's settings, to the ones the target forms use.
|
||||
func TestCreateFormCallsTheDatabaseTargetAnArchive(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var h *handlers.Handlers
|
||||
|
||||
var sess *session.Session
|
||||
|
||||
app := newTestApp(t, &h, &sess)
|
||||
app.RequireStart()
|
||||
|
||||
t.Cleanup(app.RequireStop)
|
||||
|
||||
body := renderPage(t, h, sess, "sources_new.html", map[string]any{
|
||||
dataKeyError: "",
|
||||
})
|
||||
|
||||
assert.Contains(t, body, "created with an archive target")
|
||||
assert.Contains(t, body, `class="label">Archive expiry</label>`)
|
||||
assert.Contains(t, body, `class="label">Archive rotation</label>`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user