fix(cron): also coerce null createdAtMs/updatedAtMs on load
Same present-null footgun as runHistory; route all required store ints through _store_int.
This commit is contained in:
@@ -8,7 +8,7 @@ from typing import Any, Literal
|
|||||||
from nanobot.utils.dict_keys import get_camel_snake
|
from nanobot.utils.dict_keys import get_camel_snake
|
||||||
|
|
||||||
|
|
||||||
def _required_store_int(value: Any, default: int = 0) -> int:
|
def _store_int(value: Any, default: int = 0) -> int:
|
||||||
"""Coerce JSON numerics to int; treat null/blank like a missing key."""
|
"""Coerce JSON numerics to int; treat null/blank like a missing key."""
|
||||||
if value is None or value == "":
|
if value is None or value == "":
|
||||||
return default
|
return default
|
||||||
@@ -85,11 +85,9 @@ class CronRunRecord:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_store_dict(cls, data: dict[str, Any]) -> CronRunRecord:
|
def from_store_dict(cls, data: dict[str, Any]) -> CronRunRecord:
|
||||||
return cls(
|
return cls(
|
||||||
run_at_ms=_required_store_int(get_camel_snake(data, "runAtMs", "run_at_ms", 0)),
|
run_at_ms=_store_int(get_camel_snake(data, "runAtMs", "run_at_ms", 0)),
|
||||||
status=data["status"],
|
status=data["status"],
|
||||||
duration_ms=_required_store_int(
|
duration_ms=_store_int(get_camel_snake(data, "durationMs", "duration_ms", 0)),
|
||||||
get_camel_snake(data, "durationMs", "duration_ms", 0)
|
|
||||||
),
|
|
||||||
error=data.get("error"),
|
error=data.get("error"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -155,8 +153,8 @@ class CronJob:
|
|||||||
schedule=CronSchedule.from_store_dict(data["schedule"]),
|
schedule=CronSchedule.from_store_dict(data["schedule"]),
|
||||||
payload=CronPayload.from_store_dict(data.get("payload") or {}),
|
payload=CronPayload.from_store_dict(data.get("payload") or {}),
|
||||||
state=CronJobState.from_store_dict(data.get("state") or {}),
|
state=CronJobState.from_store_dict(data.get("state") or {}),
|
||||||
created_at_ms=int(get_camel_snake(data, "createdAtMs", "created_at_ms", 0)),
|
created_at_ms=_store_int(get_camel_snake(data, "createdAtMs", "created_at_ms", 0)),
|
||||||
updated_at_ms=int(get_camel_snake(data, "updatedAtMs", "updated_at_ms", 0)),
|
updated_at_ms=_store_int(get_camel_snake(data, "updatedAtMs", "updated_at_ms", 0)),
|
||||||
delete_after_run=bool(
|
delete_after_run=bool(
|
||||||
get_camel_snake(data, "deleteAfterRun", "delete_after_run", False)
|
get_camel_snake(data, "deleteAfterRun", "delete_after_run", False)
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1032,8 +1032,8 @@ def test_load_jobs_accepts_null_run_history_ms(tmp_path) -> None:
|
|||||||
{"runAtMs": None, "status": "ok", "durationMs": None},
|
{"runAtMs": None, "status": "ok", "durationMs": None},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"createdAtMs": 0,
|
"createdAtMs": None,
|
||||||
"updatedAtMs": 0,
|
"updatedAtMs": None,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -1046,3 +1046,5 @@ def test_load_jobs_accepts_null_run_history_ms(tmp_path) -> None:
|
|||||||
assert jobs[0].state.run_history[0].run_at_ms == 0
|
assert jobs[0].state.run_history[0].run_at_ms == 0
|
||||||
assert jobs[0].state.run_history[0].duration_ms == 0
|
assert jobs[0].state.run_history[0].duration_ms == 0
|
||||||
assert jobs[0].state.run_history[0].status == "ok"
|
assert jobs[0].state.run_history[0].status == "ok"
|
||||||
|
assert jobs[0].created_at_ms == 0
|
||||||
|
assert jobs[0].updated_at_ms == 0
|
||||||
|
|||||||
Reference in New Issue
Block a user