"""#5749: a redeploy during an Ansible run logged two CancelledError tracebacks from the run's done-callback and two "Task was destroyed but it is pending!" lines for its in-memory cleanup. Steward stopping mid-run is not an error: the next start marks the run interrupted.""" import asyncio import logging from steward.ansible import executor from steward.ansible.runner import run_ended class _App: logger = logging.getLogger("test.ansible.run_end") async def test_a_run_cut_short_by_shutdown_logs_no_error(caplog): async def run(): await asyncio.sleep(60) task = asyncio.create_task(run()) task.add_done_callback(run_ended(_App, "r1")) await asyncio.sleep(0) task.cancel() with caplog.at_level(logging.INFO, logger="test.ansible.run_end"): await asyncio.gather(task, return_exceptions=True) await asyncio.sleep(0) # done-callbacks run on the next turn assert [r.levelname for r in caplog.records] == ["INFO"] assert "Ansible run r1 interrupted" in caplog.records[0].getMessage() async def test_a_run_that_raised_is_still_an_error(caplog): async def run(): raise RuntimeError("ansible-playbook vanished") task = asyncio.create_task(run()) task.add_done_callback(run_ended(_App, "r2")) with caplog.at_level(logging.INFO, logger="test.ansible.run_end"): await asyncio.gather(task, return_exceptions=True) await asyncio.sleep(0) assert [(r.levelname, r.getMessage()) for r in caplog.records] == [ ("ERROR", "Ansible run r2 raised: ansible-playbook vanished")] async def test_a_finished_run_s_state_is_dropped_by_a_timer_not_a_task(monkeypatch): """A task left sleeping is what shutdown found pending; a timer isn't one.""" monkeypatch.setattr(executor, "_MEM_TTL_SECS", 0.01) executor._run_lines["r3"] = ["ok"] before = asyncio.all_tasks() executor._schedule_cleanup("r3") assert asyncio.all_tasks() == before await asyncio.sleep(0.05) assert "r3" not in executor._run_lines