Fix webhook starvation and isolate scheduled job failures for v0.1.2 [skip ci]

This commit is contained in:
渡口浪人
2026-10-16 09:48:51 +08:00
parent e1b001a431
commit 8a3c616b5f
8 changed files with 158 additions and 64 deletions
+27
View File
@@ -373,3 +373,30 @@ def test_ai_layout_sends_metadata_only_and_validates_response(
assert b"test-ai-secret" not in calls[0]["body"]
assert image.getvalue() not in calls[0]["body"]
assert calls[0]["headers"]["Authorization"] == "Bearer test-ai-secret"
def test_webhook_matching_event_after_unrelated_backlog(client, service, monkeypatch):
import json
service.settings.image_url_hosts.append("example.com")
response = client.post(
"/api/v1/webhooks",
json={
"callback_url": "https://example.com/events",
"event_types": ["operation.failed"],
},
)
assert response.status_code == 201
for _ in range(500):
service.db.event("display.changed", {})
service.db.event("operation.failed", {"operation_id": "expected"})
sent = []
monkeypatch.setattr(
service.assets, "fetch_url", lambda url, **kwargs: sent.append(kwargs) or b""
)
service.webhooks.tick()
service.webhooks.last = 0
service.webhooks.tick()
assert len(sent) == 1
assert json.loads(sent[0]["body"])["data"] == {"operation_id": "expected"}
assert len(service.db.all("SELECT * FROM webhook_deliveries")) == 1
+55
View File
@@ -93,3 +93,58 @@ def test_worker_checks_late_sync_before_due_tasks(app, service, monkeypatch):
monkeypatch.setattr(service, "process_one", lambda: False)
scheduler.run()
assert calls[:2] == ["sync", "tick"]
def test_blocked_job_keeps_retry_slot_without_blocking_worker(
app, service, monkeypatch
):
scheduler = app.state.scheduler
service.registry.drivers["other-room"] = service.registry.get("living-room")
jobs = []
for device in ("living-room", "other-room"):
jobs.append(
scheduler.save_job(
JobInput(
name=device,
device_id=device,
schedule={"type": "daily", "time": "07:00"},
source={"type": "restore"},
)
)
)
current = datetime.now(UTC)
due = (current - timedelta(seconds=2)).isoformat()
service.db.execute("UPDATE jobs SET next_run=?", (due,))
unknown = service.submit("display", {}, "living-room", "test", "unknown")
service.db.execute(
"UPDATE operations SET status='UNKNOWN',write_started=1 WHERE id=?", (unknown,)
)
queued = service.submit("render", {}, "living-room", "test", "render")
calls = []
monkeypatch.setattr(service, "sync", lambda: None)
monkeypatch.setattr(service, "expire", lambda: calls.append("expire"))
monkeypatch.setattr(service.cloud_library, "sweep", lambda: None)
monkeypatch.setattr(service.local_retention, "sweep", lambda: None)
def process():
calls.append("process")
assert service.operation(queued)["status"] == "QUEUED"
scheduler.stop_event.set()
return False
monkeypatch.setattr(service, "process_one", process)
monkeypatch.setattr(
scheduler.stop_event, "wait", lambda timeout: scheduler.stop_event.set()
)
scheduler.run()
assert calls == ["expire", "process"]
blocked = service.db.one("SELECT * FROM jobs WHERE id=?", (jobs[0]["id"],))
assert blocked["next_run"] == due
assert blocked["last_run"] is None
assert len(service.db.all("SELECT * FROM job_runs")) == 1
assert service.db.one("SELECT * FROM job_runs")["job_id"] == jobs[1]["id"]
scheduler.tick(current + timedelta(minutes=6))
assert (
service.db.one("SELECT * FROM jobs WHERE id=?", (jobs[0]["id"],))["last_result"]
== "SKIPPED_OVERDUE"
)