initial commit
This commit is contained in:
605
internal/repository/postgres/auth/user_test.go
Normal file
605
internal/repository/postgres/auth/user_test.go
Normal file
@@ -0,0 +1,605 @@
|
||||
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)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user