Files
enkly-ai/pkg/mailkit/mail_test.go
enkly 79ce8dc4c0
Some checks failed
Go CI Pipeline / build_and_test (push) Failing after 22m0s
Init Project
2026-10-30 18:06:55 +01:00

100 lines
3.2 KiB
Go

package mailkit
import (
"strings"
"testing"
)
// TestEnvelopeAddress covers the exact bug this fixes: a configured
// From address that includes a display name (a very natural thing to
// set ENKLY_SMTP_FROM to, e.g. "enkly <noreply@example.com>") must not
// be handed to MAIL FROM/RCPT TO as-is, or Gmail answers with "555
// 5.5.2 Syntax error, cannot decode response" because the envelope
// command only accepts a bare address (RFC 5321 §4.1.2).
func TestEnvelopeAddress(t *testing.T) {
cases := []struct{ in, want string }{
{"user@example.com", "user@example.com"},
{"enkly <user@example.com>", "user@example.com"},
{" enkly <user@example.com> ", "user@example.com"},
{"\"Enkly Notifications\" <user@example.com>", "user@example.com"},
// Not parseable as an address at all: returned unchanged so the
// SMTP server's own rejection explains the problem, rather than
// this function silently swallowing it.
{"not-an-address", "not-an-address"},
}
for _, c := range cases {
if got := envelopeAddress(c.in); got != c.want {
t.Errorf("envelopeAddress(%q) = %q, want %q", c.in, got, c.want)
}
}
}
// TestNewTrimsConfig guards against the same class of bug from a
// different angle: SMTP config values arriving from os.Getenv with
// stray whitespace or an embedded "\r" (e.g. a CRLF-saved .env or
// docker-compose.yml) must not reach the wire as part of an SMTP
// command line, or the server sees a corrupted command.
func TestNewTrimsConfig(t *testing.T) {
m := New(Config{
Host: " smtp.gmail.com\r\n",
Port: " 587 ",
Username: " user@example.com \r",
Password: " app-password \r",
From: " enkly <user@example.com> \r",
})
if m.cfg.Host != "smtp.gmail.com" {
t.Errorf("Host = %q", m.cfg.Host)
}
if m.cfg.Port != "587" {
t.Errorf("Port = %q", m.cfg.Port)
}
if m.cfg.Username != "user@example.com" {
t.Errorf("Username = %q", m.cfg.Username)
}
if m.cfg.Password != "app-password" {
t.Errorf("Password = %q", m.cfg.Password)
}
if m.cfg.From != "enkly <user@example.com>" {
t.Errorf("From = %q", m.cfg.From)
}
}
// TestNewDefaults checks the Gmail host/port/from defaults still apply
// after adding trimming, so a minimal config keeps working out of the
// box.
func TestNewDefaults(t *testing.T) {
m := New(Config{Username: "user@example.com", Password: "app-password"})
if m.cfg.Host != "smtp.gmail.com" {
t.Errorf("default Host = %q", m.cfg.Host)
}
if m.cfg.Port != "587" {
t.Errorf("default Port = %q", m.cfg.Port)
}
if m.cfg.From != "user@example.com" {
t.Errorf("default From = %q", m.cfg.From)
}
}
func TestConfigured(t *testing.T) {
if New(Config{}).Configured() {
t.Fatal("empty config must not report Configured")
}
if !New(Config{Username: "u", Password: "p"}).Configured() {
t.Fatal("username+password must report Configured")
}
}
func TestBuildMessageShape(t *testing.T) {
msg := buildMessage("enkly <from@example.com>", "to@example.com", "Subject line", "body text")
for _, want := range []string{
"From: enkly <from@example.com>\r\n",
"To: to@example.com\r\n",
"Subject: Subject line\r\n",
"\r\nbody text\r\n",
} {
if !strings.Contains(msg, want) {
t.Errorf("buildMessage output missing %q; got:\n%s", want, msg)
}
}
}