oficina / calendar Public

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>
Paul Campbell pushed by paul@keymail.dev 53cd1cd6339daf6e93ebe45189fa2524af51169a parent 3057927
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)
+ }
+}