dy-dashboard/tests/test_backup_production_no_sync.py

121 lines
5.8 KiB
Python

"""Keep production numbers linked to one order, including date-only history."""
import unittest
from datetime import datetime
from unittest import mock
from flask import Flask
import routes.purchase as purchase
class BackupProductionNoSyncTest(unittest.TestCase):
def test_exact_time_wins_without_using_date_fallback(self):
read = mock.Mock(return_value=[{'id': 9}])
self.assertEqual(purchase._backup_linked_order_ids(
read, 'domestic_orders', 'P1', '2026-09-27 18:29:13'), [9])
read.assert_called_once()
self.assertEqual(read.call_args.args[1], ('P1', '2026-09-27 18:29:13'))
def test_legacy_date_matches_only_unique_backup_and_product_order(self):
read = mock.Mock(side_effect=[[], [{'id': 2804}], [{'id': 1555}]])
self.assertEqual(purchase._backup_linked_order_ids(
read, 'domestic_orders', 'P1', '2026-09-27 18:29:13'), [2804])
self.assertIn('TRIM(`计划下单时间`)=%s', read.call_args_list[1].args[0])
self.assertEqual(read.call_args_list[1].args[1], ('P1', '2026-09-27'))
def test_ambiguous_same_day_backups_are_never_broadly_updated(self):
read = mock.Mock(side_effect=[[], [{'id': 9}], [{'id': 1}, {'id': 2}]])
with self.assertRaises(ValueError):
purchase._backup_linked_order_ids(read, 'domestic_orders', 'P1', '2026-09-27 18:29:13')
def test_ambiguous_legacy_product_orders_are_rejected(self):
read = mock.Mock(side_effect=[[], [{'id': 9}, {'id': 10}], [{'id': 1}]])
with self.assertRaises(ValueError):
purchase._backup_linked_order_ids(read, 'overseas_orders', 'P1', '2026-09-27 18:29:13')
def test_multiple_packaging_rows_can_belong_to_one_backup(self):
read = mock.Mock(side_effect=[[], [{'id': 9}, {'id': 10}], [{'id': 1}]])
self.assertEqual(purchase._backup_linked_order_ids(
read, 'packaging_orders', 'P1', '2026-09-27 18:29:13'), [9, 10])
def call_save(self, replies, execute_error=None):
app = Flask(__name__)
app.secret_key = 'test-only'
conn = mock.MagicMock()
cur = conn.cursor.return_value.__enter__.return_value
cur.fetchall.side_effect = replies
if execute_error:
cur.execute.side_effect = execute_error
with app.test_request_context(json={
'product_code': 'P1', 'backup_id': 1555, 'production_order_no': 'PS2026093029'
}), mock.patch.object(purchase, '_require_purchase_operation', return_value=None), \
mock.patch.object(purchase, 'get_db', return_value=conn), \
mock.patch.object(purchase, 'add_operation_log'):
response = purchase.backup_update_prod_no.__wrapped__()
return response.get_json(), conn, cur
def test_save_and_sync_commit_together_using_target_ids(self):
body, conn, cur = self.call_save([
[{'id': 1555, 'ordered_at': datetime(2026, 9, 27, 18, 29, 13)}],
[], [{'id': 2804}], [{'id': 1555}], # domestic legacy date
[], [], # no overseas order
[{'id': 3}, {'id': 4}], # exact packaging rows
])
self.assertEqual(body['code'], 0)
self.assertEqual(body['data']['synced'], {
'domestic_orders': 1, 'overseas_orders': 0, 'packaging_orders': 2,
})
writes = [call.args for call in cur.execute.call_args_list if call.args[0].startswith('UPDATE')]
self.assertEqual([args[1] for args in writes], [
('PS2026093029', 1555), ('PS2026093029', 2804), ('PS2026093029', 3, 4)
])
for sql, params in writes:
self.assertEqual(sql.count('%s'), len(params))
conn.commit.assert_called_once()
conn.rollback.assert_not_called()
conn.close.assert_called_once()
def test_ambiguity_prevents_even_the_backup_write(self):
body, conn, cur = self.call_save([
[{'id': 1555, 'ordered_at': datetime(2026, 9, 27, 18, 29, 13)}],
[], [{'id': 2804}], [{'id': 1555}, {'id': 1556}],
])
self.assertEqual(body['code'], 409)
self.assertFalse(any(call.args[0].startswith('UPDATE') for call in cur.execute.call_args_list))
conn.commit.assert_not_called()
conn.rollback.assert_called_once()
def test_wrong_product_or_missing_backup_is_not_saved(self):
body, conn, cur = self.call_save([[]])
self.assertEqual(body['code'], 404)
self.assertIn('product_code=%s', cur.execute.call_args.args[0])
self.assertEqual(cur.execute.call_args.args[1], (1555, 'P1'))
conn.commit.assert_not_called()
conn.rollback.assert_called_once()
def test_write_failure_rolls_back_all_related_updates(self):
app = Flask(__name__)
conn = mock.MagicMock()
cur = conn.cursor.return_value.__enter__.return_value
cur.fetchall.side_effect = [
[{'id': 1555, 'ordered_at': datetime(2026, 9, 27, 18, 29, 13)}],
[{'id': 2804}], [], [], [], [],
]
def execute(sql, params):
if sql.startswith('UPDATE domestic_orders'):
raise RuntimeError('database write failure')
cur.execute.side_effect = execute
with app.test_request_context(json={'product_code': 'P1', 'backup_id': 1555, 'production_order_no': 'PS1'}), \
mock.patch.object(purchase, '_require_purchase_operation', return_value=None), \
mock.patch.object(purchase, 'get_db', return_value=conn), \
mock.patch.object(purchase, 'add_operation_log') as audit:
with self.assertRaises(RuntimeError):
purchase.backup_update_prod_no.__wrapped__()
audit.assert_not_called()
conn.commit.assert_not_called()
conn.rollback.assert_called_once()
conn.close.assert_called_once()
if __name__ == '__main__':
unittest.main()