200 lines
7.6 KiB
Python
200 lines
7.6 KiB
Python
import html
|
|
import re
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
|
|
from app import product_status
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
|
def _sample_alert(filename):
|
|
source = (REPO_ROOT / "docs" / "html" / filename).read_text(encoding="utf-8")
|
|
title_match = re.search(r'class="eds-alert-title">\s*([^<]+)', source)
|
|
desc_match = re.search(r'class="eds-alert-desc"[^>]*>(.*?)</p>', source, re.DOTALL)
|
|
if title_match is None or desc_match is None:
|
|
raise AssertionError("商品状态样本缺少标题或说明")
|
|
description = re.sub(r"<[^>]+>", "", desc_match.group(1))
|
|
return {
|
|
"title": html.unescape(title_match.group(1)),
|
|
"description": html.unescape(description),
|
|
}
|
|
|
|
|
|
class ProductStatusTests(unittest.TestCase):
|
|
def test_real_warning_samples_are_classified(self):
|
|
reviewing = product_status.classify_alerts([_sample_alert("审核中商品提示.html")])
|
|
unlisted = product_status.classify_alerts([_sample_alert("未上架商品提示.html")])
|
|
|
|
self.assertEqual(product_status.STATUS_REVIEWING, reviewing["product_status"])
|
|
self.assertEqual(product_status.STATUS_UNLISTED, unlisted["product_status"])
|
|
|
|
def test_empty_unknown_and_multiple_alerts_follow_contract(self):
|
|
self.assertEqual(
|
|
product_status.STATUS_NORMAL,
|
|
product_status.classify_alerts([])["product_status"],
|
|
)
|
|
self.assertEqual(
|
|
product_status.STATUS_UNKNOWN,
|
|
product_status.classify_alerts([{"title": "其他警告", "description": "说明"}])["product_status"],
|
|
)
|
|
result = product_status.classify_alerts(
|
|
[
|
|
{"title": "其他警告", "description": "忽略"},
|
|
{"title": "您的商品未上架", "description": "已下架"},
|
|
{"title": "审核中", "description": "后续提示"},
|
|
]
|
|
)
|
|
self.assertEqual(product_status.STATUS_UNLISTED, result["product_status"])
|
|
self.assertIn("您的商品未上架", result["product_status_note"])
|
|
|
|
def test_invalid_values_are_unknown_and_partitioning_is_consistent(self):
|
|
tasks = [
|
|
SimpleNamespace(product_status="normal"),
|
|
SimpleNamespace(product_status="reviewing"),
|
|
SimpleNamespace(product_status=None),
|
|
SimpleNamespace(product_status="invalid"),
|
|
]
|
|
|
|
grouped = product_status.partition_tasks(tasks)
|
|
|
|
self.assertEqual("状态未知", product_status.status_label(None))
|
|
self.assertTrue(product_status.is_normal("normal"))
|
|
self.assertFalse(product_status.is_normal(None))
|
|
self.assertTrue(product_status.is_known_abnormal("unlisted"))
|
|
self.assertEqual(1, len(grouped["normal"]))
|
|
self.assertEqual(2, len(grouped["unknown"]))
|
|
|
|
def test_note_is_normalized_and_limited(self):
|
|
result = product_status.classify_alerts(
|
|
[{"title": " 审核中\n", "description": " " + "x" * 2200}]
|
|
)
|
|
|
|
self.assertEqual(product_status.STATUS_REVIEWING, result["product_status"])
|
|
self.assertLessEqual(len(result["product_status_note"]), 2000)
|
|
self.assertTrue(result["product_status_note"].startswith("标题:审核中;说明:"))
|
|
|
|
def test_generation_plan_filters_unknown_by_default_and_freezes_snapshot(self):
|
|
normal = SimpleNamespace(
|
|
id=1,
|
|
updated_at="2026-07-18T10:00:00",
|
|
product_status="normal",
|
|
new_title=None,
|
|
new_cover_path=None,
|
|
)
|
|
unlisted = {
|
|
"id": 2,
|
|
"updated_at": "2026-07-18T10:00:01",
|
|
"product_status": "unlisted",
|
|
"new_title": "已有标题",
|
|
"new_cover_path": None,
|
|
}
|
|
unknown = SimpleNamespace(
|
|
id=3,
|
|
updated_at="2026-07-18T10:00:02",
|
|
product_status=None,
|
|
new_title=None,
|
|
new_cover_path=None,
|
|
)
|
|
|
|
normal_only = product_status.build_generation_plan(
|
|
[normal, unlisted, unknown, normal],
|
|
"title_cover",
|
|
)
|
|
all_statuses = product_status.build_generation_plan(
|
|
[normal, unlisted, unknown],
|
|
"title_cover",
|
|
product_status.SCOPE_ALL,
|
|
)
|
|
changed = product_status.build_generation_plan(
|
|
[
|
|
normal,
|
|
{
|
|
**unlisted,
|
|
"updated_at": "2026-07-18T10:01:00",
|
|
},
|
|
unknown,
|
|
],
|
|
"title_cover",
|
|
)
|
|
|
|
self.assertEqual([1, 2, 3], [task.id if hasattr(task, "id") else task["id"] for task in normal_only["base_candidates"]])
|
|
self.assertEqual([1], [task.id for task in normal_only["execution_tasks"]])
|
|
self.assertEqual(2, normal_only["scope_excluded"])
|
|
self.assertEqual(
|
|
{"normal": 1, "unlisted": 1, "reviewing": 0, "unknown": 1},
|
|
normal_only["status_counts"],
|
|
)
|
|
self.assertEqual([1, 2, 3], [task.id if hasattr(task, "id") else task["id"] for task in all_statuses["execution_tasks"]])
|
|
self.assertNotEqual(normal_only["fingerprint"], changed["fingerprint"])
|
|
|
|
def test_apply_plan_excludes_abnormal_before_content_and_deduplicates_skips(self):
|
|
tasks = [
|
|
SimpleNamespace(
|
|
id=1,
|
|
updated_at="2026-07-18T10:00:00",
|
|
product_status="normal",
|
|
new_title="新标题",
|
|
new_cover_path="new.jpg",
|
|
),
|
|
SimpleNamespace(
|
|
id=2,
|
|
updated_at="2026-07-18T10:00:01",
|
|
product_status="unlisted",
|
|
new_title=None,
|
|
new_cover_path=None,
|
|
),
|
|
SimpleNamespace(
|
|
id=3,
|
|
updated_at="2026-07-18T10:00:02",
|
|
product_status="reviewing",
|
|
new_title=None,
|
|
new_cover_path="new.jpg",
|
|
),
|
|
SimpleNamespace(
|
|
id=4,
|
|
updated_at="2026-07-18T10:00:03",
|
|
product_status=None,
|
|
new_title="新标题",
|
|
new_cover_path=None,
|
|
),
|
|
SimpleNamespace(
|
|
id=5,
|
|
updated_at="2026-07-18T10:00:04",
|
|
product_status="normal",
|
|
new_title=None,
|
|
new_cover_path=None,
|
|
),
|
|
]
|
|
|
|
plan = product_status.build_apply_plan(tasks + [tasks[0]], "title_cover")
|
|
changed = product_status.build_apply_plan(
|
|
tasks[:4]
|
|
+ [
|
|
SimpleNamespace(
|
|
id=5,
|
|
updated_at="2026-07-18T10:01:00",
|
|
product_status="normal",
|
|
new_title=None,
|
|
new_cover_path=None,
|
|
)
|
|
],
|
|
"title_cover",
|
|
)
|
|
|
|
self.assertEqual([1], [task.id for task in plan["executable"]])
|
|
self.assertEqual([2], [task.id for task in plan["unlisted"]])
|
|
self.assertEqual([3], [task.id for task in plan["reviewing"]])
|
|
self.assertEqual([4], [task.id for task in plan["unknown"]])
|
|
self.assertEqual([5], [task.id for task in plan["missing_title"]])
|
|
self.assertEqual([5], [task.id for task in plan["missing_cover"]])
|
|
self.assertEqual(3, plan["status_scope_excluded"])
|
|
self.assertEqual(1, plan["content_scope_excluded"])
|
|
self.assertEqual(4, plan["scope_excluded"])
|
|
self.assertNotEqual(plan["fingerprint"], changed["fingerprint"])
|