补强主数据可删除性查询测试
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import inspect
|
import inspect
|
||||||
|
import re
|
||||||
import unittest
|
import unittest
|
||||||
from contextlib import ExitStack
|
from contextlib import ExitStack
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -8,6 +9,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
|||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.routing import APIRoute
|
from fastapi.routing import APIRoute
|
||||||
|
from sqlalchemy.dialects import mysql, postgresql
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
@@ -753,9 +755,12 @@ class TestMasterDao(unittest.IsolatedAsyncioTestCase):
|
|||||||
|
|
||||||
async def test_master_removability_uses_the_same_reference_constraints_as_delete(self) -> None:
|
async def test_master_removability_uses_the_same_reference_constraints_as_delete(self) -> None:
|
||||||
expected = {
|
expected = {
|
||||||
'strain': (('biz_filling_record', ('strain_id',)),),
|
'strain': ('biz_strain', 'strain_id', (('biz_filling_record', ('strain_id',)),)),
|
||||||
'customer': (('biz_customer_site', ('customer_id',)),),
|
'customer': ('biz_customer', 'customer_id', (('biz_customer_site', ('customer_id',)),)),
|
||||||
'site': (
|
'site': (
|
||||||
|
'biz_customer_site',
|
||||||
|
'site_id',
|
||||||
|
(
|
||||||
('biz_production_line', ('site_id',)),
|
('biz_production_line', ('site_id',)),
|
||||||
('biz_user_site', ('site_id',)),
|
('biz_user_site', ('site_id',)),
|
||||||
('biz_shipment_order', ('target_site_id',)),
|
('biz_shipment_order', ('target_site_id',)),
|
||||||
@@ -764,12 +769,20 @@ class TestMasterDao(unittest.IsolatedAsyncioTestCase):
|
|||||||
('biz_pallet_inventory', ('site_id',)),
|
('biz_pallet_inventory', ('site_id',)),
|
||||||
('biz_pallet_event', ('before_site_id', 'after_site_id')),
|
('biz_pallet_event', ('before_site_id', 'after_site_id')),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
'line': (
|
'line': (
|
||||||
|
'biz_production_line',
|
||||||
|
'line_id',
|
||||||
|
(
|
||||||
('biz_usage_record', ('line_id',)),
|
('biz_usage_record', ('line_id',)),
|
||||||
('biz_pallet_inventory', ('current_line_id',)),
|
('biz_pallet_inventory', ('current_line_id',)),
|
||||||
('biz_pallet_event', ('before_line_id', 'after_line_id')),
|
('biz_pallet_event', ('before_line_id', 'after_line_id')),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
'warehouse': (
|
'warehouse': (
|
||||||
|
'biz_warehouse',
|
||||||
|
'warehouse_id',
|
||||||
|
(
|
||||||
('biz_user_warehouse', ('warehouse_id',)),
|
('biz_user_warehouse', ('warehouse_id',)),
|
||||||
('biz_inbound_order', ('warehouse_id',)),
|
('biz_inbound_order', ('warehouse_id',)),
|
||||||
('biz_filling_record', ('warehouse_id',)),
|
('biz_filling_record', ('warehouse_id',)),
|
||||||
@@ -778,11 +791,12 @@ class TestMasterDao(unittest.IsolatedAsyncioTestCase):
|
|||||||
('biz_pallet_inventory', ('warehouse_id',)),
|
('biz_pallet_inventory', ('warehouse_id',)),
|
||||||
('biz_pallet_event', ('before_warehouse_id', 'after_warehouse_id')),
|
('biz_pallet_event', ('before_warehouse_id', 'after_warehouse_id')),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
method = getattr(MasterDao, 'get_master_removability', None)
|
method = getattr(MasterDao, 'get_master_removability', None)
|
||||||
self.assertIsNotNone(method)
|
self.assertIsNotNone(method)
|
||||||
|
|
||||||
for resource, references in expected.items():
|
for resource, (outer_table, outer_id, references) in expected.items():
|
||||||
result = MagicMock()
|
result = MagicMock()
|
||||||
result.all.return_value = [(1, True), (2, False)]
|
result.all.return_value = [(1, True), (2, False)]
|
||||||
self.db.execute = AsyncMock(return_value=result)
|
self.db.execute = AsyncMock(return_value=result)
|
||||||
@@ -790,11 +804,29 @@ class TestMasterDao(unittest.IsolatedAsyncioTestCase):
|
|||||||
removability = await method(self.db, resource, [1, 2, 3])
|
removability = await method(self.db, resource, [1, 2, 3])
|
||||||
|
|
||||||
self.assertEqual({1: True, 2: False, 3: False}, removability)
|
self.assertEqual({1: True, 2: False, 3: False}, removability)
|
||||||
sql = compile_sql(self.db.execute.await_args.args[0])
|
statement = self.db.execute.await_args.args[0]
|
||||||
|
for dialect in (mysql.dialect(), postgresql.dialect()):
|
||||||
|
with self.subTest(resource=resource, dialect=dialect.name):
|
||||||
|
sql = ' '.join(
|
||||||
|
str(statement.compile(dialect=dialect, compile_kwargs={'literal_binds': True})).split()
|
||||||
|
)
|
||||||
|
self.assertEqual(len(references), len(re.findall(r'NOT\s+\(?EXISTS\s+\(', sql)))
|
||||||
for table, columns in references:
|
for table, columns in references:
|
||||||
self.assertIn(f'FROM {table}', sql)
|
self.assertRegex(
|
||||||
|
sql,
|
||||||
|
rf'NOT\s+\(?EXISTS\s+\(SELECT {table}\.{columns[0]} FROM {table} WHERE ',
|
||||||
|
)
|
||||||
for column in columns:
|
for column in columns:
|
||||||
self.assertIn(f'{table}.{column}', sql)
|
self.assertIn(f'{table}.{column} = {outer_table}.{outer_id}', sql)
|
||||||
|
|
||||||
|
async def test_master_removability_short_circuits_empty_resource_ids(self) -> None:
|
||||||
|
self.db.execute = AsyncMock()
|
||||||
|
|
||||||
|
for resource in RESOURCE_IDS:
|
||||||
|
with self.subTest(resource=resource):
|
||||||
|
self.assertEqual({}, await MasterDao.get_master_removability(self.db, resource, []))
|
||||||
|
|
||||||
|
self.db.execute.assert_not_awaited()
|
||||||
|
|
||||||
|
|
||||||
class TestMasterControllers(unittest.TestCase):
|
class TestMasterControllers(unittest.TestCase):
|
||||||
|
|||||||
Reference in New Issue
Block a user