Files
ewoooc/tests/test_pixelrag_crawler_integration_service.py
ogt e6d7c2fb3a
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
feat(crawler): add python pixelrag capture worker
2026-07-09 17:28:56 +08:00

342 lines
12 KiB
Python

import json
import shutil
import subprocess
import sys
import pytest
def test_pixelrag_assessment_starts_visual_evidence_fallback_not_price_write():
from services.pixelrag_crawler_integration_service import (
RESULT_PHASE1_READY,
build_pixelrag_crawler_integration_assessment,
)
payload = build_pixelrag_crawler_integration_assessment()
assert payload["success"] is True
assert payload["result"] == RESULT_PHASE1_READY
assert payload["feasible"] is True
assert payload["can_start_now"] is True
assert payload["recommended_mode"] == "pixelrag_inspired_visual_evidence_fallback"
assert payload["full_pixelrag_ready"] is False
assert payload["controlled_apply"] == {
"network_call": False,
"db_write": False,
"secret_read": False,
"github_dependency": False,
"production_price_write": False,
"rollback": "Disable the visual fallback selector or ignore generated artifacts.",
}
phase_ids = {phase["id"]: phase for phase in payload["phases"]}
assert phase_ids["PXR-1"]["status"] == "ready_to_start"
assert phase_ids["PXR-2"]["writes"] == ["artifact_file_only"]
assert phase_ids["PXR-3"]["status"] == "deferred_model_not_verified"
assert phase_ids["PXR-5"]["writes"] == ["review_diagnostics_only"]
assert any(
guard["code"] == "no_price_write_from_pixels"
for guard in payload["safety_guards"]
)
def test_pixelrag_assessment_blocks_when_visual_capture_is_not_ready():
from services.pixelrag_crawler_integration_service import (
RESULT_BLOCKED_NO_CAPTURE,
build_pixelrag_crawler_integration_assessment,
)
payload = build_pixelrag_crawler_integration_assessment(
capabilities={"playwright_artifact_pipeline": False}
)
assert payload["result"] == RESULT_BLOCKED_NO_CAPTURE
assert payload["feasible"] is False
assert payload["can_start_now"] is False
assert payload["recommended_mode"] == "prepare_visual_capture_prerequisites"
assert payload["phases"][0]["status"] == "blocked_prerequisite"
def test_pixelrag_assessment_keeps_github_and_hosted_embedding_out_of_runtime():
from services.pixelrag_crawler_integration_service import (
build_pixelrag_crawler_integration_assessment,
)
payload = build_pixelrag_crawler_integration_assessment(
capabilities={
"ollama_multimodal_embedding_ready": True,
"pgvector_visual_index_ready": True,
}
)
findings = {finding["code"]: finding for finding in payload["source_findings"]}
guards = {guard["code"]: guard for guard in payload["safety_guards"]}
assert payload["full_pixelrag_ready"] is True
assert "Ollama" in findings["qwen3_vl_embedding_optional"]["adoption"]
assert guards["no_external_embedding_api"]["status"] == "enforced"
assert guards["no_github_runtime_dependency"]["status"] == "enforced"
assert payload["controlled_apply"]["github_dependency"] is False
def test_pixelrag_report_cli_outputs_machine_readable_json():
completed = subprocess.run(
[
sys.executable,
"scripts/ops/report_pixelrag_crawler_integration.py",
"--platform",
"momo",
],
capture_output=True,
check=False,
text=True,
)
assert completed.returncode == 0
payload = json.loads(completed.stdout)
assert payload["success"] is True
assert payload["target_platforms"] == ["momo"]
assert payload["next_actions"][0]["action"] == "add_visual_evidence_manifest_lane"
def test_visual_fallback_selector_routes_parser_empty_and_low_confidence_cases():
from services.pixelrag_crawler_integration_service import (
should_emit_visual_evidence_fallback,
)
parser_empty = should_emit_visual_evidence_fallback(
parser_success=False,
parsed_item_count=0,
missing_fields=["price"],
failure_reason="HTML selector did not find rendered price block",
)
clean_parser = should_emit_visual_evidence_fallback(
parser_success=True,
parsed_item_count=8,
confidence_band="high",
)
assert parser_empty["should_emit"] is True
assert "parser_failed" in parser_empty["triggers"]
assert "critical_field_missing" in parser_empty["triggers"]
assert clean_parser["should_emit"] is False
assert clean_parser["fallback_reason"] == "structured_evidence_sufficient"
def test_visual_evidence_manifest_builds_tile_plan_without_runtime_writes():
from services.pixelrag_crawler_integration_service import (
RESULT_MANIFEST_READY,
build_pixelrag_visual_evidence_manifest,
)
payload = build_pixelrag_visual_evidence_manifest(
url="https://m.momoshop.com.tw/search.momo?searchKeyword=test",
platform="momo",
crawler="MomoCrawler.search_products",
trigger_reason="parser_empty",
viewport={"name": "desktop-1440", "width": 1440, "height": 950},
page_size={"width": 1440, "height": 1900},
tile_size={"width": 512, "height": 512},
)
assert payload["success"] is True
assert payload["result"] == RESULT_MANIFEST_READY
assert payload["status"] == "capture_requested"
assert payload["tile_plan"] == {
"tiles_x": 3,
"tiles_y": 4,
"tile_count": 12,
"overlap_px": 0,
}
assert payload["artifact"]["writes"] == ["artifact_file_only"]
assert payload["controlled_apply"]["db_write"] is False
assert payload["controlled_apply"]["network_call"] is False
assert payload["controlled_apply"]["production_price_write"] is False
def test_visual_evidence_manifest_rejects_urls_with_credentials():
from services.pixelrag_crawler_integration_service import (
RESULT_MANIFEST_REJECTED,
build_pixelrag_visual_evidence_manifest,
)
payload = build_pixelrag_visual_evidence_manifest(
url="https://user:pass@example.test/product",
platform="momo",
crawler="MomoCrawler",
trigger_reason="parser_empty",
)
assert payload["success"] is False
assert payload["result"] == RESULT_MANIFEST_REJECTED
assert "URL credentials are not allowed" in payload["errors"][0]
def test_pixelrag_report_cli_outputs_visual_manifest():
completed = subprocess.run(
[
sys.executable,
"scripts/ops/report_pixelrag_crawler_integration.py",
"--platform",
"pchome",
"--manifest-url",
"https://24h.pchome.com.tw/prod/TEST-000000001",
"--crawler",
"PChomeCrawler.search_products",
"--trigger-reason",
"parser_empty",
"--page-size",
"page=1024x1024",
"--tile-size",
"tile=512x512",
],
capture_output=True,
check=False,
text=True,
)
assert completed.returncode == 0
payload = json.loads(completed.stdout)
assert payload["result"] == "PIXELRAG_VISUAL_EVIDENCE_MANIFEST_READY"
assert payload["capture_target"]["platform"] == "pchome"
assert payload["tile_plan"]["tile_count"] == 4
def test_pixelrag_ai_automation_route_returns_assessment_and_manifest():
from flask import Flask
from routes import system_public_routes as routes
app = Flask(__name__)
with app.test_request_context(
"/api/ai-automation/pixelrag-crawler-integration?platform=momo"
):
assessment_response = routes.ai_automation_pixelrag_crawler_integration_api.__wrapped__()
assessment = assessment_response.get_json()
with app.test_request_context(
"/api/ai-automation/pixelrag-crawler-integration"
"?platform=pchome"
"&manifest_url=https://24h.pchome.com.tw/prod/TEST-000000001"
"&crawler=PChomeCrawler.search_products"
"&trigger_reason=parser_empty"
):
manifest_response = routes.ai_automation_pixelrag_crawler_integration_api.__wrapped__()
manifest = manifest_response.get_json()
assert assessment["policy"] == "read_only_pixelrag_crawler_integration_assessment_v1"
assert assessment["target_platforms"] == ["momo"]
assert assessment["controlled_apply"]["db_write"] is False
assert manifest["policy"] == "read_only_pixelrag_visual_evidence_manifest_v1"
assert manifest["result"] == "PIXELRAG_VISUAL_EVIDENCE_MANIFEST_READY"
assert manifest["capture_target"]["platform"] == "pchome"
def test_pixelrag_capture_worker_dry_run_plans_artifact_outputs(tmp_path):
if not shutil.which("node"):
pytest.skip("node is required for the dry-run capture worker test")
from services.pixelrag_crawler_integration_service import (
build_pixelrag_visual_evidence_manifest,
)
manifest = build_pixelrag_visual_evidence_manifest(
url="https://m.momoshop.com.tw/search.momo?searchKeyword=test",
platform="momo",
crawler="MomoCrawler.search_products",
trigger_reason="parser_empty",
page_size={"width": 1024, "height": 1024},
tile_size={"width": 512, "height": 512},
)
completed = subprocess.run(
[
"node",
"scripts/ops/capture_pixelrag_visual_evidence.js",
"--manifest-json",
json.dumps(manifest),
"--output-dir",
str(tmp_path),
"--dry-run",
],
capture_output=True,
check=False,
text=True,
)
assert completed.returncode == 0
payload = json.loads(completed.stdout)
assert payload["policy"] == "read_only_pixelrag_visual_capture_artifact_v1"
assert payload["status"] == "dry_run_ready"
assert payload["controlled_apply"]["artifact_write"] is False
assert payload["tile_plan"]["planned_tile_count"] == 4
assert payload["planned_output"]["screenshot"].endswith("fullpage.png")
def test_pixelrag_capture_worker_rejects_wrong_platform_host(tmp_path):
if not shutil.which("node"):
pytest.skip("node is required for the dry-run capture worker test")
from services.pixelrag_crawler_integration_service import (
build_pixelrag_visual_evidence_manifest,
)
manifest = build_pixelrag_visual_evidence_manifest(
url="https://example.test/product",
platform="momo",
crawler="MomoCrawler.search_products",
trigger_reason="parser_empty",
)
completed = subprocess.run(
[
"node",
"scripts/ops/capture_pixelrag_visual_evidence.js",
"--manifest-json",
json.dumps(manifest),
"--output-dir",
str(tmp_path),
"--dry-run",
],
capture_output=True,
check=False,
text=True,
)
assert completed.returncode == 1
payload = json.loads(completed.stdout)
assert payload["status"] == "rejected"
assert any("not allowed for platform momo" in error for error in payload["errors"])
def test_pixelrag_python_capture_worker_dry_run_plans_artifact_outputs(tmp_path):
from services.pixelrag_crawler_integration_service import (
build_pixelrag_visual_evidence_manifest,
)
manifest = build_pixelrag_visual_evidence_manifest(
url="https://24h.pchome.com.tw/",
platform="pchome",
crawler="PChomeCrawler.visual_fallback",
trigger_reason="parser_empty",
page_size={"width": 1024, "height": 1024},
tile_size={"width": 512, "height": 512},
)
completed = subprocess.run(
[
sys.executable,
"scripts/ops/capture_pixelrag_visual_evidence.py",
"--manifest-json",
json.dumps(manifest),
"--output-dir",
str(tmp_path),
"--dry-run",
],
capture_output=True,
check=False,
text=True,
)
assert completed.returncode == 0
payload = json.loads(completed.stdout)
assert payload["policy"] == "read_only_pixelrag_visual_capture_artifact_v1"
assert payload["status"] == "dry_run_ready"
assert payload["controlled_apply"]["artifact_write"] is False
assert payload["tile_plan"]["planned_tile_count"] == 4
assert payload["capture_target"]["platform"] == "pchome"