diff --git a/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py b/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py index ef5df28..358a283 100644 --- a/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py +++ b/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py @@ -609,6 +609,18 @@ 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 _business_menu_rows(sql: str) -> dict[int, list]: rows = _insert_rows(sql, 'sys_menu') selected = [row for row in rows if row and str(row[0]).isdigit() and int(row[0]) in BUSINESS_MENU_IDS] @@ -642,7 +654,7 @@ class SqlContractTest(unittest.TestCase): def test_postgres_business_datetimes_preserve_default_microsecond_precision(self) -> None: self.assertEqual(34, 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')) - zero_precision_columns = [] + invalid_columns = [] for table, expected_columns in EXPECTED_DATETIME_COLUMNS.items(): definitions = _column_definitions(tables[table]) actual_columns = { @@ -653,10 +665,17 @@ class SqlContractTest(unittest.TestCase): self.assertEqual(expected_columns, actual_columns, table) for column in expected_columns: definition = definitions[column] - self.assertRegex(definition, r'\btimestamp(?:\s+without\s+time\s+zone)?\b') - if re.search(r'\btimestamp\s*\(\s*0\s*\)', definition, flags=re.I): - zero_precision_columns.append(f'{table}.{column}') - self.assertEqual([], sorted(zero_precision_columns)) + if not _is_postgres_naive_timestamp_with_default_precision(definition): + 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: expected_permissions = {page[-1] for page in EXPECTED_PAGES.values()} | {