e4d62df7e3
- 智能供应商识别(蓉城易购/烟草/杨碧月/通用) - 百度 OCR 表格识别集成 - 规则引擎(列映射/数据清洗/单位转换/规格推断) - 条码映射管理与云端同步(Gitea REST API) - 云端同步支持:条码映射、供应商配置、商品资料、采购模板 - 拖拽一键处理(图片→OCR→Excel→合并) - 191 个单元测试 - 移除无用的模板管理功能 - 清理 IDE 产物目录 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
188 lines
5.9 KiB
Python
188 lines
5.9 KiB
Python
"""app.core.db.product_db 单元测试"""
|
|
|
|
import os
|
|
import tempfile
|
|
|
|
import pytest
|
|
import pandas as pd
|
|
|
|
from app.core.db.product_db import ProductDatabase
|
|
|
|
|
|
@pytest.fixture
|
|
def db_dir():
|
|
"""临时目录"""
|
|
with tempfile.TemporaryDirectory() as d:
|
|
yield d
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_excel(db_dir):
|
|
"""创建测试用 Excel 文件"""
|
|
path = os.path.join(db_dir, '商品资料.xlsx')
|
|
df = pd.DataFrame({
|
|
'商品条码': ['6920584471055', '6901028001133', '6925303800013'],
|
|
'商品名称': ['农夫山泉550ml', '蒙牛纯牛奶', '可口可乐330ml'],
|
|
'进货价': [1.2, 3.5, 1.8],
|
|
'单位': ['瓶', '盒', '罐'],
|
|
})
|
|
df.to_excel(path, index=False)
|
|
return path
|
|
|
|
|
|
@pytest.fixture
|
|
def db_with_data(db_dir, sample_excel):
|
|
"""已导入数据的数据库"""
|
|
db_path = os.path.join(db_dir, 'product_cache.db')
|
|
db = ProductDatabase(db_path, sample_excel)
|
|
return db
|
|
|
|
|
|
class TestProductDatabaseInit:
|
|
"""数据库初始化测试"""
|
|
|
|
def test_auto_import_on_first_run(self, db_dir, sample_excel):
|
|
"""首次运行自动从 Excel 导入"""
|
|
db_path = os.path.join(db_dir, 'product_cache.db')
|
|
assert not os.path.exists(db_path)
|
|
|
|
db = ProductDatabase(db_path, sample_excel)
|
|
|
|
assert os.path.exists(db_path)
|
|
assert db.count() == 3
|
|
|
|
def test_no_reimport_on_existing_db(self, db_dir, sample_excel):
|
|
"""数据库已存在时不重新导入"""
|
|
db_path = os.path.join(db_dir, 'product_cache.db')
|
|
|
|
db1 = ProductDatabase(db_path, sample_excel)
|
|
assert db1.count() == 3
|
|
|
|
# 删除 Excel 后仍能打开已有数据库
|
|
os.remove(sample_excel)
|
|
db2 = ProductDatabase(db_path, sample_excel)
|
|
assert db2.count() == 3
|
|
|
|
def test_missing_excel_creates_empty_db(self, db_dir):
|
|
"""Excel 不存在时创建空数据库"""
|
|
db_path = os.path.join(db_dir, 'product_cache.db')
|
|
fake_excel = os.path.join(db_dir, '不存在.xlsx')
|
|
|
|
db = ProductDatabase(db_path, fake_excel)
|
|
|
|
assert os.path.exists(db_path)
|
|
assert db.count() == 0
|
|
|
|
def test_missing_dir_created(self, db_dir, sample_excel):
|
|
"""数据库目录不存在时自动创建"""
|
|
db_path = os.path.join(db_dir, 'subdir', 'product_cache.db')
|
|
db = ProductDatabase(db_path, sample_excel)
|
|
assert os.path.exists(db_path)
|
|
assert db.count() == 3
|
|
|
|
|
|
class TestGetPrice:
|
|
"""单条查询测试"""
|
|
|
|
def test_existing_barcode(self, db_with_data):
|
|
price = db_with_data.get_price('6920584471055')
|
|
assert price == pytest.approx(1.2)
|
|
|
|
def test_nonexistent_barcode(self, db_with_data):
|
|
price = db_with_data.get_price('0000000000000')
|
|
assert price is None
|
|
|
|
def test_empty_barcode(self, db_with_data):
|
|
price = db_with_data.get_price('')
|
|
assert price is None
|
|
|
|
def test_barcode_with_spaces(self, db_with_data):
|
|
"""条码前后空格应能匹配"""
|
|
price = db_with_data.get_price(' 6920584471055 ')
|
|
assert price == pytest.approx(1.2)
|
|
|
|
|
|
class TestGetPrices:
|
|
"""批量查询测试"""
|
|
|
|
def test_multiple_barcodes(self, db_with_data):
|
|
result = db_with_data.get_prices(['6920584471055', '6901028001133'])
|
|
assert len(result) == 2
|
|
assert result['6920584471055'] == pytest.approx(1.2)
|
|
assert result['6901028001133'] == pytest.approx(3.5)
|
|
|
|
def test_partial_match(self, db_with_data):
|
|
"""部分条码存在,部分不存在"""
|
|
result = db_with_data.get_prices(['6920584471055', '0000000000000'])
|
|
assert len(result) == 1
|
|
assert '6920584471055' in result
|
|
|
|
def test_empty_list(self, db_with_data):
|
|
result = db_with_data.get_prices([])
|
|
assert result == {}
|
|
|
|
def test_all_nonexistent(self, db_with_data):
|
|
result = db_with_data.get_prices(['0000000000000', '1111111111111'])
|
|
assert result == {}
|
|
|
|
|
|
class TestReimport:
|
|
"""重新导入测试"""
|
|
|
|
def test_reimport_clears_and_reloads(self, db_dir, sample_excel):
|
|
db_path = os.path.join(db_dir, 'product_cache.db')
|
|
db = ProductDatabase(db_path, sample_excel)
|
|
assert db.count() == 3
|
|
|
|
# 修改 Excel,添加一行
|
|
df = pd.read_excel(sample_excel)
|
|
df = pd.concat([df, pd.DataFrame({
|
|
'商品条码': ['6954365200123'],
|
|
'商品名称': ['测试商品'],
|
|
'进货价': [5.0],
|
|
'单位': ['个'],
|
|
})])
|
|
df.to_excel(sample_excel, index=False)
|
|
|
|
count = db.reimport()
|
|
assert count == 4
|
|
assert db.count() == 4
|
|
assert db.get_price('6954365200123') == pytest.approx(5.0)
|
|
|
|
|
|
class TestEdgeCases:
|
|
"""边界条件测试"""
|
|
|
|
def test_excel_with_missing_price(self, db_dir):
|
|
"""Excel 中价格列为空的行"""
|
|
path = os.path.join(db_dir, '商品资料.xlsx')
|
|
df = pd.DataFrame({
|
|
'商品条码': ['6920584471055', '6901028001133'],
|
|
'商品名称': ['商品A', '商品B'],
|
|
'进货价': [1.5, None],
|
|
})
|
|
df.to_excel(path, index=False)
|
|
|
|
db_path = os.path.join(db_dir, 'product_cache.db')
|
|
db = ProductDatabase(db_path, path)
|
|
|
|
assert db.count() == 2
|
|
assert db.get_price('6920584471055') == pytest.approx(1.5)
|
|
assert db.get_price('6901028001133') == pytest.approx(0.0)
|
|
|
|
def test_excel_with_duplicate_barcodes(self, db_dir):
|
|
"""重复条码取最后一条 (INSERT OR REPLACE)"""
|
|
path = os.path.join(db_dir, '商品资料.xlsx')
|
|
df = pd.DataFrame({
|
|
'商品条码': ['6920584471055', '6920584471055'],
|
|
'商品名称': ['商品A', '商品A-新'],
|
|
'进货价': [1.0, 2.0],
|
|
})
|
|
df.to_excel(path, index=False)
|
|
|
|
db_path = os.path.join(db_dir, 'product_cache.db')
|
|
db = ProductDatabase(db_path, path)
|
|
|
|
assert db.count() == 1
|
|
assert db.get_price('6920584471055') == pytest.approx(2.0)
|