From b7f0213565ab82c955f16aea5454293d7f151a74 Mon Sep 17 00:00:00 2001
From: Jean-Paul Calderone <exarkun@twistedmatrix.com>
Date: Mon, 16 Dec 2019 15:10:39 -0500
Subject: [PATCH] [wip] steps towards testing the node traversal behavior

---
 src/_zkapauthorizer/lease_maintenance.py      |  7 +++
 src/_zkapauthorizer/tests/strategies.py       | 54 +++++++++++++++++++
 .../tests/test_lease_maintenance.py           | 43 +++++++++++++--
 3 files changed, 101 insertions(+), 3 deletions(-)

diff --git a/src/_zkapauthorizer/lease_maintenance.py b/src/_zkapauthorizer/lease_maintenance.py
index dfb251d..cf81a15 100644
--- a/src/_zkapauthorizer/lease_maintenance.py
+++ b/src/_zkapauthorizer/lease_maintenance.py
@@ -327,6 +327,10 @@ def lease_maintenance_service(
 
 def maintain_leases_from_root(root_node, storage_broker, secret_holder, min_lease_remaining, get_now):
     """
+    An operation for ``lease_maintenance_service`` which visits ``root_node``
+    and all its children and renews their leases if they have
+    ``min_lease_remaining`` or less on them.
+
     :param IFilesystemNode root_node: A Tahoe-LAFS filesystem node to use as
         the root of a node hierarchy to be maintained.
 
@@ -338,6 +342,9 @@ def maintain_leases_from_root(root_node, storage_broker, secret_holder, min_leas
         holder which can give us the lease renewal secrets needed to renew
         leases.
 
+    :param timedelta min_lease_remaining: The minimum amount of time remaining
+        to allow on a lease without renewing it.
+
     :param get_now: A no-argument callable that returns the current time as a
         ``datetime`` instance.
     """
diff --git a/src/_zkapauthorizer/tests/strategies.py b/src/_zkapauthorizer/tests/strategies.py
index f4d1ce5..4011e8c 100644
--- a/src/_zkapauthorizer/tests/strategies.py
+++ b/src/_zkapauthorizer/tests/strategies.py
@@ -583,3 +583,57 @@ def clocks(now=posix_safe_datetimes()):
         c.advance((when - _POSIX_EPOCH).total_seconds())
         return c
     return now.map(clock_at_time)
+
+
+
+
+@implementer(IFilesystemNode)
+@attr.ib
+class _LeafNode(object):
+    _storage_index = attr.ib()
+
+    def get_storage_index(self):
+        return self._storage_index
+
+
+def leaf_nodes():
+    return storage_indexes().map(_LeafNode)
+
+
+@implementer(IDirectoryNode)
+@attr.s
+class _DirectoryNode(object):
+    _storage_index = attr.ib()
+    _children = attr.ib()
+
+    def list(self):
+        return succeed(self._children)
+
+
+def directory_nodes(child_strategy):
+    """
+    Build directory nodes with children drawn from the given strategy.
+    """
+    children = dictionaries(
+        text(),
+        tuples(
+            child_strategy,
+            just({}),
+        ),
+    )
+    return builds(
+        _DirectoryNode,
+        storage_indexes(),
+        children,
+    )
+
+
+def node_hierarchies():
+    """
+    Build hierarchies of ``IDirectoryNode`` and other ``IFilesystemNode``
+    (incomplete) providers.
+    """
+    return recursive(
+        leaf_nodes(),
+        directory_nodes,
+    )
diff --git a/src/_zkapauthorizer/tests/test_lease_maintenance.py b/src/_zkapauthorizer/tests/test_lease_maintenance.py
index 670fd47..23f736b 100644
--- a/src/_zkapauthorizer/tests/test_lease_maintenance.py
+++ b/src/_zkapauthorizer/tests/test_lease_maintenance.py
@@ -34,6 +34,9 @@ from testtools import (
 from testtools.matchers import (
     Equals,
 )
+from testtools.twistedsupport import (
+    succeeds,
+)
 from hypothesis import (
     given,
     note,
@@ -61,9 +64,9 @@ from twisted.application.service import (
 from allmydata.util.hashutil import (
     CRYPTO_VAL_SIZE,
 )
-# from allmydata.client import (
-#     SecretHolder,
-# )
+from allmydata.client import (
+    SecretHolder,
+)
 
 from ..foolscap import (
     ShareStat,
@@ -76,10 +79,12 @@ from .matchers import (
 from .strategies import (
     storage_indexes,
     clocks,
+    node_hierarchies,
 )
 
 from ..lease_maintenance import (
     lease_maintenance_service,
+    maintain_leases_from_root,
 )
 
 
@@ -305,3 +310,35 @@ class LeaseMaintenanceServiceTests(TestCase):
             leases_maintained_at,
             Equals([datetime.utcfromtimestamp(clock.seconds())]),
         )
+
+
+class MaintainLeasesFromRootTests(TestCase):
+    """
+    Tests for ``maintain_leases_from_root``.
+    """
+    @given(node_hierarchies(), clocks())
+    def test_visits_all_nodes(self, root_node, clock):
+        """
+        The operation calls the specified visitor with every node from the root to
+        its deepest children.
+        """
+        visited = []
+        def visitor(node):
+            visited.append(node)
+
+        storage_broker = DummyStorageBroker(clock, [])
+        secret_holder = SecretHolder(lease_secret, convergence_secret)
+
+        operation = maintain_leases_from_root(
+            visitor,
+            root_node,
+            storage_broker,
+            secret_holder,
+            timedelta(days=3),
+            lambda: datetime.utcfromtimestamp(clock.seconds()),
+        )
+
+        self.assertThat(
+            operation(root_node),
+            succeeds(Always()),
+        )
-- 
GitLab