110 lines
4.4 KiB
Python
110 lines
4.4 KiB
Python
"""用户批量导入 CSV / XLSX / XLS 回归测试。"""
|
|
import io
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from openpyxl import Workbook
|
|
|
|
from routes.users import (
|
|
_normalize_department,
|
|
_parse_user_import_csv,
|
|
_parse_user_import_file,
|
|
_parse_user_import_xls,
|
|
)
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
APP_JS = (ROOT / 'static' / 'js' / 'app.js').read_text(encoding='utf-8')
|
|
HEADERS = ['用户名', '密码', '真实姓名', '角色', '员工级别', '岗位', '手机', '邮箱', '状态']
|
|
|
|
|
|
class UserImportFileTest(unittest.TestCase):
|
|
def test_accepts_excel_style_cr_line_endings(self):
|
|
content = (
|
|
'用户名,密码,真实姓名,角色,员工级别,岗位,手机,邮箱,状态\r'
|
|
'zhangsan,Abc123!@#45,张三,viewer,employee,采购专员,,,1\r'
|
|
)
|
|
rows = _parse_user_import_csv(content.encode('utf-8-sig'))
|
|
self.assertEqual(1, len(rows))
|
|
self.assertEqual('zhangsan', rows[0]['用户名'])
|
|
self.assertEqual('Abc123!@#45', rows[0]['密码'])
|
|
|
|
def test_accepts_crlf_and_gbk(self):
|
|
content = (
|
|
'用户名,密码,真实姓名,角色,员工级别,岗位,部门,手机,邮箱,状态\r\n'
|
|
'lisi,Xyz789$%^01,李四,viewer,employee,仓库专员,天猫,,,1\r\n'
|
|
)
|
|
rows = _parse_user_import_file(content.encode('gbk'), '用户列表.CSV')
|
|
self.assertEqual('李四', rows[0]['真实姓名'])
|
|
self.assertEqual('天猫', rows[0]['部门'])
|
|
|
|
def test_department_options_accept_blank_and_reject_unknown(self):
|
|
self.assertEqual('', _normalize_department(''))
|
|
for department in ('天猫', '京东', '天猫国际', '京东国际', '阿里', '新媒体', '拼多多', 'AI指挥部', '采购部', '产品部'):
|
|
self.assertEqual(department, _normalize_department(department))
|
|
with self.assertRaisesRegex(ValueError, '部门必须为'):
|
|
_normalize_department('其他部门')
|
|
|
|
def test_accepts_xlsx_and_preserves_integer_phone(self):
|
|
workbook = Workbook()
|
|
sheet = workbook.active
|
|
sheet.append(HEADERS)
|
|
sheet.append(['wangwu', 'Safe123!@#45', '王五', 'viewer', 'employee', '仓库专员', 13800138000, '', 1])
|
|
output = io.BytesIO()
|
|
workbook.save(output)
|
|
workbook.close()
|
|
|
|
rows = _parse_user_import_file(output.getvalue(), '用户列表.xlsx')
|
|
self.assertEqual(1, len(rows))
|
|
self.assertEqual('wangwu', rows[0]['用户名'])
|
|
self.assertEqual('13800138000', rows[0]['手机'])
|
|
self.assertEqual('1', rows[0]['状态'])
|
|
|
|
def test_accepts_xls_first_sheet(self):
|
|
class FakeSheet:
|
|
nrows = 2
|
|
|
|
def row_values(self, index):
|
|
return HEADERS if index == 0 else [
|
|
'zhaoliu', 'Strong123!@#', '赵六', 'viewer', 'employee', '采购专员', 13900139000.0, '', 1.0
|
|
]
|
|
|
|
class FakeBook:
|
|
nsheets = 1
|
|
|
|
def sheet_by_index(self, index):
|
|
self.requested_index = index
|
|
return FakeSheet()
|
|
|
|
with patch('xlrd.open_workbook', return_value=FakeBook()) as open_workbook:
|
|
rows = _parse_user_import_xls(b'fake-xls')
|
|
|
|
open_workbook.assert_called_once_with(file_contents=b'fake-xls')
|
|
self.assertEqual('zhaoliu', rows[0]['用户名'])
|
|
self.assertEqual('13900139000', rows[0]['手机'])
|
|
|
|
def test_rejects_wrong_template_headers(self):
|
|
with self.assertRaisesRegex(ValueError, '表头'):
|
|
_parse_user_import_csv('账号,口令\na,b\n'.encode('utf-8'))
|
|
|
|
def test_reports_malformed_csv_without_partial_iteration(self):
|
|
malformed = '用户名,密码\n"zhangsan,Abc123!@#45\n'.encode('utf-8')
|
|
with self.assertRaisesRegex(ValueError, 'CSV格式错误'):
|
|
_parse_user_import_csv(malformed)
|
|
|
|
def test_rejects_unsupported_extension(self):
|
|
with self.assertRaisesRegex(ValueError, '仅支持CSV、XLSX、XLS'):
|
|
_parse_user_import_file(b'content', 'users.txt')
|
|
|
|
def test_frontend_accepts_all_three_formats(self):
|
|
self.assertIn('accept=".csv,.xlsx,.xls"', APP_JS)
|
|
self.assertIn("['csv', 'xlsx', 'xls'].includes(extension)", APP_JS)
|
|
self.assertIn('file_base64: result.slice(commaIndex + 1)', APP_JS)
|
|
self.assertIn('id="userDepartmentFilter"', APP_JS)
|
|
self.assertIn('id="editDepartment"', APP_JS)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|