dy-dashboard/tests/test_spot_confirmation_edit.py
2026-10-28 11:14:17 +08:00

116 lines
6.8 KiB
Python

import unittest
from datetime import date
from unittest.mock import MagicMock, patch
from flask import Flask
from routes import spot
from services.spot_confirmation_edit import confirmation_reminder_state, validate_confirmation_values
from services.spot_confirmation_import import parse_confirmation_import, ImportValidationError
class ConfirmationEditValidationTest(unittest.TestCase):
def test_price_is_free_text_with_length_limit(self):
for price in ('0.1234', '不含税运单价4.4元 含税4.444元', ''):
self.assertEqual(validate_confirmation_values({'product_unit_price':price})['product_unit_price'], price)
with self.assertRaises(ValueError):
validate_confirmation_values({'product_unit_price':'价' * 1001})
def test_confirmation_options_and_other_reason(self):
for value in ('做','不做',''):
self.assertEqual(validate_confirmation_values({'sample_confirmed':value})['sample_confirmed'],value)
with self.assertRaises(ValueError):
validate_confirmation_values({'sample_confirmed':'是'})
for reason in ('价格高','质感差','资质不全','同质化严重'):
self.assertEqual(validate_confirmation_values({'sample_confirmation_reason':reason})['sample_confirmation_reason'],reason)
with self.assertRaises(ValueError):
validate_confirmation_values({'sample_confirmation_reason':'其他','other_reason':'原因是'+reason})
with self.assertRaises(ValueError):
validate_confirmation_values({'sample_confirmation_reason':'其他','other_reason':' '})
self.assertEqual(validate_confirmation_values({'sample_confirmation_reason':'其他','other_reason':' 尺寸不合适 '})['sample_confirmation_reason'],'其他:尺寸不合适')
self.assertEqual(validate_confirmation_values({'sample_confirmation_reason':'无'})['sample_confirmation_reason'],'无')
self.assertEqual(validate_confirmation_values({'sample_confirmed':'做'})['sample_confirmation_reason'],'无')
self.assertEqual(validate_confirmation_values({
'sample_confirmed':'做','sample_confirmation_reason':'价格高'
})['sample_confirmation_reason'],'价格高')
def test_sample_status_options_and_legacy_compatibility(self):
current = ('未付款','付款中','样品到货中','样品已到货','不授权','厂家无货','停产','其他')
for value in current + ('产品停产','厂家无现货','已到货','不做','待寄出',''):
self.assertEqual(validate_confirmation_values({'sample_status':value})['sample_status'], value)
with self.assertRaises(ValueError):
validate_confirmation_values({'sample_status':'任意状态'})
def test_confirmation_reminder_rule(self):
self.assertEqual(confirmation_reminder_state('', '', date(2026,10,16)),
{'due_date':None,'pending':False})
self.assertEqual(confirmation_reminder_state('2026-10-06', '', date(2026,10,15)),
{'due_date':'2026-10-16','pending':False})
self.assertEqual(confirmation_reminder_state('2026-10-06', '', date(2026,10,16)),
{'due_date':'2026-10-16','pending':True})
self.assertFalse(confirmation_reminder_state('2026-10-01', '做', date(2026,10,16))['pending'])
def test_import_enforces_same_rules_and_template_removals(self):
rows=parse_confirmation_import('产品名称,产品单价,样品确认,样品确认原因\n样品,12.3456,不做,其他:尺寸不合适'.encode(),'test.csv')
self.assertEqual(rows[0][1]['product_unit_price'],'12.3456')
self.assertEqual(parse_confirmation_import('产品名称,产品单价\n样品,不含税4元'.encode(),'test.csv')[0][1]['product_unit_price'],'不含税4元')
for text in ('产品名称,样品确认\n样品,是',
'产品名称,样品确认原因\n样品,其他:价格高且尺寸不合适'):
with self.assertRaises(ImportValidationError):
parse_confirmation_import(text.encode(),'test.csv')
class ConfirmationSaveTest(unittest.TestCase):
def setUp(self):
self.app=Flask(__name__)
self.app.secret_key='test'
self.conn=MagicMock()
self.cur=self.conn.cursor.return_value.__enter__.return_value
self.cur.fetchone.return_value={'id':7,'status':'通过'}
self.db=patch.object(spot,'get_db',return_value=self.conn).start()
self.perm=patch.object(spot,'operation_required',return_value=True).start()
patch.object(spot,'role_required',return_value=True).start()
patch.object(spot,'bump_data_version').start()
patch.object(spot,'add_operation_log').start()
self.addCleanup(patch.stopall)
def save(self, data):
with self.app.test_request_context('/api/spot/confirmation/save',method='POST',json=data):
response=spot.confirmation_save.__wrapped__()
return (response[0] if isinstance(response,tuple) else response).get_json()
def test_save_transaction_and_clear_optional_fields(self):
result=self.save({'application_id':7,'product_unit_price':'12.3456',
'sample_confirmed':'做','arrival_date':'','sample_confirmation_reason':'其他','other_reason':'尺寸不合适'})
self.assertEqual(result['code'],0)
self.conn.commit.assert_called_once()
sql,params=self.cur.execute.call_args.args
self.assertIn('UPDATE spot_confirmations',sql)
self.assertIn('12.3456',params)
self.assertIn(None,params)
self.assertIn('其他:尺寸不合适',params)
self.conn.close.assert_called_once()
def test_readonly_and_removed_fields_cannot_be_written(self):
for key in ('brand','product_name','recipient','receiving_department','sample_location','sample_remark'):
self.assertEqual(self.save({'application_id':7,key:'篡改'})['code'],400)
self.db.assert_not_called()
def test_permissions_are_required(self):
self.perm.return_value=False
self.assertEqual(self.save({'application_id':7,'remark':'修改'})['code'],403)
self.db.assert_not_called()
def test_rejected_or_missing_application_cannot_be_saved(self):
for value in (None,{'id':7,'status':'驳回'}):
self.cur.fetchone.return_value=value
self.assertEqual(self.save({'application_id':7,'remark':'修改'})['code'],404)
self.conn.commit.assert_not_called()
def test_db_failure_rolls_back(self):
def execute(sql,*args):
if sql.startswith('UPDATE'):
raise RuntimeError('simulated failure')
self.cur.execute.side_effect=execute
self.assertEqual(self.save({'application_id':7,'remark':'修改'})['code'],500)
self.conn.rollback.assert_called_once()
self.conn.commit.assert_not_called()