Files
base/internal/repository/postgres/auth/user_test.go
2026-04-10 18:25:21 +03:30

606 lines
16 KiB
Go

package auth
import (
"context"
"strconv"
"testing"
"time"
"base/internal/pkg/oauth"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
domainAuth "base/internal/domain/auth"
)
func TestUserRepository_Create(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRepository(db)
ctx := context.Background()
t.Run("create user successfully", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "John",
LastName: "Doe",
Email: "john.doe@example.com",
EmailVerified: false,
Status: domainAuth.UserStatusActive,
PhoneNumber: "1234567890",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
assert.NoError(t, err)
assert.NotEqual(t, uuid.Nil, user.ID)
// Verify user was created
found, err := repo.FindByID(ctx, user.ID)
assert.NoError(t, err)
assert.Equal(t, user.Email, found.Email)
assert.Equal(t, user.FirstName, found.FirstName)
assert.Equal(t, user.LastName, found.LastName)
})
t.Run("create user with duplicate email fails", func(t *testing.T) {
email := "duplicate@example.com"
user1 := &domainAuth.User{
ID: uuid.New(),
FirstName: "User",
LastName: "One",
Email: email,
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user1)
assert.NoError(t, err)
user2 := &domainAuth.User{
ID: uuid.New(),
FirstName: "User",
LastName: "Two",
Email: email,
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = repo.Create(ctx, user2)
assert.Error(t, err)
})
}
func TestUserRepository_UpsertWithAccount(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRepository(db)
ctx := context.Background()
t.Run("upsert creates new user and account", func(t *testing.T) {
email := "newuser@example.com"
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "New",
LastName: "User",
Email: email,
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
account := &domainAuth.Account{
ID: uuid.New(),
Provider: oauth.Google,
Scope: []string{"read"},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
isNew, err := repo.UpsertWithAccount(ctx, email, user, account)
assert.NoError(t, err)
assert.True(t, isNew)
// For new users, UserID is set by UpsertWithAccount
assert.Equal(t, user.ID, account.UserID)
// Verify user was created
foundUser, err := repo.FindByID(ctx, user.ID)
assert.NoError(t, err)
assert.Equal(t, user.Email, foundUser.Email)
// Note: For new users, UpsertWithAccount sets account.UserID but doesn't create the account
// The account needs to be created separately if needed
})
t.Run("upsert updates existing user with new account", func(t *testing.T) {
email := "existing@example.com"
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Existing",
LastName: "User",
Email: email,
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
// Create user first
err := repo.Create(ctx, user)
require.NoError(t, err)
// Get the user from DB to ensure we have the correct ID
foundUser, err := repo.FindByEmail(ctx, email)
require.NoError(t, err)
user.ID = foundUser.ID
// Create first account with Google provider
account1 := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Google,
Scope: []string{"read"},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
accountRepo := createTestAccountRepository(db)
err = accountRepo.Create(ctx, account1)
require.NoError(t, err)
// Upsert with different provider (GitHub) - should create new account
account2 := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID, // Set UserID before upsert
Provider: oauth.GitHub,
Scope: []string{"read", "write"},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
// Use the same user object but ensure it has the correct ID
userForUpsert := &domainAuth.User{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email,
Status: user.Status,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}
isNew, err := repo.UpsertWithAccount(ctx, email, userForUpsert, account2)
assert.NoError(t, err)
assert.False(t, isNew)
// Note: account.UserID is not updated by UpsertWithAccount when user exists,
// but it should already be set correctly
assert.Equal(t, user.ID, account2.UserID)
// Account ID should be set after creation
assert.NotEqual(t, uuid.Nil, account2.ID)
// Verify the GitHub account was created by finding it by ID
foundAccount2, err := accountRepo.FindByID(ctx, account2.ID)
assert.NoError(t, err)
assert.NotNil(t, foundAccount2)
assert.Equal(t, account2.UserID, foundAccount2.UserID)
assert.Equal(t, account2.Provider, foundAccount2.Provider)
assert.Equal(t, account2.Scope, foundAccount2.Scope)
// Verify both accounts exist
accounts, err := accountRepo.FindByUserID(ctx, user.ID)
assert.NoError(t, err)
assert.GreaterOrEqual(t, len(accounts), 2) // At least Google and GitHub accounts
})
}
func TestUserRepository_FindByID(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRepository(db)
ctx := context.Background()
t.Run("find existing user by id", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Find",
LastName: "User",
Email: "find@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
found, err := repo.FindByID(ctx, user.ID)
assert.NoError(t, err)
assert.Equal(t, user.ID, found.ID)
assert.Equal(t, user.Email, found.Email)
})
t.Run("find user with roles", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Role",
LastName: "User",
Email: "role@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
// Create role
roleRepo := createTestRoleRepository(db)
role := &domainAuth.Role{
ID: uuid.New(),
Name: "admin",
Description: "Administrator",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role)
require.NoError(t, err)
// Assign role to user
userRoleRepo := createTestUserRoleRepository(db)
err = userRoleRepo.Create(ctx, user.ID, role.ID)
require.NoError(t, err)
// Find user with roles
found, err := repo.FindByID(ctx, user.ID, domainAuth.WithRoles())
assert.NoError(t, err)
assert.Equal(t, user.ID, found.ID)
assert.Len(t, found.Roles, 1)
assert.Equal(t, role.Name, found.Roles[0].Name)
})
t.Run("find non-existent user", func(t *testing.T) {
nonExistentID := uuid.New()
found, err := repo.FindByID(ctx, nonExistentID)
assert.Error(t, err)
assert.Nil(t, found)
})
}
func TestUserRepository_FindByEmail(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRepository(db)
ctx := context.Background()
t.Run("find existing user by email", func(t *testing.T) {
email := "email@example.com"
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Email",
LastName: "User",
Email: email,
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
found, err := repo.FindByEmail(ctx, email)
assert.NoError(t, err)
assert.Equal(t, user.ID, found.ID)
assert.Equal(t, email, found.Email)
})
t.Run("find user with accounts", func(t *testing.T) {
email := "accounts@example.com"
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Accounts",
LastName: "User",
Email: email,
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
// Create account
accountRepo := createTestAccountRepository(db)
account := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Google,
Scope: []string{"read"},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = accountRepo.Create(ctx, account)
require.NoError(t, err)
// Find user with accounts
found, err := repo.FindByEmail(ctx, email, domainAuth.WithAccounts())
assert.NoError(t, err)
assert.Equal(t, user.ID, found.ID)
assert.Len(t, found.Accounts, 1)
assert.Equal(t, account.Provider, found.Accounts[0].Provider)
})
t.Run("find non-existent user by email", func(t *testing.T) {
found, err := repo.FindByEmail(ctx, "nonexistent@example.com")
assert.Error(t, err)
assert.Nil(t, found)
})
}
func TestUserRepository_Update(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRepository(db)
ctx := context.Background()
t.Run("update user successfully", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Update",
LastName: "User",
Email: "update@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
// Update user
user.FirstName = "Updated"
user.EmailVerified = true
user.Status = domainAuth.UserStatusInactive
err = repo.Update(ctx, user)
assert.NoError(t, err)
// Verify update
found, err := repo.FindByID(ctx, user.ID)
assert.NoError(t, err)
assert.Equal(t, "Updated", found.FirstName)
assert.True(t, found.EmailVerified)
assert.Equal(t, domainAuth.UserStatusInactive, found.Status)
})
}
func TestUserRepository_Delete(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRepository(db)
ctx := context.Background()
t.Run("delete user successfully", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Delete",
LastName: "User",
Email: "delete@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
err = repo.Delete(ctx, user.ID)
assert.NoError(t, err)
// Verify deletion (soft delete)
found, err := repo.FindByID(ctx, user.ID)
assert.Error(t, err)
assert.Nil(t, found)
})
}
func TestUserRepository_List(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRepository(db)
ctx := context.Background()
// Create multiple users
for i := 0; i < 5; i++ {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "User",
LastName: "Test",
Email: "user" + strconv.Itoa(i) + "@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
}
t.Run("list users with limit and offset", func(t *testing.T) {
users, err := repo.List(ctx, 3, 0)
assert.NoError(t, err)
assert.Len(t, users, 3)
users, err = repo.List(ctx, 3, 3)
assert.NoError(t, err)
assert.Len(t, users, 2) // Remaining 2 users
})
t.Run("list users with relations", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Relation",
LastName: "User",
Email: "relation@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
// Create role and assign
roleRepo := createTestRoleRepository(db)
role := &domainAuth.Role{
ID: uuid.New(),
Name: "user",
Description: "Regular user",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role)
require.NoError(t, err)
userRoleRepo := createTestUserRoleRepository(db)
err = userRoleRepo.Create(ctx, user.ID, role.ID)
require.NoError(t, err)
users, err := repo.List(ctx, 10, 0, domainAuth.WithRoles())
assert.NoError(t, err)
assert.Greater(t, len(users), 0)
// Find our user in the list
var foundUser *domainAuth.User
for _, u := range users {
if u.ID == user.ID {
foundUser = u
break
}
}
require.NotNil(t, foundUser)
assert.Len(t, foundUser.Roles, 1)
})
}
func TestUserRepository_Count(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRepository(db)
ctx := context.Background()
t.Run("count users", func(t *testing.T) {
initialCount, err := repo.Count(ctx)
assert.NoError(t, err)
assert.Equal(t, int64(0), initialCount)
// Create users
for i := 0; i < 3; i++ {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Count",
LastName: "User",
Email: "count" + strconv.Itoa(i) + "@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
}
count, err := repo.Count(ctx)
assert.NoError(t, err)
assert.Equal(t, int64(3), count)
})
}
func TestUserRepository_UserRoles(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRepository(db)
ctx := context.Background()
t.Run("get user roles", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Roles",
LastName: "User",
Email: "roles@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
roleRepo := createTestRoleRepository(db)
role1 := &domainAuth.Role{
ID: uuid.New(),
Name: "admin",
Description: "Admin role",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role1)
require.NoError(t, err)
role2 := &domainAuth.Role{
ID: uuid.New(),
Name: "user",
Description: "User role",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role2)
require.NoError(t, err)
userRoleRepo := createTestUserRoleRepository(db)
err = userRoleRepo.Create(ctx, user.ID, role1.ID)
require.NoError(t, err)
err = userRoleRepo.Create(ctx, user.ID, role2.ID)
require.NoError(t, err)
roles, err := repo.UserRoles(ctx, user.ID)
assert.NoError(t, err)
assert.Len(t, roles, 2)
})
}
func TestUserRepository_UserAccounts(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRepository(db)
ctx := context.Background()
t.Run("get user accounts", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Accounts",
LastName: "User",
Email: "accounts@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, user)
require.NoError(t, err)
accountRepo := createTestAccountRepository(db)
account1 := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Google,
Scope: []string{"read"},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = accountRepo.Create(ctx, account1)
require.NoError(t, err)
account2 := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.GitHub,
Scope: []string{"read", "write"},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = accountRepo.Create(ctx, account2)
require.NoError(t, err)
accounts, err := repo.UserAccounts(ctx, user.ID)
assert.NoError(t, err)
assert.Len(t, accounts, 2)
})
}