package service import ( "baron-sso-backend/internal/domain" "strings" "testing" "github.com/stretchr/testify/require" ) func TestBuildWorksmobileOrgUnitPayloadUsesTenantExternalKeyAndEnvDomainClassification(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") parentID := "11111111-1111-1111-1111-111111111111" tenant := domain.Tenant{ ID: "22222222-2222-2222-2222-222222222222", Name: "Saman Engineering", ParentID: &parentID, Domains: []domain.TenantDomain{ {Domain: "samaneng.com"}, }, } rootConfig := domain.JSONMap{ "worksmobile": map[string]any{ "domainMappings": map[string]any{ "samaneng.com": float64(9999), }, }, } payload, err := BuildWorksmobileOrgUnitPayload(tenant, rootConfig, 7) require.NoError(t, err) require.Equal(t, int64(1001), payload.DomainID) require.Equal(t, "Saman Engineering", payload.OrgUnitName) require.Equal(t, tenant.ID, payload.OrgUnitExternalKey) require.Equal(t, "externalKey:"+parentID, payload.ParentOrgUnitID) require.Equal(t, 7, payload.DisplayOrder) } func TestNormalizeRootChildWorksmobileOrgUnitParentClearsCrossDomainParent(t *testing.T) { rootID := "038326b6-954a-48a7-a85f-efd83f62b82a" payload := WorksmobileOrgUnitPayload{ParentOrgUnitID: "externalKey:" + rootID} tenant := domain.Tenant{ParentID: &rootID} normalized := normalizeWorksmobileOrgUnitParent(payload, tenant, nil, rootID) require.Empty(t, normalized.ParentOrgUnitID) } func TestBuildWorksmobileUserPayloadMapsBaronUserAndPrimaryTenant(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") tenantID := "33333333-3333-3333-3333-333333333333" user := domain.User{ ID: "44444444-4444-4444-4444-444444444444", Email: "john1@samaneng.com", Name: "John Doe", Phone: "+19144812222", Position: "Manager", JobTitle: "Sales management", TenantID: &tenantID, Metadata: domain.JSONMap{ "employee_id": "AB001", }, } tenant := domain.Tenant{ ID: tenantID, Slug: "saman", Name: "Saman", Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, } rootConfig := domain.JSONMap{ "worksmobile": map[string]any{ "domainMappings": map[string]any{ "samaneng.com": int64(9999), }, }, } payload, err := BuildWorksmobileUserPayload(user, tenant, rootConfig) require.NoError(t, err) require.Equal(t, int64(1001), payload.DomainID) require.Equal(t, "john1@samaneng.com", payload.Email) require.Equal(t, user.ID, payload.UserExternalKey) require.Equal(t, "John Doe", payload.UserName.LastName) require.Equal(t, "+19144812222", payload.CellPhone) require.Equal(t, "AB001", payload.EmployeeNumber) require.Equal(t, "Sales management", payload.Task) require.Empty(t, payload.PrivateEmail) require.Empty(t, payload.AliasEmails) require.Equal(t, "ko_KR", payload.Locale) require.Equal(t, "ADMIN", payload.PasswordConfig.PasswordCreationType) require.Len(t, payload.PasswordConfig.Password, 16) require.True(t, containsAny(payload.PasswordConfig.Password, "0123456789")) require.True(t, containsAny(payload.PasswordConfig.Password, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")) require.True(t, containsAny(payload.PasswordConfig.Password, "!@#$%^&*()-_=+[]{}")) require.Len(t, payload.Organizations, 1) require.Equal(t, int64(1001), payload.Organizations[0].DomainID) require.True(t, payload.Organizations[0].Primary) require.Equal(t, "externalKey:"+tenantID, payload.Organizations[0].OrgUnits[0].OrgUnitID) } func TestBuildWorksmobileUserPayloadMapsAdditionalAppointmentsToOrgUnits(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") t.Setenv("HANMAC_DOMAIN_ID", "1002") primaryTenantID := "33333333-3333-3333-3333-333333333333" secondaryTenantID := "55555555-5555-5555-5555-555555555555" user := domain.User{ ID: "44444444-4444-4444-4444-444444444444", Email: "john1@samaneng.com", Name: "John Doe", Phone: "+19144812222", TenantID: &primaryTenantID, Metadata: domain.JSONMap{ "additionalAppointments": []any{ map[string]any{ "tenantId": secondaryTenantID, "isPrimary": false, "isOwner": true, "jobTitle": "PM", "position": "팀장", }, map[string]any{ "tenantId": primaryTenantID, "isPrimary": true, "isOwner": false, "jobTitle": "Engineering", "position": "책임", }, }, }, } primaryTenant := domain.Tenant{ ID: primaryTenantID, Slug: "saman", Name: "Saman", Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, } secondaryTenant := domain.Tenant{ ID: secondaryTenantID, Slug: "hanmac", Name: "Hanmac", Domains: []domain.TenantDomain{{Domain: "hanmaceng.co.kr"}}, } payload, err := BuildWorksmobileUserPayloadForDomainTenants( user, primaryTenant, map[string]domain.Tenant{ primaryTenantID: primaryTenant, secondaryTenantID: secondaryTenant, }, nil, ) require.NoError(t, err) require.Equal(t, "Engineering", payload.Task) require.Len(t, payload.Organizations, 2) require.Equal(t, int64(1001), payload.Organizations[0].DomainID) require.True(t, payload.Organizations[0].Primary) require.Equal(t, "externalKey:"+primaryTenantID, payload.Organizations[0].OrgUnits[0].OrgUnitID) require.True(t, payload.Organizations[0].OrgUnits[0].Primary) require.NotNil(t, payload.Organizations[0].OrgUnits[0].IsManager) require.False(t, *payload.Organizations[0].OrgUnits[0].IsManager) require.Equal(t, int64(1002), payload.Organizations[1].DomainID) require.False(t, payload.Organizations[1].Primary) require.Equal(t, "externalKey:"+secondaryTenantID, payload.Organizations[1].OrgUnits[0].OrgUnitID) require.False(t, payload.Organizations[1].OrgUnits[0].Primary) require.NotNil(t, payload.Organizations[1].OrgUnits[0].IsManager) require.True(t, *payload.Organizations[1].OrgUnits[0].IsManager) } func TestResolveWorksmobileDomainIDFromTenantIgnoresRootDomainMappings(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") rootConfig := domain.JSONMap{ "worksmobile": map[string]any{ "domainMappings": map[string]any{ "samaneng.com": int64(9999), }, }, } got, err := ResolveWorksmobileDomainIDFromTenant( domain.Tenant{ Slug: "saman", Name: "삼안", Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, }, rootConfig, ) require.NoError(t, err) require.Equal(t, int64(1001), got) } func TestResolveWorksmobileDomainIDFromTenantRequiresFamilyDomainEnv(t *testing.T) { rootConfig := domain.JSONMap{ "worksmobile": map[string]any{ "domainMappings": map[string]any{ "samaneng.com": int64(9999), }, }, } _, err := ResolveWorksmobileDomainIDFromTenant( domain.Tenant{ Slug: "saman", Name: "삼안", Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, }, rootConfig, ) require.Error(t, err) require.Contains(t, err.Error(), "SAMAN_DOMAIN_ID") } func TestResolveWorksmobileDomainIDUsesEnvFamilyFallbacks(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") tests := []struct { name string tenant domain.Tenant want int64 }{ { name: "saman", tenant: domain.Tenant{Slug: "saman", Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}}, want: 1001, }, { name: "hanmac", tenant: domain.Tenant{Slug: "hanmac", Domains: []domain.TenantDomain{{Domain: "hanmaceng.co.kr"}}}, want: 1002, }, { name: "gpdtdc", tenant: domain.Tenant{Slug: "gpdtdc", Name: "총괄기획&기술개발센터"}, want: 1003, }, { name: "barongroup fallback", tenant: domain.Tenant{Slug: "family-company", Name: "기타 가족사"}, want: 1004, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ResolveWorksmobileDomainIDFromTenant(tt.tenant, nil) require.NoError(t, err) require.Equal(t, tt.want, got) }) } } func TestBuildWorksmobileUserPayloadAddsHanmacEmployeeAlias(t *testing.T) { t.Setenv("HANMAC_DOMAIN_ID", "1002") tenantID := "33333333-3333-3333-3333-333333333333" user := domain.User{ ID: "44444444-4444-4444-4444-444444444444", Email: "main@hanmaceng.co.kr", Name: "Hanmac User", TenantID: &tenantID, Metadata: domain.JSONMap{ "employee_id": "HM001", "personal_email": "private@example.com", }, } tenant := domain.Tenant{ ID: tenantID, Slug: "hanmac", Name: "한맥", Domains: []domain.TenantDomain{{Domain: "hanmaceng.co.kr"}}, } payload, err := BuildWorksmobileUserPayload(user, tenant, nil) require.NoError(t, err) require.Equal(t, int64(1002), payload.DomainID) require.Equal(t, []string{"hm001@hanmaceng.co.kr"}, payload.AliasEmails) require.Empty(t, payload.PrivateEmail) require.Equal(t, "ko_KR", payload.Locale) } func TestBuildWorksmobileUserPayloadAddsMultipleMetadataAliases(t *testing.T) { t.Setenv("SAMAN_DOMAIN_ID", "1001") tenantID := "33333333-3333-3333-3333-333333333333" user := domain.User{ ID: "44444444-4444-4444-4444-444444444444", Email: "main@samaneng.com", Name: "Saman User", TenantID: &tenantID, Metadata: domain.JSONMap{ "aliasEmails": []any{"alias1@samaneng.com", "alias2@samaneng.com", "main@samaneng.com"}, }, } tenant := domain.Tenant{ ID: tenantID, Slug: "saman", Name: "삼안", Domains: []domain.TenantDomain{{Domain: "samaneng.com"}}, } payload, err := BuildWorksmobileUserPayload(user, tenant, nil) require.NoError(t, err) require.Equal(t, []string{"alias1@samaneng.com", "alias2@samaneng.com"}, payload.AliasEmails) } func TestValidateWorksmobileAliasLocalPartsRejectsPrimaryAndAliasCollisions(t *testing.T) { err := ValidateWorksmobileAliasLocalParts( "main@samaneng.com", []string{"main@hanmaceng.co.kr"}, map[string]string{}, ) require.Error(t, err) require.Contains(t, err.Error(), "local-part") err = ValidateWorksmobileAliasLocalParts( "main@samaneng.com", []string{"alias@hanmaceng.co.kr"}, map[string]string{"alias": "existing-user"}, ) require.Error(t, err) require.Contains(t, err.Error(), "이미 사용 중") } func containsAny(value string, candidates string) bool { return strings.ContainsAny(value, candidates) } func TestWorksmobileUserStatusAction(t *testing.T) { require.Equal(t, WorksmobileUserActionUpsert, WorksmobileUserStatusAction(domain.UserStatusActive)) require.Equal(t, WorksmobileUserActionSuspend, WorksmobileUserStatusAction(domain.UserStatusInactive)) require.Equal(t, WorksmobileUserActionSuspend, WorksmobileUserStatusAction(domain.UserStatusSuspended)) require.Equal(t, WorksmobileUserActionSuspend, WorksmobileUserStatusAction(domain.UserStatusLeaveOfAbsence)) } func TestValidateWorksmobileExternalKeyRejectsUnsupportedCharacters(t *testing.T) { require.NoError(t, ValidateWorksmobileExternalKey("44444444-4444-4444-4444-444444444444")) require.Error(t, ValidateWorksmobileExternalKey("user/with/slash")) require.Error(t, ValidateWorksmobileExternalKey("user#with-hash")) }