package infra import ( "fmt" "net" "strings" "testing" ) // startMockSMTPForSend starts a mock SMTP server and returns host/port separately. func startMockSMTPForSend(t *testing.T) (host string, port int, 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) } }() h, ps, _ := net.SplitHostPort(ln.Addr().String()) p := 0 fmt.Sscanf(ps, "%d", &p) return h, p, func() { ln.Close() } } func TestSMTPSend(t *testing.T) { t.Run("envia mensaje texto plano via mock smtp", func(t *testing.T) { host, port, stop := startMockSMTPForSend(t) defer stop() cfg := SMTPConfig{Host: host, Port: port, TLSMode: ""} client, err := SMTPConnect(cfg) if err != nil { t.Fatalf("SMTPConnect: %v", err) } defer client.Quit() msg := EmailBuildText("alice@example.com", []string{"bob@example.com"}, "Test", "Hello Bob") if err := SMTPSend(client, msg); err != nil { t.Fatalf("SMTPSend: %v", err) } }) t.Run("envia mensaje html via mock smtp", func(t *testing.T) { host, port, stop := startMockSMTPForSend(t) defer stop() cfg := SMTPConfig{Host: host, Port: port, TLSMode: ""} client, err := SMTPConnect(cfg) if err != nil { t.Fatalf("SMTPConnect: %v", err) } defer client.Quit() msg := EmailBuildHTML("alice@example.com", []string{"bob@example.com"}, "HTML Test", "Hello") if err := SMTPSend(client, msg); err != nil { t.Fatalf("SMTPSend: %v", err) } }) t.Run("envia con adjunto via mock smtp", func(t *testing.T) { host, port, stop := startMockSMTPForSend(t) defer stop() cfg := SMTPConfig{Host: host, Port: port, TLSMode: ""} client, err := SMTPConnect(cfg) if err != nil { t.Fatalf("SMTPConnect: %v", err) } defer client.Quit() msg := EmailBuildText("alice@example.com", []string{"bob@example.com"}, "Att Test", "See attachment") att := EmailAttachment{Filename: "data.txt", ContentType: "text/plain", Data: []byte("file content")} msg = EmailWithAttachment(msg, att) if err := SMTPSend(client, msg); err != nil { t.Fatalf("SMTPSend with attachment: %v", err) } }) } // TestExtractAddr tests the internal extractAddr helper. func TestExtractAddr(t *testing.T) { cases := []struct { input string want string }{ {"Alice ", "alice@example.com"}, {"bob@example.com", "bob@example.com"}, {" carol@example.com ", "carol@example.com"}, } for _, c := range cases { got := extractAddr(c.input) if got != c.want { t.Errorf("extractAddr(%q) = %q, want %q", c.input, got, c.want) } } } // TestBuildMIME tests the MIME builder with different body combinations. func TestBuildMIME(t *testing.T) { t.Run("solo texto plano", func(t *testing.T) { msg := EmailBuildText("a@a.com", []string{"b@b.com"}, "Subj", "Hello plain") body, err := buildMIME(msg) if err != nil { t.Fatalf("buildMIME: %v", err) } s := string(body) if !strings.Contains(s, "text/plain") { t.Errorf("expected text/plain in MIME, got:\n%s", s) } if !strings.Contains(s, "Hello plain") { t.Errorf("expected body text in MIME, got:\n%s", s) } }) t.Run("solo html", func(t *testing.T) { msg := EmailBuildHTML("a@a.com", []string{"b@b.com"}, "Subj", "Bold") body, err := buildMIME(msg) if err != nil { t.Fatalf("buildMIME: %v", err) } s := string(body) if !strings.Contains(s, "text/html") { t.Errorf("expected text/html in MIME, got:\n%s", s) } }) t.Run("multipart con adjunto", func(t *testing.T) { msg := EmailBuildText("a@a.com", []string{"b@b.com"}, "Subj", "body") att := EmailAttachment{Filename: "f.pdf", ContentType: "application/pdf", Data: []byte("pdf")} msg = EmailWithAttachment(msg, att) body, err := buildMIME(msg) if err != nil { t.Fatalf("buildMIME: %v", err) } s := string(body) if !strings.Contains(s, "multipart/mixed") { t.Errorf("expected multipart/mixed in MIME, got:\n%s", s) } if !strings.Contains(s, "f.pdf") { t.Errorf("expected attachment filename in MIME, got:\n%s", s) } }) }