""" 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