1
0
forked from baron/baron-sso
Files
baron-sso/backend/internal/repository/user_repository_test.go

358 lines
11 KiB
Go

package repository
import (
"baron-sso-backend/internal/domain"
"context"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
)
func TestUserRepository(t *testing.T) {
repo := NewUserRepository(testDB)
ctx := context.Background()
// Ensure User table exists and clean for tests
_ = testDB.AutoMigrate(&domain.User{})
t.Run("Create and FindByEmail", func(t *testing.T) {
user := &domain.User{
Email: "test@example.com",
Name: "Test User",
Role: "user",
}
err := repo.Create(ctx, user)
assert.NoError(t, err)
assert.NotEmpty(t, user.ID)
found, err := repo.FindByEmail(ctx, "test@example.com")
assert.NoError(t, err)
assert.Equal(t, user.ID, found.ID)
assert.Equal(t, "Test User", found.Name)
})
t.Run("Update User Info", func(t *testing.T) {
user := &domain.User{
Email: "update@example.com",
Name: "Before Update",
Role: "user",
}
_ = repo.Create(ctx, user)
user.Name = "After Update"
user.Phone = "010-1234-5678"
err := repo.Update(ctx, user)
assert.NoError(t, err)
found, err := repo.FindByEmail(ctx, "update@example.com")
assert.NoError(t, err)
assert.Equal(t, "After Update", found.Name)
assert.Equal(t, "010-1234-5678", found.Phone)
})
t.Run("Create and Update preserve top-level user grade for compatibility", func(t *testing.T) {
testDB.Exec("DELETE FROM user_login_ids")
testDB.Exec("DELETE FROM users WHERE email IN ?", []string{"grade-create@example.com", "grade-update@example.com"})
created := &domain.User{
Email: "grade-create@example.com",
Name: "Grade Create",
Role: domain.RoleUser,
Grade: "책임",
}
require.NoError(t, repo.Create(ctx, created))
found, err := repo.FindByEmail(ctx, created.Email)
require.NoError(t, err)
require.Equal(t, "책임", found.Grade)
updated := &domain.User{
ID: uuid.NewString(),
Email: "grade-update@example.com",
Name: "Grade Update",
Role: domain.RoleUser,
Grade: "수석",
}
require.NoError(t, repo.Update(ctx, updated))
found, err = repo.FindByEmail(ctx, updated.Email)
require.NoError(t, err)
require.Equal(t, "수석", found.Grade)
})
t.Run("Update preserves archived email reservation", func(t *testing.T) {
testDB.Exec("DELETE FROM user_login_ids")
testDB.Exec("DELETE FROM users")
archived := &domain.User{
ID: "00000000-0000-0000-0000-00000000a001",
Email: "reserved@example.com",
Name: "Archived User",
Role: domain.RoleUser,
Status: domain.UserStatusArchived,
}
replacement := &domain.User{
ID: "00000000-0000-0000-0000-00000000a002",
Email: "reserved@example.com",
Name: "Replacement User",
Role: domain.RoleUser,
Status: domain.UserStatusActive,
}
require.NoError(t, repo.Create(ctx, archived))
err := repo.Update(ctx, replacement)
require.Error(t, err)
require.Contains(t, err.Error(), "archived user")
found, err := repo.FindByEmail(ctx, archived.Email)
require.NoError(t, err)
require.Equal(t, archived.ID, found.ID)
require.Equal(t, domain.UserStatusArchived, found.Status)
})
t.Run("List Users with Search", func(t *testing.T) {
// Add some users
_ = repo.Create(ctx, &domain.User{Email: "alice@test.com", Name: "Alice", Role: "user"})
_ = repo.Create(ctx, &domain.User{Email: "bob@test.com", Name: "Bob", Role: "user"})
users, total, _, err := repo.List(ctx, 0, 10, "Alice", []string{}, "")
assert.NoError(t, err)
assert.True(t, total >= 1)
assert.Equal(t, "Alice", users[0].Name)
})
t.Run("Delete User", func(t *testing.T) {
require.NoError(t, testDB.AutoMigrate(&domain.UserLoginID{}))
require.NoError(t, testDB.Exec("DELETE FROM user_login_ids").Error)
require.NoError(t, testDB.Exec("DELETE FROM users WHERE email = ?", "delete@example.com").Error)
user := &domain.User{Email: "delete@example.com", Name: "To Delete"}
require.NoError(t, repo.Create(ctx, user))
require.NoError(t, repo.UpdateUserLoginIDs(ctx, user.ID, []domain.UserLoginID{
{UserID: user.ID, TenantID: uuid.NewString(), FieldKey: "employee_id", LoginID: "DELETE001"},
}))
err := repo.Delete(ctx, user.ID)
assert.NoError(t, err)
found, err := repo.FindByEmail(ctx, "delete@example.com")
assert.Error(t, err) // Should not be found
assert.Nil(t, found)
var hardDeleted domain.User
err = testDB.Unscoped().Where("id = ?", user.ID).First(&hardDeleted).Error
require.ErrorIs(t, err, gorm.ErrRecordNotFound)
var loginIDCount int64
require.NoError(t, testDB.Unscoped().Model(&domain.UserLoginID{}).Where("user_id = ?", user.ID).Count(&loginIDCount).Error)
require.Zero(t, loginIDCount)
})
t.Run("CountByCompanyCodes", func(t *testing.T) {
// Clean start for this subtest
testDB.Exec("DELETE FROM user_login_ids")
testDB.Exec("DELETE FROM users")
testDB.Exec("DELETE FROM tenant_domains")
tenantA := createUserRepositoryTestTenant(t, "tenant-a")
tenantB := createUserRepositoryTestTenant(t, "tenant-b")
users := []domain.User{
{Email: "u1@a.com", Name: "U1", TenantID: &tenantA.ID},
{Email: "u2@a.com", Name: "U2", TenantID: &tenantA.ID},
{Email: "u3@b.com", Name: "U3", TenantID: &tenantB.ID},
{Email: "u4@none.com", Name: "U4"},
}
for _, u := range users {
_ = repo.Create(ctx, &u)
}
counts, err := repo.CountByCompanyCodes(ctx, []string{"tenant-a", "tenant-b", "tenant-c"})
assert.NoError(t, err)
assert.Equal(t, int64(2), counts["tenant-a"])
assert.Equal(t, int64(1), counts["tenant-b"])
assert.Equal(t, int64(0), counts["tenant-c"])
})
t.Run("CountByCompanyCodes excludes soft deleted cache rows", func(t *testing.T) {
testDB.Exec("DELETE FROM user_login_ids")
testDB.Exec("DELETE FROM users")
testDB.Exec("DELETE FROM tenant_domains")
tenantA := createUserRepositoryTestTenant(t, "tenant-a")
active := &domain.User{Email: "active@a.com", Name: "Active", TenantID: &tenantA.ID}
deleted := &domain.User{Email: "deleted@a.com", Name: "Deleted", TenantID: &tenantA.ID}
secondDeleted := &domain.User{Email: "second-deleted@a.com", Name: "Second Deleted", TenantID: &tenantA.ID}
assert.NoError(t, repo.Create(ctx, active))
assert.NoError(t, repo.Create(ctx, deleted))
assert.NoError(t, repo.Create(ctx, secondDeleted))
assert.NoError(t, repo.Delete(ctx, deleted.ID))
assert.NoError(t, repo.Delete(ctx, secondDeleted.ID))
counts, err := repo.CountByCompanyCodes(ctx, []string{"tenant-a"})
assert.NoError(t, err)
assert.Equal(t, int64(1), counts["tenant-a"])
})
t.Run("Multi-Identifier Support", func(t *testing.T) {
_ = testDB.AutoMigrate(&domain.UserLoginID{})
testDB.Exec("DELETE FROM user_login_ids")
testDB.Exec("DELETE FROM users")
user := &domain.User{Email: "multi@test.com", Name: "Multi"}
_ = repo.Create(ctx, user)
t1 := "00000000-0000-0000-0000-000000000001"
t2 := "00000000-0000-0000-0000-000000000002"
loginIDs := []domain.UserLoginID{
{UserID: user.ID, TenantID: t1, FieldKey: "emp_id", LoginID: "E001"},
{UserID: user.ID, TenantID: t2, FieldKey: "student_id", LoginID: "S001"},
}
err := repo.UpdateUserLoginIDs(ctx, user.ID, loginIDs)
assert.NoError(t, err)
// Get and Verify
saved, err := repo.GetUserLoginIDs(ctx, user.ID)
assert.NoError(t, err)
assert.Len(t, saved, 2)
// IsLoginIDTaken
taken, err := repo.IsLoginIDTaken(ctx, "E001")
assert.NoError(t, err)
assert.True(t, taken)
taken, err = repo.IsLoginIDTaken(ctx, "UNKNOWN")
assert.NoError(t, err)
assert.False(t, taken)
// FindTenantIDByLoginID
tid, err := repo.FindTenantIDByLoginID(ctx, "S001")
assert.NoError(t, err)
assert.Equal(t, t2, tid)
// Update (Replace)
newList := []domain.UserLoginID{
{UserID: user.ID, TenantID: t1, FieldKey: "emp_id", LoginID: "E002"},
}
err = repo.UpdateUserLoginIDs(ctx, user.ID, newList)
assert.NoError(t, err)
saved, _ = repo.GetUserLoginIDs(ctx, user.ID)
assert.Len(t, saved, 1)
assert.Equal(t, "E002", saved[0].LoginID)
})
}
func TestUserRepository_ListIncludesAdditionalTenantAppointments(t *testing.T) {
repo := NewUserRepository(testDB)
ctx := context.Background()
require.NoError(t, testDB.Exec("DELETE FROM user_login_ids").Error)
require.NoError(t, testDB.Exec("DELETE FROM users").Error)
primaryTenant := createUserRepositoryTestTenant(t, "repo-primary-tenant")
additionalTenant := createUserRepositoryTestTenant(t, "repo-additional-tenant")
primaryTenantID := primaryTenant.ID
additionalTenantID := additionalTenant.ID
users := []domain.User{
{
ID: uuid.NewString(),
Email: "primary-member@example.com",
Name: "Primary Member",
Role: domain.RoleUser,
TenantID: &additionalTenantID,
},
{
ID: uuid.NewString(),
Email: "additional-member@example.com",
Name: "Additional Member",
Role: domain.RoleUser,
TenantID: &primaryTenantID,
Metadata: domain.JSONMap{
"additionalAppointments": []any{
map[string]any{
"tenantId": additionalTenant.ID,
"tenantSlug": additionalTenant.Slug,
"tenantName": additionalTenant.Name,
"isPrimary": false,
},
},
},
},
}
for i := range users {
require.NoError(t, repo.Create(ctx, &users[i]))
}
listed, total, _, err := repo.List(ctx, 0, 20, "", []string{additionalTenant.ID}, "")
require.NoError(t, err)
require.Equal(t, int64(2), total)
require.Len(t, listed, 2)
emails := []string{listed[0].Email, listed[1].Email}
assert.Contains(t, emails, "primary-member@example.com")
assert.Contains(t, emails, "additional-member@example.com")
counts, err := repo.CountByTenantIDs(ctx, []string{additionalTenant.ID})
require.NoError(t, err)
assert.Equal(t, int64(2), counts[additionalTenant.ID])
}
func TestUserRepository_ListIncludesAdditionalTenantAppointmentsBySlug(t *testing.T) {
repo := NewUserRepository(testDB)
ctx := context.Background()
require.NoError(t, testDB.Exec("DELETE FROM user_login_ids").Error)
require.NoError(t, testDB.Exec("DELETE FROM users").Error)
primaryTenant := createUserRepositoryTestTenant(t, "repo-private-primary-tenant")
visibleTenant := createUserRepositoryTestTenant(t, "repo-visible-leader-tenant")
primaryTenantID := primaryTenant.ID
user := domain.User{
ID: uuid.NewString(),
Email: "slug-appointment-leader@example.com",
Name: "Slug Appointment Leader",
Role: domain.RoleUser,
TenantID: &primaryTenantID,
Metadata: domain.JSONMap{
"additionalAppointments": []any{
map[string]any{
"tenantSlug": visibleTenant.Slug,
"tenantName": visibleTenant.Name,
"isOwner": true,
},
},
},
}
require.NoError(t, repo.Create(ctx, &user))
listed, total, _, err := repo.List(ctx, 0, 20, "", []string{visibleTenant.ID}, "")
require.NoError(t, err)
require.Equal(t, int64(1), total)
require.Len(t, listed, 1)
assert.Equal(t, "slug-appointment-leader@example.com", listed[0].Email)
counts, err := repo.CountByTenantIDs(ctx, []string{visibleTenant.ID})
require.NoError(t, err)
assert.Equal(t, int64(1), counts[visibleTenant.ID])
}
func createUserRepositoryTestTenant(t *testing.T, slug string) domain.Tenant {
t.Helper()
require.NoError(t, testDB.Unscoped().Where("slug = ?", slug).Delete(&domain.Tenant{}).Error)
tenant := domain.Tenant{
ID: uuid.NewString(),
Name: "Tenant " + slug,
Slug: slug,
Type: domain.TenantTypeCompany,
Status: domain.TenantStatusActive,
}
require.NoError(t, testDB.Create(&tenant).Error)
return tenant
}