Files
goyco/internal/health/smtp_test.go
2026-02-15 12:05:25 +01:00

310 lines
6.3 KiB
Go

package health
import (
"context"
"net"
"strconv"
"strings"
"testing"
"time"
)
func TestSMTPChecker_Check_NoAuth(t *testing.T) {
config := SMTPConfig{
Host: "smtp.example.com",
Port: 25,
}
checker := NewSMTPChecker(config)
if checker.config.Username != "" {
t.Error("expected empty username")
}
if checker.config.Password != "" {
t.Error("expected empty password")
}
}
func TestSMTPChecker_Check_InvalidPort(t *testing.T) {
config := SMTPConfig{
Host: "localhost",
Port: 99999,
}
checker := NewSMTPChecker(config)
ctx := context.Background()
result := checker.Check(ctx)
if result.Status != StatusDegraded {
t.Errorf("expected degraded status for invalid port, got %s", result.Status)
}
}
func TestSMTPChecker_Check_ConnectionRefused(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to create listener: %v", err)
}
port := listener.Addr().(*net.TCPAddr).Port
listener.Close()
config := SMTPConfig{
Host: "127.0.0.1",
Port: port,
}
checker := NewSMTPChecker(config)
ctx := context.Background()
result := checker.Check(ctx)
if result.Status != StatusDegraded {
t.Errorf("expected degraded status for connection refused, got %s", result.Status)
}
}
func TestSMTPChecker_Check_WithMockServer(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to create listener: %v", err)
}
defer listener.Close()
port := listener.Addr().(*net.TCPAddr).Port
serverDone := make(chan bool)
go func() {
defer close(serverDone)
conn, err := listener.Accept()
if err != nil {
return
}
defer conn.Close()
conn.Write([]byte("220 test.example.com ESMTP ready\r\n"))
buf := make([]byte, 1024)
n, _ := conn.Read(buf)
if n > 0 {
conn.Write([]byte("250-test.example.com\r\n250-STARTTLS\r\n250 AUTH PLAIN\r\n"))
}
n, _ = conn.Read(buf)
if n > 0 {
conn.Write([]byte("220 2.0.0 Ready to start TLS\r\n"))
}
n, _ = conn.Read(buf)
if n > 0 {
conn.Write([]byte("235 2.7.0 Authentication successful\r\n"))
}
n, _ = conn.Read(buf)
if n > 0 {
conn.Write([]byte("221 2.0.0 Bye\r\n"))
}
}()
config := SMTPConfig{
Host: "127.0.0.1",
Port: port,
Username: "test@example.com",
Password: "password",
}
checker := NewSMTPChecker(config)
ctx := context.Background()
result := checker.Check(ctx)
select {
case <-serverDone:
case <-time.After(2 * time.Second):
t.Error("server timeout")
}
if result.Status != StatusHealthy && result.Status != StatusDegraded {
t.Errorf("unexpected status: %s", result.Status)
}
if result.Details["host"] == nil {
t.Errorf("expected host in details, got %v", result.Details["host"])
}
}
func TestSMTPChecker_Check_EHLOFailure(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to create listener: %v", err)
}
defer listener.Close()
port := listener.Addr().(*net.TCPAddr).Port
serverDone := make(chan bool)
go func() {
defer close(serverDone)
conn, err := listener.Accept()
if err != nil {
return
}
defer conn.Close()
conn.Write([]byte("220 test.example.com ESMTP ready\r\n"))
buf := make([]byte, 1024)
n, _ := conn.Read(buf)
if n > 0 {
conn.Write([]byte("500 Syntax error, command unrecognized\r\n"))
}
}()
config := SMTPConfig{
Host: "127.0.0.1",
Port: port,
}
checker := NewSMTPChecker(config)
ctx := context.Background()
result := checker.Check(ctx)
select {
case <-serverDone:
case <-time.After(2 * time.Second):
t.Error("server timeout")
}
if result.Status != StatusDegraded {
t.Errorf("expected degraded status for EHLO failure, got %s", result.Status)
}
if !strings.Contains(result.Message, "EHLO") {
t.Errorf("expected EHLO error in message, got: %s", result.Message)
}
}
func TestSMTPChecker_Check_STARTTLSNotSupported(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to create listener: %v", err)
}
defer listener.Close()
port := listener.Addr().(*net.TCPAddr).Port
serverDone := make(chan bool)
go func() {
defer close(serverDone)
conn, err := listener.Accept()
if err != nil {
return
}
defer conn.Close()
conn.Write([]byte("220 test.example.com ESMTP ready\r\n"))
buf := make([]byte, 1024)
n, _ := conn.Read(buf)
if n > 0 {
conn.Write([]byte("250-test.example.com\r\n250 AUTH PLAIN\r\n"))
}
n, _ = conn.Read(buf)
if n > 0 {
conn.Write([]byte("221 2.0.0 Bye\r\n"))
}
}()
config := SMTPConfig{
Host: "127.0.0.1",
Port: port,
}
checker := NewSMTPChecker(config)
ctx := context.Background()
result := checker.Check(ctx)
select {
case <-serverDone:
case <-time.After(2 * time.Second):
t.Error("server timeout")
}
if result.Details["starttls"] != "not supported" && result.Details["starttls"] != "failed" {
t.Errorf("expected starttls not supported or failed, got: %v", result.Details["starttls"])
}
}
func TestSMTPChecker_Check_AuthNotConfigured(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to create listener: %v", err)
}
defer listener.Close()
port := listener.Addr().(*net.TCPAddr).Port
serverDone := make(chan bool)
go func() {
defer close(serverDone)
conn, err := listener.Accept()
if err != nil {
return
}
defer conn.Close()
conn.Write([]byte("220 test.example.com ESMTP ready\r\n"))
buf := make([]byte, 1024)
n, _ := conn.Read(buf)
if n > 0 {
conn.Write([]byte("250-test.example.com\r\n250-STARTTLS\r\n250 AUTH PLAIN\r\n"))
}
n, _ = conn.Read(buf)
if n > 0 {
conn.Write([]byte("221 2.0.0 Bye\r\n"))
}
}()
config := SMTPConfig{
Host: "127.0.0.1",
Port: port,
}
checker := NewSMTPChecker(config)
ctx := context.Background()
result := checker.Check(ctx)
select {
case <-serverDone:
case <-time.After(2 * time.Second):
t.Error("server timeout")
}
if result.Details["auth"] != "not configured" {
t.Errorf("expected auth not configured, got: %v", result.Details["auth"])
}
}
func TestSMTPConfig_GetAddress(t *testing.T) {
config := SMTPConfig{
Host: "smtp.example.com",
Port: 587,
}
address := getSMTPAddress(config)
expected := "smtp.example.com:587"
if address != expected {
t.Errorf("expected address %s, got %s", expected, address)
}
}
func getSMTPAddress(config SMTPConfig) string {
return config.Host + ":" + strconv.Itoa(config.Port)
}