initial commit

This commit is contained in:
m.zare
2026-04-10 18:25:21 +03:30
commit 77ca6c34a3
263 changed files with 34470 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
package auth
import (
"context"
"go.uber.org/fx"
"github.com/google/uuid"
"gorm.io/gorm"
domainAuth "base/internal/domain/auth"
)
type accountRepository struct {
db *gorm.DB
}
func NewAccountRepository(lc fx.Lifecycle, db *gorm.DB) domainAuth.AccountRepository {
lc.Append(
fx.Hook{
OnStart: func(ctx context.Context) error {
return nil
},
OnStop: func(ctx context.Context) error {
return nil
},
})
return &accountRepository{db: db}
}
func (r *accountRepository) Create(ctx context.Context, account *domainAuth.Account) error {
model := toAccountModel(account)
if err := r.db.WithContext(ctx).Create(model).Error; err != nil {
return err
}
copyAccountFromModel(account, model)
return nil
}
func (r *accountRepository) FindByID(ctx context.Context, id uuid.UUID) (*domainAuth.Account, error) {
var model AccountModel
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&model).Error; err != nil {
return nil, err
}
return toAccountDomain(&model), nil
}
func (r *accountRepository) FindByUserID(ctx context.Context, userID uuid.UUID) ([]*domainAuth.Account, error) {
var models []AccountModel
if err := r.db.WithContext(ctx).Where("user_id = ?", userID).Find(&models).Error; err != nil {
return nil, err
}
accounts := make([]*domainAuth.Account, len(models))
for i, model := range models {
accounts[i] = toAccountDomain(&model)
}
return accounts, nil
}
func (r *accountRepository) Update(ctx context.Context, account *domainAuth.Account) error {
model := toAccountModel(account)
return r.db.WithContext(ctx).Model(&AccountModel{}).Where("id = ?", account.ID).Updates(model).Error
}
func (r *accountRepository) Delete(ctx context.Context, id uuid.UUID) error {
return r.db.WithContext(ctx).Delete(&AccountModel{}, "id = ?", id).Error
}
func (r *accountRepository) List(ctx context.Context, limit, offset int) ([]*domainAuth.Account, error) {
var models []AccountModel
if err := r.db.WithContext(ctx).Limit(limit).Offset(offset).Find(&models).Error; err != nil {
return nil, err
}
accounts := make([]*domainAuth.Account, len(models))
for i, model := range models {
accounts[i] = toAccountDomain(&model)
}
return accounts, nil
}
func (r *accountRepository) Count(ctx context.Context) (int64, error) {
var count int64
if err := r.db.WithContext(ctx).Model(&AccountModel{}).Count(&count).Error; err != nil {
return 0, err
}
return count, nil
}

View File

@@ -0,0 +1,381 @@
package auth
import (
"context"
"encoding/json"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
domainAuth "base/internal/domain/auth"
"base/internal/pkg/oauth"
)
func TestAccountRepository_Create(t *testing.T) {
db := setupTestDB(t)
repo := createTestAccountRepository(db)
userRepo := createTestUserRepository(db)
ctx := context.Background()
t.Run("create account successfully", func(t *testing.T) {
// Create user first
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Account",
LastName: "User",
Email: "account@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := userRepo.Create(ctx, user)
require.NoError(t, err)
account := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Google,
Password: nil,
Scope: []string{"read", "write"},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = repo.Create(ctx, account)
assert.NoError(t, err)
assert.NotEqual(t, uuid.Nil, account.ID)
// Verify account was created
found, err := repo.FindByID(ctx, account.ID)
assert.NoError(t, err)
assert.Equal(t, account.UserID, found.UserID)
assert.Equal(t, account.Provider, found.Provider)
assert.Equal(t, account.Scope, found.Scope)
})
t.Run("create account with password", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Password",
LastName: "User",
Email: "password@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := userRepo.Create(ctx, user)
require.NoError(t, err)
password := "hashedpassword"
account := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Credentials,
Password: &password,
Scope: []string{},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = repo.Create(ctx, account)
assert.NoError(t, err)
found, err := repo.FindByID(ctx, account.ID)
assert.NoError(t, err)
assert.NotNil(t, found.Password)
assert.Equal(t, password, *found.Password)
})
t.Run("create account with meta", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Meta",
LastName: "User",
Email: "meta@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := userRepo.Create(ctx, user)
require.NoError(t, err)
metaJSON := json.RawMessage(`{"key": "value", "number": 123}`)
account := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Google,
Meta: metaJSON,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = repo.Create(ctx, account)
assert.NoError(t, err)
found, err := repo.FindByID(ctx, account.ID)
assert.NoError(t, err)
assert.NotNil(t, found.Meta)
})
}
func TestAccountRepository_FindByID(t *testing.T) {
db := setupTestDB(t)
repo := createTestAccountRepository(db)
userRepo := createTestUserRepository(db)
ctx := context.Background()
t.Run("find existing account 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 := userRepo.Create(ctx, user)
require.NoError(t, err)
account := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Google,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = repo.Create(ctx, account)
require.NoError(t, err)
found, err := repo.FindByID(ctx, account.ID)
assert.NoError(t, err)
assert.Equal(t, account.ID, found.ID)
assert.Equal(t, account.UserID, found.UserID)
})
t.Run("find non-existent account", func(t *testing.T) {
nonExistentID := uuid.New()
found, err := repo.FindByID(ctx, nonExistentID)
assert.Error(t, err)
assert.Nil(t, found)
})
}
func TestAccountRepository_FindByUserID(t *testing.T) {
db := setupTestDB(t)
repo := createTestAccountRepository(db)
userRepo := createTestUserRepository(db)
ctx := context.Background()
t.Run("find accounts by user id", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Multi",
LastName: "Account",
Email: "multi@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := userRepo.Create(ctx, user)
require.NoError(t, err)
// Create multiple accounts
account1 := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Google,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = repo.Create(ctx, account1)
require.NoError(t, err)
account2 := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.GitHub,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = repo.Create(ctx, account2)
require.NoError(t, err)
accounts, err := repo.FindByUserID(ctx, user.ID)
assert.NoError(t, err)
assert.Len(t, accounts, 2)
})
}
func TestAccountRepository_Update(t *testing.T) {
db := setupTestDB(t)
repo := createTestAccountRepository(db)
userRepo := createTestUserRepository(db)
ctx := context.Background()
t.Run("update account 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 := userRepo.Create(ctx, user)
require.NoError(t, err)
account := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Google,
Scope: []string{"read"},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = repo.Create(ctx, account)
require.NoError(t, err)
// Update account
account.Scope = []string{"read", "write", "admin"}
newToken := "newtoken"
account.AccessToken = &newToken
err = repo.Update(ctx, account)
assert.NoError(t, err)
// Verify update
found, err := repo.FindByID(ctx, account.ID)
assert.NoError(t, err)
assert.Equal(t, []string{"read", "write", "admin"}, found.Scope)
assert.NotNil(t, found.AccessToken)
assert.Equal(t, newToken, *found.AccessToken)
})
}
func TestAccountRepository_Delete(t *testing.T) {
db := setupTestDB(t)
repo := createTestAccountRepository(db)
userRepo := createTestUserRepository(db)
ctx := context.Background()
t.Run("delete account 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 := userRepo.Create(ctx, user)
require.NoError(t, err)
account := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Google,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = repo.Create(ctx, account)
require.NoError(t, err)
err = repo.Delete(ctx, account.ID)
assert.NoError(t, err)
// Verify deletion
found, err := repo.FindByID(ctx, account.ID)
assert.Error(t, err)
assert.Nil(t, found)
})
}
func TestAccountRepository_List(t *testing.T) {
db := setupTestDB(t)
repo := createTestAccountRepository(db)
userRepo := createTestUserRepository(db)
ctx := context.Background()
// Create user and multiple accounts
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "List",
LastName: "User",
Email: "list@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := userRepo.Create(ctx, user)
require.NoError(t, err)
for i := 0; i < 5; i++ {
account := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Provider(i % 4), // Cycle through providers
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, account)
require.NoError(t, err)
}
t.Run("list accounts with limit and offset", func(t *testing.T) {
accounts, err := repo.List(ctx, 3, 0)
assert.NoError(t, err)
assert.Len(t, accounts, 3)
accounts, err = repo.List(ctx, 3, 3)
assert.NoError(t, err)
assert.Len(t, accounts, 2) // Remaining 2 accounts
})
}
func TestAccountRepository_Count(t *testing.T) {
db := setupTestDB(t)
repo := createTestAccountRepository(db)
userRepo := createTestUserRepository(db)
ctx := context.Background()
t.Run("count accounts", func(t *testing.T) {
initialCount, err := repo.Count(ctx)
assert.NoError(t, err)
assert.Equal(t, int64(0), initialCount)
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Count",
LastName: "User",
Email: "count@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = userRepo.Create(ctx, user)
require.NoError(t, err)
// Create accounts
for i := 0; i < 3; i++ {
account := &domainAuth.Account{
ID: uuid.New(),
UserID: user.ID,
Provider: oauth.Google,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, account)
require.NoError(t, err)
}
count, err := repo.Count(ctx)
assert.NoError(t, err)
assert.Equal(t, int64(3), count)
})
}

View File

@@ -0,0 +1,184 @@
package auth
import (
"encoding/json"
"time"
"gorm.io/gorm"
domainAuth "base/internal/domain/auth"
"base/internal/pkg/oauth"
)
func toUserModel(user *domainAuth.User) *UserModel {
// Note: DisplayName exists in schema but not in domain model
// Compute it from FirstName + LastName
displayName := user.FirstName + " " + user.LastName
return &UserModel{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
DisplayName: displayName,
PhoneNumber: user.PhoneNumber,
Email: user.Email,
EmailVerified: user.EmailVerified,
Status: int(user.Status),
InvitationCode: user.InvitationCode,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
DeletedAt: gorm.DeletedAt{Time: user.DeletedAt, Valid: !user.DeletedAt.IsZero()},
}
}
func toUserDomain(model *UserModel) *domainAuth.User {
var deletedAt time.Time
if model.DeletedAt.Valid {
deletedAt = model.DeletedAt.Time
}
return &domainAuth.User{
ID: model.ID,
FirstName: model.FirstName,
LastName: model.LastName,
PhoneNumber: model.PhoneNumber,
Email: model.Email,
EmailVerified: model.EmailVerified,
Status: domainAuth.UserStatus(model.Status),
InvitationCode: model.InvitationCode,
CreatedAt: model.CreatedAt,
UpdatedAt: model.UpdatedAt,
DeletedAt: deletedAt,
}
}
func copyUserFromModel(user *domainAuth.User, model *UserModel) {
user.ID = model.ID
user.PhoneNumber = model.PhoneNumber
user.EmailVerified = model.EmailVerified
user.Status = domainAuth.UserStatus(model.Status)
user.InvitationCode = model.InvitationCode
user.CreatedAt = model.CreatedAt
user.UpdatedAt = model.UpdatedAt
if model.DeletedAt.Valid {
user.DeletedAt = model.DeletedAt.Time
}
}
func toRoleModel(role *domainAuth.Role) *RoleModel {
desc := &role.Description
if role.Description == "" {
desc = nil
}
return &RoleModel{
ID: role.ID,
Name: role.Name,
Description: desc,
CreatedAt: role.CreatedAt,
UpdatedAt: role.UpdatedAt,
}
}
func toRoleDomain(model *RoleModel) *domainAuth.Role {
desc := ""
if model.Description != nil {
desc = *model.Description
}
return &domainAuth.Role{
ID: model.ID,
Name: model.Name,
Description: desc,
CreatedAt: model.CreatedAt,
UpdatedAt: model.UpdatedAt,
}
}
func copyRoleFromModel(role *domainAuth.Role, model *RoleModel) error {
role.ID = model.ID
role.CreatedAt = model.CreatedAt
role.UpdatedAt = model.UpdatedAt
return nil
}
func toAccountModel(account *domainAuth.Account) *AccountModel {
var scopeStr *string
if len(account.Scope) > 0 {
scopeBytes, _ := json.Marshal(account.Scope)
s := string(scopeBytes)
scopeStr = &s
}
// Store provider in Meta JSONB field
metaMap := make(map[string]interface{})
if len(account.Meta) > 0 {
_ = json.Unmarshal(account.Meta, &metaMap)
}
metaMap["provider"] = int(account.Provider)
metaBytes, _ := json.Marshal(metaMap)
meta := json.RawMessage(metaBytes)
return &AccountModel{
ID: account.ID,
UserID: account.UserID,
Provider: int(account.Provider), // Store provider as column for querying
Password: account.Password,
AccessToken: account.AccessToken,
RefreshToken: account.RefreshToken,
Scope: scopeStr,
Meta: &meta,
CreatedAt: account.CreatedAt,
UpdatedAt: account.UpdatedAt,
}
}
func toAccountDomain(model *AccountModel) *domainAuth.Account {
var scope []string
if model.Scope != nil {
_ = json.Unmarshal([]byte(*model.Scope), &scope)
}
var meta json.RawMessage
var provider int
// Use Provider field if available (for querying), otherwise extract from Meta
if model.Provider > 0 {
provider = model.Provider
}
if model.Meta != nil {
meta = *model.Meta
// If provider not set from field, try to extract from Meta
if provider == 0 {
var metaMap map[string]interface{}
if err := json.Unmarshal(meta, &metaMap); err == nil {
if p, ok := metaMap["provider"].(float64); ok {
provider = int(p)
}
}
}
}
// Import oauth package for Provider type
// Provider is stored as int, convert to oauth.Provider
var accountProvider oauth.Provider
if provider > 0 {
accountProvider = oauth.Provider(provider)
}
return &domainAuth.Account{
ID: model.ID,
UserID: model.UserID,
Provider: accountProvider,
Password: model.Password,
AccessToken: model.AccessToken,
RefreshToken: model.RefreshToken,
Scope: scope,
Meta: meta,
CreatedAt: model.CreatedAt,
UpdatedAt: model.UpdatedAt,
}
}
func copyAccountFromModel(account *domainAuth.Account, model *AccountModel) {
account.ID = model.ID
account.CreatedAt = model.CreatedAt
account.UpdatedAt = model.UpdatedAt
}

View File

@@ -0,0 +1,81 @@
package auth
import (
"context"
"go.uber.org/fx"
"github.com/google/uuid"
"gorm.io/gorm"
domainAuth "base/internal/domain/auth"
)
type roleRepository struct {
db *gorm.DB
}
func NewRoleRepository(lc fx.Lifecycle, db *gorm.DB) domainAuth.RoleRepository {
lc.Append(
fx.Hook{
OnStart: func(ctx context.Context) error {
return db.AutoMigrate(&domainAuth.Role{})
},
OnStop: func(ctx context.Context) error {
return nil
},
})
return &roleRepository{db: db}
}
func (r *roleRepository) Create(ctx context.Context, role *domainAuth.Role) error {
model := toRoleModel(role)
if err := r.db.WithContext(ctx).Create(model).Error; err != nil {
return err
}
return copyRoleFromModel(role, model)
}
func (r *roleRepository) FindByID(ctx context.Context, id uuid.UUID) (*domainAuth.Role, error) {
var model RoleModel
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&model).Error; err != nil {
return nil, err
}
return toRoleDomain(&model), nil
}
func (r *roleRepository) FindByName(ctx context.Context, name string) (*domainAuth.Role, error) {
var model RoleModel
if err := r.db.WithContext(ctx).Where("name = ?", name).First(&model).Error; err != nil {
return nil, err
}
return toRoleDomain(&model), nil
}
func (r *roleRepository) Update(ctx context.Context, role *domainAuth.Role) error {
model := toRoleModel(role)
return r.db.WithContext(ctx).Model(&RoleModel{}).Where("id = ?", role.ID).Updates(model).Error
}
func (r *roleRepository) Delete(ctx context.Context, id uuid.UUID) error {
return r.db.WithContext(ctx).Delete(&RoleModel{}, "id = ?", id).Error
}
func (r *roleRepository) List(ctx context.Context, limit, offset int) ([]*domainAuth.Role, error) {
var models []RoleModel
if err := r.db.WithContext(ctx).Limit(limit).Offset(offset).Find(&models).Error; err != nil {
return nil, err
}
roles := make([]*domainAuth.Role, len(models))
for i, model := range models {
roles[i] = toRoleDomain(&model)
}
return roles, nil
}
func (r *roleRepository) Count(ctx context.Context) (int64, error) {
var count int64
if err := r.db.WithContext(ctx).Model(&RoleModel{}).Count(&count).Error; err != nil {
return 0, err
}
return count, nil
}

View File

@@ -0,0 +1,235 @@
package auth
import (
"context"
"strconv"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
domainAuth "base/internal/domain/auth"
)
func TestRoleRepository_Create(t *testing.T) {
db := setupTestDB(t)
repo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("create role successfully", func(t *testing.T) {
role := &domainAuth.Role{
ID: uuid.New(),
Name: "admin",
Description: "Administrator role",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, role)
assert.NoError(t, err)
assert.NotEqual(t, uuid.Nil, role.ID)
// Verify role was created
found, err := repo.FindByID(ctx, role.ID)
assert.NoError(t, err)
assert.Equal(t, role.Name, found.Name)
assert.Equal(t, role.Description, found.Description)
})
t.Run("create role with duplicate name fails", func(t *testing.T) {
name := "duplicate"
role1 := &domainAuth.Role{
ID: uuid.New(),
Name: name,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, role1)
assert.NoError(t, err)
role2 := &domainAuth.Role{
ID: uuid.New(),
Name: name,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = repo.Create(ctx, role2)
assert.Error(t, err)
})
}
func TestRoleRepository_FindByID(t *testing.T) {
db := setupTestDB(t)
repo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("find existing role by id", func(t *testing.T) {
role := &domainAuth.Role{
ID: uuid.New(),
Name: "find",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, role)
require.NoError(t, err)
found, err := repo.FindByID(ctx, role.ID)
assert.NoError(t, err)
assert.Equal(t, role.ID, found.ID)
assert.Equal(t, role.Name, found.Name)
})
t.Run("find non-existent role", func(t *testing.T) {
nonExistentID := uuid.New()
found, err := repo.FindByID(ctx, nonExistentID)
assert.Error(t, err)
assert.Nil(t, found)
})
}
func TestRoleRepository_FindByName(t *testing.T) {
db := setupTestDB(t)
repo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("find existing role by name", func(t *testing.T) {
name := "findbyname"
role := &domainAuth.Role{
ID: uuid.New(),
Name: name,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, role)
require.NoError(t, err)
found, err := repo.FindByName(ctx, name)
assert.NoError(t, err)
assert.Equal(t, role.ID, found.ID)
assert.Equal(t, name, found.Name)
})
t.Run("find non-existent role by name", func(t *testing.T) {
found, err := repo.FindByName(ctx, "nonexistent")
assert.Error(t, err)
assert.Nil(t, found)
})
}
func TestRoleRepository_Update(t *testing.T) {
db := setupTestDB(t)
repo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("update role successfully", func(t *testing.T) {
role := &domainAuth.Role{
ID: uuid.New(),
Name: "update",
Description: "Original description",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, role)
require.NoError(t, err)
// Update role
role.Description = "Updated description"
err = repo.Update(ctx, role)
assert.NoError(t, err)
// Verify update
found, err := repo.FindByID(ctx, role.ID)
assert.NoError(t, err)
assert.Equal(t, "Updated description", found.Description)
})
}
func TestRoleRepository_Delete(t *testing.T) {
db := setupTestDB(t)
repo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("delete role successfully", func(t *testing.T) {
role := &domainAuth.Role{
ID: uuid.New(),
Name: "delete",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, role)
require.NoError(t, err)
err = repo.Delete(ctx, role.ID)
assert.NoError(t, err)
// Verify deletion (soft delete)
found, err := repo.FindByID(ctx, role.ID)
assert.Error(t, err)
assert.Nil(t, found)
})
}
func TestRoleRepository_List(t *testing.T) {
db := setupTestDB(t)
repo := createTestRoleRepository(db)
ctx := context.Background()
// Create multiple roles
for i := 0; i < 5; i++ {
role := &domainAuth.Role{
ID: uuid.New(),
Name: "role" + strconv.Itoa(i),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, role)
require.NoError(t, err)
}
t.Run("list roles with limit and offset", func(t *testing.T) {
roles, err := repo.List(ctx, 3, 0)
assert.NoError(t, err)
assert.Len(t, roles, 3)
roles, err = repo.List(ctx, 3, 3)
assert.NoError(t, err)
assert.Len(t, roles, 2) // Remaining 2 roles
})
}
func TestRoleRepository_Count(t *testing.T) {
db := setupTestDB(t)
repo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("count roles", func(t *testing.T) {
initialCount, err := repo.Count(ctx)
assert.NoError(t, err)
assert.Equal(t, int64(0), initialCount)
// Create roles
for i := 0; i < 3; i++ {
role := &domainAuth.Role{
ID: uuid.New(),
Name: "count" + strconv.Itoa(i),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := repo.Create(ctx, role)
require.NoError(t, err)
}
count, err := repo.Count(ctx)
assert.NoError(t, err)
assert.Equal(t, int64(3), count)
})
}

View File

@@ -0,0 +1,70 @@
package auth
import (
"encoding/json"
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type UserModel struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
FirstName string `gorm:"column:first_name;type:text;not null"`
LastName string `gorm:"column:last_name;type:text;not null"`
DisplayName string `gorm:"column:display_name;type:text;not null"`
PhoneNumber string `gorm:"column:phone_number;type:text"`
Email string `gorm:"column:email;type:text;not null;uniqueIndex:users_email_unique"`
EmailVerified bool `gorm:"column:email_verified;type:boolean;default:false;not null"`
Status int `gorm:"column:status;type:integer;default:0;not null"`
InvitationCode string `gorm:"column:invitation_code;type:text"`
CreatedAt time.Time `gorm:"column:created_at;type:timestamptz;not null"`
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamptz;not null"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamptz;index"`
}
func (UserModel) TableName() string {
return "users"
}
type RoleModel struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
Name string `gorm:"column:name;type:text;not null;uniqueIndex:roles_name_unique"`
Description *string `gorm:"column:description;type:text"`
CreatedAt time.Time `gorm:"column:created_at;type:timestamptz;not null"`
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamptz;not null"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamptz;index"`
}
func (RoleModel) TableName() string {
return "roles"
}
type AccountModel struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
UserID uuid.UUID `gorm:"column:user_id;type:uuid;not null;index:accounts_user_id_idx"`
Provider int `gorm:"column:provider;type:integer;index:accounts_provider_idx"` // For querying, also stored in meta
Password *string `gorm:"column:password;type:text"`
AccessToken *string `gorm:"column:access_token;type:text"`
RefreshToken *string `gorm:"column:refresh_token;type:text"`
Scope *string `gorm:"column:scope;type:text"`
Meta *json.RawMessage `gorm:"column:meta;type:jsonb"`
CreatedAt time.Time `gorm:"column:created_at;type:timestamptz;not null"`
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamptz;not null"`
}
func (AccountModel) TableName() string {
return "accounts"
}
type UserRoleModel struct {
UserID uuid.UUID `gorm:"column:user_id;type:uuid;not null;index:user_roles_user_id_idx"`
RoleID uuid.UUID `gorm:"column:role_id;type:uuid;not null;index:user_roles_role_id_idx"`
CreatedAt time.Time `gorm:"column:created_at;type:timestamptz;not null"`
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamptz;not null"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamptz;index"`
}
func (UserRoleModel) TableName() string {
return "user_roles"
}

View File

@@ -0,0 +1,108 @@
package auth
import (
"testing"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
domainAuth "base/internal/domain/auth"
)
// setupTestDB creates an in-memory SQLite database for testing
func setupTestDB(t *testing.T) *gorm.DB {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
require.NoError(t, err)
// Create tables manually with SQLite-compatible syntax
// This avoids PostgreSQL-specific syntax like gen_random_uuid() and timestamptz
createUsersTable := `
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
display_name TEXT NOT NULL,
phone_number TEXT,
email TEXT NOT NULL,
email_verified INTEGER NOT NULL DEFAULT 0,
status INTEGER NOT NULL DEFAULT 0,
invitation_code TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
deleted_at DATETIME,
UNIQUE(email)
)
`
require.NoError(t, db.Exec(createUsersTable).Error)
createRolesTable := `
CREATE TABLE IF NOT EXISTS roles (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
deleted_at DATETIME,
UNIQUE(name)
)
`
require.NoError(t, db.Exec(createRolesTable).Error)
createAccountsTable := `
CREATE TABLE IF NOT EXISTS accounts (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
provider INTEGER,
password TEXT,
access_token TEXT,
refresh_token TEXT,
scope TEXT,
meta TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
)
`
require.NoError(t, db.Exec(createAccountsTable).Error)
require.NoError(t, db.Exec("CREATE INDEX IF NOT EXISTS accounts_user_id_idx ON accounts(user_id)").Error)
require.NoError(t, db.Exec("CREATE INDEX IF NOT EXISTS accounts_provider_idx ON accounts(provider)").Error)
createUserRolesTable := `
CREATE TABLE IF NOT EXISTS user_roles (
user_id TEXT NOT NULL,
role_id TEXT NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
deleted_at DATETIME,
PRIMARY KEY (user_id, role_id)
)
`
require.NoError(t, db.Exec(createUserRolesTable).Error)
require.NoError(t, db.Exec("CREATE INDEX IF NOT EXISTS user_roles_user_id_idx ON user_roles(user_id)").Error)
require.NoError(t, db.Exec("CREATE INDEX IF NOT EXISTS user_roles_role_id_idx ON user_roles(role_id)").Error)
return db
}
// createTestUserRepository creates a user repository for testing
func createTestUserRepository(db *gorm.DB) domainAuth.UserRepository {
return &userRepository{db: db}
}
// createTestRoleRepository creates a role repository for testing
func createTestRoleRepository(db *gorm.DB) domainAuth.RoleRepository {
return &roleRepository{db: db}
}
// createTestAccountRepository creates an account repository for testing
func createTestAccountRepository(db *gorm.DB) domainAuth.AccountRepository {
return &accountRepository{db: db}
}
// createTestUserRoleRepository creates a user role repository for testing
func createTestUserRoleRepository(db *gorm.DB) domainAuth.UserRoleRepository {
return &userRoleRepository{db: db}
}

View File

@@ -0,0 +1,430 @@
package auth
import (
"context"
"errors"
"github.com/google/uuid"
"go.uber.org/fx"
"gorm.io/gorm"
domainAuth "base/internal/domain/auth"
)
type userRepository struct {
db *gorm.DB
}
func NewUserRepository(lc fx.Lifecycle, db *gorm.DB) domainAuth.UserRepository {
lc.Append(
fx.Hook{
OnStart: func(ctx context.Context) error {
return nil
},
OnStop: func(ctx context.Context) error {
return nil
},
})
return &userRepository{db: db}
}
func (r *userRepository) Create(ctx context.Context, user *domainAuth.User) error {
model := toUserModel(user)
if err := r.db.WithContext(ctx).Create(model).Error; err != nil {
return err
}
copyUserFromModel(user, model)
return nil
}
func (r *userRepository) CreateWithAccount(ctx context.Context, user *domainAuth.User, account *domainAuth.Account) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// Create user within transaction
userModel := toUserModel(user)
if err := tx.WithContext(ctx).Create(userModel).Error; err != nil {
return err
}
copyUserFromModel(user, userModel)
// Create account within transaction
accountModel := toAccountModel(account)
if err := tx.WithContext(ctx).Create(accountModel).Error; err != nil {
return err
}
copyAccountFromModel(account, accountModel)
return nil
})
}
func (r *userRepository) UpsertWithAccount(ctx context.Context, email string, user *domainAuth.User, account *domainAuth.Account) (bool, error) {
isNewUser := false
err := r.db.WithContext(ctx).Transaction(
func(tx *gorm.DB) error {
// Check if user exists by email
var existingUserModel UserModel
err := tx.WithContext(ctx).Where("email = ?", email).First(&existingUserModel).Error
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
isNewUser = true
userModel := toUserModel(user)
if err = tx.WithContext(ctx).Create(userModel).Error; err != nil {
return err
}
copyUserFromModel(user, userModel)
account.UserID = user.ID
// Create account for new user
accountModel := toAccountModel(account)
if err = tx.WithContext(ctx).Create(accountModel).Error; err != nil {
return err
}
copyAccountFromModel(account, accountModel)
}
// TODO: check no error if user exist because in find user accounts we use user.ID
if !isNewUser {
// Load all accounts for this user to check if one with this provider exists
var existingAccountModel AccountModel
findAccountsErr := tx.WithContext(ctx).
Where("user_id = ? AND provider = ?", user.ID, int(account.Provider)).
First(&existingAccountModel).Error
if findAccountsErr != nil {
if !errors.Is(findAccountsErr, gorm.ErrRecordNotFound) {
return findAccountsErr
}
accountModel := toAccountModel(account)
if err = tx.WithContext(ctx).Create(accountModel).Error; err != nil {
return err
}
copyAccountFromModel(account, accountModel)
return nil
}
accountModel := toAccountModel(account)
updateAccountErr := tx.WithContext(ctx).
Model(&AccountModel{}).
Where("id = ?", existingAccountModel.ID).
Updates(accountModel).Error
if updateAccountErr != nil {
return updateAccountErr
}
copyAccountFromModel(account, accountModel)
}
return nil
})
return isNewUser, err
}
func (r *userRepository) FindByID(ctx context.Context, id uuid.UUID, opts ...domainAuth.UserQueryOption) (*domainAuth.User, error) {
// Parse query options
options := &domainAuth.UserQueryOptions{}
for _, opt := range opts {
opt(options)
}
var model UserModel
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&model).Error; err != nil {
return nil, err
}
user := toUserDomain(&model)
// Conditionally load relations based on options
if options.LoadRoles {
roles, err := r.loadUserRoles(ctx, id)
if err != nil {
return nil, err
}
user.Roles = roles
}
if options.LoadAccounts {
accounts, err := r.loadUserAccounts(ctx, id)
if err != nil {
return nil, err
}
user.Accounts = accounts
}
return user, nil
}
func (r *userRepository) FindByEmail(ctx context.Context, email string, opts ...domainAuth.UserQueryOption) (*domainAuth.User, error) {
// Parse query options
options := &domainAuth.UserQueryOptions{}
for _, opt := range opts {
opt(options)
}
var model UserModel
if err := r.db.WithContext(ctx).Where("email = ?", email).First(&model).Error; err != nil {
return nil, err
}
user := toUserDomain(&model)
// Conditionally load relations based on options
if options.LoadRoles {
roles, err := r.loadUserRoles(ctx, user.ID)
if err != nil {
return nil, err
}
user.Roles = roles
} else {
user.Roles = []domainAuth.Role{}
}
if options.LoadAccounts {
accounts, err := r.loadUserAccounts(ctx, user.ID)
if err != nil {
return nil, err
}
user.Accounts = accounts
} else {
user.Accounts = []domainAuth.Account{}
}
return user, nil
}
func (r *userRepository) Update(ctx context.Context, user *domainAuth.User) error {
model := toUserModel(user)
return r.db.WithContext(ctx).Model(&UserModel{}).Where("id = ?", user.ID).Updates(model).Error
}
func (r *userRepository) Delete(ctx context.Context, id uuid.UUID) error {
return r.db.WithContext(ctx).Delete(&UserModel{}, "id = ?", id).Error
}
func (r *userRepository) List(ctx context.Context, limit, offset int, opts ...domainAuth.UserQueryOption) ([]*domainAuth.User, error) {
// Parse query options
options := &domainAuth.UserQueryOptions{}
for _, opt := range opts {
opt(options)
}
var models []UserModel
if err := r.db.WithContext(ctx).Limit(limit).Offset(offset).Find(&models).Error; err != nil {
return nil, err
}
if len(models) == 0 {
return []*domainAuth.User{}, nil
}
users := make([]*domainAuth.User, len(models))
userIDs := make([]uuid.UUID, len(models))
for i, model := range models {
users[i] = toUserDomain(&model)
userIDs[i] = users[i].ID
}
// Batch load relations if requested
if options.LoadRoles {
rolesMap, err := r.loadUsersRoles(ctx, userIDs)
if err != nil {
return nil, err
}
for _, user := range users {
if roles, ok := rolesMap[user.ID]; ok {
user.Roles = roles
} else {
user.Roles = []domainAuth.Role{}
}
}
} else {
for _, user := range users {
user.Roles = []domainAuth.Role{}
}
}
if options.LoadAccounts {
accountsMap, err := r.loadUsersAccounts(ctx, userIDs)
if err != nil {
return nil, err
}
for _, user := range users {
if accounts, ok := accountsMap[user.ID]; ok {
user.Accounts = accounts
} else {
user.Accounts = []domainAuth.Account{}
}
}
} else {
for _, user := range users {
user.Accounts = []domainAuth.Account{}
}
}
return users, nil
}
func (r *userRepository) Count(ctx context.Context) (int64, error) {
var count int64
if err := r.db.WithContext(ctx).Model(&UserModel{}).Count(&count).Error; err != nil {
return 0, err
}
return count, nil
}
// loadUserRoles loads roles for a single user
func (r *userRepository) loadUserRoles(ctx context.Context, userID uuid.UUID) ([]domainAuth.Role, error) {
var roleModels []RoleModel
if err := r.db.WithContext(ctx).
Table("roles").
Joins("INNER JOIN user_roles ON roles.id = user_roles.role_id").
Where("user_roles.user_id = ? AND user_roles.deleted_at IS NULL AND roles.deleted_at IS NULL", userID).
Find(&roleModels).Error; err != nil {
return nil, err
}
roles := make([]domainAuth.Role, len(roleModels))
for i, model := range roleModels {
role := toRoleDomain(&model)
roles[i] = *role
}
return roles, nil
}
func (r *userRepository) UserRoles(ctx context.Context, userID uuid.UUID) ([]domainAuth.Role, error) {
var roleModels []RoleModel
if err := r.db.WithContext(ctx).
Table("roles").
Joins("INNER JOIN user_roles ON roles.id = user_roles.role_id").
Where("user_roles.user_id = ? AND user_roles.deleted_at IS NULL AND roles.deleted_at IS NULL", userID).
Find(&roleModels).Error; err != nil {
return nil, err
}
roles := make([]domainAuth.Role, len(roleModels))
for i, model := range roleModels {
role := toRoleDomain(&model)
roles[i] = *role
}
return roles, nil
}
func (r *userRepository) loadUserAccounts(ctx context.Context, userID uuid.UUID) ([]domainAuth.Account, error) {
var accountModels []AccountModel
if err := r.db.WithContext(ctx).
Where("user_id = ?", userID).
Find(&accountModels).Error; err != nil {
return nil, err
}
accounts := make([]domainAuth.Account, len(accountModels))
for i, model := range accountModels {
account := toAccountDomain(&model)
accounts[i] = *account
}
return accounts, nil
}
func (r *userRepository) UserAccounts(ctx context.Context, userID uuid.UUID) ([]domainAuth.Account, error) {
var accountModels []AccountModel
if err := r.db.WithContext(ctx).
Where("user_id = ?", userID).
Find(&accountModels).Error; err != nil {
return nil, err
}
accounts := make([]domainAuth.Account, len(accountModels))
for i, model := range accountModels {
account := toAccountDomain(&model)
accounts[i] = *account
}
return accounts, nil
}
func (r *userRepository) loadUsersRoles(ctx context.Context, userIDs []uuid.UUID) (map[uuid.UUID][]domainAuth.Role, error) {
if len(userIDs) == 0 {
return make(map[uuid.UUID][]domainAuth.Role), nil
}
var userRoles []struct {
UserID uuid.UUID `gorm:"column:user_id"`
RoleID uuid.UUID `gorm:"column:role_id"`
}
if err := r.db.WithContext(ctx).
Table("user_roles").
Select("user_id, role_id").
Where("user_id IN ? AND deleted_at IS NULL", userIDs).
Find(&userRoles).Error; err != nil {
return nil, err
}
if len(userRoles) == 0 {
return make(map[uuid.UUID][]domainAuth.Role), nil
}
roleIDs := make([]uuid.UUID, 0, len(userRoles))
for _, ur := range userRoles {
roleIDs = append(roleIDs, ur.RoleID)
}
var roleModels []RoleModel
if err := r.db.WithContext(ctx).
Where("id IN ? AND deleted_at IS NULL", roleIDs).
Find(&roleModels).Error; err != nil {
return nil, err
}
// Create a map of role_id -> role
rolesByID := make(map[uuid.UUID]*domainAuth.Role)
for i := range roleModels {
role := toRoleDomain(&roleModels[i])
rolesByID[role.ID] = role
}
// Group roles by user_id
rolesMap := make(map[uuid.UUID][]domainAuth.Role)
for _, ur := range userRoles {
if role, ok := rolesByID[ur.RoleID]; ok {
rolesMap[ur.UserID] = append(rolesMap[ur.UserID], *role)
}
}
return rolesMap, nil
}
func (r *userRepository) loadUsersAccounts(ctx context.Context, userIDs []uuid.UUID) (map[uuid.UUID][]domainAuth.Account, error) {
if len(userIDs) == 0 {
return make(map[uuid.UUID][]domainAuth.Account), nil
}
var accountModels []AccountModel
if err := r.db.WithContext(ctx).
Where("user_id IN ?", userIDs).
Find(&accountModels).Error; err != nil {
return nil, err
}
accountsMap := make(map[uuid.UUID][]domainAuth.Account)
for _, model := range accountModels {
account := toAccountDomain(&model)
accountsMap[model.UserID] = append(accountsMap[model.UserID], *account)
}
return accountsMap, nil
}

View File

@@ -0,0 +1,96 @@
package auth
import (
"context"
"github.com/google/uuid"
"go.uber.org/fx"
"gorm.io/gorm"
domainAuth "base/internal/domain/auth"
)
type userRoleRepository struct {
db *gorm.DB
}
func NewUserRoleRepository(lc fx.Lifecycle, db *gorm.DB) domainAuth.UserRoleRepository {
lc.Append(
fx.Hook{
OnStart: func(ctx context.Context) error {
return db.AutoMigrate(UserRoleModel{})
},
OnStop: func(ctx context.Context) error {
return nil
},
})
return &userRoleRepository{db: db}
}
func (r *userRoleRepository) Create(ctx context.Context, userID, roleID uuid.UUID) error {
model := &UserRoleModel{
UserID: userID,
RoleID: roleID,
}
return r.db.WithContext(ctx).Create(model).Error
}
func (r *userRoleRepository) FindByUserID(ctx context.Context, userID uuid.UUID) ([]*domainAuth.Role, error) {
var roleModels []RoleModel
if err := r.db.WithContext(ctx).
Table("roles").
Joins("INNER JOIN user_roles ON roles.id = user_roles.role_id").
Where("user_roles.user_id = ? AND user_roles.deleted_at IS NULL", userID).
Find(&roleModels).Error; err != nil {
return nil, err
}
roles := make([]*domainAuth.Role, len(roleModels))
for i, model := range roleModels {
roles[i] = toRoleDomain(&model)
}
return roles, nil
}
func (r *userRoleRepository) FindByRoleID(ctx context.Context, roleID uuid.UUID) ([]*domainAuth.User, error) {
var userModels []UserModel
if err := r.db.WithContext(ctx).
Table("users").
Joins("INNER JOIN user_roles ON users.id = user_roles.user_id").
Where("user_roles.role_id = ? AND user_roles.deleted_at IS NULL", roleID).
Find(&userModels).Error; err != nil {
return nil, err
}
users := make([]*domainAuth.User, len(userModels))
for i, model := range userModels {
users[i] = toUserDomain(&model)
}
return users, nil
}
func (r *userRoleRepository) Delete(ctx context.Context, userID, roleID uuid.UUID) error {
return r.db.WithContext(ctx).
Where("user_id = ? AND role_id = ?", userID, roleID).
Delete(&UserRoleModel{}).Error
}
func (r *userRoleRepository) DeleteByUserID(ctx context.Context, userID uuid.UUID) error {
return r.db.WithContext(ctx).
Where("user_id = ?", userID).
Delete(&UserRoleModel{}).Error
}
func (r *userRoleRepository) DeleteByRoleID(ctx context.Context, roleID uuid.UUID) error {
return r.db.WithContext(ctx).
Where("role_id = ?", roleID).
Delete(&UserRoleModel{}).Error
}
func (r *userRoleRepository) Exists(ctx context.Context, userID, roleID uuid.UUID) (bool, error) {
var count int64
if err := r.db.WithContext(ctx).
Model(&UserRoleModel{}).
Where("user_id = ? AND role_id = ?", userID, roleID).
Count(&count).Error; err != nil {
return false, err
}
return count > 0, nil
}

View File

@@ -0,0 +1,369 @@
package auth
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
domainAuth "base/internal/domain/auth"
)
func TestUserRoleRepository_Create(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRoleRepository(db)
userRepo := createTestUserRepository(db)
roleRepo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("create user role successfully", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "User",
LastName: "Role",
Email: "userrole@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := userRepo.Create(ctx, user)
require.NoError(t, err)
role := &domainAuth.Role{
ID: uuid.New(),
Name: "test",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role)
require.NoError(t, err)
err = repo.Create(ctx, user.ID, role.ID)
assert.NoError(t, err)
// Verify user role was created
exists, err := repo.Exists(ctx, user.ID, role.ID)
assert.NoError(t, err)
assert.True(t, exists)
})
}
func TestUserRoleRepository_FindByUserID(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRoleRepository(db)
userRepo := createTestUserRepository(db)
roleRepo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("find roles by user 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 := userRepo.Create(ctx, user)
require.NoError(t, err)
role1 := &domainAuth.Role{
ID: uuid.New(),
Name: "role1",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role1)
require.NoError(t, err)
role2 := &domainAuth.Role{
ID: uuid.New(),
Name: "role2",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role2)
require.NoError(t, err)
err = repo.Create(ctx, user.ID, role1.ID)
require.NoError(t, err)
err = repo.Create(ctx, user.ID, role2.ID)
require.NoError(t, err)
roles, err := repo.FindByUserID(ctx, user.ID)
assert.NoError(t, err)
assert.Len(t, roles, 2)
})
}
func TestUserRoleRepository_FindByRoleID(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRoleRepository(db)
userRepo := createTestUserRepository(db)
roleRepo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("find users by role id", func(t *testing.T) {
role := &domainAuth.Role{
ID: uuid.New(),
Name: "shared",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := roleRepo.Create(ctx, role)
require.NoError(t, err)
user1 := &domainAuth.User{
ID: uuid.New(),
FirstName: "User",
LastName: "One",
Email: "user1@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = userRepo.Create(ctx, user1)
require.NoError(t, err)
user2 := &domainAuth.User{
ID: uuid.New(),
FirstName: "User",
LastName: "Two",
Email: "user2@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = userRepo.Create(ctx, user2)
require.NoError(t, err)
err = repo.Create(ctx, user1.ID, role.ID)
require.NoError(t, err)
err = repo.Create(ctx, user2.ID, role.ID)
require.NoError(t, err)
users, err := repo.FindByRoleID(ctx, role.ID)
assert.NoError(t, err)
assert.Len(t, users, 2)
})
}
func TestUserRoleRepository_Delete(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRoleRepository(db)
userRepo := createTestUserRepository(db)
roleRepo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("delete user role 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 := userRepo.Create(ctx, user)
require.NoError(t, err)
role := &domainAuth.Role{
ID: uuid.New(),
Name: "delete",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role)
require.NoError(t, err)
err = repo.Create(ctx, user.ID, role.ID)
require.NoError(t, err)
err = repo.Delete(ctx, user.ID, role.ID)
assert.NoError(t, err)
// Verify deletion
exists, err := repo.Exists(ctx, user.ID, role.ID)
assert.NoError(t, err)
assert.False(t, exists)
})
}
func TestUserRoleRepository_DeleteByUserID(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRoleRepository(db)
userRepo := createTestUserRepository(db)
roleRepo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("delete all roles for user", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Delete",
LastName: "All",
Email: "deleteall@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := userRepo.Create(ctx, user)
require.NoError(t, err)
role1 := &domainAuth.Role{
ID: uuid.New(),
Name: "role1",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role1)
require.NoError(t, err)
role2 := &domainAuth.Role{
ID: uuid.New(),
Name: "role2",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role2)
require.NoError(t, err)
err = repo.Create(ctx, user.ID, role1.ID)
require.NoError(t, err)
err = repo.Create(ctx, user.ID, role2.ID)
require.NoError(t, err)
err = repo.DeleteByUserID(ctx, user.ID)
assert.NoError(t, err)
// Verify all roles deleted
roles, err := repo.FindByUserID(ctx, user.ID)
assert.NoError(t, err)
assert.Len(t, roles, 0)
})
}
func TestUserRoleRepository_DeleteByRoleID(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRoleRepository(db)
userRepo := createTestUserRepository(db)
roleRepo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("delete role from all users", func(t *testing.T) {
role := &domainAuth.Role{
ID: uuid.New(),
Name: "shared",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := roleRepo.Create(ctx, role)
require.NoError(t, err)
user1 := &domainAuth.User{
ID: uuid.New(),
FirstName: "User",
LastName: "One",
Email: "user1@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = userRepo.Create(ctx, user1)
require.NoError(t, err)
user2 := &domainAuth.User{
ID: uuid.New(),
FirstName: "User",
LastName: "Two",
Email: "user2@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = userRepo.Create(ctx, user2)
require.NoError(t, err)
err = repo.Create(ctx, user1.ID, role.ID)
require.NoError(t, err)
err = repo.Create(ctx, user2.ID, role.ID)
require.NoError(t, err)
err = repo.DeleteByRoleID(ctx, role.ID)
assert.NoError(t, err)
// Verify role deleted from all users
users, err := repo.FindByRoleID(ctx, role.ID)
assert.NoError(t, err)
assert.Len(t, users, 0)
})
}
func TestUserRoleRepository_Exists(t *testing.T) {
db := setupTestDB(t)
repo := createTestUserRoleRepository(db)
userRepo := createTestUserRepository(db)
roleRepo := createTestRoleRepository(db)
ctx := context.Background()
t.Run("exists returns true for existing user role", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Exists",
LastName: "User",
Email: "exists@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := userRepo.Create(ctx, user)
require.NoError(t, err)
role := &domainAuth.Role{
ID: uuid.New(),
Name: "exists",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role)
require.NoError(t, err)
err = repo.Create(ctx, user.ID, role.ID)
require.NoError(t, err)
exists, err := repo.Exists(ctx, user.ID, role.ID)
assert.NoError(t, err)
assert.True(t, exists)
})
t.Run("exists returns false for non-existent user role", func(t *testing.T) {
user := &domainAuth.User{
ID: uuid.New(),
FirstName: "Not",
LastName: "Exists",
Email: "notexists@example.com",
Status: domainAuth.UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := userRepo.Create(ctx, user)
require.NoError(t, err)
role := &domainAuth.Role{
ID: uuid.New(),
Name: "notexists",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = roleRepo.Create(ctx, role)
require.NoError(t, err)
exists, err := repo.Exists(ctx, user.ID, role.ID)
assert.NoError(t, err)
assert.False(t, exists)
})
}

View 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)
})
}