]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
improve test coverage; discover bugs; fix bugs!
authorBob Halley <halley@dnspython.org>
Fri, 1 Aug 2025 01:08:43 +0000 (18:08 -0700)
committerBob Halley <halley@dnspython.org>
Fri, 1 Aug 2025 01:08:43 +0000 (18:08 -0700)
dns/btreezone.py
dns/zone.py
tests/test_btreezone.py [new file with mode: 0644]

index a96b27ea060d38f0905310f4ae90656d62fe3d9b..c749b4ad04934f5a43def8d66e24f02cb7a2130b 100644 (file)
@@ -151,13 +151,24 @@ class WritableVersion(dns.zone.WritableVersion):
         else:
             self.delegations = Delegations()
 
-    def _maybe_cow(self, name: dns.name.Name) -> dns.node.Node:
-        node = super()._maybe_cow(name)
-        if name == self.zone.origin:
-            node.flags |= NodeFlags.ORIGIN  # type: ignore
+    def _is_origin(self, name: dns.name.Name) -> bool:
+        # Assumes name has already been validated (and thus adjusted to the right
+        # relativity too)
+        if self.zone.relativize:
+            return name == dns.name.empty
+        else:
+            return name == self.zone.origin
+
+    def _maybe_cow_with_name(
+        self, name: dns.name.Name
+    ) -> Tuple[dns.node.Node, dns.name.Name]:
+        (node, name) = super()._maybe_cow_with_name(name)
+        node = cast(Node, node)
+        if self._is_origin(name):
+            node.flags |= NodeFlags.ORIGIN
         elif self.delegations.is_glue(name):
-            node.flags |= NodeFlags.GLUE  # type: ignore
-        return node
+            node.flags |= NodeFlags.GLUE
+        return (node, name)
 
     def update_glue_flag(self, name: dns.name.Name, is_glue: bool) -> None:
         cursor = self.nodes.cursor()  # type: ignore
@@ -203,7 +214,7 @@ class WritableVersion(dns.zone.WritableVersion):
     def put_rdataset(
         self, name: dns.name.Name, rdataset: dns.rdataset.Rdataset
     ) -> None:
-        node = self._maybe_cow(name)
+        (node, name) = self._maybe_cow_with_name(name)
         if (
             rdataset.rdtype == dns.rdatatype.NS and not node.is_origin_or_glue()  # type: ignore
         ):
@@ -219,7 +230,7 @@ class WritableVersion(dns.zone.WritableVersion):
         rdtype: dns.rdatatype.RdataType,
         covers: dns.rdatatype.RdataType,
     ) -> None:
-        node = self._maybe_cow(name)
+        (node, name) = self._maybe_cow_with_name(name)
         if rdtype == dns.rdatatype.NS and name in self.delegations:  # type: ignore
             node.flags &= ~NodeFlags.DELEGATION  # type: ignore
             self.delegations.discard(name)  # type: ignore
index cfb89ec40b88e42d008e9fee891304407c95cc42..05170fe8f02bd2dd6480745b34003614dcff802e 100644 (file)
@@ -1028,7 +1028,9 @@ class WritableVersion(Version):
         self.origin = zone.origin
         self.changed: Set[dns.name.Name] = set()
 
-    def _maybe_cow(self, name: dns.name.Name) -> dns.node.Node:
+    def _maybe_cow_with_name(
+        self, name: dns.name.Name
+    ) -> Tuple[dns.node.Node, dns.name.Name]:
         name = self._validate_name(name)
         node = self.nodes.get(name)
         if node is None or name not in self.changed:
@@ -1046,9 +1048,12 @@ class WritableVersion(Version):
                 new_node.rdatasets.extend(node.rdatasets)
             self.nodes[name] = new_node
             self.changed.add(name)
-            return new_node
+            return (new_node, name)
         else:
-            return node
+            return (node, name)
+
+    def _maybe_cow(self, name: dns.name.Name) -> dns.node.Node:
+        return self._maybe_cow_with_name(name)[0]
 
     def delete_node(self, name: dns.name.Name) -> None:
         name = self._validate_name(name)
diff --git a/tests/test_btreezone.py b/tests/test_btreezone.py
new file mode 100644 (file)
index 0000000..f2f5e7d
--- /dev/null
@@ -0,0 +1,115 @@
+from typing import cast
+
+import dns.btreezone
+import dns.rdataset
+import dns.zone
+
+Node = dns.btreezone.Node
+
+simple_zone = """
+$ORIGIN example.
+$TTL 300
+@ soa foo bar 1 2 3 4 5
+@ ns ns1
+@ ns ns2
+ns1 a 10.0.0.1
+ns2 a 10.0.0.2
+sub ns ns1.sub
+sub ns ns2.sub
+ns1.sub a 10.0.0.3
+ns2.sub a 10.0.0.4
+ns1.sub2 a 10.0.0.5
+ns2.sub2 a 10.0.0.6
+text txt "here to be after sub2"
+"""
+
+
+def make_example(text: str, relativize: bool = False) -> dns.btreezone.Zone:
+    z = dns.zone.from_text(
+        simple_zone, "example.", relativize=relativize, zone_factory=dns.btreezone.Zone
+    )
+    return cast(dns.btreezone.Zone, z)
+
+
+def do_test_node_flags(relativize: bool):
+    z = make_example(simple_zone, relativize)
+    n = cast(Node, z.get_node("@"))
+    assert not n.is_delegation()
+    assert not n.is_glue()
+    assert n.is_origin()
+    assert n.is_origin_or_glue()
+    assert n.is_immutable()
+    n = cast(Node, z.get_node("sub"))
+    assert n.is_delegation()
+    assert not n.is_glue()
+    assert not n.is_origin()
+    assert not n.is_origin_or_glue()
+    n = cast(Node, z.get_node("ns1.sub"))
+    assert not n.is_delegation()
+    assert n.is_glue()
+    assert not n.is_origin()
+    assert n.is_origin_or_glue()
+
+
+def test_node_flags_absolute():
+    do_test_node_flags(False)
+
+
+def test_node_flags_relative():
+    do_test_node_flags(True)
+
+
+def test_flags_in_constructor():
+    n = Node()
+    assert n.flags == 0
+    n = Node(dns.btreezone.NodeFlags.ORIGIN)
+    assert n.is_origin()
+
+
+def do_test_obscure_and_expose(relativize: bool):
+    z = make_example(simple_zone, relativize=relativize)
+    n = cast(Node, z.get_node("ns1.sub2"))
+    assert not n.is_delegation()
+    assert not n.is_glue()
+    assert not n.is_origin()
+    assert not n.is_origin_or_glue()
+    rds = dns.rdataset.from_text("in", "ns", 300, "ns1.sub2", "ns2.sub2")
+    with z.writer() as txn:
+        txn.replace("sub2", rds)
+    n = cast(Node, z.get_node("ns1.sub2"))
+    assert not n.is_delegation()
+    assert n.is_glue()
+    assert not n.is_origin()
+    assert n.is_origin_or_glue()
+    with z.writer() as txn:
+        txn.delete("sub2")
+        txn.delete("ns2.sub2")  # for other coverage purposes!
+    n = cast(Node, z.get_node("ns1.sub2"))
+    assert not n.is_delegation()
+    assert not n.is_glue()
+    assert not n.is_origin()
+    assert not n.is_origin_or_glue()
+    # repeat but delete just the rdataset
+    rds = dns.rdataset.from_text("in", "ns", 300, "ns1.sub2", "ns2.sub2")
+    with z.writer() as txn:
+        txn.replace("sub2", rds)
+    n = cast(Node, z.get_node("ns1.sub2"))
+    assert not n.is_delegation()
+    assert n.is_glue()
+    assert not n.is_origin()
+    assert n.is_origin_or_glue()
+    with z.writer() as txn:
+        txn.delete("sub2", "NS")
+    n = cast(Node, z.get_node("ns1.sub2"))
+    assert not n.is_delegation()
+    assert not n.is_glue()
+    assert not n.is_origin()
+    assert not n.is_origin_or_glue()
+
+
+def test_obscure_and_expose_absolute():
+    do_test_obscure_and_expose(False)
+
+
+def test_obscure_and_expose_relative():
+    do_test_obscure_and_expose(True)