122 lines
4.4 KiB
Python
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()
|