106 lines
3.2 KiB
Go
106 lines
3.2 KiB
Go
package smtp
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"net/netip"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestDialResolvedAddressesFallsBackAfterTimeout(t *testing.T) {
|
|
client, server := net.Pipe()
|
|
defer client.Close()
|
|
defer server.Close()
|
|
addresses := []netip.Addr{netip.MustParseAddr("192.0.2.1"), netip.MustParseAddr("2001:db8::1")}
|
|
calls := 0
|
|
conn, err := dialResolvedAddresses(context.Background(), addresses, "25", 10*time.Millisecond, func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
calls++
|
|
if network != "tcp" || address != net.JoinHostPort(addresses[calls-1].String(), "25") {
|
|
t.Fatalf("unexpected dial target: %s %s", network, address)
|
|
}
|
|
if calls == 1 {
|
|
<-ctx.Done()
|
|
return nil, ctx.Err()
|
|
}
|
|
if ctx.Err() != nil {
|
|
t.Fatal("fallback inherited exhausted attempt context")
|
|
}
|
|
return client, nil
|
|
})
|
|
if err != nil || conn != client || calls != 2 {
|
|
t.Fatalf("fallback failed: conn=%v calls=%d err=%v", conn, calls, err)
|
|
}
|
|
}
|
|
|
|
func TestDialResolvedAddressesHonorsCancellation(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
addresses := []netip.Addr{netip.MustParseAddr("192.0.2.1"), netip.MustParseAddr("192.0.2.2")}
|
|
calls := 0
|
|
_, err := dialResolvedAddresses(ctx, addresses, "25", time.Second, func(context.Context, string, string) (net.Conn, error) {
|
|
calls++
|
|
cancel()
|
|
return nil, context.Canceled
|
|
})
|
|
if calls != 1 || !errors.Is(err, context.Canceled) {
|
|
t.Fatalf("canceled parent was ignored: calls=%d err=%v", calls, err)
|
|
}
|
|
}
|
|
|
|
func TestDialResolvedAddressesRetainsFailures(t *testing.T) {
|
|
first := errors.New("first address failed")
|
|
second := errors.New("second address failed")
|
|
addresses := []netip.Addr{netip.MustParseAddr("192.0.2.1"), netip.MustParseAddr("192.0.2.2")}
|
|
calls := 0
|
|
_, err := dialResolvedAddresses(context.Background(), addresses, "25", time.Second, func(context.Context, string, string) (net.Conn, error) {
|
|
calls++
|
|
if calls == 1 {
|
|
return nil, first
|
|
}
|
|
return nil, second
|
|
})
|
|
if calls != 2 || !errors.Is(err, first) || !errors.Is(err, second) {
|
|
t.Fatalf("connection failures were lost: calls=%d err=%v", calls, err)
|
|
}
|
|
}
|
|
|
|
func TestSMTPContextCancelsGreeting(t *testing.T) {
|
|
client, server := net.Pipe()
|
|
defer server.Close()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
|
|
defer cancel()
|
|
started := time.Now()
|
|
if _, err := newClientWithContext(ctx, client, "localhost", "example.com"); err == nil {
|
|
t.Fatal("blocked greeting succeeded")
|
|
}
|
|
if time.Since(started) > time.Second {
|
|
t.Fatal("context did not cancel greeting")
|
|
}
|
|
}
|
|
|
|
func TestSMTPContextCancelsCommand(t *testing.T) {
|
|
client, server := net.Pipe()
|
|
defer server.Close()
|
|
go func() {
|
|
_, _ = server.Write([]byte("220 local ready\r\n"))
|
|
var command [512]byte
|
|
_, _ = server.Read(command[:])
|
|
}()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
defer cancel()
|
|
smtpClient, err := newClientWithContext(ctx, client, "localhost", "example.com")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer smtpClient.Close()
|
|
started := time.Now()
|
|
if err := smtpClient.Hello("example.com"); err == nil {
|
|
t.Fatal("blocked command succeeded")
|
|
}
|
|
if time.Since(started) > time.Second {
|
|
t.Fatal("context did not cancel command")
|
|
}
|
|
}
|