forked from baron/baron-sso
테넌트 crud 테스트 코드 추가
This commit is contained in:
@@ -44,18 +44,18 @@ describe("tenantTree utility", () => {
|
||||
|
||||
it("calculates recursive member counts correctly", () => {
|
||||
const { currentBase } = buildTenantFullTree(mockTenants, "root-1");
|
||||
|
||||
|
||||
expect(currentBase).not.toBeNull();
|
||||
if (currentBase) {
|
||||
// Direct: 10, Child: 5, Grandchild: 2 -> Total: 17
|
||||
expect(currentBase.recursiveMemberCount).toBe(17);
|
||||
expect(currentBase.children).toHaveLength(1);
|
||||
|
||||
|
||||
const child = currentBase.children[0];
|
||||
// Direct: 5, Grandchild: 2 -> Total: 7
|
||||
expect(child.recursiveMemberCount).toBe(7);
|
||||
expect(child.children).toHaveLength(1);
|
||||
|
||||
|
||||
const grandchild = child.children[0];
|
||||
// Direct: 2 -> Total: 2
|
||||
expect(grandchild.recursiveMemberCount).toBe(2);
|
||||
@@ -84,10 +84,10 @@ describe("tenantTree utility", () => {
|
||||
updatedAt: "",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
const { subTree } = buildTenantFullTree(multiRootTenants);
|
||||
expect(subTree).toHaveLength(2);
|
||||
expect(subTree.map(n => n.id)).toContain("root-1");
|
||||
expect(subTree.map(n => n.id)).toContain("root-2");
|
||||
expect(subTree.map((n) => n.id)).toContain("root-1");
|
||||
expect(subTree.map((n) => n.id)).toContain("root-2");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ test.describe("Authentication", () => {
|
||||
|
||||
// Should be on the dashboard/overview
|
||||
await expect(page.locator("aside")).toBeVisible();
|
||||
await expect(page.locator("h1")).toContainText("Admin Control");
|
||||
await expect(page.locator("h1")).toContainText(/Admin Control|운영 도구/);
|
||||
});
|
||||
|
||||
test("should logout and redirect to login page", async ({ page }) => {
|
||||
|
||||
@@ -170,31 +170,45 @@ test.describe("Tenants Management", () => {
|
||||
const parentRow = page.locator("tr", { hasText: "Parent Org" });
|
||||
await expect(parentRow).toContainText("5"); // Direct
|
||||
await expect(parentRow).toContainText("8"); // Total (5 + 3)
|
||||
|
||||
|
||||
// Check for either English or Korean labels
|
||||
const hasDirectLabel = await parentRow.evaluate(el =>
|
||||
el.textContent?.includes("Direct") || el.textContent?.includes("소속")
|
||||
const hasDirectLabel = await parentRow.evaluate(
|
||||
(el) =>
|
||||
el.textContent?.includes("Direct") || el.textContent?.includes("소속"),
|
||||
);
|
||||
const hasTotalLabel = await parentRow.evaluate(el =>
|
||||
el.textContent?.includes("Total") || el.textContent?.includes("전체")
|
||||
const hasTotalLabel = await parentRow.evaluate(
|
||||
(el) =>
|
||||
el.textContent?.includes("Total") || el.textContent?.includes("전체"),
|
||||
);
|
||||
expect(hasDirectLabel).toBe(true);
|
||||
expect(hasTotalLabel).toBe(true);
|
||||
|
||||
// Open Member List Dialog - Click the members count button
|
||||
const memberButton = parentRow.getByRole("button").filter({ hasText: /Direct|소속/ });
|
||||
const memberButton = parentRow
|
||||
.getByRole("button")
|
||||
.filter({ hasText: /Direct|소속/ });
|
||||
await memberButton.click();
|
||||
|
||||
|
||||
// Check Tabs in Member List Dialog
|
||||
// Use regex to match either language, ignoring the count suffix
|
||||
await expect(page.locator('button[role="tab"]').filter({ hasText: /소속 멤버|Direct Members/ })).toBeVisible();
|
||||
await expect(page.locator('button[role="tab"]').filter({ hasText: /하위 조직 멤버|Descendant Members/ })).toBeVisible();
|
||||
await expect(
|
||||
page
|
||||
.locator('button[role="tab"]')
|
||||
.filter({ hasText: /소속 멤버|Direct Members/ }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page
|
||||
.locator('button[role="tab"]')
|
||||
.filter({ hasText: /하위 조직 멤버|Descendant Members/ }),
|
||||
).toBeVisible();
|
||||
|
||||
// Direct Members Tab should show parent's user
|
||||
await expect(page.locator("role=dialog")).toContainText("u1@parent.com");
|
||||
|
||||
// Switch to Descendant Members Tab
|
||||
await page.click('button[role="tab"]:has-text("하위 조직 멤버"), button[role="tab"]:has-text("Descendant Members")');
|
||||
await page.click(
|
||||
'button[role="tab"]:has-text("하위 조직 멤버"), button[role="tab"]:has-text("Descendant Members")',
|
||||
);
|
||||
await expect(page.locator("role=dialog")).toContainText("u2@child.com");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ type User struct {
|
||||
Tenant *Tenant `gorm:"foreignKey:TenantID" json:"tenant,omitempty"`
|
||||
RelyingPartyID *string `gorm:"column:relying_party_id;type:uuid;index" json:"relyingPartyId,omitempty"` // RP Admin용
|
||||
Department string `gorm:"column:department" json:"department"`
|
||||
Position string `gorm:"column:position" json:"position"` // 직급 (예: 수석, 책임, 선임)
|
||||
Position string `gorm:"column:position" json:"position"` // 직급 (예: 수석, 책임, 선임)
|
||||
JobTitle string `gorm:"column:job_title" json:"jobTitle"` // 직무 (예: 프론트엔드 개발, 기획)
|
||||
Metadata JSONMap `gorm:"column:metadata;type:jsonb" json:"metadata,omitempty"`
|
||||
Status string `gorm:"column:status;default:'active'" json:"status"`
|
||||
|
||||
@@ -98,7 +98,7 @@ func (m *AsyncMockUserRepo) ListByTenant(ctx context.Context, tenantID string) (
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *AsyncMockUserRepo) List(ctx context.Context, offset, limit int, search string) ([]domain.User, int64, error) {
|
||||
func (m *AsyncMockUserRepo) List(ctx context.Context, offset, limit int, search string, companyCode string) ([]domain.User, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
@@ -110,6 +110,9 @@ func (m *AsyncMockUserRepo) CountByTenantIDs(ctx context.Context, tenantIDs []st
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *AsyncMockUserRepo) CountByCompanyCodes(ctx context.Context, codes []string) (map[string]int64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type AsyncMockRedisRepo struct {
|
||||
mock.Mock
|
||||
@@ -161,6 +164,10 @@ func (m *AsyncMockTenantService) GetTenant(ctx context.Context, id string) (*dom
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *AsyncMockTenantService) ListTenants(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
func (m *AsyncMockTenantService) ListManageableTenants(ctx context.Context, userID string) ([]domain.Tenant, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -98,10 +98,6 @@ func (h *TenantHandler) ApproveTenant(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func (h *TenantHandler) ListTenants(c *fiber.Ctx) error {
|
||||
if h.DB == nil {
|
||||
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "database not available"})
|
||||
}
|
||||
|
||||
limit := c.QueryInt("limit", 50)
|
||||
offset := c.QueryInt("offset", 0)
|
||||
parentId := c.Query("parentId")
|
||||
@@ -113,24 +109,8 @@ func (h *TenantHandler) ListTenants(c *fiber.Ctx) error {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
// Use separate queries for count and find to avoid GORM statement contamination
|
||||
countQuery := h.DB.Model(&domain.Tenant{})
|
||||
if parentId != "" {
|
||||
countQuery = countQuery.Where("parent_id = ?", parentId)
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := countQuery.Count(&total).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
findQuery := h.DB.Model(&domain.Tenant{})
|
||||
if parentId != "" {
|
||||
findQuery = findQuery.Where("parent_id = ?", parentId)
|
||||
}
|
||||
|
||||
var tenants []domain.Tenant
|
||||
if err := findQuery.Order("created_at desc").Limit(limit).Offset(offset).Preload("Domains").Find(&tenants).Error; err != nil {
|
||||
tenants, total, err := h.Service.ListTenants(c.Context(), limit, offset, parentId)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
|
||||
@@ -66,10 +66,51 @@ func (m *MockTenantService) GetTenant(ctx context.Context, id string) (*domain.T
|
||||
return args.Get(0).(*domain.Tenant), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockTenantService) ListTenants(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) {
|
||||
args := m.Called(ctx, limit, offset, parentID)
|
||||
return args.Get(0).([]domain.Tenant), args.Get(1).(int64), args.Error(2)
|
||||
}
|
||||
|
||||
func (m *MockTenantService) SetKetoService(keto service.KetoService) {
|
||||
m.Called(keto)
|
||||
}
|
||||
|
||||
type MockUserRepoForHandler struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockUserRepoForHandler) Create(ctx context.Context, user *domain.User) error { return nil }
|
||||
func (m *MockUserRepoForHandler) Update(ctx context.Context, user *domain.User) error { return nil }
|
||||
func (m *MockUserRepoForHandler) Delete(ctx context.Context, id string) error { return nil }
|
||||
func (m *MockUserRepoForHandler) FindByEmail(ctx context.Context, email string) (*domain.User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockUserRepoForHandler) FindByID(ctx context.Context, id string) (*domain.User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockUserRepoForHandler) FindByIDs(ctx context.Context, ids []string) ([]domain.User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockUserRepoForHandler) ListByTenant(ctx context.Context, tenantID string) ([]domain.User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockUserRepoForHandler) List(ctx context.Context, offset, limit int, search string, companyCode string) ([]domain.User, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
func (m *MockUserRepoForHandler) CountByTenant(ctx context.Context, tenantID string) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (m *MockUserRepoForHandler) CountByTenantIDs(ctx context.Context, tenantIDs []string) (map[string]int64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockUserRepoForHandler) CountByCompanyCodes(ctx context.Context, codes []string) (map[string]int64, error) {
|
||||
args := m.Called(ctx, codes)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(map[string]int64), args.Error(1)
|
||||
}
|
||||
|
||||
func TestTenantHandler_CreateTenant(t *testing.T) {
|
||||
app := fiber.New()
|
||||
mockSvc := new(MockTenantService)
|
||||
@@ -98,6 +139,47 @@ func TestTenantHandler_CreateTenant(t *testing.T) {
|
||||
assert.Equal(t, "t1", got["id"])
|
||||
}
|
||||
|
||||
func TestTenantHandler_ListTenants(t *testing.T) {
|
||||
app := fiber.New()
|
||||
mockSvc := new(MockTenantService)
|
||||
mockUserRepo := new(MockUserRepoForHandler)
|
||||
|
||||
h := &TenantHandler{
|
||||
Service: mockSvc,
|
||||
UserRepo: mockUserRepo,
|
||||
}
|
||||
|
||||
app.Get("/tenants", h.ListTenants)
|
||||
|
||||
tenants := []domain.Tenant{
|
||||
{ID: "t1", Name: "Tenant A", Slug: "slug-a"},
|
||||
{ID: "t2", Name: "Tenant B", Slug: "slug-b"},
|
||||
}
|
||||
mockSvc.On("ListTenants", mock.Anything, 10, 0, "").Return(tenants, int64(2), nil)
|
||||
mockUserRepo.On("CountByCompanyCodes", mock.Anything, []string{"slug-a", "slug-b"}).
|
||||
Return(map[string]int64{"slug-a": 5, "slug-b": 10}, nil)
|
||||
|
||||
req := httptest.NewRequest("GET", "/tenants?limit=10&offset=0", nil)
|
||||
resp, _ := app.Test(req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var res tenantListResponse
|
||||
json.NewDecoder(resp.Body).Decode(&res)
|
||||
|
||||
assert.Equal(t, int64(2), res.Total)
|
||||
assert.Len(t, res.Items, 2)
|
||||
|
||||
// Check if counts are mapped correctly
|
||||
for _, item := range res.Items {
|
||||
if item.Slug == "slug-a" {
|
||||
assert.Equal(t, int64(5), item.MemberCount)
|
||||
} else if item.Slug == "slug-b" {
|
||||
assert.Equal(t, int64(10), item.MemberCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTenantHandler_ApproveTenant(t *testing.T) {
|
||||
app := fiber.New()
|
||||
mockSvc := new(MockTenantService)
|
||||
|
||||
@@ -322,12 +322,12 @@ func (h *UserHandler) CreateUser(c *fiber.Ctx) error {
|
||||
// [New] Local DB Sync - Ensure user exists in read-model
|
||||
if h.UserRepo != nil {
|
||||
localUser := h.mapToLocalUser(*identity)
|
||||
|
||||
|
||||
// Sync to local DB
|
||||
go func(u *domain.User, role string, tID *string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
|
||||
// Use Update (upsert) instead of Create for robustness
|
||||
if err := h.UserRepo.Update(ctx, u); err != nil {
|
||||
slog.Error("[UserHandler] Failed to sync new user to local DB", "email", u.Email, "error", err)
|
||||
@@ -475,14 +475,14 @@ func (h *UserHandler) UpdateUser(c *fiber.Ctx) error {
|
||||
// [New] Local DB Sync - Sync synchronously to ensure immediate consistency for the caller
|
||||
if h.UserRepo != nil {
|
||||
updatedLocalUser := h.mapToLocalUser(*updated)
|
||||
|
||||
|
||||
ctx := context.Background() // Use request context if appropriate, but sync must finish
|
||||
if err := h.UserRepo.Update(ctx, updatedLocalUser); err != nil {
|
||||
slog.Error("[UserHandler] Failed to sync updated user to local DB", "userID", updatedLocalUser.ID, "error", err)
|
||||
}
|
||||
|
||||
// [Keto Sync] asynchronously as it's less critical for immediate UI count
|
||||
go h.syncKetoRole(context.Background(), updatedLocalUser.ID,
|
||||
go h.syncKetoRole(context.Background(), updatedLocalUser.ID,
|
||||
extractTraitString(updated.Traits, "grade"), oldRole, oldTenantID, updatedLocalUser.TenantID)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ type TenantRepository interface {
|
||||
FindByDomain(ctx context.Context, domainName string) (*domain.Tenant, error)
|
||||
FindByIDs(ctx context.Context, ids []string) ([]domain.Tenant, error)
|
||||
AddDomain(ctx context.Context, tenantID string, domainName string, verified bool) error
|
||||
List(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error)
|
||||
}
|
||||
|
||||
type tenantRepository struct {
|
||||
@@ -90,3 +91,23 @@ func (r *tenantRepository) AddDomain(ctx context.Context, tenantID string, domai
|
||||
}
|
||||
return r.db.WithContext(ctx).Create(&td).Error
|
||||
}
|
||||
|
||||
func (r *tenantRepository) List(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) {
|
||||
var tenants []domain.Tenant
|
||||
var total int64
|
||||
db := r.db.WithContext(ctx).Model(&domain.Tenant{})
|
||||
|
||||
if parentID != "" {
|
||||
db = db.Where("parent_id = ?", parentID)
|
||||
}
|
||||
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if err := db.Order("created_at desc").Limit(limit).Offset(offset).Preload("Domains").Find(&tenants).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return tenants, total, nil
|
||||
}
|
||||
|
||||
@@ -119,10 +119,10 @@ func (r *userRepository) CountByCompanyCodes(ctx context.Context, codes []string
|
||||
// 1. Resolve IDs for these codes to support dual counting (slug or ID)
|
||||
var tenants []domain.Tenant
|
||||
_ = r.db.WithContext(ctx).Where("slug IN ?", codes).Find(&tenants).Error
|
||||
|
||||
|
||||
idToSlug := make(map[string]string)
|
||||
slugToNormalized := make(map[string]string)
|
||||
|
||||
|
||||
for _, code := range codes {
|
||||
slugToNormalized[strings.ToLower(strings.TrimSpace(code))] = code
|
||||
}
|
||||
@@ -156,13 +156,13 @@ func (r *userRepository) CountByCompanyCodes(ctx context.Context, codes []string
|
||||
} else if res.TenantID != "" {
|
||||
slug = idToSlug[res.TenantID]
|
||||
}
|
||||
|
||||
|
||||
if slug != "" {
|
||||
normalizedSlug := strings.ToLower(strings.TrimSpace(slug))
|
||||
counts[normalizedSlug] += res.Count
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return counts, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestUserRepository(t *testing.T) {
|
||||
_ = 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")
|
||||
users, total, err := repo.List(ctx, 0, 10, "Alice", "")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, total >= 1)
|
||||
assert.Equal(t, "Alice", users[0].Name)
|
||||
@@ -73,4 +73,25 @@ func TestUserRepository(t *testing.T) {
|
||||
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 users")
|
||||
|
||||
users := []domain.User{
|
||||
{Email: "u1@a.com", Name: "U1", CompanyCode: "tenant-a"},
|
||||
{Email: "u2@a.com", Name: "U2", CompanyCode: "tenant-a"},
|
||||
{Email: "u3@b.com", Name: "U3", CompanyCode: "tenant-b"},
|
||||
{Email: "u4@none.com", Name: "U4", CompanyCode: ""},
|
||||
}
|
||||
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"])
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type TenantService interface {
|
||||
GetTenantByDomain(ctx context.Context, emailDomain string) (*domain.Tenant, error)
|
||||
GetTenantBySlug(ctx context.Context, slug string) (*domain.Tenant, error)
|
||||
GetTenant(ctx context.Context, id string) (*domain.Tenant, error)
|
||||
ListTenants(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error)
|
||||
ApproveTenant(ctx context.Context, id string) error
|
||||
SetKetoService(keto KetoService) // 추가
|
||||
}
|
||||
@@ -226,3 +227,8 @@ func (s *tenantService) GetTenantByDomain(ctx context.Context, emailDomain strin
|
||||
func (s *tenantService) GetTenantBySlug(ctx context.Context, slug string) (*domain.Tenant, error) {
|
||||
return s.repo.FindBySlug(ctx, slug)
|
||||
}
|
||||
|
||||
func (s *tenantService) ListTenants(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) {
|
||||
// Let the repository handle the query and pagination
|
||||
return s.repo.List(ctx, limit, offset, parentID)
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ func (m *MockTenantRepoForSvc) AddDomain(ctx context.Context, tenantID string, d
|
||||
return m.Called(ctx, tenantID, domainName, verified).Error(0)
|
||||
}
|
||||
|
||||
func (m *MockTenantRepoForSvc) List(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) {
|
||||
args := m.Called(ctx, limit, offset, parentID)
|
||||
return args.Get(0).([]domain.Tenant), int64(args.Int(1)), args.Error(2)
|
||||
}
|
||||
|
||||
type MockKetoSvcForTenant struct {
|
||||
mock.Mock
|
||||
}
|
||||
@@ -116,7 +121,7 @@ func (m *MockUserRepoForTenant) ListByTenant(ctx context.Context, tenantID strin
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepoForTenant) List(ctx context.Context, offset, limit int, search string) ([]domain.User, int64, error) {
|
||||
func (m *MockUserRepoForTenant) List(ctx context.Context, offset, limit int, search string, companyCode string) ([]domain.User, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
@@ -133,7 +138,13 @@ func (m *MockUserRepoForTenant) CountByTenantIDs(ctx context.Context, tenantIDs
|
||||
return args.Get(0).(map[string]int64), args.Error(1)
|
||||
}
|
||||
|
||||
|
||||
func (m *MockUserRepoForTenant) CountByCompanyCodes(ctx context.Context, codes []string) (map[string]int64, error) {
|
||||
args := m.Called(ctx, codes)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(map[string]int64), args.Error(1)
|
||||
}
|
||||
|
||||
func TestTenantService_RegisterTenant_AutoVerify(t *testing.T) {
|
||||
mockRepo := new(MockTenantRepoForSvc)
|
||||
@@ -214,3 +225,18 @@ func TestTenantService_ApproveTenant_SyncAdmin(t *testing.T) {
|
||||
mockUserRepo.AssertExpectations(t)
|
||||
mockOutbox.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestTenantService_ListTenants(t *testing.T) {
|
||||
mockRepo := new(MockTenantRepoForSvc)
|
||||
svc := NewTenantService(mockRepo, nil, nil)
|
||||
ctx := context.Background()
|
||||
|
||||
tenants := []domain.Tenant{{ID: "t1", Name: "Tenant 1"}}
|
||||
mockRepo.On("List", ctx, 10, 0, "").Return(tenants, 1, nil)
|
||||
|
||||
result, total, err := svc.ListTenants(ctx, 10, 0, "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), total)
|
||||
assert.Equal(t, tenants, result)
|
||||
mockRepo.AssertExpectations(t)
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func (m *MockUserRepository) ListByTenant(ctx context.Context, tenantID string)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) List(ctx context.Context, offset, limit int, search string) ([]domain.User, int64, error) {
|
||||
func (m *MockUserRepository) List(ctx context.Context, offset, limit int, search string, companyCode string) ([]domain.User, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
@@ -94,6 +94,13 @@ func (m *MockUserRepository) CountByTenantIDs(ctx context.Context, tenantIDs []s
|
||||
return args.Get(0).(map[string]int64), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) CountByCompanyCodes(ctx context.Context, codes []string) (map[string]int64, error) {
|
||||
args := m.Called(ctx, codes)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(map[string]int64), args.Error(1)
|
||||
}
|
||||
|
||||
type MockTenantRepository struct {
|
||||
mock.Mock
|
||||
@@ -135,6 +142,10 @@ func (m *MockTenantRepository) AddDomain(ctx context.Context, tenantID string, d
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockTenantRepository) List(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
func TestUserGroupService_Create(t *testing.T) {
|
||||
mockRepo := new(MockUserGroupRepository)
|
||||
mockTenantRepo := new(MockTenantRepository)
|
||||
|
||||
Reference in New Issue
Block a user