feature/integracion-panel-restore-targets
This commit is contained in:
381
tests/test_panel_client.py
Normal file
381
tests/test_panel_client.py
Normal file
@@ -0,0 +1,381 @@
|
||||
"""
|
||||
Pruebas del cliente del PANEL (servidor de restauración asignado por base de datos).
|
||||
Se mockea `requests` para no depender de un PANEL real.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from app.panel import panel_client
|
||||
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, status_code, json_data=None, raise_json=False):
|
||||
self.status_code = status_code
|
||||
self._json = json_data
|
||||
self._raise_json = raise_json
|
||||
|
||||
def json(self):
|
||||
if self._raise_json:
|
||||
raise ValueError("no es JSON")
|
||||
return self._json
|
||||
|
||||
|
||||
VALID_TARGET = {
|
||||
"id": 2,
|
||||
"name": "Omega",
|
||||
"server": "192.168.1.100,1433",
|
||||
"username": "sa",
|
||||
"password": "secreto",
|
||||
"data_folder": "C:\\SQLData",
|
||||
"ssh_host": "192.168.1.100",
|
||||
"ssh_port": 22,
|
||||
"ssh_username": "Administrator",
|
||||
"ssh_password": "ssh-secreto",
|
||||
"remote_inbox_path": "C:\\RestoreInbox",
|
||||
}
|
||||
|
||||
URL = "http://panel:3000"
|
||||
TOKEN = "tok"
|
||||
DB = "EMPRESA_DB"
|
||||
|
||||
|
||||
def test_target_url_vacia_devuelve_none():
|
||||
assert panel_client.get_target_for_database("", TOKEN, DB) is None
|
||||
|
||||
|
||||
def test_target_url_invalida_devuelve_none():
|
||||
assert panel_client.get_target_for_database("ftp://x", TOKEN, DB) is None
|
||||
|
||||
|
||||
def test_target_token_vacio_devuelve_none():
|
||||
assert panel_client.get_target_for_database(URL, "", DB) is None
|
||||
|
||||
|
||||
def test_target_db_vacio_devuelve_none():
|
||||
assert panel_client.get_target_for_database(URL, TOKEN, " ") is None
|
||||
|
||||
|
||||
def test_target_ok(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
def fake_get(url, headers=None, timeout=None):
|
||||
captured["url"] = url
|
||||
return FakeResponse(200, VALID_TARGET)
|
||||
|
||||
monkeypatch.setattr(panel_client.requests, "get", fake_get)
|
||||
target = panel_client.get_target_for_database(URL, TOKEN, DB)
|
||||
assert target is not None
|
||||
assert target["name"] == "Omega"
|
||||
# el db_name viaja como query param url-encoded
|
||||
assert "database=EMPRESA_DB" in captured["url"]
|
||||
|
||||
|
||||
def test_target_db_con_espacios_se_encodea(monkeypatch):
|
||||
captured = {}
|
||||
monkeypatch.setattr(
|
||||
panel_client.requests, "get",
|
||||
lambda url, **k: (captured.__setitem__("url", url), FakeResponse(200, VALID_TARGET))[1]
|
||||
)
|
||||
panel_client.get_target_for_database(URL, TOKEN, "MI BASE")
|
||||
assert "MI%20BASE" in captured["url"]
|
||||
|
||||
|
||||
def test_target_falta_campo_requerido(monkeypatch):
|
||||
incompleto = dict(VALID_TARGET)
|
||||
del incompleto["password"]
|
||||
monkeypatch.setattr(panel_client.requests, "get", lambda *a, **k: FakeResponse(200, incompleto))
|
||||
assert panel_client.get_target_for_database(URL, TOKEN, DB) is None
|
||||
|
||||
|
||||
def test_target_campo_vacio_es_invalido(monkeypatch):
|
||||
vacio = dict(VALID_TARGET, password=" ")
|
||||
monkeypatch.setattr(panel_client.requests, "get", lambda *a, **k: FakeResponse(200, vacio))
|
||||
assert panel_client.get_target_for_database(URL, TOKEN, DB) is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("code", [404, 401, 500, 503])
|
||||
def test_target_codigos_no_200_devuelven_none(monkeypatch, code):
|
||||
monkeypatch.setattr(panel_client.requests, "get", lambda *a, **k: FakeResponse(code))
|
||||
assert panel_client.get_target_for_database(URL, TOKEN, DB) is None
|
||||
|
||||
|
||||
def test_target_error_red_devuelve_none(monkeypatch):
|
||||
def boom(*a, **k):
|
||||
raise panel_client.requests.exceptions.ConnectionError("caído")
|
||||
|
||||
monkeypatch.setattr(panel_client.requests, "get", boom)
|
||||
assert panel_client.get_target_for_database(URL, TOKEN, DB) is None
|
||||
|
||||
|
||||
def test_target_json_invalido_devuelve_none(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
panel_client.requests, "get", lambda *a, **k: FakeResponse(200, raise_json=True)
|
||||
)
|
||||
assert panel_client.get_target_for_database(URL, TOKEN, DB) is None
|
||||
|
||||
|
||||
def test_report_job_result_201_true(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
def fake_post(url, json=None, headers=None, timeout=None):
|
||||
captured["json"] = json
|
||||
return FakeResponse(201)
|
||||
|
||||
monkeypatch.setattr(panel_client.requests, "post", fake_post)
|
||||
ok = panel_client.report_job_result(
|
||||
URL, TOKEN, filename="empresa.bak", status="completed",
|
||||
restore_target_id=2, db_name="EMP", duration_ms=1000
|
||||
)
|
||||
assert ok is True
|
||||
assert captured["json"]["filename"] == "empresa.bak"
|
||||
assert captured["json"]["status"] == "completed"
|
||||
|
||||
|
||||
def test_report_job_result_otro_codigo_false(monkeypatch):
|
||||
monkeypatch.setattr(panel_client.requests, "post", lambda *a, **k: FakeResponse(500))
|
||||
assert panel_client.report_job_result(URL, TOKEN, "x.bak", "failed") is False
|
||||
|
||||
|
||||
def test_report_job_result_error_red_false(monkeypatch):
|
||||
def boom(*a, **k):
|
||||
raise panel_client.requests.exceptions.Timeout("timeout")
|
||||
|
||||
monkeypatch.setattr(panel_client.requests, "post", boom)
|
||||
assert panel_client.report_job_result(URL, TOKEN, "x.bak", "failed") is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize("code", [200, 404])
|
||||
def test_test_connection_ok(monkeypatch, code):
|
||||
# 200 o 404 → conexión y token correctos
|
||||
monkeypatch.setattr(panel_client.requests, "get", lambda *a, **k: FakeResponse(code))
|
||||
ok, info = panel_client.test_connection(URL, TOKEN)
|
||||
assert ok is True
|
||||
|
||||
|
||||
def test_test_connection_token_rechazado(monkeypatch):
|
||||
monkeypatch.setattr(panel_client.requests, "get", lambda *a, **k: FakeResponse(401))
|
||||
ok, info = panel_client.test_connection(URL, TOKEN)
|
||||
assert ok is False
|
||||
assert "401" in info
|
||||
|
||||
|
||||
def test_test_connection_url_invalida():
|
||||
ok, info = panel_client.test_connection("noesurl", TOKEN)
|
||||
assert ok is False
|
||||
|
||||
|
||||
def test_report_instance_config_200_true(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
def fake_post(url, json=None, headers=None, timeout=None):
|
||||
captured["url"] = url
|
||||
captured["json"] = json
|
||||
return FakeResponse(200)
|
||||
|
||||
monkeypatch.setattr(panel_client.requests, "post", fake_post)
|
||||
ok = panel_client.report_instance_config(
|
||||
URL, TOKEN, r"D:\Backups\Entrada", host_name="WIN-01", app_version="1.0.0"
|
||||
)
|
||||
assert ok is True
|
||||
assert captured["url"].endswith("/api/restore/instance-config")
|
||||
assert captured["json"]["input_folder"] == r"D:\Backups\Entrada"
|
||||
assert captured["json"]["host_name"] == "WIN-01"
|
||||
|
||||
|
||||
def test_report_instance_config_input_vacio_false():
|
||||
assert panel_client.report_instance_config(URL, TOKEN, " ") is False
|
||||
|
||||
|
||||
def test_report_instance_config_sin_token_false():
|
||||
assert panel_client.report_instance_config(URL, "", r"D:\In") is False
|
||||
|
||||
|
||||
def test_report_instance_config_otro_codigo_false(monkeypatch):
|
||||
monkeypatch.setattr(panel_client.requests, "post", lambda *a, **k: FakeResponse(500))
|
||||
assert panel_client.report_instance_config(URL, TOKEN, r"D:\In") is False
|
||||
|
||||
|
||||
def test_report_instance_config_error_red_false(monkeypatch):
|
||||
def boom(*a, **k):
|
||||
raise panel_client.requests.exceptions.ConnectionError("caído")
|
||||
|
||||
monkeypatch.setattr(panel_client.requests, "post", boom)
|
||||
assert panel_client.report_instance_config(URL, TOKEN, r"D:\In") is False
|
||||
|
||||
|
||||
def test_target_con_instance_envia_param(monkeypatch):
|
||||
captured = {}
|
||||
monkeypatch.setattr(
|
||||
panel_client.requests, "get",
|
||||
lambda url, **k: (captured.__setitem__("url", url), FakeResponse(200, VALID_TARGET))[1]
|
||||
)
|
||||
panel_client.get_target_for_database(URL, TOKEN, DB, instance_key="Alfa")
|
||||
assert "instance=Alfa" in captured["url"]
|
||||
|
||||
|
||||
COLOCATED_TARGET = {
|
||||
"id": 1,
|
||||
"name": "Alfa",
|
||||
"server": "localhost",
|
||||
"username": "sa",
|
||||
"password": "secreto",
|
||||
"data_folder": "C:\\SQLData",
|
||||
}
|
||||
|
||||
|
||||
def test_target_colocado_no_exige_ssh(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
panel_client.requests, "get", lambda *a, **k: FakeResponse(200, COLOCATED_TARGET)
|
||||
)
|
||||
target = panel_client.get_target_for_database(URL, TOKEN, DB, instance_key="Alfa")
|
||||
assert target is not None
|
||||
assert target["name"] == "Alfa"
|
||||
|
||||
|
||||
def test_report_instance_config_con_instance_key(monkeypatch):
|
||||
captured = {}
|
||||
monkeypatch.setattr(
|
||||
panel_client.requests, "post",
|
||||
lambda url, json=None, **k: (captured.update({"json": json}), FakeResponse(200))[1]
|
||||
)
|
||||
ok = panel_client.report_instance_config(
|
||||
URL, TOKEN, r"D:\Alfa\In", instance_key="Alfa"
|
||||
)
|
||||
assert ok is True
|
||||
assert captured["json"]["instance_key"] == "Alfa"
|
||||
|
||||
|
||||
CATALOG_RESPONSE = {
|
||||
"targets": [
|
||||
{"id": 1, "name": "Alfa"},
|
||||
{"id": 2, "name": "Omega"},
|
||||
{"id": 4, "name": "Delta"},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def test_list_restore_target_names_ok(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
def fake_get(url, headers=None, timeout=None):
|
||||
captured["url"] = url
|
||||
return FakeResponse(200, CATALOG_RESPONSE)
|
||||
|
||||
monkeypatch.setattr(panel_client.requests, "get", fake_get)
|
||||
names = panel_client.list_restore_target_names(URL, TOKEN)
|
||||
assert names == ["Alfa", "Omega", "Delta"]
|
||||
assert captured["url"].endswith("/api/restore/target-catalog")
|
||||
|
||||
|
||||
def test_list_restore_target_names_url_vacia():
|
||||
assert panel_client.list_restore_target_names("", TOKEN) == []
|
||||
|
||||
|
||||
def test_list_restore_target_names_token_vacio():
|
||||
assert panel_client.list_restore_target_names(URL, "") == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("code", [401, 500, 503])
|
||||
def test_list_restore_target_names_codigos_no_200(monkeypatch, code):
|
||||
monkeypatch.setattr(panel_client.requests, "get", lambda *a, **k: FakeResponse(code))
|
||||
assert panel_client.list_restore_target_names(URL, TOKEN) == []
|
||||
|
||||
|
||||
def test_list_restore_target_names_error_red(monkeypatch):
|
||||
def boom(*a, **k):
|
||||
raise panel_client.requests.exceptions.ConnectionError("caído")
|
||||
|
||||
monkeypatch.setattr(panel_client.requests, "get", boom)
|
||||
assert panel_client.list_restore_target_names(URL, TOKEN) == []
|
||||
|
||||
|
||||
def test_list_restore_target_names_json_invalido(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
panel_client.requests, "get", lambda *a, **k: FakeResponse(200, raise_json=True)
|
||||
)
|
||||
assert panel_client.list_restore_target_names(URL, TOKEN) == []
|
||||
|
||||
|
||||
def test_list_restore_target_names_sin_lista_targets(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
panel_client.requests, "get", lambda *a, **k: FakeResponse(200, {"foo": []})
|
||||
)
|
||||
assert panel_client.list_restore_target_names(URL, TOKEN) == []
|
||||
|
||||
|
||||
ROUTE_FORWARD = {
|
||||
"action": "forward",
|
||||
"db_name": "GENERICA-TEST",
|
||||
"node_key": "GENERICA-TEST",
|
||||
"target": {
|
||||
"id": 2,
|
||||
"name": "Alfa",
|
||||
"server": "192.168.1.10,1433",
|
||||
"username": "sa",
|
||||
"password": "secreto",
|
||||
"data_folder": "C:\\SQLData",
|
||||
"ssh_host": "192.168.1.10",
|
||||
"ssh_port": 22,
|
||||
"ssh_username": "Administrator",
|
||||
"ssh_password": "ssh-secreto",
|
||||
"remote_inbox_path": "C:\\RestoreInbox",
|
||||
"input_folder": "D:\\Restore\\Alfa\\Entrada",
|
||||
},
|
||||
}
|
||||
|
||||
ROUTE_RESTORE_LOCAL = {
|
||||
"action": "restore_local",
|
||||
"db_name": "GENERICA-TEST",
|
||||
"node_key": "GENERICA-TEST",
|
||||
"target": {
|
||||
"id": 2,
|
||||
"name": "Alfa",
|
||||
"server": "192.168.1.10,1433",
|
||||
"username": "sa",
|
||||
"password": "secreto",
|
||||
"data_folder": "C:\\SQLData",
|
||||
"ssh_host": "192.168.1.10",
|
||||
"ssh_port": 22,
|
||||
"ssh_username": "Administrator",
|
||||
"ssh_password": "ssh-secreto",
|
||||
"remote_inbox_path": "C:\\RestoreInbox",
|
||||
"input_folder": "D:\\Restore\\Alfa\\Entrada",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_resolve_route_forward_ok(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
def fake_get(url, headers=None, timeout=None):
|
||||
captured["url"] = url
|
||||
return FakeResponse(200, ROUTE_FORWARD)
|
||||
|
||||
monkeypatch.setattr(panel_client.requests, "get", fake_get)
|
||||
route = panel_client.resolve_route(URL, TOKEN, "GENERICA-TEST.ZIP", instance_key="Omega")
|
||||
assert route is not None
|
||||
assert route["action"] == "forward"
|
||||
assert "resolve-route" in captured["url"]
|
||||
assert "instance=Omega" in captured["url"]
|
||||
|
||||
|
||||
def test_resolve_route_restore_local_ok(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
panel_client.requests, "get", lambda *a, **k: FakeResponse(200, ROUTE_RESTORE_LOCAL)
|
||||
)
|
||||
route = panel_client.resolve_route(URL, TOKEN, "GENERICA-TEST.ZIP", instance_key="Alfa")
|
||||
assert route is not None
|
||||
assert route["action"] == "restore_local"
|
||||
|
||||
|
||||
def test_resolve_route_503_devuelve_none(monkeypatch):
|
||||
monkeypatch.setattr(panel_client.requests, "get", lambda *a, **k: FakeResponse(503))
|
||||
assert panel_client.resolve_route(URL, TOKEN, "X.ZIP") is None
|
||||
|
||||
|
||||
def test_resolve_route_forward_sin_input_folder_invalido(monkeypatch):
|
||||
bad = dict(ROUTE_FORWARD)
|
||||
bad["target"] = dict(bad["target"], input_folder=" ")
|
||||
monkeypatch.setattr(
|
||||
panel_client.requests, "get", lambda *a, **k: FakeResponse(200, bad)
|
||||
)
|
||||
assert panel_client.resolve_route(URL, TOKEN, "X.ZIP") is None
|
||||
Reference in New Issue
Block a user