V10.588 提升比價救援穩定性與決策覆蓋
All checks were successful
CD Pipeline / deploy (push) Successful in 1m9s

This commit is contained in:
OoO
2026-06-04 21:21:40 +08:00
parent 5347c95b97
commit 58302c9fa7
5 changed files with 81 additions and 15 deletions

View File

@@ -83,8 +83,9 @@ def test_competitor_coverage_counts_only_active_product_intersection():
"def _fetch_manual_review_summary", 1
)[0]
assert "coverage:v13" in source
assert "coverage:v14" in source
assert "CATALOG_COMPARABLE_SCORE_FLOOR" in source
assert "CATALOG_IDENTITY_REVIEW_SCORE_FLOOR" in source
assert "rescore_accepted_count" in coverage_source
assert "(SELECT COUNT(*) FROM valid_competitor) AS valid_matches" not in coverage_source
assert "identity_competitor AS" in coverage_source
@@ -113,6 +114,27 @@ def test_competitor_coverage_counts_only_active_product_intersection():
assert "CATALOG_VARIANT_REVIEW_REASONS" in source
assert "CATALOG_UNIT_REVIEW_REASONS" in source
assert "CATALOG_COMPARABLE_IDENTITY_REASONS" in source
assert "\"catalog_identity_review_score_floor\": CATALOG_IDENTITY_REVIEW_SCORE_FLOOR" in coverage_source
def test_catalog_comparable_sql_includes_high_confidence_identity_review_lane():
from services.competitor_intel_repository import (
CATALOG_COMPARABLE_SCORE_FLOOR,
CATALOG_IDENTITY_REVIEW_SCORE_FLOOR,
_catalog_comparable_sql,
)
source = (ROOT / "services" / "competitor_intel_repository.py").read_text(encoding="utf-8")
coverage_source = source.split("def _fetch_competitor_coverage_uncached", 1)[1].split(
"def _fetch_manual_review_summary", 1
)[0]
sql = _catalog_comparable_sql("la")
assert f">= {CATALOG_COMPARABLE_SCORE_FLOOR}" in sql
assert f">= {CATALOG_IDENTITY_REVIEW_SCORE_FLOOR}" in sql
assert "CATALOG_IDENTITY_REVIEW_SCORE_FLOOR" not in sql
assert "OR COALESCE(la.best_match_score, 0)" in sql
assert "CATALOG_COMPARABLE_SIGNAL_REASONS" not in sql
assert "CATALOG_COMPARABLE_BLOCK_REASONS" in source
assert "\"identity_coverage_matches\": valid" in coverage_source
assert "\"manual_closed_count\": manual_closed_count" in coverage_source

View File

@@ -310,7 +310,8 @@ def test_competitor_feeder_persists_all_match_attempt_outcomes():
assert 'attempt_status="refresh_unit_comparable"' in source
assert "mode={getattr(diagnostics, 'comparison_mode'" in source
assert 'PCHOME_FEEDER_TIMEOUT", "12"' in source
assert "PChomeCrawler(timeout=REQUEST_TIMEOUT" in source
assert "crawler_timeout = BACKFILL_REQUEST_TIMEOUT if bounded_recovery else REQUEST_TIMEOUT" in source
assert "PChomeCrawler(timeout=crawler_timeout" in source
assert "CREATE TABLE IF NOT EXISTS competitor_match_attempts" in migration
assert "attempt_status" in migration
@@ -2147,9 +2148,16 @@ def test_competitor_feeder_refresh_recovers_with_fresh_search_when_known_id_is_l
def test_competitor_feeder_refresh_recovers_when_known_id_missing(monkeypatch):
from services.competitor_price_feeder import CompetitorPriceFeeder
from services.competitor_price_feeder import (
BACKFILL_MAX_SEARCH_TERMS,
BACKFILL_REQUEST_TIMEOUT,
BACKFILL_SEARCH_MAX_PAGES,
CompetitorPriceFeeder,
SEARCH_LIMIT,
)
from services.pchome_crawler import PChomeProduct
crawler_calls = {"init": [], "search": []}
recovered = PChomeProduct(
product_id="DDAB01-RECOVERED",
name="eve舒摩兒 賦活美學浴潔露-全肌防護 237ml",
@@ -2167,14 +2175,15 @@ def test_competitor_feeder_refresh_recovers_when_known_id_missing(monkeypatch):
)
class FakeCrawler:
def __init__(self, *_args, **_kwargs):
pass
def __init__(self, *_args, **kwargs):
crawler_calls["init"].append(kwargs)
def fetch_product_details(self, product_ids, batch_size=20):
assert product_ids == ["DDAB01-MISSING"]
return True, "ok", []
def search_products(self, *_args, **_kwargs):
def search_products(self, *_args, **kwargs):
crawler_calls["search"].append(kwargs)
return True, "ok", [recovered]
def fake_score(_momo_name, competitor_name, **_kwargs):
@@ -2228,9 +2237,13 @@ def test_competitor_feeder_refresh_recovers_when_known_id_missing(monkeypatch):
"product_id": 4864,
"momo_price": 441,
"competitor_product_id": "DDAB01-MISSING",
}])
}], bounded_recovery=True)
assert result.matched == 1
assert crawler_calls["init"][0]["timeout"] == BACKFILL_REQUEST_TIMEOUT
assert len(crawler_calls["search"]) <= BACKFILL_MAX_SEARCH_TERMS
assert crawler_calls["search"][0]["max_pages"] == BACKFILL_SEARCH_MAX_PAGES
assert crawler_calls["search"][0]["limit"] == SEARCH_LIMIT * BACKFILL_SEARCH_MAX_PAGES
assert writes[0]["product_id"] == "DDAB01-RECOVERED"
assert "missing_known_product_id" in writes[0]["tags"]
assert "fresh_search_recovery" in writes[0]["tags"]
@@ -2301,6 +2314,7 @@ def test_competitor_feeder_expired_recovery_allows_fresh_search(monkeypatch):
assert result.matched == 1
assert captured["skus"][0]["sku"] == "STALE-1"
assert captured["allow_missing_recovery"] is True
assert captured["bounded_recovery"] is True
assert captured["label"] == "identity_v2 過期價格搜尋救援"