修复:补强使用并发幂等复核
This commit is contained in:
@@ -133,7 +133,12 @@ class TestDaoPublicContract(unittest.TestCase):
|
||||
'site_ids',
|
||||
),
|
||||
'get_filling_by_idempotency_key': ('db', 'idempotency_key', 'warehouse_ids'),
|
||||
'get_usage_by_idempotency_key': ('db', 'idempotency_key', 'site_ids'),
|
||||
'get_usage_by_idempotency_key': (
|
||||
'db',
|
||||
'idempotency_key',
|
||||
'site_ids',
|
||||
'for_update',
|
||||
),
|
||||
'get_return_by_request_idempotency_key': ('db', 'idempotency_key'),
|
||||
'get_return_by_confirm_idempotency_key': ('db', 'idempotency_key'),
|
||||
'get_inbound_by_id': ('db', 'inbound_id', 'warehouse_ids'),
|
||||
@@ -225,6 +230,13 @@ class TestDaoPublicContract(unittest.TestCase):
|
||||
signature = inspect.signature(getattr(OperationDao, method_name))
|
||||
self.assertIsNone(signature.parameters['site_ids'].default)
|
||||
|
||||
usage_key_signature = inspect.signature(OperationDao.get_usage_by_idempotency_key)
|
||||
usage_key_lock = usage_key_signature.parameters.get('for_update')
|
||||
if usage_key_lock is None:
|
||||
self.fail('get_usage_by_idempotency_key 缺少 for_update 参数')
|
||||
self.assertIs(inspect.Parameter.KEYWORD_ONLY, usage_key_lock.kind)
|
||||
self.assertFalse(usage_key_lock.default)
|
||||
|
||||
return_list_signature = inspect.signature(OperationDao.get_return_list)
|
||||
self.assertEqual(('db', 'query_object', 'is_page'), tuple(return_list_signature.parameters))
|
||||
self.assertIs(True, return_list_signature.parameters['is_page'].default)
|
||||
@@ -943,6 +955,54 @@ class TestOperationDaoReads(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertNotIn('site_id IN', sql)
|
||||
self.assertNotIn('WHERE false', sql)
|
||||
|
||||
async def test_usage_idempotency_current_read_locks_refreshes_and_preserves_scope(self) -> None:
|
||||
self.assertIn(
|
||||
'for_update',
|
||||
inspect.signature(OperationDao.get_usage_by_idempotency_key).parameters,
|
||||
)
|
||||
scalar_result = MagicMock()
|
||||
scalar_result.scalar_one_or_none.return_value = None
|
||||
self.db.execute.return_value = scalar_result
|
||||
|
||||
cases = (
|
||||
([11, 9, 11], 'biz_usage_record.site_id IN (9, 11)'),
|
||||
([], None),
|
||||
(None, None),
|
||||
)
|
||||
for site_ids, expected_scope in cases:
|
||||
with self.subTest(site_ids=site_ids):
|
||||
self.db.reset_mock()
|
||||
self.db.execute.return_value = scalar_result
|
||||
await OperationDao.get_usage_by_idempotency_key(
|
||||
self.db,
|
||||
'usage-key',
|
||||
site_ids=site_ids,
|
||||
for_update=True,
|
||||
)
|
||||
statement = self.db.execute.await_args.args[0]
|
||||
|
||||
self.assertTrue(statement.get_execution_options().get('populate_existing'))
|
||||
for dialect in (mysql.dialect(), postgresql.dialect()):
|
||||
sql = compile_sql(statement, dialect)
|
||||
self.assertIn('FOR UPDATE', sql)
|
||||
if expected_scope is not None:
|
||||
self.assertIn(expected_scope, sql)
|
||||
elif site_ids == []:
|
||||
self.assertTrue('false' in sql.lower() or '0 = 1' in sql, sql)
|
||||
else:
|
||||
self.assertNotIn('site_id IN', sql)
|
||||
|
||||
self.db.reset_mock()
|
||||
self.db.execute.return_value = scalar_result
|
||||
await OperationDao.get_usage_by_idempotency_key(
|
||||
self.db,
|
||||
'usage-key',
|
||||
site_ids=[9],
|
||||
)
|
||||
unlocked_statement = self.db.execute.await_args.args[0]
|
||||
self.assertNotIn('FOR UPDATE', compile_sql(unlocked_statement, mysql.dialect()))
|
||||
self.assertFalse(unlocked_statement.get_execution_options().get('populate_existing', False))
|
||||
|
||||
def test_time_conditions_include_fractional_last_second_with_exclusive_next_day(self) -> None:
|
||||
lower, upper = OperationDao._time_conditions(
|
||||
BizUsageRecord.occurred_at,
|
||||
|
||||
Reference in New Issue
Block a user