Keep calendartest's Keymail discovery in the process
Astra's round 5 on #28: internal/calendartest had no TestMain, so its unforced POST /signin cases (delivery, auth, booking review, signin door) asked the real resolver whether example.com runs Keymail. It now installs keymailtest exactly as internal/calendar does, and fails the run if a sign-in mail worker or log pump outlives the tests. TestKeymailDiscoveryStaysInProcess requires example.com, which public DNS resolves, to come back NXDOMAIN; without the stub it resolved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+66
−0
internal/calendartest/main_test.go+66 −0
CI passed — run details
CI log
=== vet === go vet ./... go vet -tags browser ./internal/calendartest/ === fmt === === test === go test ./... ? amadan.net/oficina/calendar/cmd/calendar [no test files] ok amadan.net/oficina/calendar/internal/calendar 5.119s ? amadan.net/oficina/calendar/internal/calendar/icons [no test files] ? amadan.net/oficina/calendar/internal/calendar/keymailtest [no test files] ? amadan.net/oficina/calendar/internal/calendar/seal [no test files] ok amadan.net/oficina/calendar/internal/calendartest 2.279s ok amadan.net/oficina/calendar/internal/guardstest 0.052s ? amadan.net/oficina/calendar/scripts/writeestilo [no test files] === migration-check === go tool rastrillo migration check rastrillo migration check: models and migrations agree
diff --git a/internal/calendartest/main_test.go b/internal/calendartest/main_test.go| new file mode 100644 |
| index 0000000..97f7ee6 |
| --- /dev/null |
| +++ b/internal/calendartest/main_test.go |
| @@ -0,0 +1,66 @@ |
| +package calendartest |
| + |
| +import ( |
| + "context" |
| + "errors" |
| + "fmt" |
| + "net" |
| + "os" |
| + "runtime" |
| + "strings" |
| + "testing" |
| + "time" |
| + |
| + "amadan.net/oficina/calendar/internal/calendar/keymailtest" |
| +) |
| + |
| +// TestMain keeps Keymail classification inside the process: every POST |
| +// /signin now classifies its address, and without this each one would |
| +// ask the real network whether example.com runs Keymail. Every DNS |
| +// query is answered NXDOMAIN; no fixture is registered here, so every |
| +// address is plain email. |
| +func TestMain(m *testing.M) { |
| + restore, err := keymailtest.Install() |
| + if err != nil { |
| + fmt.Fprintln(os.Stderr, "keymailtest.Install:", err) |
| + os.Exit(1) |
| + } |
| + code := m.Run() |
| + // No test may leave a sign-in mail worker running: every app a test |
| + // builds is closed in its cleanup. |
| + if code == 0 { |
| + deadline := time.Now().Add(5 * time.Second) |
| + for mailWorkers() > 0 && time.Now().Before(deadline) { |
| + time.Sleep(10 * time.Millisecond) |
| + } |
| + if n := mailWorkers(); n > 0 { |
| + fmt.Fprintf(os.Stderr, "FAIL: %d sign-in mail queue goroutines still running after the tests\n", n) |
| + code = 1 |
| + } |
| + } |
| + restore() |
| + os.Exit(code) |
| +} |
| + |
| +// mailWorkers counts goroutines running a sign-in mail queue's worker |
| +// or log pump (internal/calendar/signin_mail.go). |
| +func mailWorkers() int { |
| + buf := make([]byte, 1<<22) |
| + stacks := string(buf[:runtime.Stack(buf, true)]) |
| + return strings.Count(stacks, "(*mailQueue).run(") + strings.Count(stacks, "(*mailQueue).pump(") |
| +} |
| + |
| +// TestKeymailDiscoveryStaysInProcess: every unforced POST /signin |
| +// classifies its address (rastrillo/auth's Keymail discovery), so this |
| +// package's tests must not ask the real network whether example.com runs |
| +// Keymail. With keymailtest installed every query is answered NXDOMAIN |
| +// in-process, so even example.com, which public DNS resolves, must not. |
| +func TestKeymailDiscoveryStaysInProcess(t *testing.T) { |
| + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) |
| + defer cancel() |
| + addrs, err := net.DefaultResolver.LookupHost(ctx, "example.com") |
| + var dnsErr *net.DNSError |
| + if !errors.As(err, &dnsErr) || !dnsErr.IsNotFound { |
| + t.Fatalf("LookupHost(example.com) = %v, %v; want NXDOMAIN from keymailtest's in-process resolver", addrs, err) |
| + } |
| +} |