PicoBot/skills/lark-slides/scripts/iconpark_tool_test.py
ooodc a7883dbed9 refactor(todo): 重构待办事项管理逻辑及更新状态规则
- 移除 TodoItem 中的 priority、created_at 和 updated_at 字段
- 强制每个任务都必须有唯一 id,且由用户负责生成
- 修改合并模式逻辑,merge=true 下保留未提及的旧任务
- 支持已完成和已取消任务重新激活(状态改回 pending 或 in_progress)
- 禁止 in_progress 状态退回到 pending,必须标记为 completed 或 cancelled
- 优化状态转换校验,允许特定状态间合法切换
- 简化任务变更消息,移除详细的新增/更新/移除统计
- 更新文档和示例,明确 id 必须由用户生成和使用
- 修复和补充测试,增强状态转换和合并模式验证
- 调整任务时间戳生成逻辑,统一使用当前时间及索引
- 该变更提供更合理的任务状态机械及管理模式,提升稳定性和易用性
2026-06-13 09:22:33 +08:00

178 lines
7.7 KiB
Python

# Copyright (c) 2026 Lark Technologies Pte. Ltd.
# SPDX-License-Identifier: MIT
from __future__ import annotations
import json
import subprocess
import sys
import unittest
from pathlib import Path
import iconpark_tool
SCRIPT_PATH = Path(__file__).resolve().with_name("iconpark_tool.py")
class IconParkToolTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.index_data = iconpark_tool.load_index()
def test_search_icons_finds_growth_trend(self) -> None:
results = iconpark_tool.search_icons(self.index_data, {"query": "增长趋势", "limit": 5})
self.assertTrue(results)
self.assertTrue(
any(entry["iconType"] == "iconpark/Charts/positive-dynamics.svg" for entry in results)
)
def test_search_icons_supports_english_query(self) -> None:
results = iconpark_tool.search_icons(self.index_data, {"query": "security protect", "limit": 3})
self.assertTrue(results)
self.assertEqual(results[0]["iconType"], "iconpark/Safe/protect.svg")
def test_search_icons_supports_category_filter(self) -> None:
results = iconpark_tool.search_icons(
self.index_data,
{"query": "data", "category": "Charts", "limit": 10},
)
self.assertTrue(results)
self.assertTrue(all(entry["category"] == "Charts" for entry in results))
def test_search_icons_does_not_expand_ai_inside_words(self) -> None:
mail_results = iconpark_tool.search_icons(self.index_data, {"query": "mail", "limit": 5})
self.assertEqual(mail_results[0]["iconType"], "iconpark/Office/envelope-one.svg")
self.assertNotEqual(mail_results[0]["iconType"], "iconpark/Others/magic.svg")
fail_results = iconpark_tool.search_icons(self.index_data, {"query": "fail", "limit": 5})
self.assertNotEqual(fail_results[0]["iconType"], "iconpark/Others/magic.svg")
def test_search_icons_supports_template_icon_queries(self) -> None:
cases = [
("arrow", "iconpark/Arrows/arrow-right.svg"),
("right", "iconpark/Arrows/right.svg"),
("PPT", "iconpark/Music/ppt.svg"),
("table", "iconpark/Office/table.svg"),
("会议", "iconpark/Office/schedule.svg"),
("飞书", "iconpark/Brand/bydesign.svg"),
]
for query, icon_type in cases:
with self.subTest(query=query):
results = iconpark_tool.search_icons(self.index_data, {"query": query, "limit": 10})
self.assertTrue(
any(entry["iconType"] == icon_type for entry in results),
f"{icon_type} not found in {results}",
)
def test_search_icons_defaults_to_wider_candidate_set(self) -> None:
results = iconpark_tool.search_icons(self.index_data, {"query": "data"})
self.assertEqual(len(results), 8)
def test_search_icons_boosts_common_slide_terms(self) -> None:
results = iconpark_tool.search_icons(self.index_data, {"query": "会议", "limit": 3})
self.assertTrue(
any(entry["iconType"] == "iconpark/Office/schedule.svg" for entry in results),
f"iconpark/Office/schedule.svg not found in {results}",
)
def test_search_icons_keeps_high_value_top_results(self) -> None:
cases = [
("安全", "iconpark/Safe/protect.svg"),
("邮件", "iconpark/Office/mail-open.svg"),
("会议", "iconpark/Office/schedule.svg"),
("增长趋势", "iconpark/Charts/chart-line.svg"),
("飞书", "iconpark/Brand/bydesign.svg"),
]
for query, icon_type in cases:
with self.subTest(query=query):
results = iconpark_tool.search_icons(self.index_data, {"query": query, "limit": 3})
self.assertTrue(results)
self.assertEqual(results[0]["iconType"], icon_type)
def test_search_icons_requires_query(self) -> None:
with self.assertRaises(iconpark_tool.IconParkToolError):
iconpark_tool.search_icons(self.index_data, {"limit": 5})
def test_search_icons_rejects_invalid_limit(self) -> None:
with self.assertRaises(iconpark_tool.IconParkToolError):
iconpark_tool.search_icons(self.index_data, {"query": "data", "limit": "abc"})
def test_resolve_icon_accepts_name_and_icon_type(self) -> None:
by_name = iconpark_tool.resolve_icon(self.index_data, "chart-line")
by_type = iconpark_tool.resolve_icon(self.index_data, "iconpark/Charts/chart-line.svg")
self.assertEqual(by_name["iconType"], "iconpark/Charts/chart-line.svg")
self.assertEqual(by_name, by_type)
def test_resolve_icon_accepts_template_icon_type(self) -> None:
result = iconpark_tool.resolve_icon(self.index_data, "iconpark/Arrows/arrow-right.svg")
self.assertEqual(result["iconType"], "iconpark/Arrows/arrow-right.svg")
def test_resolve_icon_rejects_unknown_name(self) -> None:
with self.assertRaises(iconpark_tool.IconParkToolError):
iconpark_tool.resolve_icon(self.index_data, "not-a-real-icon")
def test_list_categories_counts_index(self) -> None:
categories = iconpark_tool.list_categories(self.index_data)
self.assertTrue(any(entry["category"] == "Charts" and entry["count"] > 0 for entry in categories))
class IconParkToolCLITest(unittest.TestCase):
def run_tool(self, *args: str) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[sys.executable, str(SCRIPT_PATH), *args],
capture_output=True,
check=False,
text=True,
)
def test_cli_search_writes_json_to_stdout(self) -> None:
result = self.run_tool("search", "--query", "增长趋势", "--limit", "5")
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(result.stderr, "")
output = json.loads(result.stdout)
self.assertTrue(output)
self.assertTrue(
any(entry["iconType"] == "iconpark/Charts/positive-dynamics.svg" for entry in output)
)
def test_cli_resolve_writes_json_to_stdout(self) -> None:
result = self.run_tool("resolve", "--name", "chart-line")
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(result.stderr, "")
output = json.loads(result.stdout)
self.assertEqual(output["iconType"], "iconpark/Charts/chart-line.svg")
def test_cli_list_categories_writes_json_to_stdout(self) -> None:
result = self.run_tool("list-categories")
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(result.stderr, "")
output = json.loads(result.stdout)
self.assertTrue(any(entry["category"] == "Charts" and entry["count"] > 0 for entry in output))
def test_cli_help_writes_usage_to_stderr(self) -> None:
result = self.run_tool("--help")
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(result.stdout, "")
self.assertIn("Usage:", result.stderr)
self.assertIn("python3 iconpark_tool.py search", result.stderr)
def test_cli_invalid_argument_writes_error_to_stderr(self) -> None:
result = self.run_tool("search", "增长趋势")
self.assertEqual(result.returncode, 1)
self.assertEqual(result.stdout, "")
self.assertIn("iconpark-tool error: unexpected argument: 增长趋势", result.stderr)
def test_cli_unknown_command_writes_usage_and_error_to_stderr(self) -> None:
result = self.run_tool("unknown")
self.assertEqual(result.returncode, 1)
self.assertEqual(result.stdout, "")
self.assertIn("Usage:", result.stderr)
self.assertIn("iconpark-tool error: unknown command: unknown", result.stderr)
if __name__ == "__main__":
unittest.main()