package oltp
import (
"path/filepath"
"testing"
)
func resetDB(t *testing.T) *DB {
t.Helper()
db, err := Open(filepath.Join(t.TempDir(), "o.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { db.Close() })
return db
}
// The whole point: somebody who has forgotten their password chooses a
// new one themselves, and no operator ever holds it.
func TestAResetLetsSomebodyChooseTheirOwnPassword(t *testing.T) {
db := resetDB(t)
if err := db.AddAccount("jo", "the-old-password", false); err != nil {
t.Fatal(err)
}
token, err := db.ResetPassword("jo")
if err != nil {
t.Fatalf("FAIL: could not mint a reset: %v", err)
}
if name, ok := db.InviteFor(token); !ok || name != "jo" {
t.Fatalf("FAIL: the token names %q ok=%v", name, ok)
}
if k := db.TokenKind(token); k != "reset" {
t.Errorf("FAIL: the token reads as %q, so the page says the wrong thing", k)
} else {
t.Log("ok: the token knows it is a reset")
}
// The old password still works until the link is spent: a reset
// that locked the account would make a lost link worse than the
// problem it was sent to solve.
if !db.VerifyPassword("jo", "the-old-password") {
t.Error("FAIL: minting a reset locked the account out")
} else {
t.Log("ok: the old password holds until the link is used")
}
if _, err := db.RedeemInvite(token, "a-brand-new-password"); err != nil {
t.Fatalf("FAIL: redeeming: %v", err)
}
if !db.VerifyPassword("jo", "a-brand-new-password") {
t.Error("FAIL: the new password does not work")
} else {
t.Log("ok: the new password works")
}
if db.VerifyPassword("jo", "the-old-password") {
t.Error("FAIL: the old password still works after the reset")
} else {
t.Log("ok: the old password is gone")
}
// Spent, and spent once.
if _, ok := db.InviteFor(token); ok {
t.Error("FAIL: the link can be used twice")
} else {
t.Log("ok: the link is one-time")
}
}
// Minting again supersedes, so "send it again" cannot leave two live
// credentials behind where the operator believes there is one.
func TestASecondResetKillsTheFirst(t *testing.T) {
db := resetDB(t)
if err := db.AddAccount("jo", "the-old-password", false); err != nil {
t.Fatal(err)
}
first, err := db.ResetPassword("jo")
if err != nil {
t.Fatal(err)
}
second, err := db.ResetPassword("jo")
if err != nil {
t.Fatal(err)
}
if _, ok := db.InviteFor(first); ok {
t.Error("FAIL: the superseded link is still live")
} else {
t.Log("ok: the older link is dead")
}
if _, ok := db.InviteFor(second); !ok {
t.Error("FAIL: the new link is not live")
} else {
t.Log("ok: the newer link works")
}
}
// An account that has never been claimed wants the invite it is already
// waiting on, not a reset of a password it does not have.
func TestAPendingAccountIsToldToUseItsInvite(t *testing.T) {
db := resetDB(t)
if _, err := db.InviteAccount("newbie", "", false); err != nil {
t.Fatal(err)
}
if _, err := db.ResetPassword("newbie"); err == nil {
t.Error("FAIL: a pending account was given a reset")
} else {
t.Logf("ok: refused — %v", err)
}
if _, err := db.ResetPassword("nobody"); err == nil {
t.Error("FAIL: an account that does not exist was given a reset")
} else {
t.Logf("ok: refused — %v", err)
}
}