73 lines
2.3 KiB
Go
73 lines
2.3 KiB
Go
package auth
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"database/sql"
|
|
"encoding/hex"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestConcurrentRegistrationAndVerification(t *testing.T) {
|
|
dsn := os.Getenv("PLATFORM_TEST_DATABASE_URL")
|
|
if dsn == "" {
|
|
t.Skip("requires isolated integration database")
|
|
}
|
|
db, err := sql.Open("pgx", dsn)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
var raw [12]byte
|
|
rand.Read(raw[:])
|
|
project := "email-" + hex.EncodeToString(raw[:])
|
|
frontend, _ := url.Parse("https://app.example.com/auth/callback")
|
|
s := &service{db: db, projects: map[string]*url.URL{project: frontend}}
|
|
defer func() {
|
|
_, _ = db.Exec("DELETE FROM project_accounts WHERE project=$1", project)
|
|
_, _ = db.Exec("DELETE FROM email_registrations WHERE project=$1", project)
|
|
_, _ = db.Exec("DELETE FROM email_outbox WHERE project=$1", project)
|
|
}()
|
|
request := httptest.NewRequest("POST", "https://api.example.com/register", nil)
|
|
out := httptest.NewRecorder()
|
|
if err = s.registerEmail(out, request, project, "fixture@example.invalid", "long-fixture-password"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var body string
|
|
if err = db.QueryRow("SELECT body FROM email_outbox WHERE project=$1", project).Scan(&body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
match := regexp.MustCompile(`verify_email=([A-Za-z0-9_-]{43})`).FindStringSubmatch(body)
|
|
if len(match) != 2 {
|
|
t.Fatal("missing verification link")
|
|
}
|
|
done := make(chan error, 2)
|
|
go func() {
|
|
r := httptest.NewRequest("POST", "https://api.example.com/register", nil)
|
|
done <- s.registerEmail(httptest.NewRecorder(), r, project, "fixture@example.invalid", "another-fixture-password")
|
|
}()
|
|
go func() {
|
|
r := httptest.NewRequest("POST", "https://api.example.com/verify-email", strings.NewReader(`{"token":"`+match[1]+`"}`))
|
|
done <- s.verifyEmail(httptest.NewRecorder(), r, project)
|
|
}()
|
|
for range 2 {
|
|
if err := <-done; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
var accounts, pending int
|
|
if err = db.QueryRow("SELECT count(*) FROM email_credentials WHERE project=$1", project).Scan(&accounts); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.QueryRow("SELECT count(*) FROM email_registrations WHERE project=$1", project).Scan(&pending); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if accounts+pending != 1 {
|
|
t.Fatalf("concurrent registration left accounts=%d pending=%d", accounts, pending)
|
|
}
|
|
}
|