116 lines
6.8 KiB
Python
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()
|