1
0
forked from baron/baron-sso
Files
baron-sso/backend/internal/service/org_chart_service_test.go

252 lines
8.4 KiB
Go

package service
import (
"baron-sso-backend/internal/domain"
"baron-sso-backend/internal/repository"
"bytes"
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/xuri/excelize/v2"
)
type mockTenantRepo struct {
mock.Mock
repository.TenantRepository
}
func (m *mockTenantRepo) FindBySlug(ctx context.Context, slug string) (*domain.Tenant, error) {
args := m.Called(ctx, slug)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*domain.Tenant), args.Error(1)
}
func (m *mockTenantRepo) Create(ctx context.Context, tenant *domain.Tenant) error {
args := m.Called(ctx, tenant)
return args.Error(0)
}
func (m *mockTenantRepo) AddDomain(ctx context.Context, tenantID, domainName string, isPrimary bool) error {
args := m.Called(ctx, tenantID, domainName, isPrimary)
return args.Error(0)
}
type mockUserGroupRepo struct {
mock.Mock
repository.UserGroupRepository
}
func (m *mockUserGroupRepo) ListByTenantID(ctx context.Context, tenantID string) ([]domain.UserGroup, error) {
args := m.Called(ctx, tenantID)
return args.Get(0).([]domain.UserGroup), args.Error(1)
}
func (m *mockUserGroupRepo) Create(ctx context.Context, ug *domain.UserGroup) error {
args := m.Called(ctx, ug)
return args.Error(0)
}
type mockUserRepo struct {
mock.Mock
repository.UserRepository
}
func (m *mockUserRepo) Update(ctx context.Context, user *domain.User) error {
args := m.Called(ctx, user)
return args.Error(0)
}
type mockKetoOutboxRepo struct {
mock.Mock
repository.KetoOutboxRepository
}
func (m *mockKetoOutboxRepo) Create(ctx context.Context, outbox *domain.KetoOutbox) error {
args := m.Called(ctx, outbox)
return args.Error(0)
}
type mockKratosService struct {
mock.Mock
}
func (m *mockKratosService) ListIdentities(ctx context.Context) ([]KratosIdentity, error) {
return nil, nil
}
func (m *mockKratosService) FindIdentityIDByIdentifier(ctx context.Context, identifier string) (string, error) {
args := m.Called(ctx, identifier)
return args.String(0), args.Error(1)
}
func (m *mockKratosService) GetIdentity(ctx context.Context, identityID string) (*KratosIdentity, error) {
return nil, nil
}
func (m *mockKratosService) UpdateIdentity(ctx context.Context, id string, traits map[string]interface{}, state string) (*KratosIdentity, error) {
args := m.Called(ctx, id, traits, state)
return nil, args.Error(1)
}
func (m *mockKratosService) UpdateIdentityPassword(ctx context.Context, id, pw string) error {
return nil
}
func (m *mockKratosService) DeleteIdentity(ctx context.Context, id string) error {
return nil
}
func (m *mockKratosService) CreateUser(ctx context.Context, user *domain.BrokerUser, password string) (string, error) {
args := m.Called(ctx, user, password)
return args.String(0), args.Error(1)
}
func setupMocksForImport(tenantRepo *mockTenantRepo, ugRepo *mockUserGroupRepo, userRepo *mockUserRepo, ketoRepo *mockKetoOutboxRepo, kratos *mockKratosService, ctx context.Context, companySlug, email string) {
tenantRepo.On("FindBySlug", ctx, companySlug).Return(&domain.Tenant{ID: companySlug + "-id", Slug: companySlug}, nil).Maybe()
tenantRepo.On("FindBySlug", ctx, mock.Anything).Return(&domain.Tenant{ID: "fallback-id", Slug: "fallback"}, nil).Maybe()
tenantRepo.On("AddDomain", ctx, mock.Anything, mock.Anything, true).Return(nil).Maybe()
tenantRepo.On("Create", ctx, mock.Anything).Return(nil).Maybe()
ugRepo.On("ListByTenantID", ctx, mock.Anything).Return([]domain.UserGroup{}, nil).Maybe()
ugRepo.On("Create", ctx, mock.Anything).Return(nil).Maybe()
kratos.On("FindIdentityIDByIdentifier", ctx, email).Return("user-id", nil).Maybe()
kratos.On("UpdateIdentity", ctx, "user-id", mock.Anything, "active").Return(nil, nil).Maybe()
userRepo.On("Update", ctx, mock.Anything).Return(nil).Maybe()
ketoRepo.On("Create", ctx, mock.Anything).Return(nil).Maybe()
}
func TestImportOrgChart_CSV_BOM(t *testing.T) {
tenantRepo := new(mockTenantRepo)
ugRepo := new(mockUserGroupRepo)
userRepo := new(mockUserRepo)
ketoRepo := new(mockKetoOutboxRepo)
kratos := new(mockKratosService)
svc := NewOrgChartService(tenantRepo, ugRepo, userRepo, ketoRepo, kratos)
csvData := "\xef\xbb\xbf이메일,이름,소속,직급,그룹,디비젼,팀,구분\n" +
"test@example.com,홍길동,한맥,사원,엔지니어링,구조,계획,팀원"
ctx := context.Background()
setupMocksForImport(tenantRepo, ugRepo, userRepo, ketoRepo, kratos, ctx, "hanmac", "test@example.com")
res, err := svc.ImportOrgChart(ctx, "root-id", bytes.NewReader([]byte(csvData)), "test.csv", "")
assert.NoError(t, err)
assert.NotNil(t, res)
}
func TestImportOrgChart_XLSX(t *testing.T) {
tenantRepo := new(mockTenantRepo)
ugRepo := new(mockUserGroupRepo)
userRepo := new(mockUserRepo)
ketoRepo := new(mockKetoOutboxRepo)
kratos := new(mockKratosService)
svc := NewOrgChartService(tenantRepo, ugRepo, userRepo, ketoRepo, kratos)
xlsx := excelize.NewFile()
xlsx.SetCellValue("Sheet1", "A1", "이메일")
xlsx.SetCellValue("Sheet1", "B1", "이름")
xlsx.SetCellValue("Sheet1", "C1", "소속")
xlsx.SetCellValue("Sheet1", "A2", "xlsx@example.com")
xlsx.SetCellValue("Sheet1", "B2", "엑셀맨")
xlsx.SetCellValue("Sheet1", "C2", "삼안")
var buf bytes.Buffer
xlsx.Write(&buf)
ctx := context.Background()
setupMocksForImport(tenantRepo, ugRepo, userRepo, ketoRepo, kratos, ctx, "saman", "xlsx@example.com")
res, err := svc.ImportOrgChart(ctx, "root-id", &buf, "test.xlsx", "")
assert.NoError(t, err)
assert.NotNil(t, res)
}
func TestImportOrgChart_MissingColumns(t *testing.T) {
svc := NewOrgChartService(nil, nil, nil, nil, nil)
ctx := context.Background()
csvData := "소속,직급\n한맥,부장"
res, err := svc.ImportOrgChart(ctx, "root", bytes.NewReader([]byte(csvData)), "test.csv", "")
assert.Error(t, err)
assert.Nil(t, res)
}
func TestImportOrgChart_RobustHeader(t *testing.T) {
tenantRepo := new(mockTenantRepo)
ugRepo := new(mockUserGroupRepo)
userRepo := new(mockUserRepo)
ketoRepo := new(mockKetoOutboxRepo)
kratos := new(mockKratosService)
svc := NewOrgChartService(tenantRepo, ugRepo, userRepo, ketoRepo, kratos)
csvData := "\n\n 이 메 일 , 이 름 , 소 속 \n" +
"robust@example.com,로버스트,바론"
ctx := context.Background()
setupMocksForImport(tenantRepo, ugRepo, userRepo, ketoRepo, kratos, ctx, "baron", "robust@example.com")
res, err := svc.ImportOrgChart(ctx, "root-id", bytes.NewReader([]byte(csvData)), "test.csv", "")
assert.NoError(t, err)
assert.NotNil(t, res)
}
func TestImportOrgChart_MultiSheet_ComplexHeaders(t *testing.T) {
tenantRepo := new(mockTenantRepo)
ugRepo := new(mockUserGroupRepo)
userRepo := new(mockUserRepo)
ketoRepo := new(mockKetoOutboxRepo)
kratos := new(mockKratosService)
svc := NewOrgChartService(tenantRepo, ugRepo, userRepo, ketoRepo, kratos)
xlsx := excelize.NewFile()
xlsx.NewSheet("Sheet2")
xlsx.SetCellValue("Sheet2", "A3", " 이 메 일 ")
xlsx.SetCellValue("Sheet2", "B3", " 성 함 / 이 름 ")
xlsx.SetCellValue("Sheet2", "C3", " 소 속 회 사 ")
xlsx.SetCellValue("Sheet2", "A4", "sheet2@example.com")
xlsx.SetCellValue("Sheet2", "B4", "시트투")
xlsx.SetCellValue("Sheet2", "C4", "한맥")
var buf bytes.Buffer
xlsx.Write(&buf)
ctx := context.Background()
setupMocksForImport(tenantRepo, ugRepo, userRepo, ketoRepo, kratos, ctx, "hanmac", "sheet2@example.com")
res, err := svc.ImportOrgChart(ctx, "root-id", &buf, "test.xlsx", "")
assert.NoError(t, err)
assert.NotNil(t, res)
}
func TestImportOrgChart_MessyHeader(t *testing.T) {
tenantRepo := new(mockTenantRepo)
ugRepo := new(mockUserGroupRepo)
userRepo := new(mockUserRepo)
ketoRepo := new(mockKetoOutboxRepo)
kratos := new(mockKratosService)
svc := NewOrgChartService(tenantRepo, ugRepo, userRepo, ketoRepo, kratos)
csvData := " 이메일(ID)* , 성 명 , [소속] \n" +
"messy@example.com,메시,바론"
ctx := context.Background()
setupMocksForImport(tenantRepo, ugRepo, userRepo, ketoRepo, kratos, ctx, "baron", "messy@example.com")
res, err := svc.ImportOrgChart(ctx, "root-id", bytes.NewReader([]byte(csvData)), "test.csv", "")
assert.NoError(t, err)
assert.NotNil(t, res)
}
func (m *mockKratosService) ListIdentitySessions(ctx context.Context, identityID string) ([]KratosSession, error) {
return nil, nil
}
func (m *mockKratosService) GetSession(ctx context.Context, sessionID string) (*KratosSession, error) {
return nil, nil
}
func (m *mockKratosService) DeleteSession(ctx context.Context, sessionID string) error {
return nil
}