Files
houhuan e4d62df7e3 feat: 益选 OCR 订单处理系统初始提交
- 智能供应商识别(蓉城易购/烟草/杨碧月/通用)
- 百度 OCR 表格识别集成
- 规则引擎(列映射/数据清洗/单位转换/规格推断)
- 条码映射管理与云端同步(Gitea REST API)
- 云端同步支持:条码映射、供应商配置、商品资料、采购模板
- 拖拽一键处理(图片→OCR→Excel→合并)
- 191 个单元测试
- 移除无用的模板管理功能
- 清理 IDE 产物目录

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 19:51:13 +08:00

252 lines
8.2 KiB
Python

"""app.core.excel.validators 单元测试"""
import pytest
from app.core.excel.validators import ProductValidator
@pytest.fixture
def validator():
return ProductValidator()
class TestValidateBarcode:
"""条码验证测试"""
def test_valid_barcode_13_digits(self, validator):
ok, val, err = validator.validate_barcode("6920584471055")
assert ok is True
assert val == "6920584471055"
assert err is None
def test_valid_barcode_8_digits(self, validator):
ok, val, err = validator.validate_barcode("12345678")
assert ok is True
assert val == "12345678"
def test_valid_barcode_12_digits(self, validator):
ok, val, err = validator.validate_barcode("692058447105")
assert ok is True
def test_none_returns_invalid(self, validator):
ok, val, err = validator.validate_barcode(None)
assert ok is False
assert err == "条码为空"
def test_warehouse_identifier(self, validator):
ok, val, err = validator.validate_barcode("仓库")
assert ok is False
assert val == "仓库"
assert err == "条码为仓库标识"
def test_warehouse_full_name(self, validator):
ok, val, err = validator.validate_barcode("仓库全名")
assert ok is False
def test_prefix_5_to_6_correction(self, validator):
"""5开头(非53)的长条码应修正为6开头"""
ok, val, err = validator.validate_barcode("5920584471055")
assert ok is True
assert val.startswith("6")
assert val == "6920584471055"
def test_prefix_53_not_corrected(self, validator):
"""53开头的条码不修正"""
ok, val, err = validator.validate_barcode("5321545613000")
assert ok is True
assert val.startswith("53")
def test_14_digit_trailing_zero_truncated(self, validator):
"""14位条码末尾是0时截断到13位"""
ok, val, err = validator.validate_barcode("69205844710550")
assert ok is True
assert len(val) == 13
def test_14_digit_no_trailing_zero_invalid(self, validator):
"""14位条码末尾不是0时报错"""
ok, val, err = validator.validate_barcode("69205844710551")
assert ok is False
assert "长度异常" in err
def test_too_short_invalid(self, validator):
ok, val, err = validator.validate_barcode("1234567")
assert ok is False
assert "长度异常" in err
def test_too_long_invalid(self, validator):
ok, val, err = validator.validate_barcode("1" * 14)
# 14 digits with trailing 0s gets truncated, but "111...1" has no trailing 0
ok2, val2, err2 = validator.validate_barcode("1" * 15)
assert ok2 is False
def test_no_digits_invalid(self, validator):
ok, val, err = validator.validate_barcode("abc")
assert ok is False
assert err == "条码不包含数字"
def test_float_input_cleaned(self, validator):
"""浮点数输入应清理为整数字符串"""
ok, val, err = validator.validate_barcode(6920584471055.0)
assert ok is True
assert val == "6920584471055"
def test_special_barcode_5321545613(self, validator):
"""特殊条码 5321545613 应通过验证"""
ok, val, err = validator.validate_barcode("5321545613")
assert ok is True
assert val == "5321545613"
class TestValidatePrice:
"""单价验证测试"""
def test_valid_price(self, validator):
ok, val, is_gift, err = validator.validate_price(10.5)
assert ok is True
assert val == 10.5
assert is_gift is False
def test_zero_price_is_gift(self, validator):
ok, val, is_gift, err = validator.validate_price(0)
assert ok is True
assert val == 0.0
assert is_gift is True
def test_none_is_gift(self, validator):
ok, val, is_gift, err = validator.validate_price(None)
assert ok is False
assert is_gift is True
def test_gift_string(self, validator):
ok, val, is_gift, err = validator.validate_price("赠品")
assert ok is True
assert is_gift is True
def test_gift_english(self, validator):
ok, val, is_gift, err = validator.validate_price("gift")
assert ok is True
assert is_gift is True
def test_price_string_with_yen(self, validator):
ok, val, is_gift, err = validator.validate_price("¥123.45")
assert ok is True
assert val == 123.45
assert is_gift is False
def test_price_string_with_comma(self, validator):
ok, val, is_gift, err = validator.validate_price("1,234.56")
assert ok is True
assert val == 1234.56
def test_negative_price_invalid(self, validator):
ok, val, is_gift, err = validator.validate_price(-5)
assert ok is False
assert is_gift is True
def test_empty_string_is_gift(self, validator):
ok, val, is_gift, err = validator.validate_price("")
assert ok is True
assert is_gift is True
class TestValidateQuantity:
"""数量验证测试"""
def test_valid_quantity(self, validator):
ok, val, err = validator.validate_quantity(10)
assert ok is True
assert val == 10.0
def test_float_quantity(self, validator):
ok, val, err = validator.validate_quantity(2.5)
assert ok is True
assert val == 2.5
def test_string_quantity(self, validator):
ok, val, err = validator.validate_quantity("15")
assert ok is True
assert val == 15.0
def test_string_with_unit(self, validator):
ok, val, err = validator.validate_quantity("10瓶")
assert ok is True
assert val == 10.0
def test_none_invalid(self, validator):
ok, val, err = validator.validate_quantity(None)
assert ok is False
assert err == "数量为空"
def test_zero_invalid(self, validator):
ok, val, err = validator.validate_quantity(0)
assert ok is False
assert "必须大于0" in err
def test_negative_invalid(self, validator):
ok, val, err = validator.validate_quantity(-3)
assert ok is False
assert "必须大于0" in err
def test_non_numeric_string_invalid(self, validator):
ok, val, err = validator.validate_quantity("abc")
assert ok is False
assert err == "数量不包含数字"
class TestValidateProduct:
"""商品数据整体验证测试"""
def test_valid_product(self, validator):
product = {
'barcode': '6920584471055',
'price': 10.5,
'quantity': 5,
'amount': 52.5,
}
result = validator.validate_product(product)
assert result['barcode'] == '6920584471055'
assert result['price'] == 10.5
assert result['quantity'] == 5.0
assert result.get('is_gift') is None or result.get('is_gift') is False
def test_gift_product(self, validator):
product = {
'barcode': '6920584471055',
'price': '赠品',
'quantity': 5,
}
result = validator.validate_product(product)
assert result['is_gift'] is True
assert result['price'] == 0.0
def test_quantity_from_amount_and_price(self, validator):
"""数量为空时,通过金额/单价计算"""
product = {
'barcode': '6920584471055',
'price': 10.0,
'amount': 50.0,
'quantity': None,
}
result = validator.validate_product(product)
assert result['quantity'] == 5.0 # 50 / 10
def test_invalid_barcode_still_uses_fixed(self, validator):
"""条码验证失败但有修复值时仍使用修复值"""
product = {
'barcode': '5920584471055', # 5开头, 会被修正为6开头
'price': 10.0,
'quantity': 5,
}
result = validator.validate_product(product)
assert result['barcode'] == '6920584471055'
def test_amount_zero_marks_gift(self, validator):
"""金额为0时标记为赠品"""
product = {
'barcode': '6920584471055',
'price': 10.0,
'quantity': 5,
'amount': 0,
}
result = validator.validate_product(product)
assert result.get('is_gift') is True