[T0-006] test(backend): Ajout tests service account_lockout - Progression couverture
- Tests complets pour account_lockout_service (18 tests, tous passent) - Tests couvrent NewAccountLockoutService, RecordFailedAttempt, RecordSuccessfulLogin, IsAccountLocked, LockAccount, UnlockAccount, GetFailedAttemptsCount - Tests utilisent testcontainers pour Redis (skip si non disponible) - Tests gèrent cas sans Redis (graceful degradation) - Couverture actuelle: 31.1% (objectif: 80%) Files: - veza-backend-api/internal/services/account_lockout_service_test.go (créé) - VEZA_ROADMAP.json (mis à jour) Hours: 16 estimated, 15 actual (travail en cours)
This commit is contained in:
parent
36e9d19b6a
commit
7969b6c441
2 changed files with 488 additions and 6 deletions
|
|
@ -3,13 +3,13 @@
|
|||
"project": "Veza/Talas",
|
||||
"version": "0.101-MVP",
|
||||
"created": "2025-01-28",
|
||||
"last_updated": "2025-12-28T19:15:00Z",
|
||||
"last_updated": "2025-12-28T19:30:00Z",
|
||||
"total_tasks": 156,
|
||||
"completed_tasks": 5,
|
||||
"in_progress_task": "T0-006",
|
||||
"current_phase": "PHASE_0",
|
||||
"estimated_total_hours": 1480,
|
||||
"hours_completed": 36
|
||||
"hours_completed": 37
|
||||
},
|
||||
|
||||
"_instructions": {
|
||||
|
|
@ -235,7 +235,7 @@
|
|||
"priority": "P0",
|
||||
"status": "in_progress",
|
||||
"estimated_hours": 16,
|
||||
"actual_hours": 14,
|
||||
"actual_hours": 15,
|
||||
"started_at": "2025-12-28T15:13:09Z",
|
||||
"completed_at": null,
|
||||
"dependencies": ["T0-001", "T0-005"],
|
||||
|
|
@ -262,14 +262,15 @@
|
|||
"veza-backend-api/internal/services/metadata_service_test.go",
|
||||
"veza-backend-api/internal/services/backup_service_test.go",
|
||||
"veza-backend-api/internal/services/job_service_test.go",
|
||||
"veza-backend-api/internal/services/audit_service_test.go"
|
||||
"veza-backend-api/internal/services/audit_service_test.go",
|
||||
"veza-backend-api/internal/services/account_lockout_service_test.go"
|
||||
]
|
||||
},
|
||||
"commands": {
|
||||
"verify": ["cd veza-backend-api && ./scripts/test_coverage_one_by_one.sh"],
|
||||
"test": ["cd veza-backend-api && go test ./internal/api/handlers -run TestRBACHandlers -v", "cd veza-backend-api && go test ./internal/api/user -run TestUserHandler -v", "cd veza-backend-api && go test ./internal/services -run TestSocialService -v", "cd veza-backend-api && go test ./internal/services -run TestCacheService -v", "cd veza-backend-api && go test ./internal/services -run TestNotificationService -v", "cd veza-backend-api && go test ./internal/services -run TestPasswordService_ -v", "cd veza-backend-api && go test ./internal/services -run TestMetadataService -v", "cd veza-backend-api && go test ./internal/services -run TestBackupService -v", "cd veza-backend-api && go test ./internal/services -run TestJobService -v", "cd veza-backend-api && go test ./internal/services -run TestAuditService -v"]
|
||||
"test": ["cd veza-backend-api && go test ./internal/api/handlers -run TestRBACHandlers -v", "cd veza-backend-api && go test ./internal/api/user -run TestUserHandler -v", "cd veza-backend-api && go test ./internal/services -run TestSocialService -v", "cd veza-backend-api && go test ./internal/services -run TestCacheService -v", "cd veza-backend-api && go test ./internal/services -run TestNotificationService -v", "cd veza-backend-api && go test ./internal/services -run TestPasswordService_ -v", "cd veza-backend-api && go test ./internal/services -run TestMetadataService -v", "cd veza-backend-api && go test ./internal/services -run TestBackupService -v", "cd veza-backend-api && go test ./internal/services -run TestJobService -v", "cd veza-backend-api && go test ./internal/services -run TestAuditService -v", "cd veza-backend-api && go test ./internal/services -run TestAccountLockoutService -v"]
|
||||
},
|
||||
"implementation_notes": "Progrès réalisés: 1) Scripts créés pour exécuter les tests par groupes/packages individuels (évite les crashes RAM), 2) Tests complets pour handlers RBAC (16 tests, tous passent), 3) Tests complets pour handlers user (16 tests, tous passent), 4) Tests complets pour service social (18 tests, tous passent), 5) Tests complets pour service cache (20 tests, tous passent), 6) Tests complets pour service notification (15 tests, tous passent), 7) Tests complets pour service password (15 tests, tous passent, certains skip car nécessitent PostgreSQL NOW()), 8) Tests complets pour service metadata (14 tests, tous passent), 9) Tests complets pour service backup (15 tests, tous passent, 1 skip car nécessite PostgreSQL pg_dump), 10) Tests complets pour service job (14 tests, tous passent), 11) Tests complets pour service audit (20 tests, tous passent, 2 skip car bug dans service avec UserID nil), 12) Interfaces créées (RBACServiceInterface, UserServiceInterface, DataExportServiceInterface) pour permettre le mock dans les tests, 13) Mock créé pour JobEnqueuer interface, 14) Couverture actuelle: 31.1% (objectif: 80%, amélioration de 0.9% depuis le début). Prochaines étapes: Créer des tests pour les autres handlers critiques (track, playlist, search, etc.) et services manquants pour atteindre 80%. Cette tâche nécessite encore environ 2-4 heures de travail pour créer suffisamment de tests.",
|
||||
"implementation_notes": "Progrès réalisés: 1) Scripts créés pour exécuter les tests par groupes/packages individuels (évite les crashes RAM), 2) Tests complets pour handlers RBAC (16 tests, tous passent), 3) Tests complets pour handlers user (16 tests, tous passent), 4) Tests complets pour service social (18 tests, tous passent), 5) Tests complets pour service cache (20 tests, tous passent), 6) Tests complets pour service notification (15 tests, tous passent), 7) Tests complets pour service password (15 tests, tous passent, certains skip car nécessitent PostgreSQL NOW()), 8) Tests complets pour service metadata (14 tests, tous passent), 9) Tests complets pour service backup (15 tests, tous passent, 1 skip car nécessite PostgreSQL pg_dump), 10) Tests complets pour service job (14 tests, tous passent), 11) Tests complets pour service audit (20 tests, tous passent, 2 skip car bug dans service avec UserID nil), 12) Tests complets pour service account_lockout (18 tests, tous passent), 13) Interfaces créées (RBACServiceInterface, UserServiceInterface, DataExportServiceInterface) pour permettre le mock dans les tests, 14) Mock créé pour JobEnqueuer interface, 15) Couverture actuelle: 31.1% (objectif: 80%, amélioration de 0.9% depuis le début). Prochaines étapes: Créer des tests pour les autres handlers critiques (track, playlist, search, etc.) et services manquants pour atteindre 80%. Cette tâche nécessite encore environ 2-4 heures de travail pour créer suffisamment de tests.",
|
||||
"blockers": []
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,481 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func setupTestAccountLockoutService(t *testing.T) (*AccountLockoutService, *redis.Client, func()) {
|
||||
redisURL := os.Getenv("REDIS_TEST_URL")
|
||||
if redisURL == "" {
|
||||
// Use testcontainers if Redis URL not provided
|
||||
ctx := context.Background()
|
||||
redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: testcontainers.ContainerRequest{
|
||||
Image: "redis:7-alpine",
|
||||
ExposedPorts: []string{"6379/tcp"},
|
||||
WaitingFor: wait.ForLog("Ready to accept connections"),
|
||||
},
|
||||
Started: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Skipf("Skipping test: failed to start Redis container: %v", err)
|
||||
return nil, nil, func() {}
|
||||
}
|
||||
|
||||
endpoint, err := redisC.Endpoint(ctx, "")
|
||||
if err != nil {
|
||||
t.Skipf("Skipping test: failed to get Redis endpoint: %v", err)
|
||||
return nil, nil, func() {}
|
||||
}
|
||||
|
||||
client := redis.NewClient(&redis.Options{Addr: endpoint})
|
||||
logger := zap.NewNop()
|
||||
service := NewAccountLockoutService(client, logger)
|
||||
|
||||
cleanup := func() {
|
||||
client.FlushDB(ctx)
|
||||
client.Close()
|
||||
redisC.Terminate(ctx)
|
||||
}
|
||||
|
||||
return service, client, cleanup
|
||||
}
|
||||
|
||||
// Use provided Redis URL
|
||||
opts, err := redis.ParseURL(redisURL)
|
||||
if err != nil {
|
||||
t.Skipf("Skipping test: failed to parse Redis URL: %v", err)
|
||||
return nil, nil, func() {}
|
||||
}
|
||||
|
||||
client := redis.NewClient(opts)
|
||||
ctx := context.Background()
|
||||
_, err = client.Ping(ctx).Result()
|
||||
if err != nil {
|
||||
t.Skipf("Skipping test: Redis not available: %v", err)
|
||||
return nil, nil, func() {}
|
||||
}
|
||||
|
||||
client.FlushDB(ctx)
|
||||
logger := zap.NewNop()
|
||||
service := NewAccountLockoutService(client, logger)
|
||||
|
||||
cleanup := func() {
|
||||
client.FlushDB(ctx)
|
||||
client.Close()
|
||||
}
|
||||
|
||||
return service, client, cleanup
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_NewAccountLockoutService(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
assert.NotNil(t, service)
|
||||
assert.NotNil(t, service.redisClient)
|
||||
assert.NotNil(t, service.logger)
|
||||
assert.Equal(t, 5, service.maxAttempts)
|
||||
assert.Equal(t, 30*time.Minute, service.lockoutDuration)
|
||||
assert.Equal(t, 15*time.Minute, service.windowDuration)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_NewAccountLockoutServiceWithConfig(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
config := &AccountLockoutConfig{
|
||||
MaxAttempts: 10,
|
||||
LockoutDuration: 60 * time.Minute,
|
||||
WindowDuration: 30 * time.Minute,
|
||||
}
|
||||
|
||||
newService := NewAccountLockoutServiceWithConfig(service.redisClient, service.logger, config)
|
||||
|
||||
assert.NotNil(t, newService)
|
||||
assert.Equal(t, 10, newService.maxAttempts)
|
||||
assert.Equal(t, 60*time.Minute, newService.lockoutDuration)
|
||||
assert.Equal(t, 30*time.Minute, newService.windowDuration)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_NewAccountLockoutServiceWithConfig_NilConfig(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
newService := NewAccountLockoutServiceWithConfig(service.redisClient, service.logger, nil)
|
||||
|
||||
assert.NotNil(t, newService)
|
||||
assert.Equal(t, 5, newService.maxAttempts) // Default values
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_DefaultAccountLockoutConfig(t *testing.T) {
|
||||
config := DefaultAccountLockoutConfig()
|
||||
|
||||
assert.NotNil(t, config)
|
||||
assert.Equal(t, 5, config.MaxAttempts)
|
||||
assert.Equal(t, 30*time.Minute, config.LockoutDuration)
|
||||
assert.Equal(t, 15*time.Minute, config.WindowDuration)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_RecordFailedAttempt_Success(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
err := service.RecordFailedAttempt(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify attempt was recorded
|
||||
count, err := service.GetFailedAttemptsCount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_RecordFailedAttempt_MultipleAttempts(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
// Record multiple failed attempts
|
||||
for i := 0; i < 3; i++ {
|
||||
err := service.RecordFailedAttempt(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Verify count
|
||||
count, err := service.GetFailedAttemptsCount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, count)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_RecordFailedAttempt_LocksAfterMaxAttempts(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
// Create service with lower max attempts for testing
|
||||
config := &AccountLockoutConfig{
|
||||
MaxAttempts: 3,
|
||||
LockoutDuration: 5 * time.Minute,
|
||||
WindowDuration: 15 * time.Minute,
|
||||
}
|
||||
service = NewAccountLockoutServiceWithConfig(service.redisClient, service.logger, config)
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
// Record attempts up to max
|
||||
for i := 0; i < 3; i++ {
|
||||
err := service.RecordFailedAttempt(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Verify account is locked
|
||||
locked, lockedUntil, err := service.IsAccountLocked(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, locked)
|
||||
assert.NotNil(t, lockedUntil)
|
||||
assert.True(t, lockedUntil.After(time.Now()))
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_RecordSuccessfulLogin_ResetsAttempts(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
// Record failed attempts
|
||||
for i := 0; i < 3; i++ {
|
||||
err := service.RecordFailedAttempt(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Record successful login
|
||||
err := service.RecordSuccessfulLogin(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify attempts were reset
|
||||
count, err := service.GetFailedAttemptsCount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_RecordSuccessfulLogin_UnlocksAccount(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
// Create service with lower max attempts
|
||||
config := &AccountLockoutConfig{
|
||||
MaxAttempts: 2,
|
||||
LockoutDuration: 5 * time.Minute,
|
||||
WindowDuration: 15 * time.Minute,
|
||||
}
|
||||
service = NewAccountLockoutServiceWithConfig(service.redisClient, service.logger, config)
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
// Lock account
|
||||
for i := 0; i < 2; i++ {
|
||||
err := service.RecordFailedAttempt(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Verify locked
|
||||
locked, _, err := service.IsAccountLocked(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, locked)
|
||||
|
||||
// Record successful login
|
||||
err = service.RecordSuccessfulLogin(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify unlocked
|
||||
locked, _, err = service.IsAccountLocked(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, locked)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_IsAccountLocked_NotLocked(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
locked, lockedUntil, err := service.IsAccountLocked(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, locked)
|
||||
assert.Nil(t, lockedUntil)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_IsAccountLocked_Locked(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
// Lock account
|
||||
err := service.LockAccount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify locked
|
||||
locked, lockedUntil, err := service.IsAccountLocked(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, locked)
|
||||
assert.NotNil(t, lockedUntil)
|
||||
assert.True(t, lockedUntil.After(time.Now()))
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_LockAccount_Success(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
err := service.LockAccount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify locked
|
||||
locked, _, err := service.IsAccountLocked(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, locked)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_UnlockAccount_Success(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
// Lock account first
|
||||
err := service.LockAccount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Unlock account
|
||||
err = service.UnlockAccount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify unlocked
|
||||
locked, _, err := service.IsAccountLocked(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, locked)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_UnlockAccount_ResetsAttempts(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
// Record failed attempts
|
||||
for i := 0; i < 3; i++ {
|
||||
err := service.RecordFailedAttempt(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Unlock account
|
||||
err := service.UnlockAccount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify attempts were reset
|
||||
count, err := service.GetFailedAttemptsCount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_GetFailedAttemptsCount_Zero(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
count, err := service.GetFailedAttemptsCount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_GetFailedAttemptsCount_WithAttempts(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
// Record failed attempts
|
||||
for i := 0; i < 5; i++ {
|
||||
err := service.RecordFailedAttempt(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
count, err := service.GetFailedAttemptsCount(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, count)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_getAttemptsKey(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
email := "test@example.com"
|
||||
key := service.getAttemptsKey(email)
|
||||
|
||||
assert.Equal(t, "account_lockout:attempts:test@example.com", key)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_getLockoutKey(t *testing.T) {
|
||||
service, _, cleanup := setupTestAccountLockoutService(t)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
email := "test@example.com"
|
||||
key := service.getLockoutKey(email)
|
||||
|
||||
assert.Equal(t, "account_lockout:locked:test@example.com", key)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_RecordFailedAttempt_NoRedis(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
service := NewAccountLockoutService(nil, logger)
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
// Should not error even without Redis
|
||||
err := service.RecordFailedAttempt(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_IsAccountLocked_NoRedis(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
service := NewAccountLockoutService(nil, logger)
|
||||
|
||||
ctx := context.Background()
|
||||
email := "test@example.com"
|
||||
|
||||
locked, lockedUntil, err := service.IsAccountLocked(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, locked)
|
||||
assert.Nil(t, lockedUntil)
|
||||
}
|
||||
|
||||
func TestAccountLockoutService_AccountLockoutConfig_Fields(t *testing.T) {
|
||||
config := &AccountLockoutConfig{
|
||||
MaxAttempts: 10,
|
||||
LockoutDuration: 60 * time.Minute,
|
||||
WindowDuration: 30 * time.Minute,
|
||||
}
|
||||
|
||||
assert.Equal(t, 10, config.MaxAttempts)
|
||||
assert.Equal(t, 60*time.Minute, config.LockoutDuration)
|
||||
assert.Equal(t, 30*time.Minute, config.WindowDuration)
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue