更新登录页及优化相关功能
This commit is contained in:
@@ -10,6 +10,7 @@ PATCH_PATH = SQL_ROOT / 'patches/20260724_pallet_workflow_mysql.sql'
|
||||
MENU_PROMOTION_PATCH_PATH = SQL_ROOT / 'patches/20260727_promote_business_menus_mysql.sql'
|
||||
BUSINESS_MENU_ICON_PATCH_PATH = SQL_ROOT / 'patches/20260728_add_business_menu_icons_mysql.sql'
|
||||
TEST_BATCH_MYSQL_PATCH_PATH = SQL_ROOT / 'patches/20260922_test_data_batch_mysql.sql'
|
||||
SHIPMENT_RECEIPT_PATCH_PATH = SQL_ROOT / 'patches/20261008_shipment_receipt_mysql.sql'
|
||||
SYS_MENU_VALUE_COUNT = 20
|
||||
SYS_MENU_PERMS_INDEX = 13
|
||||
SYS_MENU_ICON_INDEX = 14
|
||||
@@ -374,8 +375,79 @@ LEGACY_EXPECTED_UNIQUE_KEYS = {table: set(keys) for table, keys in EXPECTED_UNIQ
|
||||
LEGACY_EXPECTED_FOREIGN_KEYS = {table: set(keys) for table, keys in EXPECTED_FOREIGN_KEYS.items()}
|
||||
LEGACY_EXPECTED_CHECKS = {table: set(checks) for table, checks in EXPECTED_CHECKS.items()}
|
||||
|
||||
EXPECTED_COLUMNS['biz_filling_record'].update({'production_date', 'expiry_date'})
|
||||
EXPECTED_CHECKS['biz_filling_record'].add(
|
||||
'(production_dateisnullandexpiry_dateisnull)or(production_dateisnotnullandexpiry_dateisnotnullandexpiry_date>=production_date)'
|
||||
)
|
||||
|
||||
CURRENT_TEST_BATCH_OWNED_TABLES = TEST_BATCH_OWNED_TABLES | {'biz_tunnel'}
|
||||
EXPECTED_TABLES.add('biz_tunnel')
|
||||
EXPECTED_COLUMNS.update(
|
||||
{
|
||||
'biz_tunnel': {
|
||||
'tunnel_id',
|
||||
'site_id',
|
||||
'tunnel_code',
|
||||
'tunnel_name',
|
||||
'current_trace_no',
|
||||
'current_trace_started_at',
|
||||
'status',
|
||||
}
|
||||
| AUDIT_COLUMNS,
|
||||
'biz_usage_record': EXPECTED_COLUMNS['biz_usage_record']
|
||||
| {'tunnel_id', 'trace_no', 'batch_no_snapshot'},
|
||||
'biz_return_item': EXPECTED_COLUMNS['biz_return_item']
|
||||
| {'tunnel_id_snapshot', 'trace_no_snapshot', 'batch_no_snapshot'},
|
||||
'biz_pallet_inventory': EXPECTED_COLUMNS['biz_pallet_inventory'] | {'tunnel_id', 'trace_no'},
|
||||
'biz_pallet_event': EXPECTED_COLUMNS['biz_pallet_event']
|
||||
| {'before_tunnel_id', 'after_tunnel_id', 'trace_no', 'batch_no_snapshot'},
|
||||
}
|
||||
)
|
||||
EXPECTED_DATETIME_COLUMNS['biz_tunnel'] = {'current_trace_started_at', 'create_time', 'update_time'}
|
||||
EXPECTED_PRIMARY_KEYS['biz_tunnel'] = ('tunnel_id',)
|
||||
EXPECTED_UNIQUE_KEYS['biz_tunnel'] = {('site_id', 'tunnel_code')}
|
||||
EXPECTED_FOREIGN_KEYS.update(
|
||||
{
|
||||
'biz_tunnel': {('site_id', 'biz_customer_site.site_id')},
|
||||
'biz_usage_record': EXPECTED_FOREIGN_KEYS['biz_usage_record']
|
||||
| {('tunnel_id', 'biz_tunnel.tunnel_id')},
|
||||
'biz_return_item': EXPECTED_FOREIGN_KEYS['biz_return_item']
|
||||
| {('tunnel_id_snapshot', 'biz_tunnel.tunnel_id')},
|
||||
'biz_pallet_inventory': EXPECTED_FOREIGN_KEYS['biz_pallet_inventory']
|
||||
| {('tunnel_id', 'biz_tunnel.tunnel_id')},
|
||||
'biz_pallet_event': EXPECTED_FOREIGN_KEYS['biz_pallet_event']
|
||||
| {
|
||||
('before_tunnel_id', 'biz_tunnel.tunnel_id'),
|
||||
('after_tunnel_id', 'biz_tunnel.tunnel_id'),
|
||||
},
|
||||
}
|
||||
)
|
||||
EXPECTED_CHECKS['biz_tunnel'] = {"statusin('0','1')"}
|
||||
EXPECTED_CHECKS['biz_pallet_event'] = {
|
||||
"event_typein('inbound_confirmed','filling_confirmed','shipment_confirmed','shipment_received','tunnel_entered','usage_recorded',"
|
||||
"'return_requested','return_confirmed')"
|
||||
}
|
||||
|
||||
# 新契约必须位于 LEGACY_EXPECTED_* 的独立副本之后,旧补丁保持原始结构。
|
||||
for fifo_table in ('biz_usage_record', 'biz_pallet_event'):
|
||||
EXPECTED_COLUMNS[fifo_table].update({'fifo_skip_reason', 'fifo_recommendation'})
|
||||
|
||||
EXPECTED_COLUMNS['biz_shipment_order'].update({
|
||||
'receipt_state', 'received_by', 'received_at', 'receipt_idempotency_key', 'receipt_request_hash',
|
||||
})
|
||||
EXPECTED_DATETIME_COLUMNS['biz_shipment_order'].add('received_at')
|
||||
EXPECTED_UNIQUE_KEYS['biz_shipment_order'].add(('target_site_id', 'receipt_idempotency_key'))
|
||||
EXPECTED_FOREIGN_KEYS['biz_shipment_order'].add(('received_by', 'sys_user.user_id'))
|
||||
EXPECTED_CHECKS['biz_shipment_order'].add("receipt_statein('pending','received','legacy')")
|
||||
EXPECTED_COLUMNS['biz_pallet_inventory'].add('site_arrived_at')
|
||||
EXPECTED_DATETIME_COLUMNS['biz_pallet_inventory'].add('site_arrived_at')
|
||||
EXPECTED_CHECKS['biz_pallet_inventory'] = {
|
||||
check.replace("'filled_in_warehouse','at_site_unused'", "'filled_in_warehouse','in_transit','at_site_unused'")
|
||||
for check in EXPECTED_CHECKS['biz_pallet_inventory']
|
||||
}
|
||||
|
||||
EXPECTED_TABLES |= EXPECTED_TEST_BATCH_TABLES
|
||||
for table_name in TEST_BATCH_OWNED_TABLES:
|
||||
for table_name in CURRENT_TEST_BATCH_OWNED_TABLES:
|
||||
EXPECTED_COLUMNS[table_name].add('test_batch_id')
|
||||
EXPECTED_FOREIGN_KEYS[table_name].add(('test_batch_id', 'biz_test_batch.batch_id'))
|
||||
|
||||
@@ -452,6 +524,7 @@ EXPECTED_PAGES = {
|
||||
505: ('产线资料', 121, 4, 'line', 'master/line/index', 'master:line:list'),
|
||||
506: ('仓库资料', 121, 5, 'warehouse', 'master/warehouse/index', 'master:warehouse:list'),
|
||||
507: ('托盘资料', 121, 6, 'pallet', 'master/pallet/index', 'master:pallet:list'),
|
||||
520: ('隧道资料', 121, 7, 'tunnel', 'master/tunnel/index', 'master:tunnel:list'),
|
||||
508: ('仓库入库', 122, 1, 'inbound', 'business/inbound/index', 'business:inbound:list'),
|
||||
509: ('工厂装填', 122, 2, 'filling', 'business/filling/index', 'business:filling:list'),
|
||||
510: ('发货出库', 122, 3, 'shipment', 'business/shipment/index', 'business:shipment:list'),
|
||||
@@ -494,6 +567,9 @@ BUTTON_SPECS = """
|
||||
1088 507 2 托盘新增 master:pallet:add
|
||||
1089 507 3 托盘修改 master:pallet:edit
|
||||
1091 507 5 托盘打印 master:pallet:print
|
||||
1116 520 1 隧道查询 master:tunnel:query
|
||||
1117 520 2 隧道新增 master:tunnel:add
|
||||
1118 520 3 隧道修改 master:tunnel:edit
|
||||
1092 508 1 入库查询 business:inbound:query
|
||||
1093 508 2 入库新增 business:inbound:add
|
||||
1094 508 3 入库修改 business:inbound:edit
|
||||
@@ -532,6 +608,7 @@ EXPECTED_PAGE_PERMISSIONS = frozenset(
|
||||
'master:line:list',
|
||||
'master:warehouse:list',
|
||||
'master:pallet:list',
|
||||
'master:tunnel:list',
|
||||
'business:inbound:list',
|
||||
'business:filling:list',
|
||||
'business:shipment:list',
|
||||
@@ -568,6 +645,9 @@ EXPECTED_BUTTON_PERMISSIONS = frozenset(
|
||||
'master:pallet:add',
|
||||
'master:pallet:edit',
|
||||
'master:pallet:print',
|
||||
'master:tunnel:query',
|
||||
'master:tunnel:add',
|
||||
'master:tunnel:edit',
|
||||
'business:inbound:query',
|
||||
'business:inbound:add',
|
||||
'business:inbound:edit',
|
||||
@@ -604,6 +684,7 @@ BUSINESS_MENU_SPECS = """
|
||||
505|121|产线资料|line||4|master:line:list|master/line/index
|
||||
506|121|仓库资料|warehouse||5|master:warehouse:list|master/warehouse/index
|
||||
507|121|托盘资料|pallet||6|master:pallet:list|master/pallet/index
|
||||
520|121|隧道资料|tunnel||7|master:tunnel:list|master/tunnel/index
|
||||
508|122|仓库入库|inbound||1|business:inbound:list|business/inbound/index
|
||||
509|122|工厂装填|filling||2|business:filling:list|business/filling/index
|
||||
510|122|发货出库|shipment||3|business:shipment:list|business/shipment/index
|
||||
@@ -636,6 +717,9 @@ BUSINESS_MENU_SPECS = """
|
||||
1088|507|托盘新增|||2|master:pallet:add|
|
||||
1089|507|托盘修改|||3|master:pallet:edit|
|
||||
1091|507|托盘打印|||5|master:pallet:print|
|
||||
1116|520|隧道查询|||1|master:tunnel:query|
|
||||
1117|520|隧道新增|||2|master:tunnel:add|
|
||||
1118|520|隧道修改|||3|master:tunnel:edit|
|
||||
1092|508|入库查询|||1|business:inbound:query|
|
||||
1093|508|入库新增|||2|business:inbound:add|
|
||||
1094|508|入库修改|||3|business:inbound:edit|
|
||||
@@ -699,9 +783,11 @@ LEGACY_EXPECTED_BUSINESS_MENU_FIELDS.update(
|
||||
for menu_id, (name, parent_id, order_num, permission) in LEGACY_REMOVED_BUTTON_SPECS.items()
|
||||
}
|
||||
)
|
||||
LEGACY_EXPECTED_PAGE_PERMISSIONS = EXPECTED_PAGE_PERMISSIONS - {'system:testbatch:list'}
|
||||
LEGACY_EXPECTED_PAGE_PERMISSIONS = EXPECTED_PAGE_PERMISSIONS - {'system:testbatch:list', 'master:tunnel:list'}
|
||||
LEGACY_EXPECTED_BUTTON_PERMISSIONS = EXPECTED_BUTTON_PERMISSIONS - {
|
||||
permission for permission in EXPECTED_BUTTON_PERMISSIONS if permission.startswith('system:testbatch:')
|
||||
permission
|
||||
for permission in EXPECTED_BUTTON_PERMISSIONS
|
||||
if permission.startswith(('system:testbatch:', 'master:tunnel:'))
|
||||
} | {spec[3] for spec in LEGACY_REMOVED_BUTTON_SPECS.values()}
|
||||
LEGACY_EXPECTED_PERMISSIONS = LEGACY_EXPECTED_PAGE_PERMISSIONS | LEGACY_EXPECTED_BUTTON_PERMISSIONS
|
||||
LEGACY_PATCH_BUSINESS_MENU_FIELDS = LEGACY_EXPECTED_BUSINESS_MENU_FIELDS | {
|
||||
@@ -709,6 +795,11 @@ LEGACY_PATCH_BUSINESS_MENU_FIELDS = LEGACY_EXPECTED_BUSINESS_MENU_FIELDS | {
|
||||
122: (1, '业务管理', 'businessOperation', '', 11, '', None),
|
||||
123: (1, '报表查询', 'businessReport', '', 12, '', None),
|
||||
}
|
||||
# 同样在菜单旧快照生成后加入收货权限,避免改变历史迁移的权限清单。
|
||||
EXPECTED_BUTTONS[1119] = ('收货核对', 510, 6, 'business:shipment:receive')
|
||||
EXPECTED_BUSINESS_MENU_FIELDS[1119] = (510, '收货核对', '', '', 6, 'business:shipment:receive', '')
|
||||
EXPECTED_BUTTON_PERMISSIONS |= {'business:shipment:receive'}
|
||||
EXPECTED_PERMISSIONS = EXPECTED_PAGE_PERMISSIONS | EXPECTED_BUTTON_PERMISSIONS
|
||||
EXPECTED_TOP_LEVEL_MENU_POSITIONS = {
|
||||
121: ('基础资料维护', 0, 1),
|
||||
122: ('业务管理', 0, 2),
|
||||
@@ -734,7 +825,7 @@ MENU_PROMOTION_IDS = set(EXPECTED_TOP_LEVEL_MENU_POSITIONS)
|
||||
BUSINESS_MENU_IDS = (
|
||||
set(range(121, 124))
|
||||
| set(range(502, 519))
|
||||
| {519}
|
||||
| {519, 520, 1119}
|
||||
| set(range(1065, 1068))
|
||||
| set(range(1069, 1072))
|
||||
| set(range(1073, 1076))
|
||||
@@ -750,7 +841,7 @@ BUSINESS_MENU_IDS = (
|
||||
| set(range(1099, 1102))
|
||||
| {1103}
|
||||
| set(range(1104, 1109))
|
||||
| set(range(1109, 1116))
|
||||
| set(range(1109, 1119))
|
||||
)
|
||||
TEST_BATCH_MENU_IDS = {519, 1109, 1110, 1111, 1112, 1113, 1114, 1115}
|
||||
MYSQL_MENU_STABLE_COLUMNS = (
|
||||
@@ -1033,11 +1124,12 @@ def _assert_test_batch_sql_contract(
|
||||
create_batch = normalized.index('create table biz_test_batch (')
|
||||
create_batch_user = normalized.index('create table biz_test_batch_user (')
|
||||
owned_marker = 'alter table' if is_patch else 'create table'
|
||||
first_owned = min(normalized.index(f'{owned_marker} {table_name} ') for table_name in TEST_BATCH_OWNED_TABLES)
|
||||
owned_tables = TEST_BATCH_OWNED_TABLES if is_patch else CURRENT_TEST_BATCH_OWNED_TABLES
|
||||
first_owned = min(normalized.index(f'{owned_marker} {table_name} ') for table_name in owned_tables)
|
||||
test_case.assertLess(create_batch, create_batch_user)
|
||||
test_case.assertLess(create_batch_user, first_owned)
|
||||
|
||||
for table_name in TEST_BATCH_OWNED_TABLES:
|
||||
for table_name in owned_tables:
|
||||
with test_case.subTest(dialect=dialect, patch=is_patch, table=table_name):
|
||||
if is_patch:
|
||||
test_case.assertRegex(
|
||||
@@ -1319,6 +1411,103 @@ def _assert_business_menu_icon_guard_structure(test_case: unittest.TestCase, sql
|
||||
return pre_guard, post_guard
|
||||
|
||||
|
||||
class ShipmentReceiptSqlContractTest(unittest.TestCase):
|
||||
def patch_sql(self) -> str:
|
||||
self.assertTrue(SHIPMENT_RECEIPT_PATCH_PATH.is_file(), '缺少收货单次迁移脚本')
|
||||
return SHIPMENT_RECEIPT_PATCH_PATH.read_text(encoding='utf-8')
|
||||
|
||||
def test_patch_additions_match_initializer_columns_and_constraints(self) -> None:
|
||||
def normalize(value: str) -> str:
|
||||
return re.sub(r'\b(bigint)\(20\)', r'\1', value).split(' comment ')[0]
|
||||
|
||||
raw = _strip_sql_comments(self.patch_sql())
|
||||
initial = _create_table_bodies((SQL_ROOT / INITIALIZERS[0]).read_text(encoding='utf-8'))
|
||||
alters = re.findall(r'\balter\s+table\s+(\w+)\s+(.*?);', raw, re.I | re.S)
|
||||
self.assertEqual(['biz_shipment_order', 'biz_pallet_inventory', 'biz_pallet_event'], [table for table, _ in alters])
|
||||
expected_columns = {
|
||||
'biz_shipment_order': {'receipt_state', 'received_by', 'received_at', 'receipt_idempotency_key', 'receipt_request_hash'},
|
||||
'biz_pallet_inventory': {'site_arrived_at'},
|
||||
'biz_pallet_event': set(),
|
||||
}
|
||||
for table, body in alters:
|
||||
with self.subTest(table=table):
|
||||
additions = ','.join(
|
||||
re.sub(r'^add\s+(?:column\s+)?', '', clause.strip(), flags=re.I)
|
||||
for clause in _split_values(body) if clause.strip().lower().startswith('add ')
|
||||
)
|
||||
columns, _, uniques, foreign_keys, checks = _table_contract(additions)
|
||||
self.assertEqual(expected_columns[table], columns)
|
||||
self.assertEqual(
|
||||
{('target_site_id', 'receipt_idempotency_key')} if table == 'biz_shipment_order' else set(), uniques,
|
||||
)
|
||||
self.assertEqual({('received_by', 'sys_user.user_id')} if table == 'biz_shipment_order' else set(), foreign_keys)
|
||||
expected_checks = (
|
||||
{"receipt_statein('pending','received','legacy')"} if table == 'biz_shipment_order'
|
||||
else {check for check in EXPECTED_CHECKS[table] if check.startswith(('flow_statein', 'event_typein'))}
|
||||
)
|
||||
self.assertEqual(expected_checks, checks)
|
||||
initializer_columns = _column_definitions(initial[table])
|
||||
for column, definition in _column_definitions(additions).items():
|
||||
normalized = _normalized_sql_statement(re.sub(r'\s+after\s+\w+\s*$', '', definition, flags=re.I))
|
||||
# 类型、可空性和默认值均应与初始化定义一致,注释不参与比较。
|
||||
self.assertEqual(normalize(_normalized_sql_statement(initializer_columns[column])), normalize(normalized))
|
||||
|
||||
def test_patch_only_marks_confirmed_legacy_and_does_not_touch_inventory_or_receipt_time(self) -> None:
|
||||
raw = _strip_sql_comments(self.patch_sql())
|
||||
updates = re.findall(r'\bupdate\s+([a-z_]+)\s+set\s+(.*?);', raw, re.I | re.S)
|
||||
self.assertEqual(1, len(updates))
|
||||
self.assertEqual('biz_shipment_order', updates[0][0].lower())
|
||||
self.assertEqual("receipt_state = 'legacy' where status = 'confirmed'", _normalized_sql_statement(updates[0][1]))
|
||||
inserts = re.findall(r'\binsert\s+into\s+(\w+)', raw, re.I)
|
||||
self.assertEqual(['sys_menu'], inserts)
|
||||
self.assertNotRegex(raw.lower(), r'\b(?:delete\s+from|replace\s+into|truncate\s+table)\b')
|
||||
self.assertNotRegex(raw.lower(), r'\b(?:received_at|site_arrived_at)\s*=')
|
||||
|
||||
def test_patch_guards_conflicting_menu_ids_and_permissions_before_any_alter(self) -> None:
|
||||
raw = _strip_sql_comments(self.patch_sql()).lower()
|
||||
guard = raw[:raw.index('alter table')]
|
||||
for expression in (
|
||||
r'from\s+sys_menu\s+where\s+menu_id\s*=\s*1119\s+or\s+perms\s*=\s*\'business:shipment:receive\'',
|
||||
r'from\s+sys_role_menu\s+where\s+menu_id\s*=\s*1119',
|
||||
r'menu_id\s*=\s*510\s+and\s+perms\s*=\s*\'business:shipment:list\'',
|
||||
):
|
||||
self.assertRegex(guard, expression)
|
||||
self.assertIn("signal sqlstate '45000'", guard)
|
||||
self.assertIn('resignal', guard)
|
||||
rows = _insert_rows(raw, 'sys_menu')
|
||||
self.assertEqual(1, len(rows))
|
||||
self.assertEqual(SYS_MENU_VALUE_COUNT, len(rows[0]))
|
||||
self.assertEqual(['1119', '收货核对', '510', '6'], rows[0][:4])
|
||||
self.assertEqual('business:shipment:receive', rows[0][SYS_MENU_PERMS_INDEX])
|
||||
self.assertNotRegex(raw, r'\b(?:insert\s+into|update|delete\s+from)\s+sys_role_menu\b')
|
||||
|
||||
def test_patch_requires_prerequisites_and_rejects_repeated_or_partial_execution(self) -> None:
|
||||
raw = self.patch_sql()
|
||||
for phrase in ('MySQL 8', '停写', '备份', '单次', 'DDL', '隐式提交', '20261005_tunnel_material_flow_mysql.sql', '20261008_material_dates_mysql.sql'):
|
||||
self.assertIn(phrase, raw)
|
||||
guard = _strip_sql_comments(raw).lower().split('alter table', maxsplit=1)[0]
|
||||
self.assertIn('version()', guard)
|
||||
self.assertIn('information_schema.columns', guard)
|
||||
for field in ('production_date', 'expiry_date', 'tunnel_id', 'trace_no', 'receipt_state', 'received_by',
|
||||
'received_at', 'receipt_idempotency_key', 'receipt_request_hash', 'site_arrived_at'):
|
||||
self.assertIn(f"'{field}'", guard)
|
||||
self.assertIn('information_schema.check_constraints', guard)
|
||||
self.assertIn("'ck_biz_filling_record_material_dates'", guard)
|
||||
self.assertIn("'ck_biz_pallet_event_event_type'", guard)
|
||||
self.assertIn('tunnel_entered', guard)
|
||||
self.assertLess(raw.lower().rindex('alter table'), raw.lower().index('start transaction'))
|
||||
|
||||
def test_current_expectations_leave_legacy_schema_and_menu_snapshots_unchanged(self) -> None:
|
||||
self.assertNotIn('receipt_state', LEGACY_EXPECTED_COLUMNS['biz_shipment_order'])
|
||||
self.assertNotIn('site_arrived_at', LEGACY_EXPECTED_COLUMNS['biz_pallet_inventory'])
|
||||
self.assertNotIn(('target_site_id', 'receipt_idempotency_key'), LEGACY_EXPECTED_UNIQUE_KEYS['biz_shipment_order'])
|
||||
self.assertNotIn(('received_by', 'sys_user.user_id'), LEGACY_EXPECTED_FOREIGN_KEYS['biz_shipment_order'])
|
||||
self.assertNotIn('in_transit', ' '.join(LEGACY_EXPECTED_CHECKS['biz_pallet_inventory']))
|
||||
self.assertNotIn('shipment_received', ' '.join(LEGACY_EXPECTED_CHECKS['biz_pallet_event']))
|
||||
self.assertNotIn('business:shipment:receive', LEGACY_EXPECTED_PERMISSIONS)
|
||||
self.assertNotIn(1119, LEGACY_EXPECTED_BUSINESS_MENU_FIELDS)
|
||||
|
||||
|
||||
class SqlContractTest(unittest.TestCase):
|
||||
def test_test_batch_initializers_and_patches_match_contract(self) -> None:
|
||||
scripts = (
|
||||
@@ -1407,7 +1596,7 @@ class SqlContractTest(unittest.TestCase):
|
||||
self.assertEqual(EXPECTED_CHECKS[table], checks)
|
||||
|
||||
def test_mysql_business_datetimes_use_native_datetime_columns(self) -> None:
|
||||
self.assertEqual(40, sum(len(columns) for columns in EXPECTED_DATETIME_COLUMNS.values()))
|
||||
self.assertEqual(45, sum(len(columns) for columns in EXPECTED_DATETIME_COLUMNS.values()))
|
||||
tables = _create_table_bodies((SQL_ROOT / 'shierfeng-fastapi.sql').read_text(encoding='utf-8'))
|
||||
invalid_columns = []
|
||||
for table, expected_columns in EXPECTED_DATETIME_COLUMNS.items():
|
||||
@@ -1425,11 +1614,11 @@ class SqlContractTest(unittest.TestCase):
|
||||
self.assertEqual([], sorted(invalid_columns))
|
||||
|
||||
def test_initializers_define_fixed_business_menus_and_permissions(self) -> None:
|
||||
self.assertEqual(18, len(EXPECTED_PAGE_PERMISSIONS))
|
||||
self.assertEqual(43, len(EXPECTED_BUTTON_PERMISSIONS))
|
||||
self.assertEqual(19, len(EXPECTED_PAGE_PERMISSIONS))
|
||||
self.assertEqual(47, len(EXPECTED_BUTTON_PERMISSIONS))
|
||||
self.assertFalse(EXPECTED_PAGE_PERMISSIONS & EXPECTED_BUTTON_PERMISSIONS)
|
||||
self.assertEqual(61, len(EXPECTED_PERMISSIONS))
|
||||
self.assertEqual(64, len(EXPECTED_BUSINESS_MENU_FIELDS))
|
||||
self.assertEqual(66, len(EXPECTED_PERMISSIONS))
|
||||
self.assertEqual(69, len(EXPECTED_BUSINESS_MENU_FIELDS))
|
||||
for filename in INITIALIZERS:
|
||||
with self.subTest(filename=filename):
|
||||
sql = (SQL_ROOT / filename).read_text(encoding='utf-8')
|
||||
|
||||
Reference in New Issue
Block a user