package service import ( "baron-sso-backend/internal/domain" "context" "testing" "github.com/stretchr/testify/require" ) func TestWorksmobileSyncServiceRejectsAliasLocalPartAlreadyUsedByOtherUser(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") rootID := "root-tenant" tenantID := "saman-tenant" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } tenant := domain.Tenant{ ID: tenantID, Slug: "saman", Name: "삼안", Type: domain.TenantTypeCompany, ParentID: &rootID, Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, } target := domain.User{ ID: "target-user", Email: "target@samaneng.com", Name: "Target", TenantID: &tenantID, Metadata: domain.JSONMap{ "aliasEmails": []any{"used@hanmaceng.co.kr"}, }, } existing := domain.User{ ID: "existing-user", Email: "used@samaneng.com", Name: "Existing", TenantID: &tenantID, } outboxRepo := &fakeWorksmobileOutboxRepo{} service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, tenantID: tenant}, list: []domain.Tenant{root, tenant}}, &fakeWorksmobileUserRepo{byID: map[string]domain.User{target.ID: target}, byTenant: []domain.User{target, existing}}, outboxRepo, nil, ) item, err := service.EnqueueUserSync(context.Background(), rootID, target.ID) require.Nil(t, item) require.Error(t, err) require.Contains(t, err.Error(), "이미 사용 중") require.Empty(t, outboxRepo.created) } func TestWorksmobileSyncServiceEnqueuesSuspendedUserStatusWithOrganizations(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") rootID := "root-tenant" tenantID := "saman-tenant" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "Hanmac Family", } tenant := domain.Tenant{ ID: tenantID, Slug: "saman", Name: "Saman", Type: domain.TenantTypeCompany, ParentID: &rootID, Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, } target := domain.User{ ID: "target-user", Email: "target@samaneng.com", Name: "Target", Status: domain.UserStatusSuspended, TenantID: &tenantID, } outboxRepo := &fakeWorksmobileOutboxRepo{} service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, tenantID: tenant}, list: []domain.Tenant{root, tenant}}, &fakeWorksmobileUserRepo{byID: map[string]domain.User{target.ID: target}, byTenant: []domain.User{target}}, outboxRepo, nil, ) item, err := service.EnqueueUserSync(context.Background(), rootID, target.ID) require.NoError(t, err) require.NotNil(t, item) require.Len(t, outboxRepo.created, 1) require.Equal(t, domain.WorksmobileActionSuspend, outboxRepo.created[0].Action) require.Equal(t, domain.UserStatusSuspended, outboxRepo.created[0].Payload["baronStatus"]) request, ok := outboxRepo.created[0].Payload["request"].(WorksmobileUserPayload) require.True(t, ok) require.NotEmpty(t, request.Organizations) require.Equal(t, "target@samaneng.com", outboxRepo.created[0].Payload["loginEmail"]) } func TestWorksmobileSyncServiceOverviewExposesAdminTenantIDForPasswordManageLink(t *testing.T) { t.Setenv("WORKS_ADMIN_TENANT_ID", "works-tenant-1") root := domain.Tenant{ ID: "root-tenant", Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{root.ID: root}}, &fakeWorksmobileUserRepo{}, &fakeWorksmobileOutboxRepo{}, nil, ) overview, err := service.GetTenantOverview(context.Background(), root.ID) require.NoError(t, err) require.Equal(t, "works-tenant-1", overview.Config.AdminTenantID) } func TestCompareWorksmobileGroupsUsesOrganizationsAndBarongroupChildCompanies(t *testing.T) { parentID := "root-tenant" root := domain.Tenant{ ID: parentID, Name: "한맥가족", Slug: HanmacFamilyTenantSlug, Type: domain.TenantTypeCompanyGroup, } hanmac := domain.Tenant{ ID: "hanmac-tenant", Name: "한맥기술", Slug: "hanmac", Type: domain.TenantTypeCompany, ParentID: &parentID, } barongroup := domain.Tenant{ ID: "barongroup-tenant", Name: "바론그룹", Slug: "baron-group", Type: domain.TenantTypeCompany, ParentID: &parentID, } barongroupChildCompany := domain.Tenant{ ID: "barongroup-child-company", Name: "바론그룹 하위 회사", Type: domain.TenantTypeCompany, ParentID: &barongroup.ID, } organization := domain.Tenant{ ID: "organization-tenant", Name: "정규 조직", Type: domain.TenantTypeOrganization, ParentID: &hanmac.ID, } legacyUserGroup := domain.Tenant{ ID: "legacy-user-group-tenant", Name: "레거시 사용자 그룹", Type: domain.TenantTypeUserGroup, ParentID: &hanmac.ID, } items := compareWorksmobileGroups( []domain.Tenant{root, hanmac, barongroup, barongroupChildCompany, organization, legacyUserGroup}, []WorksmobileRemoteGroup{ {ID: "works-root", ExternalID: root.ID, DisplayName: root.Name}, {ID: "works-hanmac", ExternalID: hanmac.ID, DisplayName: hanmac.Name, Email: "hanmac@hanmaceng.co.kr"}, {ID: "works-barongroup", ExternalID: barongroup.ID, DisplayName: barongroup.Name}, {ID: "works-barongroup-child", ExternalID: barongroupChildCompany.ID, DisplayName: barongroupChildCompany.Name}, {ID: "works-organization", ExternalID: organization.ID, DisplayName: organization.Name, ParentID: "works-hanmac"}, {ID: "works-legacy-user-group", ExternalID: legacyUserGroup.ID, DisplayName: legacyUserGroup.Name}, {ID: "works-orphan", ExternalID: "works-orphan", DisplayName: "WORKS 전용 조직"}, }, true, ) require.Len(t, items, 3) require.Equal(t, barongroupChildCompany.ID, items[0].BaronID) require.Equal(t, "matched", items[0].Status) require.Equal(t, organization.ID, items[1].BaronID) require.Equal(t, "matched", items[1].Status) require.Equal(t, "works-hanmac", items[1].WorksmobileParentID) require.Equal(t, hanmac.Name, items[1].WorksmobileParentName) require.Equal(t, "hanmac@hanmaceng.co.kr", items[1].WorksmobileParentEmail) require.Equal(t, hanmac.ID, items[1].WorksmobileParentExternalKey) require.Equal(t, "works-hanmac", items[1].BaronParentWorksmobileID) require.Equal(t, hanmac.Name, items[1].BaronParentWorksmobileName) require.Equal(t, "hanmac@hanmaceng.co.kr", items[1].BaronParentWorksmobileEmail) require.Equal(t, "works-orphan", items[2].ExternalKey) require.Equal(t, "missing_in_baron", items[2].Status) } func TestWorksmobileSyncServiceRejectsDomainCompanyOrgUnitSync(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") rootID := "root-tenant" companyID := "company-tenant" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } company := domain.Tenant{ ID: companyID, Slug: "saman", Name: "삼안", Type: domain.TenantTypeCompany, ParentID: &rootID, Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, } outboxRepo := &fakeWorksmobileOutboxRepo{} service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, companyID: company}, list: []domain.Tenant{root, company}}, &fakeWorksmobileUserRepo{}, outboxRepo, nil, ) item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, companyID) require.Nil(t, item) require.Error(t, err) require.Contains(t, err.Error(), "worksmobile orgunit tenant") require.Empty(t, outboxRepo.created) } func TestWorksmobileSyncServiceEnqueuesBarongroupChildCompanyOrgUnitSync(t *testing.T) { t.Setenv("BARONGROUP_DOMAIN_ID", "1004") rootID := "root-tenant" barongroupID := "barongroup-tenant" companyID := "barongroup-child-company" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } barongroup := domain.Tenant{ ID: barongroupID, Slug: "baron-group", Name: "바론그룹", Type: domain.TenantTypeCompany, ParentID: &rootID, } company := domain.Tenant{ ID: companyID, Slug: "barongroup-child", Name: "바론그룹 하위 회사", Type: domain.TenantTypeCompany, ParentID: &barongroupID, } outboxRepo := &fakeWorksmobileOutboxRepo{} service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, barongroupID: barongroup, companyID: company}, list: []domain.Tenant{root, barongroup, company}}, &fakeWorksmobileUserRepo{}, outboxRepo, nil, ) item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, companyID) require.NoError(t, err) require.NotNil(t, item) require.Len(t, outboxRepo.created, 1) request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload) require.Equal(t, companyID, request.OrgUnitExternalKey) require.Empty(t, request.ParentOrgUnitID) } func TestWorksmobileSyncServiceEnqueuesOrganizationOrgUnitSync(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") rootID := "root-tenant" companyID := "company-tenant" organizationID := "organization-tenant" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } company := domain.Tenant{ ID: companyID, Slug: "saman", Name: "삼안", Type: domain.TenantTypeCompany, ParentID: &rootID, Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, } organization := domain.Tenant{ ID: organizationID, Slug: "engineering", Name: "기술본부", Type: domain.TenantTypeOrganization, ParentID: &companyID, } outboxRepo := &fakeWorksmobileOutboxRepo{} service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{ tenants: map[string]domain.Tenant{rootID: root, companyID: company, organizationID: organization}, list: []domain.Tenant{root, company, organization}, }, &fakeWorksmobileUserRepo{}, outboxRepo, nil, ) item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, organizationID) require.NoError(t, err) require.NotNil(t, item) require.Len(t, outboxRepo.created, 1) request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload) require.Equal(t, organizationID, request.OrgUnitExternalKey) require.Empty(t, request.ParentOrgUnitID) require.Equal(t, 1, request.DisplayOrder) } func TestWorksmobileSyncServiceEnqueuesExternalKeyMissingOrgUnitDelete(t *testing.T) { rootID := "root-tenant" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } outboxRepo := &fakeWorksmobileOutboxRepo{} client := &fakeWorksmobileDirectoryClient{ groups: []WorksmobileRemoteGroup{ { ID: "works-org-1", DisplayName: "WORKS 전용 조직", DomainID: 1001, ParentID: "works-parent", }, }, } service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root}}, &fakeWorksmobileUserRepo{}, outboxRepo, client, ) item, err := service.EnqueueOrgUnitDelete(context.Background(), rootID, "works-org-1") require.NoError(t, err) require.NotNil(t, item) require.Len(t, outboxRepo.created, 1) require.Equal(t, domain.WorksmobileActionDelete, outboxRepo.created[0].Action) require.Equal(t, "works-org-1", outboxRepo.created[0].Payload["worksmobileId"]) } func TestWorksmobileSyncServiceEnqueuesExternalKeyPresentWorksOnlyOrgUnitDelete(t *testing.T) { rootID := "root-tenant" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } outboxRepo := &fakeWorksmobileOutboxRepo{} client := &fakeWorksmobileDirectoryClient{ groups: []WorksmobileRemoteGroup{ { ID: "works-org-1", ExternalID: "baron-tenant-1", ParentID: "works-parent", }, }, } service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root}}, &fakeWorksmobileUserRepo{}, outboxRepo, client, ) item, err := service.EnqueueOrgUnitDelete(context.Background(), rootID, "works-org-1") require.NoError(t, err) require.NotNil(t, item) require.Len(t, outboxRepo.created, 1) require.Equal(t, domain.WorksmobileActionDelete, outboxRepo.created[0].Action) require.Equal(t, "baron-tenant-1", outboxRepo.created[0].Payload["externalKey"]) } func TestWorksmobileSyncServiceReconcilesWorksOnlyOrgUnitBySlugLocalPart(t *testing.T) { t.Setenv("GPDTDC_DOMAIN_ID", "1001") rootID := "root-tenant" orgID := "baron-org-1" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } organization := domain.Tenant{ ID: orgID, Slug: "tech-dev-center", Name: "기술개발센터", Type: domain.TenantTypeOrganization, ParentID: &rootID, } outboxRepo := &fakeWorksmobileOutboxRepo{} client := &fakeWorksmobileDirectoryClient{ groups: []WorksmobileRemoteGroup{ { ID: "works-org-1", ExternalID: "legacy-external-key", DisplayName: "기술개발센터", MailLocalPart: "tech-dev-center", DomainID: 1001, ParentID: "works-parent", }, }, } service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{ tenants: map[string]domain.Tenant{rootID: root, orgID: organization}, list: []domain.Tenant{root, organization}, }, &fakeWorksmobileUserRepo{}, outboxRepo, client, ) item, err := service.EnqueueOrgUnitDelete(context.Background(), rootID, "works-org-1") require.NoError(t, err) require.NotNil(t, item) require.Len(t, outboxRepo.created, 1) require.Equal(t, domain.WorksmobileActionUpsert, outboxRepo.created[0].Action) require.Equal(t, orgID, outboxRepo.created[0].ResourceID) request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload) require.Equal(t, orgID, request.OrgUnitExternalKey) require.Equal(t, "tech-dev-center", outboxRepo.created[0].Payload["matchLocalPart"]) } func TestCompareWorksmobileGroupsFillsParentDisplayFromBaronParentMatch(t *testing.T) { rootID := "root-tenant" parent := domain.Tenant{ ID: "parent-tenant", Name: "삼안", Slug: "saman", Type: domain.TenantTypeCompany, ParentID: &rootID, } child := domain.Tenant{ ID: "child-tenant", Name: "업무", Slug: "operations", Type: domain.TenantTypeOrganization, ParentID: &parent.ID, } items := compareWorksmobileGroups( []domain.Tenant{ {ID: rootID, Name: "한맥가족", Slug: HanmacFamilyTenantSlug, Type: domain.TenantTypeCompanyGroup}, parent, child, }, []WorksmobileRemoteGroup{ {ID: "works-parent", ExternalID: parent.ID, DisplayName: "삼안", Email: "saman@samaneng.com"}, {ID: "works-child", ExternalID: child.ID, DisplayName: "업무", ParentID: "works-parent"}, }, true, ) require.Len(t, items, 1) require.Equal(t, child.ID, items[0].BaronID) require.Equal(t, "works-parent", items[0].WorksmobileParentID) require.Equal(t, "삼안", items[0].WorksmobileParentName) require.Equal(t, "saman@samaneng.com", items[0].WorksmobileParentEmail) require.Equal(t, parent.ID, items[0].WorksmobileParentExternalKey) } func TestWorksmobileSyncServiceReconcilesTopLevelWorksOnlyOrgUnitBeforeProtectedDeleteGuard(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") rootID := "root-tenant" orgID := "baron-operations" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } samanID := "saman-tenant" saman := domain.Tenant{ ID: samanID, Slug: "saman", Name: "삼안", Type: domain.TenantTypeCompany, ParentID: &rootID, Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, } organization := domain.Tenant{ ID: orgID, Slug: "operations", Name: "업무", Type: domain.TenantTypeOrganization, ParentID: &samanID, } outboxRepo := &fakeWorksmobileOutboxRepo{} client := &fakeWorksmobileDirectoryClient{ groups: []WorksmobileRemoteGroup{ { ID: "works-operations", ExternalID: "legacy-operations-id", DisplayName: "업무팀", Email: "operations@samaneng.com", MailLocalPart: "operations", DomainID: 1001, }, }, } service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{ tenants: map[string]domain.Tenant{rootID: root, samanID: saman, orgID: organization}, list: []domain.Tenant{root, saman, organization}, }, &fakeWorksmobileUserRepo{}, outboxRepo, client, ) item, err := service.EnqueueOrgUnitDelete(context.Background(), rootID, "works-operations") require.NoError(t, err) require.NotNil(t, item) require.Len(t, outboxRepo.created, 1) require.Equal(t, domain.WorksmobileActionUpsert, outboxRepo.created[0].Action) require.Equal(t, orgID, outboxRepo.created[0].ResourceID) request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload) require.Equal(t, orgID, request.OrgUnitExternalKey) require.Equal(t, "operations", outboxRepo.created[0].Payload["matchLocalPart"]) } func TestWorksmobileSyncServiceRejectsProtectedDomainRootOrgUnitDelete(t *testing.T) { rootID := "root-tenant" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } outboxRepo := &fakeWorksmobileOutboxRepo{} client := &fakeWorksmobileDirectoryClient{ groups: []WorksmobileRemoteGroup{ { ID: "works-root", DisplayName: "한맥기술", }, }, } service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root}, list: []domain.Tenant{root}}, &fakeWorksmobileUserRepo{}, outboxRepo, client, ) item, err := service.EnqueueOrgUnitDelete(context.Background(), rootID, "works-root") require.Nil(t, item) require.Error(t, err) require.Contains(t, err.Error(), "protected worksmobile domain root") require.Empty(t, outboxRepo.created) } func TestWorksmobileSyncServiceTreatsHanmacFamilyChildCompaniesAsDomainRoots(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") t.Setenv("HANMAC_DOMAIN_ID", "1002") t.Setenv("GPDTDC_DOMAIN_ID", "1003") t.Setenv("BARONGROUP_DOMAIN_ID", "1004") rootID := "root-tenant" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } tests := []struct { name string company domain.Tenant organization domain.Tenant wantDomainID int64 wantEmail string }{ { name: "saman", company: domain.Tenant{ ID: "company-saman", Slug: "saman", Name: "삼안", Type: domain.TenantTypeCompany, ParentID: &rootID, Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, }, organization: domain.Tenant{ ID: "org-saman-planning", Slug: "saman-planning", Name: "삼안 기획팀", Type: domain.TenantTypeOrganization, }, wantDomainID: 1001, wantEmail: "saman-planning@samaneng.com", }, { name: "hanmac", company: domain.Tenant{ ID: "company-hanmac", Slug: "hanmac", Name: "한맥기술", Type: domain.TenantTypeCompany, ParentID: &rootID, Domains: []domain.TenantDomain{{Domain: "hanmaceng.co.kr"}}, }, organization: domain.Tenant{ ID: "org-hanmac-planning", Slug: "hanmac-planning", Name: "한맥 기획팀", Type: domain.TenantTypeOrganization, }, wantDomainID: 1002, wantEmail: "hanmac-planning@hanmaceng.co.kr", }, { name: "gpdtdc", company: domain.Tenant{ ID: "company-gpdtdc", Slug: "gpdtdc", Name: "총괄기획&기술개발센터", Type: domain.TenantTypeCompany, ParentID: &rootID, }, organization: domain.Tenant{ ID: "org-gpdtdc-planning", Slug: "gpdtdc-planning", Name: "총괄 기획팀", Type: domain.TenantTypeOrganization, }, wantDomainID: 1003, wantEmail: "gpdtdc-planning@baroncs.co.kr", }, { name: "baron-group", company: domain.Tenant{ ID: "company-barongroup", Slug: "baron-group", Name: "바론그룹", Type: domain.TenantTypeCompany, ParentID: &rootID, }, organization: domain.Tenant{ ID: "org-baron-planning", Slug: "baron-planning", Name: "바론 기획팀", Type: domain.TenantTypeOrganization, }, wantDomainID: 1004, wantEmail: "baron-planning@brsw.kr", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { organization := tt.organization organization.ParentID = &tt.company.ID outboxRepo := &fakeWorksmobileOutboxRepo{} service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{ tenants: map[string]domain.Tenant{ rootID: root, tt.company.ID: tt.company, organization.ID: organization, }, list: []domain.Tenant{root, tt.company, organization}, }, &fakeWorksmobileUserRepo{}, outboxRepo, nil, ) item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, organization.ID) require.NoError(t, err) require.NotNil(t, item) require.Len(t, outboxRepo.created, 1) request := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload) require.Equal(t, organization.ID, request.OrgUnitExternalKey) require.Equal(t, tt.wantDomainID, request.DomainID) require.Equal(t, tt.wantEmail, request.Email) require.Empty(t, request.ParentOrgUnitID) }) } } func TestWorksmobileDomainClassificationUsesAncestorCompanyForGPDTDCOrganization(t *testing.T) { t.Setenv("GPDTDC_DOMAIN_ID", "1003") rootID := "root-tenant" companyID := "company-tenant" organizationID := "organization-tenant" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } company := domain.Tenant{ ID: companyID, Slug: "gpdtdc", Name: "총괄기획&기술개발센터", Type: domain.TenantTypeCompany, ParentID: &rootID, Domains: []domain.TenantDomain{{Domain: "baroncs.co.kr"}}, } organization := domain.Tenant{ ID: organizationID, Slug: "gpd", Name: "총괄기획실", Type: domain.TenantTypeOrganization, ParentID: &companyID, } tenantByID := worksmobileTenantByID([]domain.Tenant{root, company, organization}) domainTenant := worksmobileDomainClassificationTenant(organization, tenantByID) payload, err := BuildWorksmobileOrgUnitPayloadForDomainTenant(organization, domainTenant, nil, 1) require.NoError(t, err) require.Equal(t, companyID, domainTenant.ID) require.Equal(t, int64(1003), payload.DomainID) require.Equal(t, "gpd@baroncs.co.kr", payload.Email) } func TestWorksmobileSyncServiceKeepsCompanyUsersInComparisonScope(t *testing.T) { rootID := "root-tenant" companyID := "company-tenant" userGroupID := "user-group-tenant" root := domain.Tenant{ ID: rootID, Slug: HanmacFamilyTenantSlug, Name: "한맥가족", } company := domain.Tenant{ ID: companyID, Name: "계열사", Type: domain.TenantTypeCompany, ParentID: &rootID, } userGroup := domain.Tenant{ ID: userGroupID, Name: "연동 조직", Type: domain.TenantTypeOrganization, ParentID: &companyID, } userRepo := &fakeWorksmobileUserRepo{} service := NewWorksmobileSyncService( &fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root, companyID: company, userGroupID: userGroup}, list: []domain.Tenant{root, company, userGroup}}, userRepo, &fakeWorksmobileOutboxRepo{}, &fakeWorksmobileDirectoryClient{}, ) _, err := service.GetComparison(context.Background(), rootID, true) require.NoError(t, err) require.ElementsMatch(t, []string{companyID, userGroupID}, userRepo.requestedTenantIDs) } type fakeWorksmobileTenantService struct { tenants map[string]domain.Tenant list []domain.Tenant } func (f *fakeWorksmobileTenantService) RegisterTenant(ctx context.Context, name, slug, tenantType, description string, domains []string, parentID *string, creatorID string) (*domain.Tenant, error) { return nil, nil } func (f *fakeWorksmobileTenantService) RequestRegistration(ctx context.Context, name, slug, description string, domainName string, adminEmail string) (*domain.Tenant, error) { return nil, nil } func (f *fakeWorksmobileTenantService) GetTenantByDomain(ctx context.Context, emailDomain string) (*domain.Tenant, error) { return nil, nil } func (f *fakeWorksmobileTenantService) GetTenantBySlug(ctx context.Context, slug string) (*domain.Tenant, error) { return nil, nil } func (f *fakeWorksmobileTenantService) GetTenant(ctx context.Context, id string) (*domain.Tenant, error) { tenant := f.tenants[id] return &tenant, nil } func (f *fakeWorksmobileTenantService) ListTenants(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) { return f.list, int64(len(f.list)), nil } func (f *fakeWorksmobileTenantService) ListManageableTenants(ctx context.Context, userID string) ([]domain.Tenant, error) { return nil, nil } func (f *fakeWorksmobileTenantService) ListJoinedTenants(ctx context.Context, userID string) ([]domain.Tenant, error) { return nil, nil } func (f *fakeWorksmobileTenantService) IsDomainAllowed(ctx context.Context, domainName string) (bool, error) { return false, nil } func (f *fakeWorksmobileTenantService) ApproveTenant(ctx context.Context, id string) error { return nil } func (f *fakeWorksmobileTenantService) ProvisionTenantByDomain(ctx context.Context, domainName string) (*domain.Tenant, error) { return nil, nil } func (f *fakeWorksmobileTenantService) SetKetoService(keto KetoService) {} func (f *fakeWorksmobileTenantService) DeleteTenantsBulk(ctx context.Context, ids []string) error { return nil } type fakeWorksmobileUserRepo struct { byID map[string]domain.User byTenant []domain.User requestedTenantIDs []string } func (f *fakeWorksmobileUserRepo) Create(ctx context.Context, user *domain.User) error { return nil } func (f *fakeWorksmobileUserRepo) Update(ctx context.Context, user *domain.User) error { return nil } func (f *fakeWorksmobileUserRepo) FindByEmail(ctx context.Context, email string) (*domain.User, error) { return nil, nil } func (f *fakeWorksmobileUserRepo) FindByID(ctx context.Context, id string) (*domain.User, error) { user := f.byID[id] return &user, nil } func (f *fakeWorksmobileUserRepo) FindByIDs(ctx context.Context, ids []string) ([]domain.User, error) { return nil, nil } func (f *fakeWorksmobileUserRepo) ListByTenant(ctx context.Context, tenantID string) ([]domain.User, error) { return nil, nil } func (f *fakeWorksmobileUserRepo) List(ctx context.Context, offset, limit int, search string, tenantSlug string) ([]domain.User, int64, error) { return nil, 0, nil } func (f *fakeWorksmobileUserRepo) CountByTenant(ctx context.Context, tenantID string) (int64, error) { return 0, nil } func (f *fakeWorksmobileUserRepo) CountByTenantIDs(ctx context.Context, tenantIDs []string) (map[string]int64, error) { return nil, nil } func (f *fakeWorksmobileUserRepo) CountByCompanyCodes(ctx context.Context, codes []string) (map[string]int64, error) { return nil, nil } func (f *fakeWorksmobileUserRepo) FindByTenantIDs(ctx context.Context, tenantIDs []string) ([]domain.User, error) { f.requestedTenantIDs = append([]string(nil), tenantIDs...) return f.byTenant, nil } func (f *fakeWorksmobileUserRepo) FindByCompanyCodes(ctx context.Context, codes []string) ([]domain.User, error) { return nil, nil } func (f *fakeWorksmobileUserRepo) Delete(ctx context.Context, id string) error { return nil } func (f *fakeWorksmobileUserRepo) UpdateUserLoginIDs(ctx context.Context, userID string, loginIDs []domain.UserLoginID) error { return nil } func (f *fakeWorksmobileUserRepo) GetUserLoginIDs(ctx context.Context, userID string) ([]domain.UserLoginID, error) { return nil, nil } func (f *fakeWorksmobileUserRepo) IsLoginIDTaken(ctx context.Context, loginID string) (bool, error) { return false, nil } func (f *fakeWorksmobileUserRepo) FindTenantIDByLoginID(ctx context.Context, loginID string) (string, error) { return "", nil }