"""Tests for AI outfit Excel service. Pure service tests: no GUI dependency. """ import shutil import sys import tempfile import unittest from pathlib import Path from unittest.mock import patch from openpyxl import Workbook, load_workbook sys.path.insert(0, str(Path(__file__).parent.parent / "src")) class TestOutfitExcelService(unittest.TestCase): def setUp(self): self.tmp = Path(tempfile.mkdtemp()) self.excel_path = self.tmp / "outfit.xlsx" self._write_workbook() def tearDown(self): shutil.rmtree(str(self.tmp), ignore_errors=True) def _write_workbook(self): wb = Workbook() ws = wb.active ws.append(["标题", "货号", "衣服图", "结果图", "状态", "原因"]) ws.append(["T恤", "TY001", r"D:\img\ty001.png", "", "", ""]) ws.append(["已完成", "TY002", r"D:\img\ty002.png", r"D:\out\ty002.jpg", "完成", ""]) ws.append(["失败行", "TY003", r"D:\img\ty003.png", "", "失败", "旧错误"]) ws.append(["缺路径", "TY004", "", "", "", ""]) ws.append([" 保留空格 ", 1005, r"D:\img\ty005.png", "", "", ""]) wb.save(str(self.excel_path)) wb.close() def _read_row(self, row): wb = load_workbook(str(self.excel_path)) try: ws = wb.active return ( ws.cell(row, 4).value, ws.cell(row, 5).value, ws.cell(row, 6).value, ) finally: wb.close() def test_load_tasks_skips_done_failed_and_incomplete_by_default(self): from services.excel_service import load_outfit_tasks tasks = load_outfit_tasks(self.excel_path) self.assertEqual([task.row_index for task in tasks], [2, 6]) self.assertEqual(tasks[0].title, "T恤") self.assertEqual(tasks[0].product_id, "TY001") self.assertEqual(tasks[0].garment_path, r"D:\img\ty001.png") def test_load_tasks_includes_failed_when_retry_enabled(self): from services.excel_service import load_outfit_tasks tasks = load_outfit_tasks(self.excel_path, retry_failed=True) self.assertEqual([task.row_index for task in tasks], [2, 4, 6]) self.assertEqual(tasks[1].status, "失败") def test_load_tasks_preserves_user_cell_text(self): from services.excel_service import load_outfit_tasks tasks = load_outfit_tasks(self.excel_path) task = tasks[-1] self.assertEqual(task.title, " 保留空格 ") self.assertEqual(task.product_id, "1005") def test_write_success_result_updates_d_e_f_and_saves(self): from core.models import OutfitResult, OutfitTask from services.excel_service import write_outfit_result task = OutfitTask(row_index=2, title="T恤", product_id="TY001", garment_path="g.png") result = OutfitResult(task=task, success=True, output_path=r"D:\out\TY001.jpg") write_outfit_result(self.excel_path, result) self.assertEqual(self._read_row(2), (r"D:\out\TY001.jpg", "完成", None)) def test_write_failure_result_updates_e_f_and_saves(self): from core.models import OutfitResult, OutfitTask from services.excel_service import write_outfit_result task = OutfitTask(row_index=2, title="T恤", product_id="TY001", garment_path="g.png") result = OutfitResult(task=task, success=False, error="衣服图打不开") write_outfit_result(self.excel_path, result) self.assertEqual(self._read_row(2), (None, "失败", "衣服图打不开")) def test_check_excel_writable_true_for_existing_workbook(self): from services.excel_service import check_excel_writable self.assertTrue(check_excel_writable(self.excel_path)) def test_ensure_excel_writable_raises_when_probe_fails(self): from services.excel_service import ExcelInUseError, ensure_excel_writable with patch("builtins.open", side_effect=OSError("locked")): with self.assertRaises(ExcelInUseError): ensure_excel_writable(self.excel_path) if __name__ == "__main__": unittest.main()