cart and checkout
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
package customerauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPurposeTokenRoundTrip(t *testing.T) {
|
||||
s := NewSigner([]byte("test-secret"))
|
||||
tok := s.IssuePurpose(purposePasswordReset, "user@example.com", time.Hour)
|
||||
sub, err := s.ParsePurpose(purposePasswordReset, tok)
|
||||
if err != nil {
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
if sub != "user@example.com" {
|
||||
t.Fatalf("got subject %q, want user@example.com", sub)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPurposeTokenWrongPurposeRejected(t *testing.T) {
|
||||
s := NewSigner([]byte("test-secret"))
|
||||
tok := s.IssuePurpose(purposeVerifyEmail, "user@example.com", time.Hour)
|
||||
// A verify token must not be accepted as a reset token.
|
||||
if _, err := s.ParsePurpose(purposePasswordReset, tok); err != ErrInvalidToken {
|
||||
t.Fatalf("got %v, want ErrInvalidToken", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPurposeTokenExpiredAndTampered(t *testing.T) {
|
||||
s := NewSigner([]byte("test-secret"))
|
||||
expired := s.IssuePurpose(purposeVerifyEmail, "user@example.com", -time.Second)
|
||||
if _, err := s.ParsePurpose(purposeVerifyEmail, expired); err != ErrExpiredToken {
|
||||
t.Fatalf("expired: got %v, want ErrExpiredToken", err)
|
||||
}
|
||||
tok := s.IssuePurpose(purposeVerifyEmail, "user@example.com", time.Hour)
|
||||
if _, err := s.ParsePurpose(purposeVerifyEmail, tok+"x"); err != ErrInvalidToken {
|
||||
t.Fatalf("tampered: got %v, want ErrInvalidToken", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginLimiterLocksOutAndResets(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
l := NewLoginLimiter(3, time.Minute)
|
||||
const key = "user@example.com"
|
||||
for i := range 3 {
|
||||
if ok, _ := l.Allowed(ctx, key); !ok {
|
||||
t.Fatalf("locked out early after %d failures", i)
|
||||
}
|
||||
l.RecordFailure(ctx, key)
|
||||
}
|
||||
ok, retry := l.Allowed(ctx, key)
|
||||
if ok {
|
||||
t.Fatal("expected lockout after 3 failures")
|
||||
}
|
||||
if retry <= 0 {
|
||||
t.Fatalf("expected positive retry-after, got %v", retry)
|
||||
}
|
||||
// A successful auth clears the history.
|
||||
l.Reset(ctx, key)
|
||||
if ok, _ := l.Allowed(ctx, key); !ok {
|
||||
t.Fatal("expected reset to clear lockout")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginLimiterNilIsNoop(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
var l *LoginLimiter
|
||||
if ok, _ := l.Allowed(ctx, "x"); !ok {
|
||||
t.Fatal("nil limiter should allow")
|
||||
}
|
||||
l.RecordFailure(ctx, "x") // must not panic
|
||||
l.Reset(ctx, "x") // must not panic
|
||||
}
|
||||
|
||||
func TestStoreMarkVerifiedAndUpdateHash(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
path := filepath.Join(t.TempDir(), "credentials.json")
|
||||
store, err := LoadCredentialStore(path)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
if err := store.Register(ctx, "u@example.com", 1, "hash1", "ts"); err != nil {
|
||||
t.Fatalf("register: %v", err)
|
||||
}
|
||||
|
||||
// Unknown email is a no-op, not an error.
|
||||
if found, err := store.MarkVerified(ctx, "missing@example.com", "now"); err != nil || found {
|
||||
t.Fatalf("MarkVerified(missing) = (%v, %v), want (false, nil)", found, err)
|
||||
}
|
||||
|
||||
if found, err := store.MarkVerified(ctx, "U@EXAMPLE.COM", "2026-01-01T00:00:00Z"); err != nil || !found {
|
||||
t.Fatalf("MarkVerified = (%v, %v), want (true, nil)", found, err)
|
||||
}
|
||||
if found, err := store.UpdateHash(ctx, "u@example.com", "hash2"); err != nil || !found {
|
||||
t.Fatalf("UpdateHash = (%v, %v), want (true, nil)", found, err)
|
||||
}
|
||||
|
||||
// Both changes survive a reload from disk.
|
||||
reloaded, err := LoadCredentialStore(path)
|
||||
if err != nil {
|
||||
t.Fatalf("reload: %v", err)
|
||||
}
|
||||
rec, ok := reloaded.Get(ctx, "u@example.com")
|
||||
if !ok {
|
||||
t.Fatal("record missing after reload")
|
||||
}
|
||||
if rec.Hash != "hash2" {
|
||||
t.Fatalf("hash = %q, want hash2", rec.Hash)
|
||||
}
|
||||
if rec.VerifiedAt == "" {
|
||||
t.Fatal("verifiedAt not persisted")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user