첫 커밋: 로컬 프로젝트 업로드
This commit is contained in:
273
baron-sso/backend/internal/repository/user_repository_test.go
Normal file
273
baron-sso/backend/internal/repository/user_repository_test.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
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("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) {
|
||||
user := &domain.User{Email: "delete@example.com", Name: "To Delete"}
|
||||
_ = repo.Create(ctx, user)
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
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 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
|
||||
}
|
||||
Reference in New Issue
Block a user