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