"""Tests para traceroute_host (CLI `traceroute`, estilo dict sin excepciones). Sin red: se monkeypatchea ``subprocess.run`` en el namespace del modulo ``traceroute_host`` para devolver salidas fijas o lanzar excepciones. """ import os import subprocess import sys sys.path.insert(0, os.path.dirname(__file__)) import traceroute_host as tr_mod from traceroute_host import traceroute_host # Salida real de traceroute: cabecera + 3 hops, uno de ellos "* * *". RAW_OK = """\ traceroute to 1.1.1.1 (1.1.1.1), 30 hops max, 60 byte packets 1 gateway (192.168.1.1) 1.234 ms 1.111 ms 1.050 ms 2 * * * 3 one.one.one.one (1.1.1.1) 9.876 ms 9.500 ms 9.700 ms """ # Salida con un unico hop sin respuesta. RAW_ALL_STARS = """\ traceroute to 10.0.0.1 (10.0.0.1), 30 hops max, 60 byte packets 1 * * * """ class _FakeProc: """Stand-in de CompletedProcess: solo expone stdout y returncode.""" def __init__(self, stdout: str, returncode: int = 0): self.stdout = stdout self.returncode = returncode def _patch_run(monkeypatch, *, stdout=None, raises=None): """Sustituye subprocess.run en el modulo traceroute_host.""" def fake_run(*args, **kwargs): if raises is not None: raise raises return _FakeProc(stdout) monkeypatch.setattr(tr_mod.subprocess, "run", fake_run) def test_golden_varios_hops(monkeypatch): """Traceroute con varios hops: parsea numero de hop, host, IP y rtt.""" _patch_run(monkeypatch, stdout=RAW_OK) result = traceroute_host("1.1.1.1") assert result["status"] == "ok" assert result["host"] == "1.1.1.1" assert result["raw"] == RAW_OK hops = result["hops"] assert [h["hop"] for h in hops] == [1, 2, 3] # Hop 1: gateway con IP y tres rtt. hop1 = hops[0] assert len(hop1["hosts"]) == 1 assert hop1["hosts"][0]["name"] == "gateway" assert hop1["hosts"][0]["ip"] == "192.168.1.1" assert hop1["hosts"][0]["rtt_ms"] == [1.234, 1.111, 1.050] # Hop 2: sin respuesta -> hosts vacio. assert hops[1]["hosts"] == [] # Hop 3: destino alcanzado. hop3 = hops[2] assert len(hop3["hosts"]) == 1 assert hop3["hosts"][0]["name"] == "one.one.one.one" assert hop3["hosts"][0]["ip"] == "1.1.1.1" assert hop3["hosts"][0]["rtt_ms"] == [9.876, 9.500, 9.700] def test_edge_hop_sin_respuesta(monkeypatch): """Un solo hop '* * *': hosts vacio para ese salto.""" _patch_run(monkeypatch, stdout=RAW_ALL_STARS) result = traceroute_host("10.0.0.1") assert result["status"] == "ok" assert len(result["hops"]) == 1 assert result["hops"][0]["hop"] == 1 assert result["hops"][0]["hosts"] == [] def test_error_host_vacio(monkeypatch): """Host en blanco: status error sin invocar subprocess.""" def boom(*args, **kwargs): raise AssertionError("subprocess.run no debe llamarse con host vacio") monkeypatch.setattr(tr_mod.subprocess, "run", boom) result = traceroute_host(" ") assert result["status"] == "error" assert "vacio" in result["error"]