Files
foundry-core/internal/auth/email_test.go
codex-bot f69575d48f
Foundry Go checks / check (push) Failing after 2m1s
Foundry Go checks / check (pull_request) Failing after 1m50s
Serialize email verification and resend to preserve registration state
2026-11-05 18:53:50 +08:00

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)
}
}