1
0
forked from baron/baron-sso

feat: improve Worksmobile tenant sync handling

This commit is contained in:
2026-06-02 18:05:36 +09:00
parent d6d39ca300
commit d32ca69eee
58 changed files with 4035 additions and 1400 deletions

View File

@@ -494,6 +494,70 @@ func TestWorksmobileSyncServiceOverviewKeepsSafeRecentJobChangeLogPayload(t *tes
}, orgPayload["requestSummary"])
}
func TestWorksmobileSyncServiceEnqueueTenantUpsertReflectsChangedParentOrgUnit(t *testing.T) {
t.Setenv("SAMAN_DOMAIN_ID", "1001")
rootID := "root-tenant"
companyID := "saman-tenant"
newParentID := "new-parent-org"
childID := "child-org"
root := domain.Tenant{
ID: rootID,
Slug: HanmacFamilyTenantSlug,
Name: "한맥가족",
Type: domain.TenantTypeCompanyGroup,
}
company := domain.Tenant{
ID: companyID,
Slug: "saman",
Name: "삼안",
Type: domain.TenantTypeCompany,
ParentID: &rootID,
Domains: []domain.TenantDomain{{Domain: "samaneng.com"}},
}
newParent := domain.Tenant{
ID: newParentID,
Slug: "planning",
Name: "총괄기획",
Type: domain.TenantTypeOrganization,
ParentID: &companyID,
}
child := domain.Tenant{
ID: childID,
Slug: "people-growth",
Name: "인재성장",
Type: domain.TenantTypeOrganization,
ParentID: &newParentID,
}
outboxRepo := &fakeWorksmobileOutboxRepo{}
service := NewWorksmobileSyncService(
&fakeWorksmobileTenantService{
tenants: map[string]domain.Tenant{
rootID: root,
companyID: company,
newParentID: newParent,
childID: child,
},
list: []domain.Tenant{root, company, newParent, child},
},
&fakeWorksmobileUserRepo{},
outboxRepo,
nil,
)
err := service.EnqueueTenantUpsertIfInScope(context.Background(), child)
require.NoError(t, err)
require.Len(t, outboxRepo.created, 1)
require.Equal(t, domain.WorksmobileResourceOrgUnit, outboxRepo.created[0].ResourceType)
require.Equal(t, domain.WorksmobileActionUpsert, outboxRepo.created[0].Action)
require.Equal(t, childID, outboxRepo.created[0].ResourceID)
request, ok := outboxRepo.created[0].Payload["request"].(WorksmobileOrgUnitPayload)
require.True(t, ok)
require.Equal(t, childID, request.OrgUnitExternalKey)
require.Equal(t, "externalKey:"+newParentID, request.ParentOrgUnitID)
require.Equal(t, "people-growth", outboxRepo.created[0].Payload["matchLocalPart"])
}
func TestCompareWorksmobileGroupsUsesOrganizationsAndBarongroupChildCompanies(t *testing.T) {
parentID := "root-tenant"
root := domain.Tenant{
@@ -1085,10 +1149,34 @@ func TestWorksmobileSyncServiceRejectsProtectedDomainRootOrgUnitDelete(t *testin
require.Empty(t, outboxRepo.created)
}
func TestWorksmobileSyncServiceDeletesPendingJobsForTenantRoot(t *testing.T) {
rootID := "root-tenant"
root := domain.Tenant{
ID: rootID,
Slug: HanmacFamilyTenantSlug,
Name: "한맥가족",
}
outboxRepo := &fakeWorksmobileOutboxRepo{deletedPendingCount: 2}
service := NewWorksmobileSyncService(
&fakeWorksmobileTenantService{tenants: map[string]domain.Tenant{rootID: root}, list: []domain.Tenant{root}},
&fakeWorksmobileUserRepo{},
outboxRepo,
nil,
)
result, err := service.DeletePendingJobs(context.Background(), rootID)
require.NoError(t, err)
require.Equal(t, 2, result.DeletedCount)
require.Equal(t, rootID, outboxRepo.deletedPendingTenantRootID)
}
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("HALLA_DOMAIN_ID", "1005")
t.Setenv("WORKS_DEFAULT_DOMAIN_HALLA", "hallasanup.com")
t.Setenv("BARONGROUP_DOMAIN_ID", "1004")
rootID := "root-tenant"
root := domain.Tenant{
@@ -1177,6 +1265,43 @@ func TestWorksmobileSyncServiceTreatsHanmacFamilyChildCompaniesAsDomainRoots(t *
wantDomainID: 1004,
wantEmail: "baron-planning@brsw.kr",
},
{
name: "halla",
company: domain.Tenant{
ID: "company-halla",
Slug: "halla",
Name: "한라산업개발",
Type: domain.TenantTypeCompany,
ParentID: &rootID,
Domains: []domain.TenantDomain{{Domain: "hallasanup.com"}},
},
organization: domain.Tenant{
ID: "org-halla-planning",
Slug: "halla-planning",
Name: "한라 기획팀",
Type: domain.TenantTypeOrganization,
},
wantDomainID: 1005,
wantEmail: "halla-planning@hallasanup.com",
},
{
name: "hanlla legacy slug",
company: domain.Tenant{
ID: "company-hanlla",
Slug: "hanlla",
Name: "한라산업개발",
Type: domain.TenantTypeOrganization,
ParentID: &rootID,
},
organization: domain.Tenant{
ID: "org-hanlla-construction-sites",
Slug: "hanlla-construction-sites",
Name: "시공현장",
Type: domain.TenantTypeOrganization,
},
wantDomainID: 1005,
wantEmail: "hanlla-construction-sites@hallasanup.com",
},
}
for _, tt := range tests {
@@ -1467,6 +1592,181 @@ func TestWorksmobileSyncServiceBackfillDryRunSkipsArchivedUsers(t *testing.T) {
require.Equal(t, 1, outboxRepo.created[0].Payload["userCount"])
}
func TestWorksmobileSyncServiceBackfillDryRunSkipsWorksmobileExcludedSubtree(t *testing.T) {
rootID := "root-tenant"
excludedCompanyID := "excluded-company"
excludedOrgID := "excluded-org"
includedCompanyID := "included-company"
includedOrgID := "included-org"
root := domain.Tenant{
ID: rootID,
Slug: HanmacFamilyTenantSlug,
Name: "한맥가족",
}
excludedCompany := domain.Tenant{
ID: excludedCompanyID,
Slug: "saman",
Name: "삼안",
Type: domain.TenantTypeCompany,
ParentID: &rootID,
Config: domain.JSONMap{"worksmobileExcluded": true},
}
excludedOrg := domain.Tenant{
ID: excludedOrgID,
Slug: "excluded-team",
Name: "제외팀",
Type: domain.TenantTypeOrganization,
ParentID: &excludedCompanyID,
}
includedCompany := domain.Tenant{
ID: includedCompanyID,
Slug: "halla",
Name: "한라산업개발",
Type: domain.TenantTypeCompany,
ParentID: &rootID,
}
includedOrg := domain.Tenant{
ID: includedOrgID,
Slug: "included-team",
Name: "연동팀",
Type: domain.TenantTypeOrganization,
ParentID: &includedCompanyID,
}
excludedUser := domain.User{
ID: "excluded-user",
Email: "excluded@samaneng.com",
Name: "Excluded User",
TenantID: &excludedOrgID,
Status: domain.UserStatusActive,
}
includedUser := domain.User{
ID: "included-user",
Email: "included@hallasanup.com",
Name: "Included User",
TenantID: &includedOrgID,
Status: domain.UserStatusActive,
}
outboxRepo := &fakeWorksmobileOutboxRepo{}
service := NewWorksmobileSyncService(
&fakeWorksmobileTenantService{
tenants: map[string]domain.Tenant{
rootID: root,
excludedCompanyID: excludedCompany,
excludedOrgID: excludedOrg,
includedCompanyID: includedCompany,
includedOrgID: includedOrg,
},
list: []domain.Tenant{root, excludedCompany, excludedOrg, includedCompany, includedOrg},
},
&fakeWorksmobileUserRepo{byTenant: []domain.User{excludedUser, includedUser}},
outboxRepo,
nil,
)
dryRun, err := service.EnqueueBackfillDryRun(context.Background(), rootID)
require.NoError(t, err)
require.Equal(t, 1, dryRun.OrgUnitCount)
require.Equal(t, 1, dryRun.UserCount)
require.Len(t, outboxRepo.created, 1)
require.ElementsMatch(t, []string{includedOrgID}, outboxRepo.created[0].Payload["tenantIds"])
require.Equal(t, 1, outboxRepo.created[0].Payload["userCount"])
}
func TestWorksmobileSyncServiceRejectsExcludedOrgUnitSync(t *testing.T) {
rootID := "root-tenant"
excludedCompanyID := "excluded-company"
excludedOrgID := "excluded-org"
root := domain.Tenant{
ID: rootID,
Slug: HanmacFamilyTenantSlug,
Name: "한맥가족",
}
excludedCompany := domain.Tenant{
ID: excludedCompanyID,
Slug: "saman",
Name: "삼안",
Type: domain.TenantTypeCompany,
ParentID: &rootID,
Config: domain.JSONMap{"worksmobileExcluded": true},
}
excludedOrg := domain.Tenant{
ID: excludedOrgID,
Slug: "excluded-team",
Name: "제외팀",
Type: domain.TenantTypeOrganization,
ParentID: &excludedCompanyID,
}
outboxRepo := &fakeWorksmobileOutboxRepo{}
service := NewWorksmobileSyncService(
&fakeWorksmobileTenantService{
tenants: map[string]domain.Tenant{rootID: root, excludedCompanyID: excludedCompany, excludedOrgID: excludedOrg},
list: []domain.Tenant{root, excludedCompany, excludedOrg},
},
&fakeWorksmobileUserRepo{},
outboxRepo,
nil,
)
item, err := service.EnqueueOrgUnitSync(context.Background(), rootID, excludedOrgID)
require.Nil(t, item)
require.ErrorContains(t, err, "excluded from Worksmobile sync")
require.Empty(t, outboxRepo.created)
}
func TestWorksmobileSyncServiceSkipsExcludedTenantAndUserEventSync(t *testing.T) {
rootID := "root-tenant"
excludedCompanyID := "excluded-company"
excludedOrgID := "excluded-org"
root := domain.Tenant{
ID: rootID,
Slug: HanmacFamilyTenantSlug,
Name: "한맥가족",
}
excludedCompany := domain.Tenant{
ID: excludedCompanyID,
Slug: "saman",
Name: "삼안",
Type: domain.TenantTypeCompany,
ParentID: &rootID,
Config: domain.JSONMap{"worksmobileExcluded": true},
}
excludedOrg := domain.Tenant{
ID: excludedOrgID,
Slug: "excluded-team",
Name: "제외팀",
Type: domain.TenantTypeOrganization,
ParentID: &excludedCompanyID,
}
user := domain.User{
ID: "excluded-user",
Email: "excluded@samaneng.com",
Name: "Excluded User",
TenantID: &excludedOrgID,
Status: domain.UserStatusActive,
}
outboxRepo := &fakeWorksmobileOutboxRepo{}
service := NewWorksmobileSyncService(
&fakeWorksmobileTenantService{
tenants: map[string]domain.Tenant{rootID: root, excludedCompanyID: excludedCompany, excludedOrgID: excludedOrg},
list: []domain.Tenant{root, excludedCompany, excludedOrg},
},
&fakeWorksmobileUserRepo{byID: map[string]domain.User{user.ID: user}},
outboxRepo,
nil,
)
require.NoError(t, service.EnqueueTenantUpsertIfInScope(context.Background(), excludedOrg))
require.NoError(t, service.EnqueueTenantDeleteIfInScope(context.Background(), excludedOrg))
require.NoError(t, service.EnqueueUserUpsertIfInScope(context.Background(), user))
item, err := service.EnqueueUserSync(context.Background(), rootID, user.ID, "")
require.Nil(t, item)
require.ErrorContains(t, err, "excluded from Worksmobile sync")
require.Empty(t, outboxRepo.created)
}
func TestCompareWorksmobileUsersMarksManagerChangeNeedsUpdate(t *testing.T) {
tenantID := "tenant-leaf"
user := domain.User{
@@ -1751,7 +2051,23 @@ func (f *fakeWorksmobileUserRepo) CountByCompanyCodes(ctx context.Context, codes
func (f *fakeWorksmobileUserRepo) FindByTenantIDs(ctx context.Context, tenantIDs []string) ([]domain.User, error) {
f.requestedTenantIDs = append([]string(nil), tenantIDs...)
return f.byTenant, nil
if len(tenantIDs) == 0 {
return nil, nil
}
allowed := make(map[string]bool, len(tenantIDs))
for _, tenantID := range tenantIDs {
allowed[tenantID] = true
}
users := make([]domain.User, 0, len(f.byTenant))
for _, user := range f.byTenant {
if user.TenantID == nil {
continue
}
if allowed[*user.TenantID] {
users = append(users, user)
}
}
return users, nil
}
func (f *fakeWorksmobileUserRepo) FindByCompanyCodes(ctx context.Context, codes []string) ([]domain.User, error) {