test(e2e): follow the panel's markup for applications and the send log
test / test (push) Has been cancelled

Two scrapes had drifted from the pages they read, and the suite has not
been run since either page changed.

applicationID still looked for an application as a table row
(<td class="code">login</td> ... /applications/N/mode). Applications became
a list of blocks in 8add005, so the lookup had been failing for several
commits, including the one currently deployed — this is stale test, not a
regression. It now anchors on the login heading and takes the id from the
first action posted under it, whichever that is, so reordering a block's
controls will not break it again.

The level-2 rate-limit check looked for the application's login among the
send-log rows. c70c115 took that column off the log — the log identifies a
message and names the application only on a row's own page — so the check
now filters the log by application instead. That is the same attribution
through a server-side WHERE app_login rather than a substring match on
rendered HTML.

Verified on selfpost.mixfed.ru: make e2e green, all subtests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mixeme
2026-08-07 22:59:12 +03:00
parent d2dffc2e9a
commit 82fc821d1a
3 changed files with 21 additions and 11 deletions
+18 -7
View File
@@ -189,9 +189,17 @@ func (c *panelClient) setRateLimit(path, allowedIP string, maxMessages, windowSe
}
// sendLogRows returns the raw /deliveries/rows HTML fragment, filtered to one
// domain, for polling a row's status without parsing full HTML into structs.
func (c *panelClient) sendLogRows(domain string) (string, error) {
_, body, err := c.get("/deliveries/rows?domain=" + url.QueryEscape(domain))
// domain and optionally to one application login, for polling a row's status
// without parsing full HTML into structs. The application is a filter rather
// than something to search the returned rows for: the log's columns identify a
// message (time, from, to, subject, status) and the sending application is only
// named on a row's own /deliveries/{id} page.
func (c *panelClient) sendLogRows(domain, app string) (string, error) {
q := url.Values{"domain": {domain}}
if app != "" {
q.Set("app", app)
}
_, body, err := c.get("/deliveries/rows?" + q.Encode())
return body, err
}
@@ -202,15 +210,18 @@ func (c *panelClient) status() (*http.Response, error) {
return resp, err
}
// applicationID scrapes an application's numeric id off its domain page row,
// keyed by login — needed to build /applications/{id}/ratelimit, which the
// add-application response (just the login/password) does not carry.
// applicationID scrapes an application's numeric id off its block on the domain
// page, keyed by login — needed to build /applications/{id}/ratelimit, which the
// add-application response (just the login/password) does not carry. The id is
// taken from the first action posted under that login, whichever it is, so
// reordering the block's controls does not break the scrape; only the login
// heading itself is anchored on.
func (c *panelClient) applicationID(domainID, login string) (string, error) {
_, body, err := c.get("/domains/" + domainID)
if err != nil {
return "", err
}
pattern := `(?s)<td class="code">` + regexp.QuoteMeta(login) + `</td>.*?/applications/(\d+)/mode`
pattern := `(?s)<p class="app-login">` + regexp.QuoteMeta(login) + `</p>.*?/applications/(\d+)/`
m := regexp.MustCompile(pattern).FindStringSubmatch(body)
if m == nil {
return "", fmt.Errorf("could not find application id for login %q", login)