1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2026-10-29 15:02:30 +00:00
Files
go-chasquid-smtp/internal/normalize/normalize_test.go
T
Alberto Bertogli 8b19760f4e test: Use modern testing features instead of custom helpers
Many of our tests rely on helpers that predate features which are now
available in the standard library.

This patch replaces them with the standard equivalents:

- MustTempDir/RemoveIfOk -> t.ArtifactDir() or t.TempDir() (depending on
  whether it is useful to keep the data on request).
- GetFreePort + waitForServer -> opening the listeners before starting
  the servers. To support this, localrpc.Server gains a Serve method. We
  may re-introduce these in the future if more use cases appear.
- WaitFor and real-time timing checks -> testing/synctest.
- context.Background() -> t.Context().
- Benchmark b.N loops -> b.Loop().
- reflect.DeepEqual -> cmp.Diff, and proto.Equal for protobufs.
2026-10-03 10:34:35 +01:00

217 lines
4.5 KiB
Go

package normalize
import (
"bytes"
"strings"
"testing"
)
func TestUser(t *testing.T) {
valid := []struct{ user, norm string }{
{"ÑAndÚ", "ñandú"},
{"Pingüino", "pingüino"},
}
for _, c := range valid {
nu, err := User(c.user)
if nu != c.norm {
t.Errorf("%q normalized to %q, expected %q", c.user, nu, c.norm)
}
if err != nil {
t.Errorf("%q error: %v", c.user, err)
}
}
invalid := []string{
"á é", "a\te", "x ", "x\xa0y", "x\x85y", "x\vy", "x\fy", "x\ry",
"henry\u2163", "\u265a", "\u00b9",
}
for _, u := range invalid {
nu, err := User(u)
if err == nil {
t.Errorf("expected User(%+q) to fail, but did not", u)
}
if nu != u {
t.Errorf("%+q failed norm, but returned %+q", u, nu)
}
}
}
func TestDomain(t *testing.T) {
valid := []struct{ user, norm string }{
{"ÑAndÚ", "ñandú"},
{"Pingüino", "pingüino"},
{"xn--aca-6ma", "ñaca"},
{"xn--lca", "ñ"}, // Punycode is for 'Ñ'.
{"e\u0301", "é"}, // Transform to NFC form.
}
for _, c := range valid {
nu, err := Domain(c.user)
if nu != c.norm {
t.Errorf("%q normalized to %q, expected %q", c.user, nu, c.norm)
}
if err != nil {
t.Errorf("%q error: %v", c.user, err)
}
}
invalid := []string{"xn---", "xn--xyz-ñ"}
for _, u := range invalid {
nu, err := Domain(u)
if err == nil {
t.Errorf("expected Domain(%+q) to fail, but did not", u)
}
if nu != u {
t.Errorf("%+q failed norm, but returned %+q", u, nu)
}
}
}
func TestAddr(t *testing.T) {
valid := []struct{ user, norm string }{
{"ÑAndÚ@pampa", "ñandú@pampa"},
{"Pingüino@patagonia", "pingüino@patagonia"},
{"pe\u0301@le\u0301a", "pé@léa"}, // Transform to NFC form.
}
for _, c := range valid {
nu, err := Addr(c.user)
if nu != c.norm {
t.Errorf("%q normalized to %q, expected %q", c.user, nu, c.norm)
}
if err != nil {
t.Errorf("%q error: %v", c.user, err)
}
}
invalid := []string{
"á é@i", "henry\u2163@throne", "a@xn---",
}
for _, u := range invalid {
nu, err := Addr(u)
if err == nil {
t.Errorf("expected Addr(%+q) to fail, but did not", u)
}
if nu != u {
t.Errorf("%+q failed norm, but returned %+q", u, nu)
}
}
}
func TestDomainToUnicode(t *testing.T) {
valid := []struct{ domain, expected string }{
{"<>", "<>"},
{"a@b", "a@b"},
{"a@Ñ", "a@ñ"},
{"xn--lca@xn--lca", "xn--lca@ñ"}, // Punycode is for 'Ñ'.
{"a@e\u0301", "a@é"}, // Transform to NFC form.
// Degenerate case, we don't expect to ever produce this; at least
// check it does not crash.
{"", "@"},
}
for _, c := range valid {
got, err := DomainToUnicode(c.domain)
if got != c.expected {
t.Errorf("DomainToUnicode(%q) = %q, expected %q",
c.domain, got, c.expected)
}
if err != nil {
t.Errorf("DomainToUnicode(%q) error: %v", c.domain, err)
}
}
invalid := []string{"a@xn---", "a@xn--xyz-ñ"}
for _, u := range invalid {
got, err := DomainToUnicode(u)
if err == nil {
t.Errorf("expected DomainToUnicode(%+q) to fail, but did not", u)
}
if got != u {
t.Errorf("%+q failed norm, but returned %+q", u, got)
}
}
}
func TestToCRLF(t *testing.T) {
cases := []struct {
in, out string
}{
{"", ""},
{"a", "a"},
// Does not end in newline.
{"a\n", "a\r\n"},
{"a\nb", "a\r\nb"},
{"a\r\nb", "a\r\nb"},
// Ends in newline.
{"a\nb\n", "a\r\nb\r\n"},
{"a\r\nb\n", "a\r\nb\r\n"},
{"a\r\nb\r\n", "a\r\nb\r\n"},
{"a\r\nb\n\n", "a\r\nb\r\n\r\n"},
{"a\r\nb\r\n\r\n", "a\r\nb\r\n\r\n"},
}
for _, c := range cases {
got := string(ToCRLF([]byte(c.in)))
if got != c.out {
t.Errorf("ToCRLF(%q) = %q, expected %q", c.in, got, c.out)
}
got = StringToCRLF(c.in)
if got != c.out {
t.Errorf("StringToCRLF(%q) = %q, expected %q", c.in, got, c.out)
}
}
}
func FuzzUser(f *testing.F) {
f.Fuzz(func(t *testing.T, user string) {
User(user)
})
}
func FuzzDomain(f *testing.F) {
f.Fuzz(func(t *testing.T, domain string) {
Domain(domain)
})
}
func FuzzAddr(f *testing.F) {
f.Fuzz(func(t *testing.T, addr string) {
Addr(addr)
})
}
func FuzzDomainToUnicode(f *testing.F) {
f.Fuzz(func(t *testing.T, addr string) {
DomainToUnicode(addr)
})
}
func BenchmarkToCRLF(b *testing.B) {
// Generate a 1000-line message.
bb := bytes.Buffer{}
for range 1000 {
bb.WriteString("this is a very pretty line 🐅\n")
}
buf := bb.Bytes()
for b.Loop() {
ToCRLF(buf)
}
}
func BenchmarkStringToCRLF(b *testing.B) {
// Generate a 1000-line message.
sb := strings.Builder{}
for range 1000 {
sb.WriteString("this is a very pretty line 🐅\n")
}
s := sb.String()
for b.Loop() {
StringToCRLF(s)
}
}