fix(scheduler): address !7 REQUEST_CHANGES
- seed: check completed_key before no_source so same-day last_completed is already_completed (not 10m retry) - scheduler: resolve TaskControl ok/error before logging; ok=False logs task_fail, not task_ok - candidate_cleanup: catch failures, record task, retry ~5m like discover
This commit is contained in:
1 parent
bb18d232f6
commit
1697573d95
7 files changed
+264
-17
No files matched your search
@@ -152,10 +152,87 @@ def test_task_control_overrides_ok_and_next_run():
|
||||
assert task.next_run == 2_000.0
|
||||
|
||||
|
||||
|
||||
|
||||
class _CaptureLogger:
|
||||
def __init__(self):
|
||||
self.events = []
|
||||
|
||||
def info(self, event, **fields):
|
||||
self.events.append(("info", event, fields))
|
||||
|
||||
def warning(self, event, **fields):
|
||||
self.events.append(("warning", event, fields))
|
||||
|
||||
def error(self, event, **fields):
|
||||
self.events.append(("error", event, fields))
|
||||
|
||||
|
||||
def test_task_control_ok_false_logs_fail_not_ok():
|
||||
"""TaskControl(ok=False) 不得记 scheduler.task_ok(Skip ≠ success)。"""
|
||||
clock = [1_000.0]
|
||||
logger = _CaptureLogger()
|
||||
task = ScheduledTask(
|
||||
"quiet",
|
||||
60,
|
||||
lambda: TaskControl(ok=False, next_run=1_500.0, error="snapshot_quiet_window"),
|
||||
)
|
||||
scheduler = Scheduler([task], clock=lambda: clock[0], logger=logger)
|
||||
results = scheduler.run_due()
|
||||
assert results[0].ok is False
|
||||
assert results[0].error == "snapshot_quiet_window"
|
||||
assert ("info", "scheduler.task_ok", {"task": "quiet"}) not in logger.events
|
||||
fail_events = [e for e in logger.events if e[1] == "scheduler.task_fail"]
|
||||
assert len(fail_events) == 1
|
||||
assert fail_events[0][0] == "warning"
|
||||
assert fail_events[0][2]["task"] == "quiet"
|
||||
assert fail_events[0][2]["error"] == "snapshot_quiet_window"
|
||||
|
||||
|
||||
def test_task_control_ok_true_still_logs_task_ok():
|
||||
clock = [1_000.0]
|
||||
logger = _CaptureLogger()
|
||||
task = ScheduledTask(
|
||||
"ok_task",
|
||||
60,
|
||||
lambda: TaskControl(ok=True, next_run=1_200.0),
|
||||
)
|
||||
scheduler = Scheduler([task], clock=lambda: clock[0], logger=logger)
|
||||
results = scheduler.run_due()
|
||||
assert results[0].ok is True
|
||||
assert ("info", "scheduler.task_ok", {"task": "ok_task"}) in logger.events
|
||||
assert not any(e[1] == "scheduler.task_fail" for e in logger.events)
|
||||
|
||||
|
||||
def test_raised_exception_logs_task_fail():
|
||||
clock = [100.0]
|
||||
logger = _CaptureLogger()
|
||||
|
||||
def boom():
|
||||
raise RuntimeError("db down")
|
||||
|
||||
scheduler = Scheduler(
|
||||
[ScheduledTask("bad", 10, boom)],
|
||||
clock=lambda: clock[0],
|
||||
logger=logger,
|
||||
)
|
||||
results = scheduler.run_due()
|
||||
assert results[0].ok is False
|
||||
assert results[0].error == "db down"
|
||||
assert not any(e[1] == "scheduler.task_ok" for e in logger.events)
|
||||
assert not any(e[1] == "scheduler.task_fail" for e in logger.events)
|
||||
err_events = [e for e in logger.events if e[1] == "scheduler.task_error"]
|
||||
assert len(err_events) == 1
|
||||
assert err_events[0][0] == "error"
|
||||
assert err_events[0][2]["error"] == "db down"
|
||||
|
||||
def main():
|
||||
test_run_when_due_and_reschedule()
|
||||
test_next_run_fn_controls_reschedule()
|
||||
test_task_control_overrides_ok_and_next_run()
|
||||
test_task_control_ok_false_logs_fail_not_ok()
|
||||
test_task_control_ok_true_still_logs_task_ok()
|
||||
test_raised_exception_logs_task_fail()
|
||||
test_task_error_does_not_block_other_tasks()
|
||||
test_run_forever_with_stop_event()
|
||||
test_max_iterations_and_validation()
|
||||
|
||||
Reference in new issue
Block a user