Files
unshackle/tests/remote/unit/test_serve_service_refresh.py
imSp4rky d239964f0b fix(serve): apply staged service reloads when sessions end, with a max staging age
A staged tag was only retried on the refresh tick or a job completion, so a service with steady remote sessions never swapped in. Session end, expiry and eviction now trigger a single-flight apply, the tick applies after the pull, and serve.services_staged_max_age swaps an overdue tag in even while busy.
2026-09-22 10:40:49 -06:00

58 lines
2.1 KiB
Python

"""Pins the serve refresh loop: staged reloads apply after the pull, not only on the next tick."""
from __future__ import annotations
import asyncio
from typing import Any
import pytest
from aiohttp import web
from unshackle.commands.serve import _install_service_refresh
from unshackle.core import services
from unshackle.core.api import download_manager
from unshackle.core.config import config
pytestmark = pytest.mark.unit
async def test_refresh_tick_applies_tags_staged_during_the_pull(monkeypatch: pytest.MonkeyPatch) -> None:
"""A tag whose last session ended while the pull ran is staged although idle; the same tick swaps it in."""
pulled = asyncio.Event()
seen: list[set[str]] = []
def fake_refresh(busy: set[str]) -> list[dict[str, Any]]:
# The pull read the busy set before the session ended, so the tag lands in PENDING while idle.
services.PENDING.add("EXAMPLE")
pulled.set()
return []
def fake_apply(busy: set[str]) -> list[str]:
seen.append(set(services.PENDING))
ready = sorted(services.PENDING - busy)
services.PENDING.difference_update(ready)
return ready
monkeypatch.setattr(config, "serve", {"services_refresh_interval": 1})
monkeypatch.setattr(services, "PENDING", set())
monkeypatch.setattr(services, "repo_specs", lambda: ["x"])
monkeypatch.setattr(services, "log_load_issues", lambda: None)
monkeypatch.setattr(services, "record_loaded_commits", lambda: None)
monkeypatch.setattr(services, "refresh_and_reload", fake_refresh)
monkeypatch.setattr(services, "apply_pending", fake_apply)
monkeypatch.setattr(download_manager, "busy_services", set)
monkeypatch.setattr(download_manager, "publish_service_event", lambda *a, **k: None)
monkeypatch.setattr(download_manager, "_reload_task", None)
app = web.Application()
_install_service_refresh(app)
await app.on_startup[-1](app)
try:
await asyncio.wait_for(pulled.wait(), timeout=3)
await asyncio.sleep(0.2)
finally:
await app.on_cleanup[-1](app)
assert {"EXAMPLE"} in seen
assert services.PENDING == set()