aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-10 11:14:21 +0200
committerDanilo M. <danix@danix.xyz>2026-09-10 11:14:21 +0200
commitf2fea43813ccebb1cf60e8afa5d4c7cdc52d0a20 (patch)
tree7988c9c9222bfbc28c469b68b079451df98297dc
parent45a68fbbe8da80a7bdf399b2e042f1982ca6a69d (diff)
downloadabusectl-f2fea43813ccebb1cf60e8afa5d4c7cdc52d0a20.tar.gz
abusectl-f2fea43813ccebb1cf60e8afa5d4c7cdc52d0a20.zip
test: cover report.py in the no-socket proof
The offline test enumerates modules by import and report was absent, so the module that writes documents naming hosts and addresses from a message was outside the second property's only actual verification. Exercised through generate() against a real case directory rather than build() alone, so the disk path is covered too. Confirmed the test can fail: a getaddrinfo planted in build() makes it fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LByBnw83xr9YP85nskzkyE
-rw-r--r--tests/test_offline.py30
1 files changed, 29 insertions, 1 deletions
diff --git a/tests/test_offline.py b/tests/test_offline.py
index 547bd96..7866950 100644
--- a/tests/test_offline.py
+++ b/tests/test_offline.py
@@ -20,11 +20,13 @@ this asserts that the default is never reached by accident during a test
run: parse stays pure, and nothing above it opens a socket unasked.
"""
+import pathlib
import socket
+import tempfile
import unittest
from unittest import mock
-from abusectl import contacts, parse, rdap
+from abusectl import case, contacts, parse, rdap, report
class NothingOpensASocket(unittest.TestCase):
@@ -82,6 +84,32 @@ class NothingOpensASocket(unittest.TestCase):
self.assertEqual(len(result), 1)
self.assertEqual(result[0]["handle"], "NET-1")
+ def test_generating_report_bodies_opens_no_socket(self):
+ # report writes documents naming hosts and addresses from the
+ # message. Nothing in that resolves any of them: the destinations
+ # come from the manifest contacts already produced, and a body is
+ # assembled from text. Written to a real case directory so the
+ # disk path is covered too, not just build().
+ raw = (b"Received: from relay.example.invalid ([192.0.2.10])\r\n"
+ b"From: sender@example.invalid\r\n"
+ b"To: victim@example.org\r\n"
+ b"Subject: test\r\n\r\nhttp://phish.example.invalid/a\r\n")
+ with tempfile.TemporaryDirectory() as tmp:
+ path = case.create(pathlib.Path(tmp), raw).path
+ manifest = case.load(path)
+ manifest["iocs"] = parse.iocs(raw, trusted=["192.0.2.0/24"])
+ manifest["contacts"] = [{
+ "iocs": [manifest["iocs"][0]["id"]],
+ "query": "phish.example.invalid",
+ "abuse": ["abuse@example.invalid"],
+ "source": "rdap",
+ }]
+ result = report.generate(
+ manifest, path,
+ {"name": "A Reporter", "email": "r@example.org"},
+ )
+ self.assertEqual(len(result["destinations"]), 1)
+
def test_the_real_transport_is_never_the_default_in_a_test(self):
"""Sanity: http_fetch exists and is the documented default."""
self.assertIs(contacts.resolve.__defaults__[-1], rdap.http_fetch)