commit 11070d9a91e0c00b4b5854fc935c5fc7a871e97f Author: Damian Johnson atagar@torproject.org Date: Sat Sep 29 12:41:24 2012 -0700
Unit tests for minimal DirectoryAuthority instances
Adding unit tests for the minimal vote and consensus directory authority entry. Presently this is just exercising the old parsing code (which I broke in a few places during some of my earlier refactoring). --- run_tests.py | 2 + stem/descriptor/networkstatus.py | 29 +++++++----- test/mocking.py | 38 ++++++++++++++++ test/unit/descriptor/networkstatus/__init__.py | 2 +- .../networkstatus/directory_authority.py | 47 ++++++++++++++++++++ 5 files changed, 105 insertions(+), 13 deletions(-)
diff --git a/run_tests.py b/run_tests.py index 54e74b2..8f76dd3 100755 --- a/run_tests.py +++ b/run_tests.py @@ -21,6 +21,7 @@ import test.unit.descriptor.reader import test.unit.descriptor.server_descriptor import test.unit.descriptor.extrainfo_descriptor import test.unit.descriptor.networkstatus.entry +import test.unit.descriptor.networkstatus.directory_authority import test.unit.descriptor.networkstatus.key_certificate import test.unit.descriptor.networkstatus.document import test.unit.response.control_line @@ -118,6 +119,7 @@ UNIT_TESTS = ( test.unit.descriptor.server_descriptor.TestServerDescriptor, test.unit.descriptor.extrainfo_descriptor.TestExtraInfoDescriptor, test.unit.descriptor.networkstatus.entry.TestRouterStatusEntry, + test.unit.descriptor.networkstatus.directory_authority.TestDirectoryAuthority, test.unit.descriptor.networkstatus.key_certificate.TestKeyCertificate, test.unit.descriptor.networkstatus.document.TestNetworkStatusDocument, test.unit.exit_policy.rule.TestExitPolicyRule, diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index 7c95f96..8147a26 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -699,6 +699,7 @@ class DirectoryAuthority(stem.descriptor.Descriptor):
self.nickname = None self.fingerprint = None + self.hostname = None self.address = None self.dir_port = None self.or_port = None @@ -712,7 +713,7 @@ class DirectoryAuthority(stem.descriptor.Descriptor): self._unrecognized_lines = []
#self._parse(raw_contents, validate, is_vote) - self._parse_old(raw_contents, validate, is_vote) + self._parse_old(raw_content, validate, is_vote)
def _parse(self, content, validate, is_vote): """ @@ -753,26 +754,24 @@ class DirectoryAuthority(stem.descriptor.Descriptor): #if validate and len(values) > 1 and keyword in ('r', 's', 'v', 'w', 'p'): # raise ValueError("Router status entries can only have a single '%s' line, got %i:\n%s" % (key, len(values), content))
- def _parse_old(self, content, validate, is_vote): - self.nickname, self.fingerprint, self.address, self.ip = None, None, None, None - self.dir_port, self.or_port, self.legacy_dir_key = None, None, None - self.key_certificate, self.contact, self.vote_digest = None, None, None - + def _parse_old(self, raw_content, validate, is_vote): content = StringIO(raw_content) dir_source = _read_keyword_line("dir-source", content, validate) - self.nickname, self.fingerprint, self.address, self.ip, self.dir_port, self.or_port = dir_source.split(" ") + self.nickname, self.fingerprint, self.hostname, self.address, self.dir_port, self.or_port = dir_source.split(" ") self.dir_port = int(self.dir_port) self.or_port = int(self.or_port)
self.contact = _read_keyword_line("contact", content, validate) - if vote: + if is_vote: self.legacy_dir_key = _read_keyword_line("legacy-dir-key", content, validate, True) self.key_certificate = KeyCertificate(content.read(), validate) else: self.vote_digest = _read_keyword_line("vote-digest", content, True, validate) - self.unrecognized_lines = content.read() - if self.unrecognized_lines and validate: - raise ValueError("Unrecognized trailing data in directory authority information") + + remainder = content.read() + + if remainder: + self._unrecognized_lines = remainder.split("\n")
def get_unrecognized_lines(self): """ @@ -781,7 +780,7 @@ class DirectoryAuthority(stem.descriptor.Descriptor): :returns: a list of unrecognized lines """
- return self.unrecognized_lines + return self._unrecognized_lines
class KeyCertificate(stem.descriptor.Descriptor): """ @@ -931,6 +930,12 @@ class KeyCertificate(stem.descriptor.Descriptor): """
return self._unrecognized_lines + + def __cmp__(self, other): + if not isinstance(other, KeyCertificate): + return 1 + + return str(self) > str(other)
# TODO: microdescriptors have a slightly different format (including a # 'method') - should probably be a subclass diff --git a/test/mocking.py b/test/mocking.py index 7008a2a..89ea2f6 100644 --- a/test/mocking.py +++ b/test/mocking.py @@ -28,7 +28,9 @@ calling :func:`test.mocking.revert_mocking`. get_relay_extrainfo_descriptor - stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor get_bridge_extrainfo_descriptor - stem.descriptor.extrainfo_descriptor.BridgeExtraInfoDescriptor get_router_status_entry - stem.descriptor.networkstatus.RouterStatusEntry + get_directory_authority - stem.descriptor.networkstatus.DirectoryAuthority get_key_certificate - stem.descriptor.networkstatus.KeyCertificate + get_network_status_document - stem.descriptor.networkstatus.NetworkStatusDocument """
import inspect @@ -109,6 +111,11 @@ ROUTER_STATUS_ENTRY_HEADER = ( ("s", "Fast Named Running Stable Valid"), )
+AUTHORITY_HEADER = ( + ("dir-source", "turtles 27B6B5996C426270A5C95488AA5BCEB6BCC86956 no.place.com 76.73.17.194 9030 9090"), + ("contact", "Mike Perry <email>"), +) + KEY_CERTIFICATE_HEADER = ( ("dir-key-certificate-version", "3"), ("fingerprint", "27B6B5996C426270A5C95488AA5BCEB6BCC86956"), @@ -534,6 +541,37 @@ def get_router_status_entry(attr = None, exclude = (), content = False): else: return stem.descriptor.networkstatus.RouterStatusEntry(desc_content, validate = True)
+def get_directory_authority(attr = None, exclude = (), is_vote = False, content = False): + """ + Provides the descriptor content for... + stem.descriptor.networkstatus.DirectoryAuthority + + :param dict attr: keyword/value mappings to be included in the descriptor + :param list exclude: mandatory keywords to exclude from the descriptor + :param bool is_vote: True if this is for a vote, False if it's for a consensus + :param bool content: provides the str content of the descriptor rather than the class if True + + :returns: DirectoryAuthority for the requested descriptor content + """ + + if attr is None: + attr = {} + + if not is_vote: + # entries from a consensus also have a mandatory 'vote-digest' field + if not ('vote-digest' in attr or (exclude and 'vote-digest' in exclude)): + attr['vote-digest'] = '0B6D1E9A300B895AA2D0B427F92917B6995C3C1C' + + desc_content = _get_descriptor_content(attr, exclude, AUTHORITY_HEADER) + + if is_vote: + desc_content += "\n" + str(get_key_certificate()) + + if content: + return desc_content + else: + return stem.descriptor.networkstatus.DirectoryAuthority(desc_content, validate = True, is_vote = is_vote) + def get_key_certificate(attr = None, exclude = (), content = False): """ Provides the descriptor content for... diff --git a/test/unit/descriptor/networkstatus/__init__.py b/test/unit/descriptor/networkstatus/__init__.py index d8c9657..b2314cc 100644 --- a/test/unit/descriptor/networkstatus/__init__.py +++ b/test/unit/descriptor/networkstatus/__init__.py @@ -2,5 +2,5 @@ Unit tests for stem.descriptor.networkstatus. """
-__all__ = ["entry", "key_certificate", "document"] +__all__ = ["entry", "directory_authority", "key_certificate", "document"]
diff --git a/test/unit/descriptor/networkstatus/directory_authority.py b/test/unit/descriptor/networkstatus/directory_authority.py new file mode 100644 index 0000000..dd4c5fa --- /dev/null +++ b/test/unit/descriptor/networkstatus/directory_authority.py @@ -0,0 +1,47 @@ +""" +Unit tests for the DirectoryAuthority of stem.descriptor.networkstatus. +""" + +import unittest + +from test.mocking import get_directory_authority, get_key_certificate + +class TestDirectoryAuthority(unittest.TestCase): + def test_minimal_consensus_authority(self): + """ + Parses a minimal directory authority for a consensus. + """ + + authority = get_directory_authority() + + self.assertEqual("turtles", authority.nickname) + self.assertEqual("27B6B5996C426270A5C95488AA5BCEB6BCC86956", authority.fingerprint) + self.assertEqual("no.place.com", authority.hostname) + self.assertEqual("76.73.17.194", authority.address) + self.assertEqual(9030, authority.dir_port) + self.assertEqual(9090, authority.or_port) + self.assertEqual("Mike Perry <email>", authority.contact) + self.assertEqual("0B6D1E9A300B895AA2D0B427F92917B6995C3C1C", authority.vote_digest) + self.assertEqual(None, authority.legacy_dir_key) + self.assertEqual(None, authority.key_certificate) + self.assertEqual([], authority.get_unrecognized_lines()) + + def test_minimal_vote_authority(self): + """ + Parses a minimal directory authority for a vote. + """ + + authority = get_directory_authority(is_vote = True) + + self.assertEqual("turtles", authority.nickname) + self.assertEqual("27B6B5996C426270A5C95488AA5BCEB6BCC86956", authority.fingerprint) + self.assertEqual("no.place.com", authority.hostname) + self.assertEqual("76.73.17.194", authority.address) + self.assertEqual(9030, authority.dir_port) + self.assertEqual(9090, authority.or_port) + self.assertEqual("Mike Perry <email>", authority.contact) + self.assertEqual(None, authority.vote_digest) + self.assertEqual(None, authority.legacy_dir_key) + self.assertEqual(get_key_certificate(), authority.key_certificate) + self.assertEqual([], authority.get_unrecognized_lines()) +