package mailx_test
import (
"mime"
"strings"
"testing"
netmail "net/mail"
"pkg.jfrech.com/brief/mailx"
)
type TestCase struct {
DisplayName string
AddrSpec string
NameAddr string
}
func testCases() []TestCase {
return []TestCase{
TestCase{
"", "a@b.c",
"",
},
TestCase{
"d e", "a@b.c",
"\"d e\" ",
},
TestCase{
"def", "a@b.c",
"\"def\" ",
},
TestCase{
"\"", "quote@example.com",
"\"\\\"\" ",
},
TestCase{
"\"", "\"@example.com",
"\"\\\"\" <\"\\\"\"@example.com>",
},
}
}
func TestAddress(t *testing.T) {
for j, tc := range testCases() {
nameAddr := mailx.FormatAddress(mailx.Address{tc.DisplayName, tc.AddrSpec})
if nameAddr != tc.NameAddr {
t.Errorf("tcs[%d]:\n got %q,\n want %q", j, nameAddr, tc.NameAddr)
}
}
}
func FuzzAddress(f *testing.F) {
// positive seeds
for _, tc := range testCases() {
f.Add(tc.DisplayName, tc.AddrSpec)
}
// negative seeds
f.Add("", "0")
f.Add("0", " \x82@0")
f.Add("0", "=??B??=@0")
f.Add("\x14", "0@0")
f.Fuzz(func(t *testing.T, displayName, addrSpec string) {
if roundtrip, err := netmail.ParseAddress((&netmail.Address{displayName, addrSpec}).String()); err != nil {
t.SkipNow()
} else if displayName != roundtrip.Name || addrSpec != roundtrip.Address {
t.SkipNow()
}
// NOTE(jfrech): 2024-05-13: Annoyingly, [net/mail.Address.String]
// declares (cf. https://pkg.go.dev/net/mail@go1.22.3#Address.String
// [2024-05-13]) in its doc comment
// how it only "=?charset?B/Q?encoded?="-encodes non-ASCII characters,
// meaning literal display names which appear to be q-atoms
// are incorrectly not encoded themselves as q-atoms.
//
// The following .String() could behave in a parsable manner:
// // Unfortunately, err != nil
// _, err := mail.ParseAddress((&mail.Address{"a", "=??B??=@c"}).String())
if strings.Contains(addrSpec, "?=") {
t.SkipNow()
}
//
nameAddrNetmail := (&netmail.Address{displayName, addrSpec}).String()
nameAddrNetmailDecoded, err := new(mime.WordDecoder).DecodeHeader(nameAddrNetmail)
if err != nil {
t.Fatalf("displayName=%q, addrSpec=%q, nameAddrNetmail=%q, err=%v", displayName, addrSpec, nameAddrNetmail, err)
}
nameAddrBriefmailx := mailx.FormatAddress(mailx.Address{displayName, addrSpec})
eq := func(s, z string) bool {
a, err := netmail.ParseAddress(s)
if err != nil {
t.Errorf("netmail.ParseAddress(%q): %v", s, err)
return false
}
b, err := netmail.ParseAddress(s)
if err != nil {
t.Errorf("netmail.ParseAddress(%q): %v", z, err)
return false
}
return *a == *b
}
if !eq(nameAddrNetmail, nameAddrNetmailDecoded) || !eq(nameAddrNetmail, nameAddrBriefmailx) {
t.Errorf("mismatch:\n nameAddrNetmail = %q,\n nameAddrNetmailDecoded = %q,\n nameAddrBriefmailx = %q", nameAddrNetmail, nameAddrNetmailDecoded, nameAddrBriefmailx)
}
/*
addr0 := &netmail.Address{displayName, addrSpec}
addr1, err := netmail.ParseAddress(addr0.String())
if err != nil {
t.Fatalf("netmail.Parse(%q): %v", addr0.String(), err)
}
t.Logf("addr0.String() = %s", addr0.String())
t.Logf("addr1.String() = %s", addr1.String())
if *addr0 != *addr1 {
t.Errorf("&netmail.Address{%q, %q} decayed into &netmail.Address{%q, %q}", addr0.Name, addr0.Address, addr1.Name, addr1.Address)
}
*/
})
}