"""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()