Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions cli/crawl.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,19 @@ def crawl_all(project, limit):
click.echo(f"\n{'='*50}")
click.echo(f"Running: {name}")
click.echo(f"{'='*50}")
# detached=True keeps crawl_all running each spider inline, unchanged.
# Full crawls self-submit to Pueue like a single `crawl` (parallel,
# disconnect-safe); --limit runs stay inline.
_run_spider(
project, name, None, limit, None, "auto", False, None, False, False, True
project, name, None, limit, None, "auto", False, None, False, False, False
)

if not limit:
click.echo(
f"\n📋 All {len(spider_names)} spiders queued in Pueue "
"(detached; concurrency = your `pueue parallel` setting)."
)
click.echo(" Monitor: ./scrapai crawl-status · pueue status")


@click.command("crawl-status")
@click.argument("spider", required=False)
Expand Down
62 changes: 62 additions & 0 deletions tests/unit/test_crawl_pueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,65 @@ def test_passes_flags_through():
]:
i = cmd.index(flag)
assert cmd[i] == flag and cmd[i + 1] == val


# --- crawl-all queues every spider through the same detach path ---------------


def test_crawl_all_enqueues_each_spider(monkeypatch):
"""`crawl-all` (no --limit) must submit EVERY spider through the same
auto-detach path as a single `crawl` (detached=False → Pueue), not run them
inline — inline was sequential, terminal-bound, and died on disconnect."""
from unittest.mock import MagicMock, Mock, patch

from click.testing import CliRunner

import importlib

crawl_mod = importlib.import_module("cli.crawl") # cli/__init__ shadows the
# attribute with the command
calls = []
monkeypatch.setattr(crawl_mod, "_run_spider", lambda *a, **k: calls.append(a))

recs = []
for n in ("a_org", "b_org"):
r = Mock()
r.name = n
recs.append(r)
with patch("core.db.get_db") as mock_get_db:
db = Mock()
db.query.return_value.filter.return_value.all.return_value = recs
cm = MagicMock()
cm.__enter__.return_value = db
mock_get_db.return_value = cm
res = CliRunner().invoke(crawl_mod.crawl_all, ["--project", "proj"])
assert res.exit_code == 0, res.output
assert [c[1] for c in calls] == ["a_org", "b_org"] # every spider
assert all(c[10] is False for c in calls) # detached=False → Pueue path
assert "queued in Pueue" in res.output


def test_crawl_all_with_limit_stays_inline(monkeypatch):
from unittest.mock import MagicMock, Mock, patch

from click.testing import CliRunner

import importlib

crawl_mod = importlib.import_module("cli.crawl")
calls = []
monkeypatch.setattr(crawl_mod, "_run_spider", lambda *a, **k: calls.append(a))
r = Mock()
r.name = "a_org"
with patch("core.db.get_db") as mock_get_db:
db = Mock()
db.query.return_value.filter.return_value.all.return_value = [r]
cm = MagicMock()
cm.__enter__.return_value = db
mock_get_db.return_value = cm
res = CliRunner().invoke(
crawl_mod.crawl_all, ["--project", "proj", "--limit", "5"]
)
assert res.exit_code == 0, res.output
assert calls[0][3] == 5 # limit threads through
assert "queued in Pueue" not in res.output # inline test mode