|
1 | 1 | import asyncio |
2 | | -from datetime import timedelta |
| 2 | +from datetime import datetime, timedelta, timezone |
| 3 | +from types import TracebackType |
3 | 4 |
|
| 5 | +from pytest import MonkeyPatch |
4 | 6 | from typer.testing import CliRunner |
5 | 7 |
|
6 | | -from docket.cli import app |
7 | | -from docket.docket import Docket |
| 8 | +from docket.cli import app, list_workers, print_workers, workers_for_task |
| 9 | +from docket.docket import Docket, WorkerInfo |
8 | 10 | from docket.worker import Worker |
9 | 11 |
|
10 | 12 |
|
@@ -63,3 +65,108 @@ async def test_list_workers_for_task(docket: Docket, runner: CliRunner): |
63 | 65 |
|
64 | 66 | assert "worker-1" in result.output |
65 | 67 | assert "worker-2" in result.output |
| 68 | + |
| 69 | + |
| 70 | +def test_print_workers_highlight(monkeypatch: MonkeyPatch): |
| 71 | + """print_workers should render a table for the supplied workers.""" |
| 72 | + now = datetime.now(timezone.utc) |
| 73 | + workers = [ |
| 74 | + WorkerInfo(name="worker-1", last_seen=now, tasks={"trace", "sleep"}), |
| 75 | + ] |
| 76 | + |
| 77 | + class RecordingConsole: |
| 78 | + def __init__(self) -> None: |
| 79 | + self.objects: list[object] = [] |
| 80 | + |
| 81 | + def print(self, obj: object) -> None: |
| 82 | + self.objects.append(obj) |
| 83 | + |
| 84 | + recorder = RecordingConsole() |
| 85 | + monkeypatch.setattr("docket.cli.Console", lambda: recorder) |
| 86 | + |
| 87 | + print_workers("demo-docket", workers, highlight_task="sleep") |
| 88 | + |
| 89 | + assert recorder.objects, "Console.print should be invoked" |
| 90 | + table = recorder.objects[0] |
| 91 | + assert getattr(table, "title", "") == "Workers in Docket: demo-docket" |
| 92 | + |
| 93 | + |
| 94 | +def test_list_workers_delegates_to_print(monkeypatch: MonkeyPatch): |
| 95 | + """list_workers should fetch workers and delegate to print_workers.""" |
| 96 | + now = datetime.now(timezone.utc) |
| 97 | + workers = [WorkerInfo(name="worker-1", last_seen=now, tasks={"trace"})] |
| 98 | + captured: list[tuple[str, list[WorkerInfo], str | None]] = [] |
| 99 | + |
| 100 | + def fake_print( |
| 101 | + docket_name: str, |
| 102 | + worker_list: list[WorkerInfo], |
| 103 | + highlight_task: str | None = None, |
| 104 | + ) -> None: |
| 105 | + captured.append((docket_name, worker_list, highlight_task)) |
| 106 | + |
| 107 | + class FakeDocket: |
| 108 | + def __init__(self, name: str, url: str) -> None: |
| 109 | + self.name = name |
| 110 | + self.url = url |
| 111 | + |
| 112 | + async def __aenter__(self) -> "FakeDocket": |
| 113 | + return self |
| 114 | + |
| 115 | + async def __aexit__( |
| 116 | + self, |
| 117 | + exc_type: type[BaseException] | None, |
| 118 | + exc: BaseException | None, |
| 119 | + tb: TracebackType | None, |
| 120 | + ) -> bool: |
| 121 | + return False |
| 122 | + |
| 123 | + async def workers(self) -> list[WorkerInfo]: |
| 124 | + return workers |
| 125 | + |
| 126 | + monkeypatch.setattr("docket.cli.print_workers", fake_print) |
| 127 | + monkeypatch.setattr("docket.cli.Docket", FakeDocket) |
| 128 | + |
| 129 | + list_workers(docket_="demo", url="redis://localhost:6379/0") |
| 130 | + |
| 131 | + assert captured == [("demo", workers, None)] |
| 132 | + |
| 133 | + |
| 134 | +def test_workers_for_task_delegates_to_print(monkeypatch: MonkeyPatch): |
| 135 | + """workers_for_task should fetch filtered workers and highlight the task.""" |
| 136 | + now = datetime.now(timezone.utc) |
| 137 | + workers = [WorkerInfo(name="worker-2", last_seen=now, tasks={"trace"})] |
| 138 | + captured: list[tuple[str, list[WorkerInfo], str | None]] = [] |
| 139 | + |
| 140 | + def fake_print( |
| 141 | + docket_name: str, |
| 142 | + worker_list: list[WorkerInfo], |
| 143 | + highlight_task: str | None = None, |
| 144 | + ) -> None: |
| 145 | + captured.append((docket_name, worker_list, highlight_task)) |
| 146 | + |
| 147 | + class FakeDocket: |
| 148 | + def __init__(self, name: str, url: str) -> None: |
| 149 | + self.name = name |
| 150 | + self.url = url |
| 151 | + |
| 152 | + async def __aenter__(self) -> "FakeDocket": |
| 153 | + return self |
| 154 | + |
| 155 | + async def __aexit__( |
| 156 | + self, |
| 157 | + exc_type: type[BaseException] | None, |
| 158 | + exc: BaseException | None, |
| 159 | + tb: TracebackType | None, |
| 160 | + ) -> bool: |
| 161 | + return False |
| 162 | + |
| 163 | + async def task_workers(self, task: str) -> list[WorkerInfo]: |
| 164 | + assert task == "trace" |
| 165 | + return workers |
| 166 | + |
| 167 | + monkeypatch.setattr("docket.cli.print_workers", fake_print) |
| 168 | + monkeypatch.setattr("docket.cli.Docket", FakeDocket) |
| 169 | + |
| 170 | + workers_for_task("trace", docket_="demo", url="redis://localhost:6379/0") |
| 171 | + |
| 172 | + assert captured == [("demo", workers, "trace")] |
0 commit comments