"""Tests for agent update settings, poll, request and ingest lifecycle.""" import uuid from datetime import datetime, timedelta, timezone from unittest.mock import patch from app.models import Host from app.services.agent_poll import build_agent_poll_payload from app.services.agent_update import ( AGENT_UPDATE_SUCCESS, AgentUpdateNotAllowedError, process_agent_update_ingest, request_agent_update, ) from app.services.agent_update_settings import upsert_agent_update_settings from app.services.ingest import ingest_event from tests.test_ingest import VALID_EVENT def test_agent_update_settings_sac_mode(db_session): cfg = upsert_agent_update_settings( db_session, mode="sac", recommended_ssh_version="2.1.0-SAC", fallback_after_minutes=10, ) assert cfg.mode == "sac" assert cfg.recommended_ssh_version == "2.1.0-SAC" assert cfg.fallback_after_minutes == 10 def test_request_agent_update_sets_pending(db_session): upsert_agent_update_settings(db_session, mode="sac", recommended_ssh_version="2.1.0-SAC") host = Host( hostname="linux-1", os_family="linux", product="ssh-monitor", product_version="2.0.0-SAC", agent_instance_id="agent-uuid-1", ) db_session.add(host) db_session.commit() request_agent_update(db_session, host) assert host.pending_agent_update is True assert host.pending_update_target_version == "2.1.0-SAC" assert host.agent_update_state == "requested" def test_request_agent_update_blocked_in_gpo_mode(db_session): upsert_agent_update_settings(db_session, mode="gpo") host = Host( hostname="linux-2", os_family="linux", product="ssh-monitor", agent_instance_id="agent-uuid-2", ) db_session.add(host) db_session.commit() try: request_agent_update(db_session, host) raise AssertionError("expected AgentUpdateNotAllowedError") except AgentUpdateNotAllowedError: pass def test_poll_payload_includes_update_and_config(db_session): upsert_agent_update_settings(db_session, mode="sac") host = Host( hostname="win-1", os_family="windows", product="rdp-login-monitor", agent_instance_id="agent-uuid-3", pending_agent_update=True, pending_update_target_version="2.1.0-SAC", agent_config={"ServerDisplayName": "Test PC"}, agent_config_revision=3, ) db_session.add(host) db_session.commit() payload = build_agent_poll_payload(db_session, host) assert payload["config_revision"] == 3 assert payload["config"]["settings"]["ServerDisplayName"] == "Test PC" assert payload["update"]["requested"] is True assert payload["update"]["target_version"] == "2.1.0-SAC" assert payload["update"]["source"] == "sac" def test_ingest_agent_update_success_clears_pending(db_session): host = Host( hostname="linux-3", os_family="linux", product="ssh-monitor", agent_instance_id="agent-uuid-4", pending_agent_update=True, pending_update_target_version="2.1.0-SAC", ) db_session.add(host) db_session.commit() process_agent_update_ingest( db_session, host, AGENT_UPDATE_SUCCESS, {"product_version": "2.1.0-SAC"}, ) assert host.pending_agent_update is False assert host.product_version == "2.1.0-SAC" assert host.agent_update_state == "success" def test_api_request_agent_update(jwt_headers, client, db_session): upsert_agent_update_settings(db_session, mode="sac", recommended_ssh_version="2.1.0-SAC") host = Host( hostname="api-linux", os_family="linux", product="ssh-monitor", agent_instance_id="agent-api-1", ) db_session.add(host) db_session.commit() response = client.post(f"/api/v1/hosts/{host.id}/actions/request-agent-update", headers=jwt_headers) assert response.status_code == 200 body = response.json() assert body["pending_agent_update"] is True assert body["target_version"] == "2.1.0-SAC" def test_api_agent_poll_extended(auth_headers, client, db_session): upsert_agent_update_settings(db_session, mode="sac") host = Host( hostname="poll-host", os_family="linux", product="ssh-monitor", agent_instance_id="poll-agent-id", pending_agent_update=True, pending_update_target_version="2.1.0-SAC", ) db_session.add(host) db_session.commit() response = client.get( "/api/v1/agent/commands", params={"agent_instance_id": "poll-agent-id"}, headers=auth_headers, ) assert response.status_code == 200 body = response.json() assert body["update"]["requested"] is True assert body["commands"] == [] def test_api_patch_agent_config(jwt_headers, client, db_session): host = Host( hostname="cfg-host", os_family="windows", product="rdp-login-monitor", ) db_session.add(host) db_session.commit() response = client.patch( f"/api/v1/hosts/{host.id}/agent-config", json={"settings": {"ServerDisplayName": "UNMS Kalina", "GetInventory": True}}, headers=jwt_headers, ) assert response.status_code == 200 body = response.json() assert body["config_revision"] == 1 assert body["settings"]["ServerDisplayName"] == "UNMS Kalina" def test_api_agent_update_fallback_ssh(jwt_headers, client, db_session, monkeypatch): monkeypatch.setenv("SAC_LINUX_ADMIN_USER", "root") monkeypatch.setenv("SAC_LINUX_ADMIN_PASSWORD", "pw") from app.config import get_settings get_settings.cache_clear() host = Host( hostname="fb-linux", os_family="linux", product="ssh-monitor", ipv4="10.10.36.9", ) db_session.add(host) db_session.commit() with patch("app.services.agent_update.run_ssh_monitor_update") as mock_update: from app.services.ssh_connect import SshCommandResult mock_update.return_value = SshCommandResult( ok=True, message="updated", target="fb-linux", agent_version="2.1.0-SAC", ) response = client.post( f"/api/v1/hosts/{host.id}/actions/agent-update-fallback", headers=jwt_headers, ) assert response.status_code == 200 assert response.json()["ok"] is True assert response.json()["product_version"] == "2.1.0-SAC"