From bc7b8f53f4ced61656f924a67448d037524c15a6 Mon Sep 17 00:00:00 2001 From: Tiago Yamamoto Date: Wed, 24 Dec 2025 14:53:29 -0300 Subject: [PATCH] fix(auth): make login status check case-insensitive --- backend/internal/core/domain/entity/user.go | 6 +- backend/internal/core/usecases/auth/login.go | 6 +- .../internal/core/usecases/auth/login_test.go | 101 ++++++++++++++++++ 3 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 backend/internal/core/usecases/auth/login_test.go diff --git a/backend/internal/core/domain/entity/user.go b/backend/internal/core/domain/entity/user.go index 2d4e871..50c4f3e 100644 --- a/backend/internal/core/domain/entity/user.go +++ b/backend/internal/core/domain/entity/user.go @@ -14,6 +14,10 @@ const ( RoleRecruiter = "recruiter" // RoleCandidate is a job seeker (formerly candidate) RoleCandidate = "candidate" + + // User Status + UserStatusActive = "active" + UserStatusInactive = "inactive" ) // User represents a user within a specific Tenant (Company). @@ -37,7 +41,7 @@ func NewUser(id, tenantID, name, email string) *User { TenantID: tenantID, Name: name, Email: email, - Status: "ACTIVE", + Status: UserStatusActive, Roles: []Role{}, CreatedAt: time.Now(), UpdatedAt: time.Now(), diff --git a/backend/internal/core/usecases/auth/login.go b/backend/internal/core/usecases/auth/login.go index 0dd6512..b62b60c 100644 --- a/backend/internal/core/usecases/auth/login.go +++ b/backend/internal/core/usecases/auth/login.go @@ -4,7 +4,9 @@ import ( "context" "errors" "fmt" + "strings" + "github.com/rede5/gohorsejobs/backend/internal/core/domain/entity" "github.com/rede5/gohorsejobs/backend/internal/core/dto" "github.com/rede5/gohorsejobs/backend/internal/core/ports" ) @@ -46,8 +48,8 @@ func (uc *LoginUseCase) Execute(ctx context.Context, input dto.LoginRequest) (*d fmt.Printf("[LOGIN DEBUG] Password verification PASSED\n") // 3. Check Status - if user.Status != "ACTIVE" { - fmt.Printf("[LOGIN DEBUG] Status check FAILED: Expected ACTIVE, got '%s'\n", user.Status) + if !strings.EqualFold(user.Status, entity.UserStatusActive) { + fmt.Printf("[LOGIN DEBUG] Status check FAILED: Expected %s, got '%s'\n", entity.UserStatusActive, user.Status) return nil, errors.New("account inactive") } fmt.Printf("[LOGIN DEBUG] Status check PASSED\n") diff --git a/backend/internal/core/usecases/auth/login_test.go b/backend/internal/core/usecases/auth/login_test.go new file mode 100644 index 0000000..4db7555 --- /dev/null +++ b/backend/internal/core/usecases/auth/login_test.go @@ -0,0 +1,101 @@ +package auth_test + +import ( + "context" + "strings" + "testing" + + "github.com/rede5/gohorsejobs/backend/internal/core/domain/entity" + "github.com/rede5/gohorsejobs/backend/internal/core/dto" + "github.com/rede5/gohorsejobs/backend/internal/core/usecases/auth" +) + +// Reuse mocks (could be moved to a shared test helper) +// Reposting necessary mocks here for standalone execution if needed, +// though if in same package `auth_test` they might conflict if I'm not careful. +// Since `register_candidate_test.go` is package `auth_test`, I can reuse `MockUserRepo` if it's in the same package. +// But `MockUserRepo` is defined in `register_candidate_test.go`, so it is available to other files in `auth_test` in the same directory? +// Yes, Go tests in the same package and directory share symbols. +// I will assuming `register_candidate_test.go` provides the mocks. + +func TestLoginUseCase_Execute_StatusCheck(t *testing.T) { + // Setup shared mocks + authSvc := &MockAuthService{ + GenerateTokenFunc: func(userID, tenantID string, roles []string) (string, error) { + return "mock-token", nil + }, + } + + tests := []struct { + name string + userStatus string + expectError bool + }{ + { + name: "Active user (lowercase) - Should Success", + userStatus: "active", + expectError: false, + }, + { + name: "Active user (uppercase) - Should Success", + userStatus: "ACTIVE", + expectError: false, + }, + { + name: "Active user (mixed case) - Should Success", + userStatus: "Active", + expectError: false, + }, + { + name: "Inactive user - Should Fail", + userStatus: "inactive", + expectError: true, + }, + { + name: "Banned user - Should Fail", + userStatus: "banned", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + userRepo := &MockUserRepo{ + FindByEmailFunc: func(ctx context.Context, email string) (*entity.User, error) { + return &entity.User{ + ID: "user-123", + Email: email, + PasswordHash: "hashed_pass", + Status: tt.userStatus, + Roles: []entity.Role{}, + }, nil + }, + } + + uc := auth.NewLoginUseCase(userRepo, authSvc) + + input := dto.LoginRequest{ + Email: "test@example.com", + Password: "password", + } + + resp, err := uc.Execute(context.Background(), input) + + if tt.expectError { + if err == nil { + t.Errorf("Expected error for status '%s', got nil", tt.userStatus) + } else if !strings.Contains(err.Error(), "account inactive") { + // The actual error message from login.go is "account inactive" + t.Errorf("Expected 'account inactive' error, got '%v'", err) + } + } else { + if err != nil { + t.Errorf("Expected success for status '%s', got error: %v", tt.userStatus, err) + } + if resp == nil { + t.Error("Expected response, got nil") + } + } + }) + } +}