mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-12 20:15:10 +00:00
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.
58 lines
2.1 KiB
Python
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()
|