From f2fea43813ccebb1cf60e8afa5d4c7cdc52d0a20 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Thu, 10 Sep 2026 11:14:21 +0200 Subject: 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 Claude-Session: https://claude.ai/code/session_01LByBnw83xr9YP85nskzkyE --- tests/test_offline.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) 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) -- cgit v1.2.3