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