e2e: a negative case passes only on its own refusal (audit stage 0)

sendResult.refusedAt(stage, code, text) and replyIs(err, code, text):
nil only for the server's reply with that code and text at that stage;
a reset, a timeout or another reply is described for the failure.

- level 1: only anvil's "450 4.7.1 Error: too much mail from", only after
  a message of the loop was accepted. Held to that, it failed twice on
  the stand, each time for a real reason:
  1. waitForSMTPSReady took a TCP connect for readiness; Docker's port
     proxy accepts before Postfix listens and then resets - the reset
     once taken for the limit. It now waits for the TLS 220 greeting.
  2. the first message then got "553 ... not owned by user l1app": an
     smtpd idle from before the panel's postfix reload took the
     connection with the old sender_login_maps. The level-1 limit had
     never been what the case saw. The first message now waits (15 s at
     most) for the map, failing at once on any other refusal.
  Now: "level-1 limit tripped on attempt 51/60: 450 4.7.1 Error: too
  much mail from 10.77.0.1".
- level 2: 451 4.7.1 at MAIL, and both MAIL FROM of one connection.
- sender mismatch: 553 at RCPT, "not owned by user".
- no AUTH and the relay case: 554 "Client host rejected: Access
  denied" at RCPT; the relay case's comment claimed
  reject_unauth_destination, which on 465 comes after the client
  restriction - port 25 holds "554 Relay access denied".
- port 25: 550 5.1.1 for unknown recipients and report addresses.
- panel down: a 4xx reply (textproto), not just text starting with 4.

The product side (a second after an application is saved when its first
message can get 553) is written in the audit plan, not changed. Audit plan
line ticked with what was found, roadmap 7/61. No CHANGELOG line: tests only.

Evidence: make e2e on the test server for this tree, exit 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-14 14:10:47 +03:00
co-authored by Claude Opus 5.5
parent 8b9798884a
commit 96425dd515
6 changed files with 156 additions and 49 deletions
+18 -1
View File
@@ -563,7 +563,7 @@ has already run is never edited — the next file is added; the published
development.md that the image is rebuilt after each Go patch release
(`golang:1.26-bookworm` floats, so a rebuild suffices). — **Haiku**
- [x] The four `staticcheck` items. — **Haiku**
- [ ] **A negative e2e case passes only on the refusal it is about** (owner,
- [x] **A negative e2e case passes only on the refusal it is about** (owner,
2026-10-14; seen, not found by the audit). `negative_level1_ratelimit`
sends up to sixty messages and waits for one to be refused; it once
passed on "limit tripped on attempt 1/60" when the connection was reset
@@ -573,6 +573,23 @@ has already run is never edited — the next file is added; the published
came. Then every other negative case of the suite is read for the same
mistake - any failure taken for the expected refusal - and held to its
own reply code. - **Opus**
*Done 2026-10-14; what the stand showed:* `waitForSMTPSReady` took a TCP
connect for readiness, and Docker's port proxy accepts before Postfix
listens - that was the reset; it now waits for the TLS greeting. Held to
anvil's `450`, the case then failed on its first message: `553 … not owned
by user l1app`, an smtpd process from before the panel's `postfix reload`
still holding the old `sender_login_maps` - so the level-1 limit had never
been the refusal the case saw. The first message now waits (at most 15 s)
for the map; the limit trips at 51/60 with `450 4.7.1 Error: too much mail
from`. In the product this is a second or so after an application is
saved in which its first message can get a permanent 553; not worth a
change (an application is configured after it is created, not within the
second). The other cases are held to `451 4.7.1` (level 2, both MAIL FROM
of one connection), `553` at RCPT (sender mismatch), `554 … Client host
rejected` at RCPT (no AUTH, and the relay case, whose comment claimed
`reject_unauth_destination` - on 465 the client restriction comes first;
port 25 holds `554 … Relay access denied`), `550 5.1.1` (port 25 unknown
recipients), any `4xx` reply (panel down).
### Stage 1 — mail that is shown wrong (H3, H2, M12, M13) → `2.5.0`
+1 -1
View File
@@ -42,7 +42,7 @@ in `git log` and [CHANGELOG.md](../CHANGELOG.md).
| from-header-binding | Hold a message's `From:` header against the application that sends it | shipped `2.4.0` | 9/9 | [plans/from-header-binding.md](plans/from-header-binding.md) |
| panel-redesign | Panel redesign (accepted mockups, design contract) | shipped `2.0.0` | 27/27 | [plans/panel-redesign.md](plans/panel-redesign.md) |
| schema-squash | Squash SQLite migrations into a 2.x baseline | built in panel-redesign (stage 1) | — | [plans/panel-redesign.md](plans/panel-redesign.md) |
| audit-2026-10-12 | Follow-ups of the 2026-10-12 full-tree code audit, seven stages | agreed (2026-10-12: stage 0 first, then 1+2; the rest after) | 6/61 | [audit-2026-10-12.md](audit-2026-10-12.md) |
| audit-2026-10-12 | Follow-ups of the 2026-10-12 full-tree code audit, seven stages | agreed (2026-10-12: stage 0 first, then 1+2; the rest after) | 7/61 | [audit-2026-10-12.md](audit-2026-10-12.md) |
**Recommended order** (not binding; owner, 2026-10-09: feature order follows
development convenience): **panel-redesign** shipped as `2.0.0` (2026-10-11) and