mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-07 00:37:36 +00:00
117 lines
3.3 KiB
Go
117 lines
3.3 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestMagicLinkTokenRoundTrip(t *testing.T) {
|
|
svc := NewMagicLinkServiceWithKey([]byte("0123456789abcdef0123456789abcdef"), nil, nil, nil)
|
|
t.Cleanup(func() { svc.Stop() })
|
|
|
|
token, err := svc.GenerateToken("User@Example.com", "org-123")
|
|
if err != nil {
|
|
t.Fatalf("GenerateToken error: %v", err)
|
|
}
|
|
|
|
got, err := svc.ValidateToken(token)
|
|
if err != nil {
|
|
t.Fatalf("ValidateToken error: %v", err)
|
|
}
|
|
if got.Email != "user@example.com" {
|
|
t.Fatalf("Email = %q, want user@example.com", got.Email)
|
|
}
|
|
if got.OrgID != "org-123" {
|
|
t.Fatalf("OrgID = %q, want org-123", got.OrgID)
|
|
}
|
|
if !got.Used {
|
|
t.Fatalf("Used = false, want true")
|
|
}
|
|
if got.Token != token {
|
|
t.Fatalf("Token mismatch")
|
|
}
|
|
}
|
|
|
|
func TestMagicLinkExpiredTokenRejected(t *testing.T) {
|
|
svc := NewMagicLinkServiceWithKey([]byte("0123456789abcdef0123456789abcdef"), nil, nil, nil)
|
|
t.Cleanup(func() { svc.Stop() })
|
|
|
|
base := time.Date(2026, 2, 10, 12, 0, 0, 0, time.UTC)
|
|
svc.now = func() time.Time { return base }
|
|
|
|
token, err := svc.GenerateToken("user@example.com", "org-123")
|
|
if err != nil {
|
|
t.Fatalf("GenerateToken error: %v", err)
|
|
}
|
|
|
|
svc.now = func() time.Time { return base.Add(16 * time.Minute) }
|
|
_, err = svc.ValidateToken(token)
|
|
if !errors.Is(err, ErrMagicLinkExpired) {
|
|
t.Fatalf("ValidateToken err = %v, want %v", err, ErrMagicLinkExpired)
|
|
}
|
|
}
|
|
|
|
func TestMagicLinkUsedTokenRejected(t *testing.T) {
|
|
svc := NewMagicLinkServiceWithKey([]byte("0123456789abcdef0123456789abcdef"), nil, nil, nil)
|
|
t.Cleanup(func() { svc.Stop() })
|
|
|
|
token, err := svc.GenerateToken("user@example.com", "org-123")
|
|
if err != nil {
|
|
t.Fatalf("GenerateToken error: %v", err)
|
|
}
|
|
|
|
if _, err := svc.ValidateToken(token); err != nil {
|
|
t.Fatalf("first ValidateToken error: %v", err)
|
|
}
|
|
_, err = svc.ValidateToken(token)
|
|
if !errors.Is(err, ErrMagicLinkUsed) {
|
|
t.Fatalf("second ValidateToken err = %v, want %v", err, ErrMagicLinkUsed)
|
|
}
|
|
}
|
|
|
|
func TestMagicLinkInvalidTokenRejected(t *testing.T) {
|
|
svc := NewMagicLinkServiceWithKey([]byte("0123456789abcdef0123456789abcdef"), nil, nil, nil)
|
|
t.Cleanup(func() { svc.Stop() })
|
|
|
|
token, err := svc.GenerateToken("user@example.com", "org-123")
|
|
if err != nil {
|
|
t.Fatalf("GenerateToken error: %v", err)
|
|
}
|
|
|
|
bad := token + "x"
|
|
|
|
_, err = svc.ValidateToken(bad)
|
|
if !errors.Is(err, ErrMagicLinkInvalidToken) {
|
|
t.Fatalf("ValidateToken err = %v, want %v", err, ErrMagicLinkInvalidToken)
|
|
}
|
|
}
|
|
|
|
func TestMagicLinkRateLimiting(t *testing.T) {
|
|
limiter := NewRateLimiter(3, 1*time.Hour)
|
|
svc := NewMagicLinkServiceWithKey([]byte("0123456789abcdef0123456789abcdef"), nil, nil, limiter)
|
|
t.Cleanup(func() { svc.Stop() })
|
|
|
|
email := "user@example.com"
|
|
if !svc.AllowRequest(email) {
|
|
t.Fatalf("1st request unexpectedly blocked")
|
|
}
|
|
if !svc.AllowRequest(email) {
|
|
t.Fatalf("2nd request unexpectedly blocked")
|
|
}
|
|
if !svc.AllowRequest(email) {
|
|
t.Fatalf("3rd request unexpectedly blocked")
|
|
}
|
|
if svc.AllowRequest(email) {
|
|
t.Fatalf("4th request allowed, expected rate limited")
|
|
}
|
|
}
|
|
|
|
func TestMagicLinkGenerateTokenRejectsInvalidOrgID(t *testing.T) {
|
|
svc := NewMagicLinkServiceWithKey([]byte("0123456789abcdef0123456789abcdef"), nil, nil, nil)
|
|
t.Cleanup(func() { svc.Stop() })
|
|
|
|
if _, err := svc.GenerateToken("user@example.com", "../org"); err == nil {
|
|
t.Fatalf("expected invalid orgID to be rejected")
|
|
}
|
|
}
|