调整部分前端布局和后端增删改逻辑
This commit is contained in:
@@ -5,12 +5,11 @@ from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
SQL_ROOT = Path(__file__).resolve().parents[2] / 'sql'
|
||||
INITIALIZERS = ('shierfeng-fastapi.sql', 'shierfeng-fastapi-pg.sql')
|
||||
INITIALIZERS = ('shierfeng-fastapi.sql',)
|
||||
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'
|
||||
TEST_BATCH_PG_PATCH_PATH = SQL_ROOT / 'patches/20260922_test_data_batch_pg.sql'
|
||||
SYS_MENU_VALUE_COUNT = 20
|
||||
SYS_MENU_PERMS_INDEX = 13
|
||||
SYS_MENU_ICON_INDEX = 14
|
||||
@@ -1007,18 +1006,6 @@ def _column_definitions(body: str) -> dict[str, str]:
|
||||
return definitions
|
||||
|
||||
|
||||
def _is_postgres_naive_timestamp_with_default_precision(definition: str) -> bool:
|
||||
return (
|
||||
re.fullmatch(
|
||||
r'[`"]?[a-z_][a-z0-9_]*[`"]?\s+'
|
||||
r'timestamp(?:\s+without\s+time\s+zone)?(?:\s+not\s+null)?',
|
||||
definition.strip(),
|
||||
flags=re.I,
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
|
||||
def _assert_test_batch_sql_contract(
|
||||
test_case: unittest.TestCase,
|
||||
sql: str,
|
||||
@@ -1064,18 +1051,9 @@ def _assert_test_batch_sql_contract(
|
||||
test_case.assertNotRegex(definitions['test_batch_id'], r'\bnot\s+null\b')
|
||||
|
||||
index_name = f'idx_{table_name}_test_batch_id'
|
||||
if dialect == 'mysql':
|
||||
index_pattern = rf'\b(?:add\s+)?(?:index|key)\s+{index_name}\s*\(\s*test_batch_id\s*\)'
|
||||
test_case.assertRegex(normalized, index_pattern)
|
||||
test_case.assertIn("comment '测试数据批次id;null表示正式数据'", normalized)
|
||||
else:
|
||||
index_pattern = rf'\bcreate\s+index\s+{index_name}\s+on\s+{table_name}\s*\(\s*test_batch_id\s*\)'
|
||||
test_case.assertRegex(normalized, index_pattern)
|
||||
test_case.assertRegex(
|
||||
normalized,
|
||||
rf'comment\s+on\s+column\s+{table_name}\.test_batch_id\s+is\s+'
|
||||
r"'测试数据批次id;null表示正式数据'",
|
||||
)
|
||||
index_pattern = rf'\b(?:add\s+)?(?:index|key)\s+{index_name}\s*\(\s*test_batch_id\s*\)'
|
||||
test_case.assertRegex(normalized, index_pattern)
|
||||
test_case.assertIn("comment '测试数据批次id;null表示正式数据'", normalized)
|
||||
|
||||
constraint_name = f'fk_{table_name}_test_batch_id'
|
||||
test_case.assertRegex(
|
||||
@@ -1345,9 +1323,7 @@ class SqlContractTest(unittest.TestCase):
|
||||
def test_test_batch_initializers_and_patches_match_contract(self) -> None:
|
||||
scripts = (
|
||||
(SQL_ROOT / 'shierfeng-fastapi.sql', 'mysql', False),
|
||||
(SQL_ROOT / 'shierfeng-fastapi-pg.sql', 'postgresql', False),
|
||||
(TEST_BATCH_MYSQL_PATCH_PATH, 'mysql', True),
|
||||
(TEST_BATCH_PG_PATCH_PATH, 'postgresql', True),
|
||||
)
|
||||
for path, dialect, is_patch in scripts:
|
||||
with self.subTest(path=path.name):
|
||||
@@ -1361,7 +1337,7 @@ class SqlContractTest(unittest.TestCase):
|
||||
|
||||
def test_test_batch_patches_migrate_legacy_menu_permissions_without_role_grants(self) -> None:
|
||||
legacy_removed_ids = set(LEGACY_REMOVED_BUTTON_SPECS)
|
||||
for path in (TEST_BATCH_MYSQL_PATCH_PATH, TEST_BATCH_PG_PATCH_PATH):
|
||||
for path in (TEST_BATCH_MYSQL_PATCH_PATH,):
|
||||
with self.subTest(path=path.name):
|
||||
raw = path.read_text(encoding='utf-8')
|
||||
statements = _sql_statements(raw)
|
||||
@@ -1430,32 +1406,24 @@ class SqlContractTest(unittest.TestCase):
|
||||
self.assertEqual(EXPECTED_FOREIGN_KEYS[table], foreign_keys)
|
||||
self.assertEqual(EXPECTED_CHECKS[table], checks)
|
||||
|
||||
def test_postgres_business_datetimes_preserve_default_microsecond_precision(self) -> None:
|
||||
def test_mysql_business_datetimes_use_native_datetime_columns(self) -> None:
|
||||
self.assertEqual(40, sum(len(columns) for columns in EXPECTED_DATETIME_COLUMNS.values()))
|
||||
tables = _create_table_bodies((SQL_ROOT / 'shierfeng-fastapi-pg.sql').read_text(encoding='utf-8'))
|
||||
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():
|
||||
definitions = _column_definitions(tables[table])
|
||||
actual_columns = {
|
||||
column
|
||||
for column, definition in definitions.items()
|
||||
if re.search(r'\btimestamp\b', definition, flags=re.I)
|
||||
if re.search(r'\bdatetime\b', definition, flags=re.I)
|
||||
}
|
||||
self.assertEqual(expected_columns, actual_columns, table)
|
||||
for column in expected_columns:
|
||||
definition = definitions[column]
|
||||
if not _is_postgres_naive_timestamp_with_default_precision(definition):
|
||||
if not re.search(r'\bdatetime\b', definition, flags=re.I):
|
||||
invalid_columns.append(f'{table}.{column}')
|
||||
self.assertEqual([], sorted(invalid_columns))
|
||||
|
||||
def test_postgres_datetime_type_validator_rejects_precision_and_timezone_mutations(self) -> None:
|
||||
self.assertTrue(_is_postgres_naive_timestamp_with_default_precision('occurred_at timestamp'))
|
||||
self.assertTrue(
|
||||
_is_postgres_naive_timestamp_with_default_precision('occurred_at timestamp without time zone not null')
|
||||
)
|
||||
self.assertFalse(_is_postgres_naive_timestamp_with_default_precision('occurred_at timestamp(3)'))
|
||||
self.assertFalse(_is_postgres_naive_timestamp_with_default_precision('occurred_at timestamp with time zone'))
|
||||
|
||||
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))
|
||||
@@ -1679,18 +1647,6 @@ class SqlContractTest(unittest.TestCase):
|
||||
with self.assertRaises(AssertionError):
|
||||
_assert_business_menu_fields(self, mutated)
|
||||
|
||||
def test_mysql_and_postgresql_business_menu_fields_are_identical(self) -> None:
|
||||
mysql_rows = _business_menu_rows((SQL_ROOT / INITIALIZERS[0]).read_text(encoding='utf-8'))
|
||||
postgres_rows = _business_menu_rows((SQL_ROOT / INITIALIZERS[1]).read_text(encoding='utf-8'))
|
||||
compared_indexes = tuple(range(15))
|
||||
mysql_contract = {
|
||||
menu_id: tuple(row[index] for index in compared_indexes) for menu_id, row in mysql_rows.items()
|
||||
}
|
||||
postgres_contract = {
|
||||
menu_id: tuple(row[index] for index in compared_indexes) for menu_id, row in postgres_rows.items()
|
||||
}
|
||||
self.assertEqual(mysql_contract, postgres_contract)
|
||||
|
||||
def test_initializers_do_not_grant_new_business_menus_to_roles(self) -> None:
|
||||
for filename in INITIALIZERS:
|
||||
with self.subTest(filename=filename):
|
||||
@@ -1717,14 +1673,10 @@ class SqlContractTest(unittest.TestCase):
|
||||
any(int(row[0]) in REMOVED_AI_DICT_CODES or row[4] == 'ai_provider_type' for row in dict_data)
|
||||
)
|
||||
|
||||
def test_initializers_keep_menu_allocator_and_use_native_dialects(self) -> None:
|
||||
def test_initializer_uses_mysql_native_menu_allocator(self) -> None:
|
||||
mysql = (SQL_ROOT / 'shierfeng-fastapi.sql').read_text(encoding='utf-8').lower()
|
||||
postgres = (SQL_ROOT / 'shierfeng-fastapi-pg.sql').read_text(encoding='utf-8').lower()
|
||||
self.assertRegex(mysql, r'auto_increment\s*=\s*2000')
|
||||
self.assertNotRegex(mysql, r'\bbigserial\b|\balter\s+sequence\b|\bcomment\s+on\b')
|
||||
self.assertRegex(postgres, r'alter\s+sequence\s+sys_menu_menu_id_seq\s+restart\s+2000')
|
||||
self.assertNotIn('`', postgres)
|
||||
self.assertNotRegex(postgres, r'\bauto_increment\b|\bengine\s*=')
|
||||
|
||||
def test_initializers_create_event_before_inventory_foreign_key(self) -> None:
|
||||
for filename in INITIALIZERS:
|
||||
|
||||
Reference in New Issue
Block a user