606 lines
16 KiB
Go
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)
|
|
})
|
|
}
|