feat(update): DETTE-022 — noyau MAJ silencieuse client Léa (gated, swap en stub)
Logique PURE testée : parse_version semver (R3), decide_update code-only/full (R2), should_update client (double garde anti-downgrade), download_update (staging only + SHA256, downloader injectable). Endpoint GET /api/v1/agents/update/check gated (RPA_AUTO_UPDATE_SERVER_ENABLED). Flags client+serveur OFF par défaut. Swap fichiers / Lea.bat / restart = STUBS no-op réservés révision humaine. 34 tests TDD. refs DETTE-022 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
85
tests/integration/test_update_check_endpoint.py
Normal file
85
tests/integration/test_update_check_endpoint.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""Tests intégration HTTP de GET /api/v1/agents/update/check — DETTE-022 v2.
|
||||
|
||||
Endpoint GATED (flag RPA_AUTO_UPDATE_SERVER_ENABLED), best-effort :
|
||||
- flag OFF par défaut → 503 (anti-régression : aucun effet sur le pipeline).
|
||||
- flag ON → 200 + payload {update_available, latest_version, update_type, url}.
|
||||
- auth Bearer requise (dépendance globale _verify_token).
|
||||
|
||||
La logique PURE est testée sans serveur dans tests/unit/test_update_check_server.py
|
||||
(DETTE-013). Ici on vérifie le branchement HTTP minimal.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
_ROOT = str(Path(__file__).resolve().parents[2])
|
||||
if _ROOT not in sys.path:
|
||||
sys.path.insert(0, _ROOT)
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
_TEST_API_TOKEN = "test_update_check_endpoint_token"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(monkeypatch):
|
||||
monkeypatch.setenv("RPA_API_TOKEN", _TEST_API_TOKEN)
|
||||
from fastapi.testclient import TestClient
|
||||
from agent_v0.server_v1 import api_stream
|
||||
|
||||
monkeypatch.setattr(api_stream, "API_TOKEN", _TEST_API_TOKEN)
|
||||
return TestClient(api_stream.app, raise_server_exceptions=False)
|
||||
|
||||
|
||||
def _auth_headers():
|
||||
return {"Authorization": f"Bearer {_TEST_API_TOKEN}"}
|
||||
|
||||
|
||||
class TestUpdateCheckEndpointFlag:
|
||||
def test_disabled_by_default_returns_503(self, client, monkeypatch):
|
||||
monkeypatch.delenv("RPA_AUTO_UPDATE_SERVER_ENABLED", raising=False)
|
||||
resp = client.get(
|
||||
"/api/v1/agents/update/check?current_version=1.0.1",
|
||||
headers=_auth_headers(),
|
||||
)
|
||||
assert resp.status_code == 503
|
||||
assert "RPA_AUTO_UPDATE_SERVER_ENABLED" in resp.text
|
||||
|
||||
|
||||
class TestUpdateCheckEndpointEnabled:
|
||||
@pytest.fixture(autouse=True)
|
||||
def _enable_flag(self, monkeypatch):
|
||||
monkeypatch.setenv("RPA_AUTO_UPDATE_SERVER_ENABLED", "true")
|
||||
# Version cible explicite pour rendre le test déterministe.
|
||||
monkeypatch.setenv("RPA_AGENT_LATEST_VERSION", "1.0.2")
|
||||
|
||||
def test_update_available(self, client):
|
||||
resp = client.get(
|
||||
"/api/v1/agents/update/check?current_version=1.0.1&machine_id=pc-1",
|
||||
headers=_auth_headers(),
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body["update_available"] is True
|
||||
assert body["latest_version"] == "1.0.2"
|
||||
assert body["update_type"] == "code-only"
|
||||
assert "1.0.2" in body["url"]
|
||||
|
||||
def test_up_to_date(self, client):
|
||||
resp = client.get(
|
||||
"/api/v1/agents/update/check?current_version=1.0.2&machine_id=pc-1",
|
||||
headers=_auth_headers(),
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body["update_available"] is False
|
||||
|
||||
def test_requires_auth(self, client):
|
||||
resp = client.get(
|
||||
"/api/v1/agents/update/check?current_version=1.0.1",
|
||||
)
|
||||
assert resp.status_code == 401
|
||||
Reference in New Issue
Block a user