Skip to content

Commit

Permalink
test: update converter unittest
Browse files Browse the repository at this point in the history
  • Loading branch information
narasux committed Nov 2, 2023
1 parent 2729335 commit 228bc22
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 19 deletions.
34 changes: 22 additions & 12 deletions src/bk-user/bkuser/apps/sync/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import phonenumbers
import pydantic
from django.conf import settings
from phonenumbers.phonenumberutil import NumberParseException

from bkuser.apps.data_source.constants import FieldMappingOperation
from bkuser.apps.data_source.data_models import DataSourceUserFieldMapping
Expand All @@ -37,7 +38,7 @@ def __init__(self, data_source: DataSource, logger: TaskLogger):
def _get_field_mapping(self) -> List[DataSourceUserFieldMapping]:
"""获取字段映射配置"""
if self.data_source.is_local:
self.logger.info("local data source field mapping can only generated from field settings.")
self.logger.info("local data source field mapping can only generated from field settings")
field_mapping = self._get_field_mapping_from_tenant_user_fields()
else:
# 1. 尝试从数据源配置中获取
Expand Down Expand Up @@ -81,35 +82,44 @@ def _get_field_mapping_from_tenant_user_fields(self) -> List[DataSourceUserField

def convert(self, user: RawDataSourceUser) -> DataSourceUser:
# TODO (su) 支持复杂字段映射类型,如表达式,目前都当作直接映射处理(目前只支持直接映射)
mapping = {m.source_field: m.target_field for m in self.field_mapping}
mapping = {m.target_field: m.source_field for m in self.field_mapping}
props = user.properties

username = props.get(mapping["username"])
# 1. 用户名是必须提供的,而且需要满足正则校验规则
if not username:
raise ValueError("username is required")
if not re.fullmatch(DATA_SOURCE_USERNAME_REGEX, username):
raise ValueError(f"username {username} not match pattern {DATA_SOURCE_USERNAME_REGEX.pattern}")
raise ValueError(f"username [{username}] not match pattern {DATA_SOURCE_USERNAME_REGEX.pattern}")

# 2. 全名也是必须提供的
full_name = props.get(mapping["full_name"])
if not full_name:
raise ValueError("full_name is required")

email = props.get(mapping["email"]) or ""
# 3. 如果提供了邮箱,则必须满足正则校验规则
if email and not re.fullmatch(EMAIL_REGEX, email):
raise ValueError(f"email {email} provided but not match pattern {EMAIL_REGEX.pattern}")
raise ValueError(f"email [{email}] provided but not match pattern {EMAIL_REGEX.pattern}")

phone = props.get(mapping["phone"]) or ""
phone_country_code = props.get(mapping["phone_country_code"], settings.DEFAULT_PHONE_COUNTRY_CODE)
phone_country_code = props.get(mapping["phone_country_code"]) or settings.DEFAULT_PHONE_COUNTRY_CODE

phone_number = f"+{phone_country_code}{phone}"
try:
phonenumbers.parse(phone_number, None)
except phonenumbers.phonenumberutil.NumberParseException:
raise ValueError(f"phone number {phone_number} is invalid (country_code: {phone_country_code})")
# 4. 如果提供了手机号,则需要通过 phonenumbers 的检查,确保手机号码合法
if phone:
phone_number = f"+{phone_country_code}{phone}"
try:
phonenumbers.parse(phone_number, None)
except NumberParseException:
raise ValueError(f"phone number [{phone_number}] is invalid (country_code: {phone_country_code})")

return DataSourceUser(
data_source=self.data_source,
code=user.code,
username=username,
full_name=props[mapping["full_name"]],
full_name=full_name,
email=email,
phone=phone,
phone_country_code=phone_country_code,
extras={mapping[f.name]: props.get(f.name, f.default) for f in self.custom_fields},
extras={f.name: props.get(mapping[f.name], f.default) for f in self.custom_fields},
)
48 changes: 42 additions & 6 deletions src/bk-user/tests/apps/sync/test_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def test_get_field_mapping_from_tenant_user_fields(
for f in ["username", "full_name", "email", "phone", "phone_country_code", "age", "gender", "region"]
]

def test_convert_case_1(self, bare_local_data_source, tenant_user_custom_fields, logger):
def test_convert_user_enum_field_default(self, bare_local_data_source, tenant_user_custom_fields, logger):
raw_zhangsan = RawDataSourceUser(
code="zhangsan",
properties={
Expand All @@ -85,7 +85,7 @@ def test_convert_case_1(self, bare_local_data_source, tenant_user_custom_fields,
assert zhangsan.phone_country_code == "86"
assert zhangsan.extras == {"age": "18", "gender": "male", "region": "beijing"}

def test_convert_case_2(self, bare_local_data_source, tenant_user_custom_fields, logger):
def test_convert_use_string_field_default(self, bare_local_data_source, tenant_user_custom_fields, logger):
raw_lisi = RawDataSourceUser(
code="lisi",
properties={
Expand All @@ -110,7 +110,7 @@ def test_convert_case_2(self, bare_local_data_source, tenant_user_custom_fields,
assert lisi.phone_country_code == "63"
assert lisi.extras == {"age": "28", "gender": "female", "region": ""}

def test_convert_case_not_same_field_name_mapping(self, bare_local_data_source, tenant_user_custom_fields, logger):
def test_convert_with_not_same_field_name_mapping(self, bare_local_data_source, tenant_user_custom_fields, logger):
raw_lisi = RawDataSourceUser(
code="lisi",
properties={
Expand All @@ -121,15 +121,51 @@ def test_convert_case_not_same_field_name_mapping(self, bare_local_data_source,
"phone_country_code": "63",
"age": "28",
"gender": "female",
"region": "shanghai",
"custom_region": "shanghai",
},
leaders=["zhangsan"],
departments=["dept_a", "center_aa"],
)

converter = DataSourceUserConverter(bare_local_data_source, logger)
# 修改数据以生成不同字段名映射的比较麻烦,这里采用的是直接修改 Converter 的 field_mapping 属性
converter.field_mapping[-1].target_field = "custom_region"
converter.field_mapping[-1].source_field = "custom_region"

lisi = converter.convert(raw_lisi)
assert lisi.extras == {"age": "28", "gender": "female", "custom_region": "shanghai"}
assert lisi.extras == {"age": "28", "gender": "female", "region": "shanghai"}

def test_convert_with_invalid_username(self, bare_local_data_source, logger):
raw_user = RawDataSourceUser(code="test", properties={}, leaders=[], departments=[])
with pytest.raises(ValueError, match="username is required"):
DataSourceUserConverter(bare_local_data_source, logger).convert(raw_user)

raw_user.properties["username"] = "李四"
with pytest.raises(ValueError, match="not match pattern"):
DataSourceUserConverter(bare_local_data_source, logger).convert(raw_user)

def test_convert_without_full_nane(self, bare_local_data_source, logger):
raw_user = RawDataSourceUser(
code="test", properties={"username": "test", "full_name": ""}, leaders=[], departments=[]
)
with pytest.raises(ValueError, match="full_name is required"):
DataSourceUserConverter(bare_local_data_source, logger).convert(raw_user)

def test_convert_with_invalid_email(self, bare_local_data_source, logger):
raw_user = RawDataSourceUser(
code="test",
properties={"username": "test", "full_name": "test", "email": "test"},
leaders=[],
departments=[],
)
with pytest.raises(ValueError, match="provided but not match pattern"):
DataSourceUserConverter(bare_local_data_source, logger).convert(raw_user)

def test_convert_with_invalid_phone_number(self, bare_local_data_source, logger):
raw_user = RawDataSourceUser(
code="test",
properties={"username": "test", "full_name": "test", "phone": "1", "phone_country_code": "44"},
leaders=[],
departments=[],
)
with pytest.raises(ValueError, match="phone number"):
DataSourceUserConverter(bare_local_data_source, logger).convert(raw_user)
1 change: 0 additions & 1 deletion src/bk-user/tests/apps/sync/test_syncers.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ def test_update_with_overwrite(
assert set(users.values_list("code", flat=True)) == {user.code for user in raw_users}
assert set(users.values_list("username", flat=True)) == {user.properties.get("username") for user in raw_users}
# 验证 extras 都被更新
print("aaaa ==== ", users.values("username", "extras"))
assert all(bool(e) for e in users.values_list("extras", flat=True))

# 验证内置/自定义字段被更新
Expand Down

0 comments on commit 228bc22

Please sign in to comment.