research: index complete IANA IPv4 range baseline
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user