CheddahBot/tests/test_cora_distribute.py

233 lines
7.4 KiB
Python

"""Tests for the Cora distribution watcher (scheduler._distribute_cora_file)."""
from __future__ import annotations
from dataclasses import dataclass, field
from pathlib import Path
from unittest.mock import MagicMock, patch
from cheddahbot.config import AutoCoraConfig, Config, ContentConfig, LinkBuildingConfig
@dataclass
class FakeTask:
"""Minimal ClickUp task stub for distribution tests."""
id: str = "fake_id"
name: str = ""
task_type: str = ""
custom_fields: dict = field(default_factory=dict)
def _make_scheduler(tmp_path, *, lb_folder="", content_inbox="", human_inbox=""):
"""Build a Scheduler with temp paths and mocked dependencies."""
from cheddahbot.scheduler import Scheduler
config = Config()
config.link_building = LinkBuildingConfig(watch_folder=lb_folder)
config.content = ContentConfig(cora_inbox=content_inbox)
config.autocora = AutoCoraConfig(cora_human_inbox=human_inbox, enabled=True)
config.clickup.enabled = True
config.clickup.space_id = "sp1"
config.clickup.api_token = "tok"
db = MagicMock()
agent = MagicMock()
sched = Scheduler(config=config, db=db, agent=agent)
return sched
KW_FIELDS = {"Keyword": "ac drive repair"}
def _drop_xlsx(folder: Path, name: str = "ac-drive-repair.xlsx") -> Path:
"""Create a dummy xlsx file in the given folder."""
folder.mkdir(parents=True, exist_ok=True)
p = folder / name
p.write_bytes(b"fake-xlsx-data")
return p
# ── Distribution logic tests ──
def test_distribute_lb_only(tmp_path):
"""LB task matched → copies to cora-inbox only."""
human = tmp_path / "human"
lb = tmp_path / "lb"
content = tmp_path / "content"
xlsx = _drop_xlsx(human)
sched = _make_scheduler(
tmp_path, lb_folder=str(lb), content_inbox=str(content), human_inbox=str(human)
)
tasks = [FakeTask(name="LB task", task_type="Link Building", custom_fields=KW_FIELDS)]
with patch.object(sched, "_get_clickup_client") as mock_client:
mock_client.return_value.get_tasks_from_overall_lists.return_value = tasks
sched._distribute_cora_file(xlsx)
assert (lb / xlsx.name).exists()
assert not (content / xlsx.name).exists()
assert (human / "processed" / xlsx.name).exists()
assert not xlsx.exists()
def test_distribute_content_only(tmp_path):
"""Content task matched → copies to content-cora-inbox only."""
human = tmp_path / "human"
lb = tmp_path / "lb"
content = tmp_path / "content"
xlsx = _drop_xlsx(human)
sched = _make_scheduler(
tmp_path, lb_folder=str(lb), content_inbox=str(content), human_inbox=str(human)
)
tasks = [FakeTask(name="CC task", task_type="Content Creation", custom_fields=KW_FIELDS)]
with patch.object(sched, "_get_clickup_client") as mock_client:
mock_client.return_value.get_tasks_from_overall_lists.return_value = tasks
sched._distribute_cora_file(xlsx)
assert not (lb / xlsx.name).exists()
assert (content / xlsx.name).exists()
assert (human / "processed" / xlsx.name).exists()
def test_distribute_mixed(tmp_path):
"""Both LB and Content tasks matched → copies to both inboxes."""
human = tmp_path / "human"
lb = tmp_path / "lb"
content = tmp_path / "content"
xlsx = _drop_xlsx(human)
sched = _make_scheduler(
tmp_path, lb_folder=str(lb), content_inbox=str(content), human_inbox=str(human)
)
tasks = [
FakeTask(name="LB task", task_type="Link Building", custom_fields=KW_FIELDS),
FakeTask(name="CC task", task_type="Content Creation", custom_fields=KW_FIELDS),
]
with patch.object(sched, "_get_clickup_client") as mock_client:
mock_client.return_value.get_tasks_from_overall_lists.return_value = tasks
sched._distribute_cora_file(xlsx)
assert (lb / xlsx.name).exists()
assert (content / xlsx.name).exists()
assert (human / "processed" / xlsx.name).exists()
def test_distribute_no_match(tmp_path):
"""No matching tasks → file stays in inbox, not moved to processed."""
human = tmp_path / "human"
lb = tmp_path / "lb"
content = tmp_path / "content"
xlsx = _drop_xlsx(human)
sched = _make_scheduler(
tmp_path, lb_folder=str(lb), content_inbox=str(content), human_inbox=str(human)
)
with patch.object(sched, "_get_clickup_client") as mock_client:
mock_client.return_value.get_tasks_from_overall_lists.return_value = []
sched._distribute_cora_file(xlsx)
assert xlsx.exists() # Still in inbox
assert not (human / "processed" / xlsx.name).exists()
def test_distribute_opo_task(tmp_path):
"""On Page Optimization task → copies to content inbox."""
human = tmp_path / "human"
lb = tmp_path / "lb"
content = tmp_path / "content"
xlsx = _drop_xlsx(human)
sched = _make_scheduler(
tmp_path, lb_folder=str(lb), content_inbox=str(content), human_inbox=str(human)
)
tasks = [FakeTask(name="OPO task", task_type="On Page Optimization", custom_fields=KW_FIELDS)]
with patch.object(sched, "_get_clickup_client") as mock_client:
mock_client.return_value.get_tasks_from_overall_lists.return_value = tasks
sched._distribute_cora_file(xlsx)
assert not (lb / xlsx.name).exists()
assert (content / xlsx.name).exists()
# ── Scan tests ──
def test_scan_skips_processed(tmp_path):
"""Files already in processed/ are skipped."""
human = tmp_path / "human"
lb = tmp_path / "lb"
content = tmp_path / "content"
# File in both top-level and processed/
_drop_xlsx(human)
_drop_xlsx(human / "processed")
sched = _make_scheduler(
tmp_path, lb_folder=str(lb), content_inbox=str(content), human_inbox=str(human)
)
with patch.object(sched, "_distribute_cora_file") as mock_dist:
sched._scan_cora_human_inbox()
mock_dist.assert_not_called()
def test_scan_skips_temp_files(tmp_path):
"""Office temp files (~$...) are skipped."""
human = tmp_path / "human"
lb = tmp_path / "lb"
content = tmp_path / "content"
_drop_xlsx(human, name="~$ac-drive-repair.xlsx")
sched = _make_scheduler(
tmp_path, lb_folder=str(lb), content_inbox=str(content), human_inbox=str(human)
)
with patch.object(sched, "_distribute_cora_file") as mock_dist:
sched._scan_cora_human_inbox()
mock_dist.assert_not_called()
def test_scan_empty_inbox(tmp_path):
"""Empty inbox → no-op."""
human = tmp_path / "human"
human.mkdir()
sched = _make_scheduler(tmp_path, human_inbox=str(human))
with patch.object(sched, "_distribute_cora_file") as mock_dist:
sched._scan_cora_human_inbox()
mock_dist.assert_not_called()
def test_distribute_copy_failure_no_move(tmp_path):
"""If copy fails, original is NOT moved to processed."""
human = tmp_path / "human"
xlsx = _drop_xlsx(human)
sched = _make_scheduler(tmp_path, lb_folder="/nonexistent/network/path", human_inbox=str(human))
tasks = [FakeTask(name="LB task", task_type="Link Building", custom_fields=KW_FIELDS)]
with (
patch.object(sched, "_get_clickup_client") as mock_client,
patch("cheddahbot.scheduler.shutil.copy2", side_effect=OSError("network down")),
):
mock_client.return_value.get_tasks_from_overall_lists.return_value = tasks
sched._distribute_cora_file(xlsx)
assert xlsx.exists() # Original untouched
assert not (human / "processed" / xlsx.name).exists()