e2e: the run ends on port 25, with a report, junk and inbound mail

Until now the suite only read back the configuration generated for the
two optional port-25 features. DMARC ingest shipped in 1.7.0 with a
delivery service Postfix could not start, and nothing sent a report
through it until a real one arrived on the test server on 2026-10-11.

The last steps recreate the container with INBOUND_RELAY_ENABLE and
DMARC_REPORTS_ENABLE on, over the same /data, and talk to port 25 as a
stranger's mail server:

- the container log has no postconf warning, the session survives;
- a domain takes the SelfPost-hosted report address; a gzip report
  mailed to it appears under Outbound > DMARC reports; an address nobody
  chose is refused at RCPT;
- a report on another domain and a message with no report are accepted,
  dropped with "dropped: ..." on their delivery lines, counted as two
  parse failures, absent from the queue and from the reports;
- an inbound domain with the sink as upstream and one listed recipient:
  mail for it reaches the sink, another recipient and another domain are
  refused at RCPT.

The stage directory now holds docker-compose.yml and .env, as an
operator's project directory does (a full backup needs them; the
restore scenario that uses it is not in this commit - the panel's full
backup turned out not to work, see the next one).

make e2e on the test server: ok.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-11 17:40:47 +03:00
co-authored by Claude Opus 5.5
parent 4dd46ea355
commit 8496b289ec
6 changed files with 444 additions and 4 deletions
+22 -2
View File
@@ -70,6 +70,12 @@ const (
recipient = "rcpt@sink.e2e.test"
)
// The one administrator of the run.
const (
adminUser = "e2eadmin"
adminPassword = "correct-horse-battery-staple"
)
// scenario carries state forward between the ordered subtests of TestE2E:
// each later step needs something an earlier one produced (the domain id, the
// DKIM record, the application credentials, an observed client IP).
@@ -131,10 +137,10 @@ func TestE2E(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if err := p.setup(token, "e2eadmin", "correct-horse-battery-staple"); err != nil {
if err := p.setup(token, adminUser, adminPassword); err != nil {
t.Fatalf("setup: %v", err)
}
if err := p.login("e2eadmin", "correct-horse-battery-staple"); err != nil {
if err := p.login(adminUser, adminPassword); err != nil {
t.Fatalf("login: %v", err)
}
sc.panel = p
@@ -268,6 +274,20 @@ func TestE2E(t *testing.T) {
run("negative_level1_ratelimit", func(t *testing.T) {
testLevel1RateLimit(t, sc)
})
// From here on the stand accepts mail on port 25.
run("enable_port25_features", func(t *testing.T) {
testEnablePort25Features(t, sc)
})
run("dmarc_report_reaches_the_panel", func(t *testing.T) {
testDMARCReportReachesThePanel(t, sc)
})
run("dmarc_junk_is_dropped", func(t *testing.T) {
testDMARCJunkIsDropped(t, sc)
})
run("inbound_relay_delivers_and_refuses", func(t *testing.T) {
testInboundRelay(t, sc)
})
}
// trimZoneSuffix strips the "e2e.test" suffix a fully-qualified DKIM record