Files
docs/standards/mcp/templates/app/tests/unit/test_worker.py
Vishesh 'ironeagle' Bangotra 0cb35d10a6 Phase 7: serve the vet sample from the template tools and docs
- build.py: _TEMPLATE_COMPONENTS rewritten around the vet sample (53 kinds —
  per-resource slices, jobs, worked appointment_reminder family, tests);
  seed tokens dropped from the manifest ('sample: vet' instead)
- registry.py: get_app_component(kind) no longer renames item/example seeds;
  prompts and tool docs updated to 'copy a slice and adapt'
- templates/README.md, 01_build_recipe, 40_ui_admin, wiki/03 updated off the
  old seed scheme; repo inventory/config now exclude vet (docs move under
  standards; react/vet itself is deleted in a later phase)
- regenerated MCP bundle, vendored templates, and site output
2026-10-20 14:58:17 +05:30

122 lines
3.4 KiB
Python

"""Unit tests for the worker dispatch table and job status transitions."""
from worker import HANDLERS, poll_jobs
class FakeDoc:
def __init__(self, **fields):
self.__dict__.update(fields)
def model_dump(self, **kwargs):
return dict(self.__dict__)
class FakeJobSvc:
def __init__(self, jobs):
self.jobs = {j.id: j for j in jobs}
self.patches: list[tuple[int, dict]] = []
async def search(self, filter=None):
filter = filter or {}
return [j for j in self.jobs.values() if all(j.__dict__.get(k) == v for k, v in filter.items())]
async def patch(self, id, body):
self.patches.append((int(id), body))
job = self.jobs[int(id)]
for k, v in body.items():
setattr(job, k, v)
class FakeAppointmentSvc:
def __init__(self, data):
self._data = data
async def read(self, id):
return FakeDoc(**self._data) if self._data else None
def _job(id=1, kind="appointment_reminder", payload=None):
return FakeDoc(
id=id,
name="j",
kind=kind,
status="pending",
payload=payload or {"appointment_id": 1},
retry_count=0,
)
def test_dispatch_table_has_appointment_reminder():
assert "appointment_reminder" in HANDLERS
async def test_poll_jobs_runs_pending_and_marks_done(monkeypatch):
monkeypatch.setattr("worker.settings.worker_poll_seconds", 0)
appointment = {
"id": 1,
"date": "2026-11-02T10:00:00",
"pet": {"name": "Rex", "parents": [{"name": "Kim", "email": "kim@example.com"}]},
}
job_svc = FakeJobSvc([_job()])
resources = {"job_svc": job_svc, "appointment_svc": FakeAppointmentSvc(appointment)}
import asyncio
task = asyncio.create_task(poll_jobs(resources))
for _ in range(50):
await asyncio.sleep(0.01)
if job_svc.patches and job_svc.patches[-1][1].get("status") == "done":
break
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
statuses = [body["status"] for _, body in job_svc.patches]
assert statuses == ["processing", "done"]
async def test_poll_jobs_marks_unknown_kind_failed(monkeypatch):
monkeypatch.setattr("worker.settings.worker_poll_seconds", 0)
job_svc = FakeJobSvc([_job(kind="nope")])
resources = {"job_svc": job_svc, "appointment_svc": FakeAppointmentSvc(None)}
import asyncio
task = asyncio.create_task(poll_jobs(resources))
for _ in range(50):
await asyncio.sleep(0.01)
if job_svc.patches and job_svc.patches[-1][1].get("status") == "failed":
break
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
last = job_svc.patches[-1][1]
assert last["status"] == "failed"
assert "no handler" in last["error_message"]
assert last["retry_count"] == 1
async def test_poll_jobs_skips_retries_exhausted(monkeypatch):
monkeypatch.setattr("worker.settings.worker_poll_seconds", 0)
monkeypatch.setattr("worker.settings.worker_max_retries", 1)
job = _job()
job.retry_count = 1
job_svc = FakeJobSvc([job])
resources = {"job_svc": job_svc, "appointment_svc": FakeAppointmentSvc(None)}
import asyncio
task = asyncio.create_task(poll_jobs(resources))
await asyncio.sleep(0.05)
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
assert job_svc.patches == []