Skip to content
quarry Artifact [c862934c3e]
Login

Artifact [c862934c3e]

Artifact c862934c3ec2f73d1d8966db63e2aefeb758abe342b466eb1f0fd55b53457ccc:


package oltp

import (
	"path/filepath"
	"testing"
	"time"
)

// The ordinary path: a pending address confirms into the account's
// confirmed one, and the token is spent — a second confirm refuses.
func TestPendingEmailConfirms(t *testing.T) {
	db, err := Open(filepath.Join(t.TempDir(), "o.db"))
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	if err := db.AddAccount("dan", "pw", false); err != nil {
		t.Fatal(err)
	}
	token, err := db.SetPendingEmail("dan", "[email protected]")
	if err != nil || token == "" {
		t.Fatalf("SetPendingEmail: %q %v", token, err)
	}
	a, _ := db.AccountByName("dan")
	if a.EmailPending != "[email protected]" || a.Email != "" {
		t.Fatalf("account after set = %+v", a)
	}
	if acct, ok := db.EmailTokenAccount(token); !ok || acct != "dan" {
		t.Fatalf("EmailTokenAccount = %q %v", acct, ok)
	}
	acct, addr, ok, err := db.ConfirmEmail(token)
	if err != nil || !ok || acct != "dan" || addr != "[email protected]" {
		t.Fatalf("ConfirmEmail = %q %q %v %v", acct, addr, ok, err)
	}
	a, _ = db.AccountByName("dan")
	if a.Email != "[email protected]" || a.EmailPending != "" {
		t.Fatalf("account after confirm = %+v", a)
	}
	// Spent: a second confirm of the same token refuses.
	if _, _, ok, err := db.ConfirmEmail(token); ok || err != nil {
		t.Fatalf("a spent token confirmed again: ok=%v err=%v", ok, err)
	}
	if _, ok := db.EmailTokenAccount(token); ok {
		t.Fatal("a spent token still names its account")
	}
}

// An expired token refuses exactly like a spent one — neither
// EmailTokenAccount nor ConfirmEmail treats it as live.
func TestExpiredEmailTokenRefuses(t *testing.T) {
	db, err := Open(filepath.Join(t.TempDir(), "o.db"))
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	if err := db.AddAccount("dan", "pw", false); err != nil {
		t.Fatal(err)
	}
	token, err := db.SetPendingEmail("dan", "[email protected]")
	if err != nil {
		t.Fatal(err)
	}
	// Back-date the token past its TTL directly, the same way
	// beta_test.go's siblings would if invite tokens needed it —
	// there is no public API for minting an already-expired one.
	if _, err := db.db.Exec(`UPDATE email_token SET expires = ? WHERE token = ?`,
		time.Now().UTC().Add(-time.Hour).Format(time.RFC3339), hashToken(token)); err != nil {
		t.Fatal(err)
	}
	if _, ok := db.EmailTokenAccount(token); ok {
		t.Fatal("an expired token still names its account")
	}
	if _, _, ok, err := db.ConfirmEmail(token); ok || err != nil {
		t.Fatalf("an expired token confirmed: ok=%v err=%v", ok, err)
	}
	a, _ := db.AccountByName("dan")
	if a.Email != "" {
		t.Fatalf("an expired confirm still promoted the address: %+v", a)
	}
}

// ClearEmail blanks both halves and closes the race: a token minted
// before the clear must not resurrect the address afterward.
func TestClearEmailBlanksBothAndSpendsLiveTokens(t *testing.T) {
	db, err := Open(filepath.Join(t.TempDir(), "o.db"))
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	if err := db.AddAccount("dan", "pw", false); err != nil {
		t.Fatal(err)
	}
	if err := db.SetEmail("dan", "[email protected]"); err != nil {
		t.Fatal(err)
	}
	token, err := db.SetPendingEmail("dan", "[email protected]")
	if err != nil {
		t.Fatal(err)
	}
	if err := db.ClearEmail("dan"); err != nil {
		t.Fatal(err)
	}
	a, _ := db.AccountByName("dan")
	if a.Email != "" || a.EmailPending != "" {
		t.Fatalf("account after clear = %+v", a)
	}
	if _, _, ok, err := db.ConfirmEmail(token); ok || err != nil {
		t.Fatalf("a token from before the clear still confirmed: ok=%v err=%v", ok, err)
	}
}