Files
network-v1/tests/test_sync_iana_ipv4.py
T

122 lines
4.4 KiB
Python

from __future__ import annotations
import csv
import importlib.util
import io
import tempfile
import unittest
from ipaddress import IPv4Network
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MODULE_PATH = ROOT / "scripts" / "sync_iana_ipv4.py"
def load_module():
spec = importlib.util.spec_from_file_location("sync_iana_ipv4", MODULE_PATH)
if spec is None or spec.loader is None:
raise RuntimeError("cannot load sync_iana_ipv4")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
class SyncIanaIPv4Tests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.sync = load_module()
def test_top_level_prefix_is_normalized(self):
self.assertEqual(
self.sync.top_level_network("001/8"),
IPv4Network("1.0.0.0/8"),
)
def test_registry_slug_uses_rdap_authority(self):
self.assertEqual(
self.sync.registry_slug("APNIC", "https://rdap.apnic.net/"),
"apnic",
)
self.assertEqual(
self.sync.registry_slug(
"Legacy allocation",
"https://rdap.arin.net/registryhttp://rdap.arin.net/registry",
),
"arin",
)
self.assertEqual(self.sync.registry_slug("IANA - Reserved", ""), "iana")
def test_rdap_url_concatenation_is_normalized(self):
self.assertEqual(
self.sync.clean_rdap("https://rdap.arin.net/registryhttp://rdap.arin.net/registry"),
"https://rdap.arin.net/registry",
)
self.assertEqual(self.sync.clean_rdap("https://rdap.apnic.net/"), "https://rdap.apnic.net/")
def test_filename_sorts_numerically_in_lexical_order(self):
self.assertEqual(
self.sync.range_filename(IPv4Network("1.2.3.0/24"), "apnic"),
"001-002-003-000--24--apnic.md",
)
def test_multiple_special_blocks_are_expanded(self):
self.assertEqual(
self.sync.expand_address_blocks("192.0.0.170/32, 192.0.0.171/32"),
[IPv4Network("192.0.0.170/32"), IPv4Network("192.0.0.171/32")],
)
def test_special_block_footnote_is_ignored_for_parsing(self):
self.assertEqual(
self.sync.expand_address_blocks("192.0.0.0/24 [2]"),
[IPv4Network("192.0.0.0/24")],
)
def test_source_text_whitespace_is_normalized(self):
self.assertEqual(self.sync.clean_name('"[RFC8190]\n [RFC919], Section 7"'), "[RFC8190] [RFC919], Section 7")
def test_parse_top_level_requires_complete_ipv4_coverage(self):
output = io.StringIO()
writer = csv.DictWriter(
output,
fieldnames=["Prefix", "Designation", "Date", "WHOIS", "RDAP", "Status [1]", "Note"],
)
writer.writeheader()
for first in range(256):
writer.writerow({
"Prefix": f"{first:03d}/8",
"Designation": "Example",
"Date": "2026-01",
"WHOIS": "",
"RDAP": "",
"Status [1]": "RESERVED",
"Note": "",
})
rows = self.sync.parse_top_level_csv(output.getvalue())
self.assertEqual(len(rows), 256)
self.assertEqual(rows[0]["network"], IPv4Network("0.0.0.0/8"))
self.assertEqual(rows[-1]["network"], IPv4Network("255.0.0.0/8"))
def test_parse_top_level_rejects_missing_slash_eight(self):
sample = "Prefix,Designation,Date,WHOIS,RDAP,Status [1],Note\n000/8,IANA,1981-09,,,RESERVED,\n"
with self.assertRaisesRegex(ValueError, "256"):
self.sync.parse_top_level_csv(sample)
def test_manual_range_file_is_not_overwritten(self):
with tempfile.TemporaryDirectory() as temporary:
directory = Path(temporary)
path = directory / "001-000-000-000--08--apnic.md"
path.write_text("# hand researched\n", encoding="utf-8")
changed = self.sync.write_generated(path, "# generated\n")
self.assertFalse(changed)
self.assertEqual(path.read_text(encoding="utf-8"), "# hand researched\n")
def test_generated_csv_uses_lf_line_endings(self):
with tempfile.TemporaryDirectory() as temporary:
path = Path(temporary) / "ranges.csv"
self.sync.write_csv(path, ["prefix"], [{"prefix": "1.0.0.0/8"}])
self.assertNotIn(b"\r", path.read_bytes())
if __name__ == "__main__":
unittest.main()