288 lines
7.3 KiB
Go
288 lines
7.3 KiB
Go
package mailer
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/supabase/auth/internal/conf"
|
|
)
|
|
|
|
func TestEmalValidatorService(t *testing.T) {
|
|
ctx := context.Background()
|
|
ctx, cancel := context.WithTimeout(ctx, time.Second*60)
|
|
defer cancel()
|
|
|
|
testResVal := new(atomic.Value)
|
|
testResVal.Store(`{"valid": true}`)
|
|
|
|
testHdrsVal := new(atomic.Value)
|
|
testHdrsVal.Store(map[string]string{"apikey": "test"})
|
|
|
|
// testHeaders := map[string][]string{"apikey": []string{"test"}}
|
|
testHeaders := `{"apikey": ["test"]}`
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
key := r.Header.Get("apikey")
|
|
if key == "" {
|
|
fmt.Fprintln(w, `{"error": true}`)
|
|
return
|
|
}
|
|
|
|
fmt.Fprintln(w, testResVal.Load().(string))
|
|
}))
|
|
defer ts.Close()
|
|
|
|
// Return nil err from service
|
|
// when svc and extended checks both report email as valid
|
|
{
|
|
testResVal.Store(`{"valid": true}`)
|
|
cfg := conf.MailerConfiguration{
|
|
EmailValidationExtended: true,
|
|
EmailValidationServiceURL: ts.URL,
|
|
EmailValidationServiceHeaders: testHeaders,
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ev := newEmailValidator(cfg)
|
|
err := ev.Validate(ctx, "chris.stockton@supabase.io")
|
|
if err != nil {
|
|
t.Fatalf("exp nil err; got %v", err)
|
|
}
|
|
}
|
|
|
|
// Return nil err from service when
|
|
// extended is disabled for a known invalid address
|
|
// service reports valid
|
|
{
|
|
testResVal.Store(`{"valid": true}`)
|
|
|
|
cfg := conf.MailerConfiguration{
|
|
EmailValidationExtended: false,
|
|
EmailValidationServiceURL: ts.URL,
|
|
EmailValidationServiceHeaders: testHeaders,
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ev := newEmailValidator(cfg)
|
|
err := ev.Validate(ctx, "test@gmail.com")
|
|
if err != nil {
|
|
t.Fatalf("exp nil err; got %v", err)
|
|
}
|
|
}
|
|
|
|
// Return nil err from service when
|
|
// extended is disabled for a known invalid address
|
|
// service is disabled for a known invalid address
|
|
{
|
|
testResVal.Store(`{"valid": false}`)
|
|
|
|
cfg := conf.MailerConfiguration{
|
|
EmailValidationExtended: false,
|
|
EmailValidationServiceURL: "",
|
|
EmailValidationServiceHeaders: "",
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ev := newEmailValidator(cfg)
|
|
err := ev.Validate(ctx, "test@gmail.com")
|
|
if err != nil {
|
|
t.Fatalf("exp nil err; got %v", err)
|
|
}
|
|
}
|
|
|
|
// Return err from service when
|
|
// extended reports invalid
|
|
// service is disabled for a known invalid address
|
|
{
|
|
testResVal.Store(`{"valid": true}`)
|
|
cfg := conf.MailerConfiguration{
|
|
EmailValidationExtended: true,
|
|
EmailValidationServiceURL: "",
|
|
EmailValidationServiceHeaders: "",
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ev := newEmailValidator(cfg)
|
|
err := ev.Validate(ctx, "test@gmail.com")
|
|
if err == nil {
|
|
t.Fatal("exp non-nil err")
|
|
}
|
|
}
|
|
|
|
// Return err from service when
|
|
// extended reports invalid
|
|
// service reports valid
|
|
{
|
|
testResVal.Store(`{"valid": true}`)
|
|
cfg := conf.MailerConfiguration{
|
|
EmailValidationExtended: true,
|
|
EmailValidationServiceURL: ts.URL,
|
|
EmailValidationServiceHeaders: testHeaders,
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ev := newEmailValidator(cfg)
|
|
err := ev.Validate(ctx, "test@gmail.com")
|
|
if err == nil {
|
|
t.Fatal("exp non-nil err")
|
|
}
|
|
}
|
|
|
|
// Return err from service when
|
|
// extended reports valid
|
|
// service reports invalid
|
|
{
|
|
testResVal.Store(`{"valid": false}`)
|
|
cfg := conf.MailerConfiguration{
|
|
EmailValidationExtended: true,
|
|
EmailValidationServiceURL: ts.URL,
|
|
EmailValidationServiceHeaders: testHeaders,
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ev := newEmailValidator(cfg)
|
|
err := ev.Validate(ctx, "chris.stockton@supabase.io")
|
|
if err == nil {
|
|
t.Fatal("exp non-nil err")
|
|
}
|
|
}
|
|
|
|
// Return err from service when
|
|
// extended reports invalid
|
|
// service reports invalid
|
|
{
|
|
testResVal.Store(`{"valid": false}`)
|
|
|
|
cfg := conf.MailerConfiguration{
|
|
EmailValidationExtended: false,
|
|
EmailValidationServiceURL: ts.URL,
|
|
EmailValidationServiceHeaders: testHeaders,
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ev := newEmailValidator(cfg)
|
|
err := ev.Validate(ctx, "test@gmail.com")
|
|
if err == nil {
|
|
t.Fatal("exp non-nil err")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateEmailExtended(t *testing.T) {
|
|
ctx := context.Background()
|
|
ctx, cancel := context.WithTimeout(ctx, time.Second*60)
|
|
defer cancel()
|
|
|
|
cases := []struct {
|
|
email string
|
|
timeout time.Duration
|
|
err string
|
|
}{
|
|
// valid (has mx record)
|
|
{email: "a@supabase.io"},
|
|
{email: "support@supabase.io"},
|
|
{email: "chris.stockton@supabase.io"},
|
|
|
|
// bad format
|
|
{email: "", err: "invalid_email_format"},
|
|
{email: "io", err: "invalid_email_format"},
|
|
{email: "supabase.io", err: "invalid_email_format"},
|
|
{email: "@supabase.io", err: "invalid_email_format"},
|
|
{email: "test@.supabase.io", err: "invalid_email_format"},
|
|
|
|
// invalid: valid mx records, but invalid and often typed
|
|
// (invalidEmailMap)
|
|
{email: "test@email.com", err: "invalid_email_address"},
|
|
{email: "test@gmail.com", err: "invalid_email_address"},
|
|
{email: "test@test.com", err: "invalid_email_dns"},
|
|
|
|
// very common typo
|
|
{email: "test@gamil.com", err: "invalid_email_dns"},
|
|
|
|
// invalid: valid mx records, but invalid and often typed
|
|
// (invalidHostMap)
|
|
{email: "a@example.com", err: "invalid_email_dns"},
|
|
{email: "a@example.net", err: "invalid_email_dns"},
|
|
{email: "a@example.org", err: "invalid_email_dns"},
|
|
|
|
// invalid: no mx records
|
|
{email: "a@test", err: "invalid_email_dns"},
|
|
{email: "test@local", err: "invalid_email_dns"},
|
|
{email: "test@test.local", err: "invalid_email_dns"},
|
|
{email: "test@example", err: "invalid_email_dns"},
|
|
{email: "test@invalid", err: "invalid_email_dns"},
|
|
|
|
// valid but not actually valid and typed a lot
|
|
{email: "a@invalid", err: "invalid_email_dns"},
|
|
{email: "a@a.invalid", err: "invalid_email_dns"},
|
|
{email: "test@invalid", err: "invalid_email_dns"},
|
|
|
|
// various invalid emails
|
|
{email: "test@test.localhost", err: "invalid_email_dns"},
|
|
{email: "test@invalid.example.com", err: "invalid_email_dns"},
|
|
{email: "test@no.such.email.host.supabase.io", err: "invalid_email_dns"},
|
|
|
|
// this low timeout should simulate a dns timeout, which should
|
|
// not be treated as an invalid email.
|
|
{email: "validemail@probablyaaaaaaaanotarealdomain.com",
|
|
timeout: time.Millisecond},
|
|
|
|
// likewise for a valid email
|
|
{email: "support@supabase.io", timeout: time.Millisecond},
|
|
}
|
|
|
|
cfg := conf.MailerConfiguration{
|
|
EmailValidationExtended: true,
|
|
EmailValidationServiceURL: "",
|
|
EmailValidationServiceHeaders: "",
|
|
}
|
|
ev := newEmailValidator(cfg)
|
|
|
|
for idx, tc := range cases {
|
|
func(timeout time.Duration) {
|
|
if timeout == 0 {
|
|
timeout = validateEmailTimeout
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
|
defer cancel()
|
|
|
|
now := time.Now()
|
|
err := ev.Validate(ctx, tc.email)
|
|
dur := time.Since(now)
|
|
if max := timeout + (time.Millisecond * 50); max < dur {
|
|
t.Fatal("timeout was not respected")
|
|
}
|
|
|
|
t.Logf("tc #%v - email %q", idx, tc.email)
|
|
if tc.err != "" {
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), tc.err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
}(tc.timeout)
|
|
}
|
|
}
|