233 lines
7.4 KiB
Python
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()
|