Files
ewoooc/tests/test_ppt_auto_generation_service.py
OoO d2d6bcd263
All checks were successful
CD Pipeline / deploy (push) Successful in 1m5s
重整 PPT 視覺 QA 產線首屏
2026-05-19 11:37:18 +08:00

211 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import datetime
import json
def test_build_defined_ppt_jobs_uses_latest_date():
from services.ppt_auto_generation_service import build_defined_ppt_jobs
jobs = build_defined_ppt_jobs(latest_date="2026-05-11")
by_type = {job.report_type: job for job in jobs}
assert list(by_type) == [
"daily", "weekly", "monthly", "quarterly", "half_yearly", "annual", "ttm",
"strategy", "competitor", "competitor_v4", "promo", "promo_compare",
"forecast_pre_event", "vendor", "category", "customer", "new_product",
"market_intel", "price_elasticity",
]
assert by_type["daily"].sub_arg == "2026/05/11"
assert by_type["monthly"].sub_arg == "2026/05"
assert by_type["quarterly"].sub_arg == "2026/Q2"
assert by_type["half_yearly"].sub_arg == "2026/H1"
assert by_type["annual"].sub_arg == "2026"
assert by_type["strategy"].sub_arg == "2026/05/01-2026/05/11"
assert by_type["market_intel"].sub_arg == "2026/05/11 起一週"
assert by_type["competitor"].sub_arg == "monthly"
assert by_type["promo"].sub_arg == "2026/05/05-2026/05/11"
assert by_type["promo"].expected_params["label"] == "2026/05/05~2026/05/11"
assert by_type["strategy"].expected_params == {
"report_type": "strategy",
"start": "2026/05/01",
"end": "2026/05/11",
"label": "2026/05 月策略(截至 05/11",
}
def test_auto_generation_respects_disabled_flag(monkeypatch):
monkeypatch.setenv("PPT_AUTO_GENERATION_ENABLED", "false")
from services.ppt_auto_generation_service import generate_defined_ppt_reports
result = generate_defined_ppt_reports(report_types=["daily"])
assert result["ok"] is False
assert result["status"] == "disabled"
def test_dry_run_does_not_generate(monkeypatch):
monkeypatch.setenv("PPT_AUTO_GENERATION_ENABLED", "true")
from services import ppt_auto_generation_service as svc
monkeypatch.setattr(svc, "_latest_sales_date", lambda: "2026-05-11")
result = svc.generate_defined_ppt_reports(
report_types=["daily", "monthly"],
dry_run=True,
)
assert result["ok"] is True
assert result["status"] == "planned"
assert [job["report_type"] for job in result["jobs"]] == ["daily", "monthly"]
def test_coverage_marks_ready_from_database(monkeypatch):
from services import ppt_auto_generation_service as svc
class _Rows:
def fetchall(self):
return [
(
"daily",
json.dumps({"report_type": "daily", "date": "2026/05/11"}),
"/tmp/ocbot_daily_ok.pptx",
datetime(2026, 5, 11, 20, 30),
),
(
"monthly",
json.dumps({"report_type": "monthly", "month": "2026/05"}),
"/tmp/ocbot_monthly_ok.pptx",
datetime(2026, 5, 11, 20, 30),
),
]
class _Session:
def execute(self, *_args, **_kwargs):
return _Rows()
def close(self):
return None
monkeypatch.setattr(svc, "get_session", lambda: _Session())
monkeypatch.setattr(svc, "_latest_sales_date", lambda: "2026-05-11")
monkeypatch.setenv("PPT_AUTO_GENERATION_ENABLED", "true")
result = svc.get_defined_report_coverage(
month_start=datetime(2026, 5, 1),
month_end=datetime(2026, 6, 1),
reports_dir="/tmp/does-not-exist-for-test",
report_types=["daily", "monthly", "weekly"],
)
by_key = {item["key"]: item for item in result["items"]}
assert by_key["daily"]["ready"] is True
assert by_key["daily"]["status"] == "ready"
assert by_key["daily"]["status_label"] == "已產出"
assert by_key["monthly"]["ready"] is True
assert by_key["weekly"]["ready"] is False
assert by_key["weekly"]["status"] == "missing"
assert by_key["weekly"]["status_label"] == "待排程補齊"
assert result["missing_count"] == 1
def test_due_schedule_kinds_include_periodic_boundaries():
from services.ppt_auto_generation_service import get_due_schedule_kinds
assert get_due_schedule_kinds(datetime(2026, 5, 18)) == ["daily", "weekly"]
assert get_due_schedule_kinds(datetime(2026, 7, 1)) == [
"daily",
"monthly",
"quarterly",
"half_yearly",
]
assert get_due_schedule_kinds(datetime(2026, 1, 1)) == [
"daily",
"monthly",
"quarterly",
"half_yearly",
"annual",
]
def test_schedule_cadence_status_exposes_all_periodic_contracts():
from services.ppt_auto_generation_service import get_schedule_cadence_status
cadences = get_schedule_cadence_status([
{"key": "daily", "ready": True},
{"key": "weekly", "ready": False},
{"key": "market_intel", "ready": True},
])
by_key = {cadence["key"]: cadence for cadence in cadences}
assert [cadence["schedule_text"] for cadence in cadences] == [
"每日 20:30",
"每週一 20:40",
"每月 1 日 20:50",
"每季首日 21:00",
"每半年首日 21:10",
"每年 1/1 21:20",
]
assert by_key["weekly"]["report_types"] == ["weekly", "market_intel"]
assert by_key["weekly"]["ready_count"] == 1
assert by_key["weekly"]["missing_report_types"] == ["weekly"]
assert by_key["weekly"]["missing_report_labels"] == ["週報"]
assert by_key["weekly"]["status_label"] == "已完成 1/2"
assert by_key["weekly"]["coverage_text"] == "1/2"
assert "TTM 滾動 12 月" in by_key["monthly"]["report_labels"]
def test_scheduled_generation_uses_profile_without_generating(monkeypatch):
from services import ppt_auto_generation_service as svc
calls = []
def fake_generate_defined_ppt_reports(**kwargs):
calls.append(kwargs)
return {"ok": True, "ready": 2, "errors": 0, "jobs": [{"report_type": "weekly"}]}
monkeypatch.setattr(svc, "generate_defined_ppt_reports", fake_generate_defined_ppt_reports)
result = svc.generate_scheduled_ppt_reports(schedule_kind="weekly", force=True)
assert result["ok"] is True
assert result["schedule_kinds"] == ["weekly"]
assert calls == [{
"report_types": ("weekly", "market_intel"),
"schedule_kind": "weekly",
"force": True,
}]
def test_ppt_preview_reports_missing_converter(monkeypatch, tmp_path):
from services import ppt_preview_service as svc
pptx = tmp_path / "demo.pptx"
pptx.write_bytes(b"fake pptx")
monkeypatch.setattr(svc.shutil, "which", lambda _name: None)
result = svc.build_ppt_preview(pptx, cache_dir=tmp_path / "cache")
assert result.ok is False
assert "LibreOffice" in (result.error or "")
def test_ppt_preview_cache_info_is_read_only(tmp_path):
from pathlib import Path
from services import ppt_preview_service as svc
pptx = tmp_path / "demo.pptx"
pptx.write_bytes(b"fake pptx")
cache_dir = tmp_path / "cache"
miss = svc.get_ppt_preview_cache_info(pptx, cache_dir=cache_dir)
assert miss.pdf_path
assert miss.cache_exists is False
Path(miss.pdf_path).parent.mkdir(parents=True, exist_ok=True)
Path(miss.pdf_path).write_bytes(b"%PDF-1.4\n")
hit = svc.get_ppt_preview_cache_info(pptx, cache_dir=cache_dir)
assert hit.cache_exists is True
assert hit.cache_size_kb is not None