Files
fn_registry/functions/infra/smtp_connect_test.go
T
egutierrez 6019f2aafa test: tests Go para funciones email SMTP
Tests para builders puros (build_html, build_text, with_attachment),
template_render, smtp_connect y smtp_send con mock TCP server.
Todos los tests pasan: 18 casos cubriendo pureza, inmutabilidad y envio SMTP.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 02:01:18 +02:00

121 lines
2.8 KiB
Go

package infra
import (
"bufio"
"fmt"
"net"
"strings"
"testing"
)
// mockSMTPServer starts a minimal SMTP server on a random port that greets
// and accepts any commands. Returns the listener address and a function to stop it.
func mockSMTPServer(t *testing.T) (addr string, stop func()) {
t.Helper()
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("mock smtp listen: %v", err)
}
go func() {
for {
conn, err := ln.Accept()
if err != nil {
return
}
go handleMockSMTP(conn)
}
}()
return ln.Addr().String(), func() { ln.Close() }
}
// handleMockSMTP is a minimal SMTP session handler (no TLS, no auth required).
func handleMockSMTP(conn net.Conn) {
defer conn.Close()
w := bufio.NewWriter(conn)
r := bufio.NewReader(conn)
fmt.Fprintf(w, "220 mock SMTP ready\r\n")
w.Flush()
for {
line, err := r.ReadString('\n')
if err != nil {
return
}
line = strings.TrimSpace(line)
upper := strings.ToUpper(line)
switch {
case strings.HasPrefix(upper, "EHLO"), strings.HasPrefix(upper, "HELO"):
fmt.Fprintf(w, "250 mock\r\n")
case strings.HasPrefix(upper, "MAIL FROM"):
fmt.Fprintf(w, "250 OK\r\n")
case strings.HasPrefix(upper, "RCPT TO"):
fmt.Fprintf(w, "250 OK\r\n")
case strings.HasPrefix(upper, "DATA"):
fmt.Fprintf(w, "354 End with .\r\n")
w.Flush()
// read until ".\r\n"
for {
dl, derr := r.ReadString('\n')
if derr != nil {
return
}
if strings.TrimSpace(dl) == "." {
break
}
}
fmt.Fprintf(w, "250 OK\r\n")
case strings.HasPrefix(upper, "QUIT"):
fmt.Fprintf(w, "221 Bye\r\n")
w.Flush()
return
default:
fmt.Fprintf(w, "502 Command not recognized\r\n")
}
w.Flush()
}
}
func TestSMTPConnect(t *testing.T) {
t.Run("conecta sin cifrado a servidor mock", func(t *testing.T) {
addr, stop := mockSMTPServer(t)
defer stop()
host, portStr, _ := net.SplitHostPort(addr)
port := 0
fmt.Sscanf(portStr, "%d", &port)
cfg := SMTPConfig{
Host: host,
Port: port,
TLSMode: "", // no encryption
}
client, err := SMTPConnect(cfg)
if err != nil {
t.Fatalf("SMTPConnect: %v", err)
}
client.Quit()
})
t.Run("error si el servidor no existe", func(t *testing.T) {
// Use a port that is listening but immediately closes (RST) — we start
// and immediately close a listener so the port is known-closed.
ln2, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("setup: %v", err)
}
h2, p2str, _ := net.SplitHostPort(ln2.Addr().String())
ln2.Close() // now nothing listens — connections get refused
p2 := 0
fmt.Sscanf(p2str, "%d", &p2)
cfg := SMTPConfig{
Host: h2,
Port: p2,
TLSMode: "",
}
_, connErr := SMTPConnect(cfg)
if connErr == nil {
t.Error("expected error for refused connection, got nil")
}
})
}