"""Tests para el pipeline recon_osint — SIN red ni service real. Las funciones de escaneo (whois_lookup, nmap_scan, ...) y el sink save_scan_to_osint se importan en el namespace del modulo del pipeline con ``from cybersecurity import (...)``. Para aislarlos de la red/disco los parcheamos sobre los globals del propio modulo via importlib + monkeypatch. Los tests usan kwargs minimos (target + scan_type + save) a proposito: la firma del pipeline puede ampliarse en paralelo (p.ej. con un parametro ``confirm``) sin que estos tests dejen de pasar. """ import importlib import os import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..")) # Globals del modulo del pipeline (donde viven whois_lookup, save_scan_to_osint...). mod = importlib.import_module("pipelines.recon_osint") recon_osint = mod.recon_osint def test_golden_whois_save_true_invoca_scan_y_sink(monkeypatch): """scan_type='whois', save=True: ejecuta whois_lookup y archiva con su raw.""" fake_scan = { "status": "ok", "target": "example.com", "registrar": "Acme Registrar", "expiry_date": "2028-09-14T04:00:00Z", "raw": "Domain Name: EXAMPLE.COM\nRegistrar: Acme Registrar\n", } calls = {} def fake_whois(target, **kwargs): calls["whois_target"] = target return fake_scan def fake_save(target, scan_type, raw, **kwargs): calls["save"] = { "target": target, "scan_type": scan_type, "raw": raw, "kwargs": kwargs, } return { "status": "ok", "note_path": "dominios/example.com/recon/whois-20260614-1200.md", "registered": True, "scan_id": "scan-1", } monkeypatch.setattr(mod, "whois_lookup", fake_whois) monkeypatch.setattr(mod, "save_scan_to_osint", fake_save) result = recon_osint("example.com", scan_type="whois", save=True) assert result["status"] == "ok" assert result["scan_type"] == "whois" assert result["target"] == "example.com" # Devuelve el dict crudo del scan. assert result["scan"] == fake_scan # Devuelve los datos de archivado del sink. assert result["osint"]["registered"] is True assert result["osint"]["scan_id"] == "scan-1" # whois_lookup recibio el target. assert calls["whois_target"] == "example.com" # save_scan_to_osint fue invocado con el raw del scan. assert "save" in calls assert calls["save"]["raw"] == fake_scan["raw"] assert calls["save"]["target"] == "example.com" assert calls["save"]["scan_type"] == "whois" def test_save_false_ejecuta_scan_sin_archivar(monkeypatch): """save=False: corre el scan pero NO llama save_scan_to_osint.""" fake_scan = { "status": "ok", "target": "example.com", "registrar": "Acme", "raw": "Domain Name: EXAMPLE.COM\n", } save_called = {"n": 0} monkeypatch.setattr(mod, "whois_lookup", lambda target, **kw: fake_scan) def fake_save(*args, **kwargs): # pragma: no cover - no debe llamarse save_called["n"] += 1 return {"status": "ok"} monkeypatch.setattr(mod, "save_scan_to_osint", fake_save) result = recon_osint("example.com", scan_type="whois", save=False) assert result["status"] == "ok" assert result["scan"] == fake_scan # Sin archivado: osint es None y el sink nunca se invoco. assert result["osint"] is None assert save_called["n"] == 0 def test_scan_type_invalido_error_sin_red(monkeypatch): """scan_type desconocido: status error sin invocar ninguna funcion de scan/sink.""" # Centinelas que petan si se invocan: el pipeline no debe tocar nada. def explode(*args, **kwargs): # pragma: no cover - no debe llamarse raise AssertionError("no debe ejecutarse scan ni sink con scan_type invalido") monkeypatch.setattr(mod, "whois_lookup", explode) monkeypatch.setattr(mod, "nmap_scan", explode) monkeypatch.setattr(mod, "save_scan_to_osint", explode) result = recon_osint("example.com", scan_type="bogus", save=True) assert result["status"] == "error" assert result["stage"] == "validate" assert result["scan_type"] == "bogus" assert "valid" in result and isinstance(result["valid"], list) def test_scan_fallido_no_intenta_archivar(monkeypatch): """Si el escaneo devuelve status error, no se llama al sink.""" save_called = {"n": 0} monkeypatch.setattr( mod, "whois_lookup", lambda target, **kw: {"status": "error", "error": "timeout"}, ) def fake_save(*args, **kwargs): # pragma: no cover - no debe llamarse save_called["n"] += 1 return {} monkeypatch.setattr(mod, "save_scan_to_osint", fake_save) result = recon_osint("example.com", scan_type="whois", save=True) assert result["status"] == "error" assert result["stage"] == "scan" assert save_called["n"] == 0