V10.534 收緊 PChome rescore 覆核語意
All checks were successful
CD Pipeline / deploy (push) Successful in 1m8s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m8s
This commit is contained in:
@@ -154,9 +154,9 @@ def test_dashboard_match_status_shows_rescore_accepted_review_state():
|
||||
"error_message": "matcher_rescore=accepted_current; reasons=strong_exact_spec_match",
|
||||
})
|
||||
|
||||
assert status["label"] == "重算可採用待審"
|
||||
assert status["label"] == "重算待人工覆核"
|
||||
assert status["tone"] == "watch"
|
||||
assert "人工採用後才寫入正式 PChome 價差" in status["summary"]
|
||||
assert "人工確認身份後才可寫入正式 PChome 價差" in status["summary"]
|
||||
|
||||
|
||||
def test_dashboard_match_status_uses_specific_matcher_reason_labels():
|
||||
|
||||
@@ -132,10 +132,12 @@ def test_competitor_review_queue_is_canonical_unit_price_handoff():
|
||||
assert "coverage.stale_matches" in growth_template
|
||||
assert "coverage.unit_comparable_count" in growth_template
|
||||
assert "coverage.rescore_accepted_count" in growth_template
|
||||
assert "重算待人工覆核" in growth_template
|
||||
assert "coverage.manual_accept_count" in growth_template
|
||||
assert "coverage.manual_reject_count" in growth_template
|
||||
assert "coverage.manual_unit_price_count" in growth_template
|
||||
assert "comp_coverage.rescore_accepted_count" in daily_template
|
||||
assert "重算待人工覆核" in daily_template
|
||||
assert "comp_coverage.stale_matches" in daily_template
|
||||
|
||||
|
||||
@@ -295,7 +297,7 @@ def test_review_decision_brief_is_shared_by_openclaw_and_ppt():
|
||||
assert brief["auto_execute_blocked_count"] == 1
|
||||
assert brief["severity_counts"]
|
||||
assert brief["data_quality_counts"] == {"complete": 1}
|
||||
assert "人工覆核後採用同款" in brief["text"]
|
||||
assert "人工確認身份後採用同款" in brief["text"]
|
||||
assert "HITL" in brief["text"]
|
||||
assert "DDAO4C-A79050612" in brief["text"]
|
||||
|
||||
|
||||
@@ -131,6 +131,42 @@ def test_match_attempt_rescore_audit_keeps_variant_selection_review_out_of_gate_
|
||||
assert "variant_selection_review" in decision.reasons
|
||||
|
||||
|
||||
def test_match_attempt_rescore_audit_keeps_no_match_out_of_gate_pass(monkeypatch):
|
||||
from types import SimpleNamespace
|
||||
|
||||
import services.competitor_match_attempt_rescore_audit as audit
|
||||
|
||||
monkeypatch.setattr(
|
||||
audit,
|
||||
"score_marketplace_match",
|
||||
lambda *_args, **_kwargs: SimpleNamespace(
|
||||
score=0.96,
|
||||
hard_veto=False,
|
||||
comparison_mode="exact_identity",
|
||||
match_type="no_match",
|
||||
price_basis="none",
|
||||
alert_tier="suppress",
|
||||
reasons=("strong_exact_spec_match",),
|
||||
),
|
||||
)
|
||||
|
||||
decision = audit.classify_match_attempt_row({
|
||||
"sku": "NO-MATCH",
|
||||
"attempt_status": "true_low_confidence",
|
||||
"momo_product_name": "MOMO candidate",
|
||||
"best_competitor_product_name": "PChome candidate",
|
||||
"momo_price": 100,
|
||||
"best_competitor_price": 90,
|
||||
"best_match_score": 0.96,
|
||||
})
|
||||
|
||||
assert decision.suggested_status == "low_score_current"
|
||||
assert decision.gate_pass is False
|
||||
assert decision.match_type == "no_match"
|
||||
assert decision.price_basis == "none"
|
||||
assert decision.alert_tier == "suppress"
|
||||
|
||||
|
||||
def test_match_attempt_rescore_audit_skips_missing_identity_text():
|
||||
from services.competitor_match_attempt_rescore_audit import classify_match_attempt_row
|
||||
|
||||
@@ -365,6 +401,69 @@ def test_match_attempt_rescore_retracts_variant_review_from_accepted_queue():
|
||||
assert after_rows == []
|
||||
|
||||
|
||||
def test_match_attempt_rescore_retracts_unsafe_accepted_queue(monkeypatch):
|
||||
from types import SimpleNamespace
|
||||
|
||||
import services.competitor_match_attempt_rescore_audit as audit
|
||||
|
||||
engine = create_engine("sqlite:///:memory:")
|
||||
with engine.begin() as conn:
|
||||
_create_match_attempts_table(conn)
|
||||
conn.execute(text("""
|
||||
INSERT INTO competitor_match_attempts
|
||||
(sku, source, attempt_status, momo_product_id, momo_product_name,
|
||||
momo_price, candidate_count, best_competitor_product_id,
|
||||
best_competitor_product_name, best_competitor_price,
|
||||
best_match_score, diagnostic_codes, error_message, attempted_at)
|
||||
VALUES
|
||||
(
|
||||
'NO-MATCH', 'pchome', 'rescore_accepted_current', 1,
|
||||
'MOMO candidate',
|
||||
100, 1, 'PCHOME-NO-MATCH',
|
||||
'PChome candidate',
|
||||
90, 0.96,
|
||||
'["strong_exact_spec_match"]',
|
||||
'matcher_rescore=accepted_current', '2026-05-24 14:24:17'
|
||||
)
|
||||
"""))
|
||||
|
||||
monkeypatch.setattr(
|
||||
audit,
|
||||
"score_marketplace_match",
|
||||
lambda *_args, **_kwargs: SimpleNamespace(
|
||||
score=0.96,
|
||||
hard_veto=False,
|
||||
comparison_mode="exact_identity",
|
||||
match_type="no_match",
|
||||
price_basis="none",
|
||||
alert_tier="suppress",
|
||||
reasons=("strong_exact_spec_match",),
|
||||
),
|
||||
)
|
||||
|
||||
rows = audit.fetch_unsafe_rescore_accept_review_rows(conn, limit=10)
|
||||
stats = audit.retract_unsafe_rescore_accept_reviews(conn, rows)
|
||||
latest_status = conn.execute(text("""
|
||||
SELECT attempt_status
|
||||
FROM competitor_match_attempts
|
||||
WHERE sku = 'NO-MATCH'
|
||||
ORDER BY attempted_at DESC, id DESC
|
||||
LIMIT 1
|
||||
""")).scalar()
|
||||
error_message = conn.execute(text("""
|
||||
SELECT error_message
|
||||
FROM competitor_match_attempts
|
||||
WHERE sku = 'NO-MATCH'
|
||||
ORDER BY attempted_at DESC, id DESC
|
||||
LIMIT 1
|
||||
""")).scalar()
|
||||
|
||||
assert [row["sku"] for row in rows] == ["NO-MATCH"]
|
||||
assert stats["retracted"] == 1
|
||||
assert latest_status == "true_low_confidence"
|
||||
assert "rescore_retracted_unsafe_accepted_current" in error_message
|
||||
|
||||
|
||||
def test_match_attempt_rescore_materialize_does_not_enqueue_veto():
|
||||
from services.competitor_match_attempt_rescore_audit import materialize_rescore_accept_reviews
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ def test_dashboard_v2_is_production_default_and_uses_real_dashboard_data():
|
||||
assert "overview.pending_priority" in dashboard
|
||||
assert "overview.review_queue" in dashboard
|
||||
assert "需單位價 {{ overview.unit_comparable_count" in dashboard
|
||||
assert "重算可採用 {{ overview.rescore_accepted_count" in dashboard
|
||||
assert "重算待覆核 {{ overview.rescore_accepted_count" in dashboard
|
||||
assert "{% if review_total_is_estimated %}約 {% endif %}" in dashboard
|
||||
assert "filter='ai_picks'" in dashboard
|
||||
assert "filter='pchome_review'" in dashboard
|
||||
|
||||
@@ -158,7 +158,7 @@ class TestKPIComputation:
|
||||
assert "fetch_competitor_coverage" in source
|
||||
assert "summarize_review_decision_envelopes" in source
|
||||
assert "review_decision_text" in source
|
||||
assert "重算可採用待審" in source
|
||||
assert "重算待人工覆核" in source
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user