1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2026-10-29 15:02:30 +00:00
Files
go-chasquid-smtp/internal/courier/fakeserver_test.go
T
Alberto Bertogli 8b19760f4e test: Use modern testing features instead of custom helpers
Many of our tests rely on helpers that predate features which are now
available in the standard library.

This patch replaces them with the standard equivalents:

- MustTempDir/RemoveIfOk -> t.ArtifactDir() or t.TempDir() (depending on
  whether it is useful to keep the data on request).
- GetFreePort + waitForServer -> opening the listeners before starting
  the servers. To support this, localrpc.Server gains a Serve method. We
  may re-introduce these in the future if more use cases appear.
- WaitFor and real-time timing checks -> testing/synctest.
- context.Background() -> t.Context().
- Benchmark b.N loops -> b.Loop().
- reflect.DeepEqual -> cmp.Diff, and proto.Equal for protobufs.
2026-10-03 10:34:35 +01:00

143 lines
2.8 KiB
Go

package courier
import (
"bufio"
"crypto/tls"
"crypto/x509"
"net"
"net/textproto"
"os"
"sync"
"testing"
"blitiri.com.ar/go/chasquid/internal/testlib"
)
// Fake server, to test SMTP out.
type FakeServer struct {
t *testing.T
tmpDir string
responses map[string]string
wg *sync.WaitGroup
addr string
conns int
tlsConfig *tls.Config
}
func newFakeServer(t *testing.T, responses map[string]string, conns int) *FakeServer {
s := &FakeServer{
t: t,
tmpDir: t.ArtifactDir(),
responses: responses,
conns: conns,
wg: &sync.WaitGroup{},
}
s.start()
return s
}
func (s *FakeServer) initTLS() {
var err error
s.tlsConfig, err = testlib.GenerateCert(s.tmpDir)
if err != nil {
s.t.Fatalf("error generating cert: %v", err)
}
cert, err := tls.LoadX509KeyPair(s.tmpDir+"/cert.pem", s.tmpDir+"/key.pem")
if err != nil {
s.t.Fatalf("error loading temp cert: %v", err)
}
s.tlsConfig.Certificates = []tls.Certificate{cert}
}
func (s *FakeServer) rootCA() *x509.CertPool {
s.t.Helper()
pool := x509.NewCertPool()
path := s.tmpDir + "/cert.pem"
data, err := os.ReadFile(path)
if err != nil {
s.t.Fatalf("error reading cert %q: %v", path, err)
}
ok := pool.AppendCertsFromPEM(data)
if !ok {
s.t.Fatalf("failed to load cert %q", path)
}
return pool
}
func (s *FakeServer) start() string {
s.t.Helper()
l, err := net.Listen("tcp", "localhost:0")
if err != nil {
s.t.Fatalf("fake server listen: %v", err)
}
s.addr = l.Addr().String()
s.initTLS()
accept := func() {
c, err := l.Accept()
if err != nil {
panic(err)
}
defer c.Close()
s.t.Logf("fakeServer got connection")
r := textproto.NewReader(bufio.NewReader(c))
c.Write([]byte(s.responses["_welcome"]))
for {
line, err := r.ReadLine()
if err != nil {
s.t.Logf("fakeServer exiting: %v\n", err)
return
}
s.t.Logf("fakeServer read: %q\n", line)
if line == "STARTTLS" && s.responses["_STARTTLS"] == "ok" {
c.Write([]byte(s.responses["STARTTLS"]))
tlssrv := tls.Server(c, s.tlsConfig)
err = tlssrv.Handshake()
if err != nil {
s.t.Logf("starttls handshake error: %v", err)
return
}
// Replace the connection with the wrapped one.
// Don't send a reply, as per the protocol.
c = tlssrv
defer c.Close()
r = textproto.NewReader(bufio.NewReader(c))
continue
}
c.Write([]byte(s.responses[line]))
if line == "DATA" {
_, err = r.ReadDotBytes()
if err != nil {
s.t.Logf("fakeServer exiting: %v\n", err)
return
}
c.Write([]byte(s.responses["_DATA"]))
}
}
}
for range s.conns {
s.wg.Go(accept)
}
return s.addr
}
func (s *FakeServer) HostPort() (string, string) {
host, port, _ := net.SplitHostPort(s.addr)
return host, port
}
func (s *FakeServer) Wait() {
s.wg.Wait()
}