"""Tests para grab_service_banner (sin red externa; servidor TCP local fake).""" import os import socket import socketserver import sys import threading sys.path.insert(0, os.path.dirname(__file__)) from grab_service_banner import grab_service_banner class _BannerHandler(socketserver.BaseRequestHandler): """Emite un banner SSH fake al conectar, como hace un servidor SSH real.""" def handle(self): try: self.request.sendall(b"SSH-2.0-TestServer\r\n") except OSError: pass def test_identifica_ssh_de_banner_local(): """Un servidor TCP local que emite 'SSH-2.0-...' se identifica como ssh.""" server = socketserver.TCPServer(("127.0.0.1", 0), _BannerHandler) # bind_and_activate por defecto ya hizo bind; tomamos el puerto efimero. host, port = server.server_address thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() try: result = grab_service_banner(host, port, timeout_s=2.0, send_probe=False) finally: server.shutdown() server.server_close() thread.join(timeout=2.0) assert result["status"] == "ok" assert result["service"] == "ssh" assert "SSH-2.0-TestServer" in result["banner"] assert result["product"] == "TestServer" or result["product"] # best-effort def test_host_vacio_devuelve_error(): """Un host vacio devuelve status error sin lanzar y sin tocar la red.""" result = grab_service_banner("", 22) assert result["status"] == "error" assert "vacio" in result["error"] assert set(["status", "error", "host", "port"]).issubset(result.keys()) def test_port_fuera_de_rango_devuelve_error(): """Un puerto fuera del rango 1..65535 devuelve status error sin conectar.""" result = grab_service_banner("127.0.0.1", 70000) assert result["status"] == "error" assert "rango" in result["error"] assert result["port"] == 70000