更新登录页及优化相关功能

This commit is contained in:
焦龙言
2026-10-08 13:17:59 +08:00
parent ecb254cb54
commit bcdb0524c2
139 changed files with 76688 additions and 811 deletions
@@ -2,7 +2,7 @@ import hashlib
import inspect
import json
import unittest
from datetime import datetime
from datetime import date, datetime
from types import SimpleNamespace
from typing import Any, get_type_hints
from unittest.mock import AsyncMock, MagicMock, patch
@@ -60,7 +60,7 @@ def make_page(
strain_id: int = 11,
batch_no: str = 'B-20260726',
package_spec: str = '20kg',
initial_packages: int = 18,
initial_packages: int = 100,
idempotency_key: str = 'fill-key',
) -> FillingCreateModel:
return FillingCreateModel(
@@ -68,6 +68,8 @@ def make_page(
warehouseId=warehouse_id,
strainId=strain_id,
batchNo=batch_no,
productionDate='2026-07-20',
expiryDate='2027-01-20',
packageSpec=package_spec,
initialPackages=initial_packages,
idempotencyKey=idempotency_key,
@@ -78,7 +80,7 @@ def make_pallet(
pallet_id: int = 2,
*,
qr_token: str = 'qr-2',
capacity_packages: int = 20,
capacity_packages: int = 100,
asset_status: str = '0',
test_batch_id: int | None = None,
) -> BizPallet:
@@ -200,7 +202,9 @@ class TestFillingPublicContract(unittest.TestCase):
expected_payload = json.dumps(
{
'batch_no': 'B-20260726',
'initial_packages': 18,
'production_date': '2026-07-20',
'expiry_date': '2027-01-20',
'initial_packages': 100,
'package_spec': '20kg',
'qr_token': 'qr-2',
'strain_id': 11,
@@ -364,7 +368,7 @@ class TestFillingReadServices(unittest.IsolatedAsyncioTestCase):
self.db.commit.assert_not_awaited()
self.assertEqual(PalletFlowState.EMPTY_IN_WAREHOUSE, result.flow_state)
self.assertEqual(['filling'], result.allowed_actions)
self.assertEqual(20, result.capacity_packages)
self.assertEqual(100, result.capacity_packages)
self.assertEqual(0, result.remaining_packages)
async def test_scan_checks_access_before_exposing_invalid_state(self) -> None:
@@ -534,8 +538,10 @@ class TestFillPalletService(unittest.IsolatedAsyncioTestCase):
self.assertEqual('S-011', filling.strain_code_snapshot)
self.assertEqual('香菇 11', filling.strain_name_snapshot)
self.assertEqual('B-20260726', filling.batch_no)
self.assertEqual(date(2026, 7, 20), filling.production_date)
self.assertEqual(date(2027, 1, 20), filling.expiry_date)
self.assertEqual('20kg', filling.package_spec)
self.assertEqual(18, filling.initial_packages)
self.assertEqual(100, filling.initial_packages)
self.assertEqual('ACTIVE', filling.active_slot)
self.assertEqual('fill-key', filling.idempotency_key)
self.assertEqual(FillingService._build_request_hash(self.page), filling.request_hash)
@@ -547,7 +553,7 @@ class TestFillPalletService(unittest.IsolatedAsyncioTestCase):
self.assertEqual(self.fixed_time, filling.update_time)
self.assertEqual(31, self.inventory.active_filling_id)
self.assertEqual(18, self.inventory.remaining_packages)
self.assertEqual(100, self.inventory.remaining_packages)
self.assertEqual(PalletFlowState.FILLED_IN_WAREHOUSE.value, self.inventory.flow_state)
self.assertEqual(4, self.inventory.version)
self.assertEqual(self.fixed_time, self.inventory.updated_at)
@@ -566,7 +572,7 @@ class TestFillPalletService(unittest.IsolatedAsyncioTestCase):
self.assertIsNone(event.before_line_id)
self.assertIsNone(event.after_line_id)
self.assertEqual(0, event.before_remaining_packages)
self.assertEqual(18, event.after_remaining_packages)
self.assertEqual(100, event.after_remaining_packages)
self.assertEqual(7, event.operator_id)
self.assertEqual(self.fixed_time, event.occurred_at)
self.assertEqual('fill-key', event.idempotency_key)
@@ -632,8 +638,8 @@ class TestFillPalletService(unittest.IsolatedAsyncioTestCase):
self.db.commit.assert_not_awaited()
self.db.rollback.assert_awaited_once()
async def test_fill_rejects_packages_over_persisted_pallet_capacity(self) -> None:
await self._assert_fill_rejected(page=make_page(initial_packages=21))
async def test_fill_rejects_historical_nonstandard_pallet_capacity(self) -> None:
await self._assert_fill_rejected(pallet=make_pallet(capacity_packages=20))
async def test_fill_rejects_stopped_warehouse_only_after_core_locks(self) -> None:
actions: list[str] = []
@@ -1152,7 +1158,7 @@ class TestFillingController(unittest.TestCase):
@staticmethod
def _routes() -> dict[tuple[str, str], APIRoute]:
return {
(route.path, next(iter(route.methods))): route
(route.path_format, next(iter(route.methods))): route
for route in filling_controller_module.filling_controller.routes
if isinstance(route, APIRoute)
}
@@ -1189,7 +1195,7 @@ class TestFillingController(unittest.TestCase):
self.assertEqual(ResponseBaseModel, routes[('/business/filling', 'POST')].response_model)
def test_static_routes_are_declared_before_dynamic_detail(self) -> None:
paths = [route.path for route in filling_controller_module.filling_controller.routes]
paths = [route.path_format for route in filling_controller_module.filling_controller.routes]
detail_index = paths.index('/business/filling/{filling_id}')
self.assertLess(paths.index('/business/filling/strain-options'), detail_index)
self.assertLess(paths.index('/business/filling/scan/{qr_token}'), detail_index)
@@ -1218,7 +1224,7 @@ class TestFillingController(unittest.TestCase):
set(actual.values()),
)
def test_all_routes_inject_current_user_and_only_post_has_log(self) -> None:
def test_all_routes_inject_current_user_and_expected_routes_have_log(self) -> None:
for key, route in self._routes().items():
self.assertTrue(
any(
@@ -1227,7 +1233,11 @@ class TestFillingController(unittest.TestCase):
),
key,
)
self.assertEqual(key == ('/business/filling', 'POST'), hasattr(route.endpoint, '__wrapped__'))
expected_logged = {
('/business/filling/scan/{qr_token}', 'GET'),
('/business/filling', 'POST'),
}
self.assertEqual(key in expected_logged, hasattr(route.endpoint, '__wrapped__'))
def test_controller_does_not_add_scan_export_or_draft_permissions(self) -> None:
source = inspect.getsource(filling_controller_module)