Files
mix f3bc24b638 feat: implement C.4 — hermetic container e2e gate + native release build
Separate test/e2e Go module drives the shipped deploy/docker-compose.yml
(plus a test-only override: self-signed cert, low ports, isolated compose
project) against a fake DNS zone (CoreDNS) and an smtp-sink MX, exactly as
an administrator and their applications would over HTTP/SMTP — covering the
class of failure unit tests can't see (container wiring). Positive path:
setup -> login -> domain -> DKIM record published into the fake zone ->
application -> SMTP AUTH send -> DKIM verified against the DNS-published
key -> send-log queued->sent. Negative: no-AUTH/unauthenticated relay,
sender/login mismatch, L1 (anvil) and L2 (panel) rate limits, journal-milter
fail-open, SELFPOST_HOSTNAME gate, session survives docker restart.

release.yml moves off qemu to a native per-arch build (amd64/arm64), each
gated by this suite before its tag is pushed and merged into the version
manifest.

Verified green on selfpost.example.com via `make e2e`; go vet/gofmt clean in
both modules.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 00:29:14 +03:00

30 lines
834 B
Go

package e2e
import (
"fmt"
"time"
)
// waitFor polls check every interval until it returns true or timeout elapses.
// Every wait in this suite goes through here — no fixed sleeps standing in for
// a readiness check (plan C.4): a passing check ends the wait immediately, and
// a timeout fails with what was being waited for, not a bare "timed out".
func waitFor(what string, timeout, interval time.Duration, check func() (bool, error)) error {
deadline := time.Now().Add(timeout)
var lastErr error
for {
ok, err := check()
if ok {
return nil
}
lastErr = err
if time.Now().After(deadline) {
if lastErr != nil {
return fmt.Errorf("timed out after %s waiting for %s: %w", timeout, what, lastErr)
}
return fmt.Errorf("timed out after %s waiting for %s", timeout, what)
}
time.Sleep(interval)
}
}