mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 01:50:44 +08:00
Plugins/Skills install and edit via mcpServers JSON, with clearer row actions, soft reloads, and instant loading placeholders; also drop MCP market and harden related scheduler/logging/exec guards. Co-authored-by: Cursor <cursoragent@cursor.com>
73 lines
3.1 KiB
Python
73 lines
3.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Tests for scheduled job failure classification + summary."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from runtime.scheduler.failure_class import classify_scheduled_job_error, enrich_scheduled_job_run_dict
|
|
from svc.persistence.sqlite_store import SqliteStore
|
|
|
|
|
|
def test_classify_scheduled_job_error_classes() -> None:
|
|
assert classify_scheduled_job_error("overlapping_run", status="skipped") == "overlap"
|
|
assert classify_scheduled_job_error("read_timeout after 60s", status="failed") == "timeout"
|
|
assert classify_scheduled_job_error("WhatsApp Connection Closed", status="failed") == "delivery"
|
|
assert classify_scheduled_job_error("insufficient_scope:sql:query", status="failed") == "auth"
|
|
assert classify_scheduled_job_error("Unregistered tool: mcp__netx__x", status="failed") == "mcp"
|
|
assert classify_scheduled_job_error("stale_running_cleared", status="failed") == "stale"
|
|
assert classify_scheduled_job_error("boom", status="failed") == "runtime"
|
|
assert classify_scheduled_job_error("", status="success") == ""
|
|
|
|
|
|
def test_enrich_run_dict_adds_failure_class() -> None:
|
|
out = enrich_scheduled_job_run_dict({"status": "failed", "error": "tool timeout"})
|
|
assert out["failure_class"] == "timeout"
|
|
|
|
|
|
def test_scheduled_job_failure_summary(tmp_path: Path) -> None:
|
|
store = SqliteStore(str(tmp_path / "jobs.sqlite"))
|
|
# Ensure tenant user exists if required by store create paths.
|
|
job = store.scheduled_job_create(
|
|
tenant_id="default",
|
|
name="fail-board",
|
|
description="",
|
|
schedule_kind="interval",
|
|
schedule_expr="3600",
|
|
timezone_name="UTC",
|
|
prompt_text="ping",
|
|
specialist="ops",
|
|
lang="en",
|
|
created_by_user_id="u1",
|
|
source="test",
|
|
)
|
|
run_ok = store.scheduled_job_run_create(
|
|
job_id=job.id, tenant_id="default", scheduled_at="2026-08-01T00:00:00+00:00", status="queued"
|
|
)
|
|
store.scheduled_job_run_update(
|
|
run_id=run_ok.id,
|
|
tenant_id="default",
|
|
patch={"status": "success", "finished_at": "2026-08-01T00:01:00+00:00", "error": ""},
|
|
)
|
|
run_fail = store.scheduled_job_run_create(
|
|
job_id=job.id, tenant_id="default", scheduled_at="2026-08-01T01:00:00+00:00", status="queued"
|
|
)
|
|
store.scheduled_job_run_update(
|
|
run_id=run_fail.id,
|
|
tenant_id="default",
|
|
patch={
|
|
"status": "failed",
|
|
"finished_at": "2026-08-01T01:01:00+00:00",
|
|
"error": "read_timeout after 90s",
|
|
},
|
|
)
|
|
store.scheduled_job_mark_run(
|
|
job_id=job.id, tenant_id="default", last_run_status="failed", pause_after=False
|
|
)
|
|
summary = store.scheduled_job_failure_summary(tenant_id="default", recent_limit=50)
|
|
assert summary["recent_failed"] >= 1
|
|
assert summary["recent_success"] >= 1
|
|
assert "timeout" in (summary.get("recent_fail_classes") or {})
|
|
assert any(x.get("id") == job.id for x in summary.get("jobs_last_failed") or [])
|
|
d = store.scheduled_job_run_to_dict(store.scheduled_job_run_get(run_id=run_fail.id, tenant_id="default"))
|
|
assert d.get("failure_class") == "timeout"
|