commit 72679be355dfcde7513040b336d6c1824ea3d853 Author: Damian Johnson atagar@torproject.org Date: Mon May 1 16:56:54 2017 -0700
NetworkStatusDocument creation --- stem/descriptor/__init__.py | 4 +- stem/descriptor/networkstatus.py | 149 +++++++++++ test/mocking.py | 296 --------------------- .../networkstatus/directory_authority.py | 43 +-- test/unit/descriptor/networkstatus/document_v2.py | 12 +- test/unit/descriptor/networkstatus/document_v3.py | 145 +++++----- .../descriptor/networkstatus/key_certificate.py | 47 ++-- test/unit/tutorial.py | 10 +- test/unit/tutorial_examples.py | 10 +- 9 files changed, 283 insertions(+), 433 deletions(-)
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py index c414a4c..1826b36 100644 --- a/stem/descriptor/__init__.py +++ b/stem/descriptor/__init__.py @@ -383,7 +383,9 @@ def _descriptor_content(attr = None, exclude = (), header_template = (), footer_
value = attr.pop(keyword, value)
- if not value: + if value is None: + continue + elif value == '': content.append(keyword) elif value.startswith('\n'): # some values like crypto follow the line instead diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index 2b00ebc..ff9f105 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -63,9 +63,11 @@ import stem.util.tor_tools import stem.version
from stem.descriptor import ( + CRYPTO_BLOB, PGP_BLOCK_END, Descriptor, DocumentHandler, + _descriptor_content, _descriptor_components, _read_until_keywords, _value, @@ -206,6 +208,57 @@ PARAM_RANGE = { 'AuthDirNumSRVAgreements': (1, MAX_PARAM), }
+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'), + ('dir-key-published', '2011-11-28 21:51:04'), + ('dir-key-expires', '2012-11-28 21:51:04'), + ('dir-identity-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB), + ('dir-signing-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB), +) + +KEY_CERTIFICATE_FOOTER = ( + ('dir-key-certification', '\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB), +) + +NETWORK_STATUS_DOCUMENT_HEADER_V2 = ( + ('network-status-version', '2'), + ('dir-source', '18.244.0.114 18.244.0.114 80'), + ('fingerprint', '719BE45DE224B607C53707D0E2143E2D423E74CF'), + ('contact', 'arma at mit dot edu'), + ('published', '2005-12-16 00:13:46'), + ('dir-signing-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB), +) + +NETWORK_STATUS_DOCUMENT_FOOTER_V2 = ( + ('directory-signature', 'moria2\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB), +) + +NETWORK_STATUS_DOCUMENT_HEADER = ( + ('network-status-version', '3'), + ('vote-status', 'consensus'), + ('consensus-methods', None), + ('consensus-method', None), + ('published', None), + ('valid-after', '2012-09-02 22:00:00'), + ('fresh-until', '2012-09-02 22:00:00'), + ('valid-until', '2012-09-02 22:00:00'), + ('voting-delay', '300 300'), + ('client-versions', None), + ('server-versions', None), + ('package', None), + ('known-flags', 'Authority BadExit Exit Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid'), + ('params', None), +) + +VOTE_HEADER_DEFAULTS = {'consensus-methods': '1 9', 'published': '2012-09-02 22:00:00'} +CONSENSUS_HEADER_DEFAULTS = {'consensus-method': '9'} +
class PackageVersion(collections.namedtuple('PackageVersion', ['name', 'version', 'url', 'digests'])): """ @@ -449,6 +502,10 @@ class NetworkStatusDocumentV2(NetworkStatusDocument): 'directory-signature': _parse_directory_signature_line, }
+ @classmethod + def content(cls, attr = None, exclude = ()): + return _descriptor_content(attr, exclude, NETWORK_STATUS_DOCUMENT_HEADER_V2, NETWORK_STATUS_DOCUMENT_FOOTER_V2) + def __init__(self, raw_content, validate = False): super(NetworkStatusDocumentV2, self).__init__(raw_content, lazy_load = not validate)
@@ -893,6 +950,60 @@ class NetworkStatusDocumentV3(NetworkStatusDocument): 'directory-signature': _parse_footer_directory_signature_line, }
+ @classmethod + def content(cls, attr = None, exclude = (), authorities = None, routers = None): + attr = {} if attr is None else dict(attr) + + is_vote = attr.get('vote-status') == 'vote' + extra_defaults = VOTE_HEADER_DEFAULTS if is_vote else CONSENSUS_HEADER_DEFAULTS + + if is_vote and authorities is None: + authorities = [DirectoryAuthority.create(is_vote = is_vote)] + + for k, v in extra_defaults.items(): + if exclude and k in exclude: + continue # explicitly excluding this field + elif k not in attr: + attr[k] = v + + desc_content = _descriptor_content(attr, exclude, NETWORK_STATUS_DOCUMENT_HEADER, NETWORK_STATUS_DOCUMENT_FOOTER) + + # inject the authorities and/or routers between the header and footer + + if authorities: + if b'directory-footer' in desc_content: + footer_div = desc_content.find(b'\ndirectory-footer') + 1 + elif b'directory-signature' in desc_content: + footer_div = desc_content.find(b'\ndirectory-signature') + 1 + else: + if routers: + desc_content += b'\n' + + footer_div = len(desc_content) + 1 + + authority_content = stem.util.str_tools._to_bytes('\n'.join([str(a) for a in authorities]) + '\n') + desc_content = desc_content[:footer_div] + authority_content + desc_content[footer_div:] + + if routers: + if b'directory-footer' in desc_content: + footer_div = desc_content.find(b'\ndirectory-footer') + 1 + elif b'directory-signature' in desc_content: + footer_div = desc_content.find(b'\ndirectory-signature') + 1 + else: + if routers: + desc_content += b'\n' + + footer_div = len(desc_content) + 1 + + router_content = stem.util.str_tools._to_bytes('\n'.join([str(r) for r in routers]) + '\n') + desc_content = desc_content[:footer_div] + router_content + desc_content[footer_div:] + + return desc_content + + @classmethod + def create(cls, attr = None, exclude = (), validate = True, authorities = None, routers = None): + return cls(cls.content(attr, exclude, authorities, routers), validate = validate) + def __init__(self, raw_content, validate = False, default_params = True): """ Parse a v3 network status document. @@ -1270,6 +1381,26 @@ class DirectoryAuthority(Descriptor): 'shared-rand-current-value': _parse_shared_rand_current_value, }
+ @classmethod + def content(cls, attr = None, exclude = (), is_vote = False): + attr = {} if attr is None else dict(attr) + + # include mandatory 'vote-digest' if a consensus + + if not is_vote and not ('vote-digest' in attr or (exclude and 'vote-digest' in exclude)): + attr['vote-digest'] = '0B6D1E9A300B895AA2D0B427F92917B6995C3C1C' + + content = _descriptor_content(attr, exclude, AUTHORITY_HEADER) + + if is_vote: + content += b'\n' + KeyCertificate.content() + + return content + + @classmethod + def create(cls, attr = None, exclude = (), validate = True, is_vote = False): + return cls(cls.content(attr, exclude, is_vote), validate = validate, is_vote = is_vote) + def __init__(self, raw_content, validate = False, is_vote = False): """ Parse a directory authority entry in a v3 network status document. @@ -1444,6 +1575,10 @@ class KeyCertificate(Descriptor): 'dir-key-certification': _parse_dir_key_certification_line, }
+ @classmethod + def content(cls, attr = None, exclude = ()): + return _descriptor_content(attr, exclude, KEY_CERTIFICATE_HEADER, KEY_CERTIFICATE_FOOTER) + def __init__(self, raw_content, validate = False): super(KeyCertificate, self).__init__(raw_content, lazy_load = not validate) entries = _descriptor_components(raw_content, validate) @@ -1583,3 +1718,17 @@ class BridgeNetworkStatusDocument(NetworkStatusDocument): )
self.routers = dict((desc.fingerprint, desc) for desc in router_iter) + + +DOC_SIG = DocumentSignature( + 'sha1', + '14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', + 'BF112F1C6D5543CFD0A32215ACABD4197B5279AD', + '-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB, +) + +NETWORK_STATUS_DOCUMENT_FOOTER = ( + ('directory-footer', ''), + ('bandwidth-weights', None), + ('directory-signature', '%s %s\n%s' % (DOC_SIG.identity, DOC_SIG.key_digest, DOC_SIG.signature)), +) diff --git a/test/mocking.py b/test/mocking.py index c88784a..6369598 100644 --- a/test/mocking.py +++ b/test/mocking.py @@ -12,12 +12,6 @@ Helper functions for creating mock objects. Instance Constructors get_message - stem.response.ControlMessage get_protocolinfo_response - stem.response.protocolinfo.ProtocolInfoResponse - - stem.descriptor.networkstatus - get_directory_authority - DirectoryAuthority - get_key_certificate - KeyCertificate - get_network_status_document_v2 - NetworkStatusDocumentV2 - get_network_status_document_v3 - NetworkStatusDocumentV3 """
import hashlib @@ -35,78 +29,6 @@ import stem.prereq import stem.response import stem.util.str_tools
-try: - # added in python 2.7 - from collections import OrderedDict -except ImportError: - from stem.util.ordereddict import OrderedDict - -CRYPTO_BLOB = """ -MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg -skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+ -WPi4Fl2qryzTb3QO5r5x7T8OsG2IBUET1bLQzmtbC560SYR49IvVAgMBAAE= -""" - -DOC_SIG = stem.descriptor.networkstatus.DocumentSignature( - 'sha1', - '14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', - 'BF112F1C6D5543CFD0A32215ACABD4197B5279AD', - '-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB) - -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'), - ('dir-key-published', '2011-11-28 21:51:04'), - ('dir-key-expires', '2012-11-28 21:51:04'), - ('dir-identity-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB), - ('dir-signing-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB), -) - -KEY_CERTIFICATE_FOOTER = ( - ('dir-key-certification', '\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB), -) - -NETWORK_STATUS_DOCUMENT_HEADER_V2 = ( - ('network-status-version', '2'), - ('dir-source', '18.244.0.114 18.244.0.114 80'), - ('fingerprint', '719BE45DE224B607C53707D0E2143E2D423E74CF'), - ('contact', 'arma at mit dot edu'), - ('published', '2005-12-16 00:13:46'), - ('dir-signing-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB), -) - -NETWORK_STATUS_DOCUMENT_FOOTER_V2 = ( - ('directory-signature', 'moria2\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB), -) - -NETWORK_STATUS_DOCUMENT_HEADER = ( - ('network-status-version', '3'), - ('vote-status', 'consensus'), - ('consensus-methods', None), - ('consensus-method', None), - ('published', None), - ('valid-after', '2012-09-02 22:00:00'), - ('fresh-until', '2012-09-02 22:00:00'), - ('valid-until', '2012-09-02 22:00:00'), - ('voting-delay', '300 300'), - ('client-versions', None), - ('server-versions', None), - ('package', None), - ('known-flags', 'Authority BadExit Exit Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid'), - ('params', None), -) - -NETWORK_STATUS_DOCUMENT_FOOTER = ( - ('directory-footer', ''), - ('bandwidth-weights', None), - ('directory-signature', '%s %s\n%s' % (DOC_SIG.identity, DOC_SIG.key_digest, DOC_SIG.signature)), -) -
def get_all_combinations(attr, include_empty = False): """ @@ -194,221 +116,3 @@ def get_protocolinfo_response(**attributes): setattr(protocolinfo_response, attr, attributes[attr])
return protocolinfo_response - - -def _get_descriptor_content(attr = None, exclude = (), header_template = (), footer_template = ()): - """ - Constructs a minimal descriptor with the given attributes. The content we - provide back is of the form... - - * header_template (with matching attr filled in) - * unused attr entries - * footer_template (with matching attr filled in) - - So for instance... - - :: - - get_descriptor_content( - attr = {'nickname': 'caerSidi', 'contact': 'atagar'}, - header_template = ( - ('nickname', 'foobar'), - ('fingerprint', '12345'), - ), - ) - - ... would result in... - - :: - - nickname caerSidi - fingerprint 12345 - contact atagar - - :param dict attr: keyword/value mappings to be included in the descriptor - :param list exclude: mandatory keywords to exclude from the descriptor - :param tuple header_template: key/value pairs for mandatory fields before unrecognized content - :param tuple footer_template: key/value pairs for mandatory fields after unrecognized content - - :returns: str with the requested descriptor content - """ - - header_content, footer_content = [], [] - attr = {} if attr is None else dict(attr) - - attr = OrderedDict(attr) # shallow copy since we're destructive - - for content, template in ((header_content, header_template), - (footer_content, footer_template)): - for keyword, value in template: - if keyword in exclude: - continue - elif keyword in attr: - value = attr[keyword] - del attr[keyword] - - if value is None: - continue - elif value == '': - content.append(keyword) - elif keyword == 'onion-key' or keyword == 'signing-key' or keyword == 'router-signature': - content.append('%s%s' % (keyword, value)) - else: - content.append('%s %s' % (keyword, value)) - - remainder = [] - - for k, v in attr.items(): - if v: - remainder.append('%s %s' % (k, v)) - else: - remainder.append(k) - - return stem.util.str_tools._to_bytes('\n'.join(header_content + remainder + footer_content)) - - -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 - """ - - attr = {} if attr is None else dict(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 += b'\n' + get_key_certificate(content = True) - - 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... - stem.descriptor.networkstatus.KeyCertificate - - :param dict attr: keyword/value mappings to be included in the descriptor - :param list exclude: mandatory keywords to exclude from the descriptor - :param bool content: provides the str content of the descriptor rather than the class if True - - :returns: KeyCertificate for the requested descriptor content - """ - - desc_content = _get_descriptor_content(attr, exclude, KEY_CERTIFICATE_HEADER, KEY_CERTIFICATE_FOOTER) - - if content: - return desc_content - else: - return stem.descriptor.networkstatus.KeyCertificate(desc_content, validate = True) - - -def get_network_status_document_v2(attr = None, exclude = (), content = False): - """ - Provides the descriptor content for... - stem.descriptor.networkstatus.NetworkStatusDocumentV2 - - :param dict attr: keyword/value mappings to be included in the descriptor - :param list exclude: mandatory keywords to exclude from the descriptor - :param bool content: provides the str content of the descriptor rather than the class if True - - :returns: NetworkStatusDocumentV2 for the requested descriptor content - """ - - desc_content = _get_descriptor_content(attr, exclude, NETWORK_STATUS_DOCUMENT_HEADER_V2, NETWORK_STATUS_DOCUMENT_FOOTER_V2) - - if content: - return desc_content - else: - return stem.descriptor.networkstatus.NetworkStatusDocumentV2(desc_content, validate = True) - - -def get_network_status_document_v3(attr = None, exclude = (), authorities = None, routers = None, content = False): - """ - Provides the descriptor content for... - stem.descriptor.networkstatus.NetworkStatusDocumentV3 - - :param dict attr: keyword/value mappings to be included in the descriptor - :param list exclude: mandatory keywords to exclude from the descriptor - :param list authorities: directory authorities to include in the document - :param list routers: router status entries to include in the document - :param bool content: provides the str content of the descriptor rather than the class if True - - :returns: NetworkStatusDocumentV3 for the requested descriptor content - """ - - attr = {} if attr is None else dict(attr) - - # add defaults only found in a vote, consensus, or microdescriptor - - if attr.get('vote-status') == 'vote': - extra_defaults = { - 'consensus-methods': '1 9', - 'published': '2012-09-02 22:00:00', - } - - # votes need an authority to be valid - - if authorities is None: - authorities = [get_directory_authority(is_vote = True)] - else: - extra_defaults = { - 'consensus-method': '9', - } - - for k, v in extra_defaults.items(): - if exclude and k in exclude: - continue # explicitly excluding this field - elif k not in attr: - attr[k] = v - - desc_content = _get_descriptor_content(attr, exclude, NETWORK_STATUS_DOCUMENT_HEADER, NETWORK_STATUS_DOCUMENT_FOOTER) - - # inject the authorities and/or routers between the header and footer - if authorities: - if b'directory-footer' in desc_content: - footer_div = desc_content.find(b'\ndirectory-footer') + 1 - elif b'directory-signature' in desc_content: - footer_div = desc_content.find(b'\ndirectory-signature') + 1 - else: - if routers: - desc_content += b'\n' - - footer_div = len(desc_content) + 1 - - authority_content = stem.util.str_tools._to_bytes('\n'.join([str(a) for a in authorities]) + '\n') - desc_content = desc_content[:footer_div] + authority_content + desc_content[footer_div:] - - if routers: - if b'directory-footer' in desc_content: - footer_div = desc_content.find(b'\ndirectory-footer') + 1 - elif b'directory-signature' in desc_content: - footer_div = desc_content.find(b'\ndirectory-signature') + 1 - else: - if routers: - desc_content += b'\n' - - footer_div = len(desc_content) + 1 - - router_content = stem.util.str_tools._to_bytes('\n'.join([str(r) for r in routers]) + '\n') - desc_content = desc_content[:footer_div] + router_content + desc_content[footer_div:] - - if content: - return desc_content - else: - return stem.descriptor.networkstatus.NetworkStatusDocumentV3(desc_content, validate = True) diff --git a/test/unit/descriptor/networkstatus/directory_authority.py b/test/unit/descriptor/networkstatus/directory_authority.py index c1dc319..a80cb44 100644 --- a/test/unit/descriptor/networkstatus/directory_authority.py +++ b/test/unit/descriptor/networkstatus/directory_authority.py @@ -4,8 +4,11 @@ Unit tests for the DirectoryAuthority of stem.descriptor.networkstatus.
import unittest
-from stem.descriptor.networkstatus import DirectoryAuthority -from test.mocking import get_directory_authority, get_key_certificate, AUTHORITY_HEADER +from stem.descriptor.networkstatus import ( + AUTHORITY_HEADER, + DirectoryAuthority, + KeyCertificate, +)
class TestDirectoryAuthority(unittest.TestCase): @@ -14,7 +17,7 @@ class TestDirectoryAuthority(unittest.TestCase): Parses a minimal directory authority for a consensus. """
- authority = get_directory_authority() + authority = DirectoryAuthority.create()
self.assertEqual('turtles', authority.nickname) self.assertEqual('27B6B5996C426270A5C95488AA5BCEB6BCC86956', authority.fingerprint) @@ -34,7 +37,7 @@ class TestDirectoryAuthority(unittest.TestCase): Parses a minimal directory authority for a vote. """
- authority = get_directory_authority(is_vote = True) + authority = DirectoryAuthority.create(is_vote = True)
self.assertEqual('turtles', authority.nickname) self.assertEqual('27B6B5996C426270A5C95488AA5BCEB6BCC86956', authority.fingerprint) @@ -46,7 +49,7 @@ class TestDirectoryAuthority(unittest.TestCase): 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(KeyCertificate.create(), authority.key_certificate) self.assertEqual([], authority.get_unrecognized_lines())
def test_unrecognized_line(self): @@ -54,7 +57,7 @@ class TestDirectoryAuthority(unittest.TestCase): Includes unrecognized content in the descriptor. """
- authority = get_directory_authority({'pepperjack': 'is oh so tasty!'}) + authority = DirectoryAuthority.create({'pepperjack': 'is oh so tasty!'}) self.assertEqual(['pepperjack is oh so tasty!'], authority.get_unrecognized_lines())
def test_legacy_authority(self): @@ -83,7 +86,7 @@ class TestDirectoryAuthority(unittest.TestCase): Includes a non-mandatory field before the 'dir-source' line. """
- content = b'ho-hum 567\n' + get_directory_authority(content = True) + content = b'ho-hum 567\n' + DirectoryAuthority.content() self.assertRaises(ValueError, DirectoryAuthority, content, True)
authority = DirectoryAuthority(content, False) @@ -96,7 +99,7 @@ class TestDirectoryAuthority(unittest.TestCase): """
for excluded_field in ('dir-source', 'contact'): - content = get_directory_authority(exclude = (excluded_field,), content = True) + content = DirectoryAuthority.content(exclude = (excluded_field,)) self.assertRaises(ValueError, DirectoryAuthority, content, True)
authority = DirectoryAuthority(content, False) @@ -111,7 +114,7 @@ class TestDirectoryAuthority(unittest.TestCase): Includes blank lines, which should be ignored. """
- authority = get_directory_authority({'dir-source': AUTHORITY_HEADER[0][1] + '\n\n\n'}) + authority = DirectoryAuthority.create({'dir-source': AUTHORITY_HEADER[0][1] + '\n\n\n'}) self.assertEqual('Mike Perry <email>', authority.contact)
def test_duplicate_lines(self): @@ -119,7 +122,7 @@ class TestDirectoryAuthority(unittest.TestCase): Duplicates linesin the entry. """
- lines = get_directory_authority(content = True).split(b'\n') + lines = DirectoryAuthority.content().split(b'\n')
for index, duplicate_line in enumerate(lines): content = b'\n'.join(lines[:index] + [duplicate_line] + lines[index:]) @@ -135,7 +138,7 @@ class TestDirectoryAuthority(unittest.TestCase):
for missing_value in AUTHORITY_HEADER[0][1].split(' '): dir_source = AUTHORITY_HEADER[0][1].replace(missing_value, '').replace(' ', ' ') - content = get_directory_authority({'dir-source': dir_source}, content = True) + content = DirectoryAuthority.content({'dir-source': dir_source}) self.assertRaises(ValueError, DirectoryAuthority, content, True)
authority = DirectoryAuthority(content, False) @@ -160,7 +163,7 @@ class TestDirectoryAuthority(unittest.TestCase):
for value in test_values: dir_source = AUTHORITY_HEADER[0][1].replace('27B6B5996C426270A5C95488AA5BCEB6BCC86956', value) - content = get_directory_authority({'dir-source': dir_source}, content = True) + content = DirectoryAuthority.content({'dir-source': dir_source}) self.assertRaises(ValueError, DirectoryAuthority, content, True)
authority = DirectoryAuthority(content, False) @@ -182,7 +185,7 @@ class TestDirectoryAuthority(unittest.TestCase):
for value in test_values: dir_source = AUTHORITY_HEADER[0][1].replace('76.73.17.194', value) - content = get_directory_authority({'dir-source': dir_source}, content = True) + content = DirectoryAuthority.content({'dir-source': dir_source}) self.assertRaises(ValueError, DirectoryAuthority, content, True)
authority = DirectoryAuthority(content, False) @@ -214,7 +217,7 @@ class TestDirectoryAuthority(unittest.TestCase): if include_dir_port: dir_source = dir_source.replace('9030', value)
- content = get_directory_authority({'dir-source': dir_source}, content = True) + content = DirectoryAuthority.content({'dir-source': dir_source}) self.assertRaises(ValueError, DirectoryAuthority, content, True)
authority = DirectoryAuthority(content, False) @@ -228,11 +231,11 @@ class TestDirectoryAuthority(unittest.TestCase): """
test_value = '65968CCB6BECB5AA88459C5A072624C6995B6B72' - authority = get_directory_authority({'legacy-dir-key': test_value}, is_vote = True) + authority = DirectoryAuthority.create({'legacy-dir-key': test_value}, is_vote = True) self.assertEqual(test_value, authority.legacy_dir_key)
# check that we'll fail if legacy-dir-key appears in a consensus - content = get_directory_authority({'legacy-dir-key': test_value}, content = True) + content = DirectoryAuthority.content({'legacy-dir-key': test_value}) self.assertRaises(ValueError, DirectoryAuthority, content, True)
test_values = ( @@ -242,7 +245,7 @@ class TestDirectoryAuthority(unittest.TestCase): )
for value in test_values: - content = get_directory_authority({'legacy-dir-key': value}, content = True) + content = DirectoryAuthority.content({'legacy-dir-key': value}) self.assertRaises(ValueError, DirectoryAuthority, content, True)
authority = DirectoryAuthority(content, False) @@ -253,17 +256,17 @@ class TestDirectoryAuthority(unittest.TestCase): Includes or exclude a key certificate from the directory entry. """
- key_cert = get_key_certificate(content = True) + key_cert = KeyCertificate.content()
# include a key cert with a consensus - content = get_directory_authority(content = True) + b'\n' + key_cert + content = DirectoryAuthority.content() + b'\n' + key_cert self.assertRaises(ValueError, DirectoryAuthority, content, True)
authority = DirectoryAuthority(content, False) self.assertEqual('turtles', authority.nickname)
# exclude key cert from a vote - content = get_directory_authority(content = True, is_vote = True).replace(b'\n' + key_cert, b'') + content = DirectoryAuthority.content(is_vote = True).replace(b'\n' + key_cert, b'') self.assertRaises(ValueError, DirectoryAuthority, content, True, True)
authority = DirectoryAuthority(content, False, True) diff --git a/test/unit/descriptor/networkstatus/document_v2.py b/test/unit/descriptor/networkstatus/document_v2.py index 54cbccc..7b6fdb1 100644 --- a/test/unit/descriptor/networkstatus/document_v2.py +++ b/test/unit/descriptor/networkstatus/document_v2.py @@ -5,9 +5,11 @@ Unit tests for the NetworkStatusDocumentV2 of stem.descriptor.networkstatus. import datetime import unittest
-import stem.descriptor - -from test.mocking import get_network_status_document_v2, NETWORK_STATUS_DOCUMENT_HEADER_V2, NETWORK_STATUS_DOCUMENT_FOOTER_V2 +from stem.descriptor.networkstatus import ( + NETWORK_STATUS_DOCUMENT_HEADER_V2, + NETWORK_STATUS_DOCUMENT_FOOTER_V2, + NetworkStatusDocumentV2, +)
from test.unit.descriptor import get_resource
@@ -32,7 +34,7 @@ TpQQk3nNQF8z6UIvdlvP+DnJV4izWVkQEZgUZgIVM0E=
with open(get_resource('cached-consensus-v2'), 'rb') as descriptor_file: descriptor_file.readline() # strip header - document = stem.descriptor.networkstatus.NetworkStatusDocumentV2(descriptor_file.read()) + document = NetworkStatusDocumentV2(descriptor_file.read())
self.assertEqual(2, document.version) self.assertEqual('18.244.0.114', document.hostname) @@ -93,7 +95,7 @@ TpQQk3nNQF8z6UIvdlvP+DnJV4izWVkQEZgUZgIVM0E= Parses a minimal v2 network status document. """
- document = get_network_status_document_v2() + document = NetworkStatusDocumentV2.create()
self.assertEqual({}, document.routers) self.assertEqual(2, document.version) diff --git a/test/unit/descriptor/networkstatus/document_v3.py b/test/unit/descriptor/networkstatus/document_v3.py index d6ea34e..4bfdc9e 100644 --- a/test/unit/descriptor/networkstatus/document_v3.py +++ b/test/unit/descriptor/networkstatus/document_v3.py @@ -16,6 +16,8 @@ from stem.util import str_type from stem.descriptor.networkstatus import ( HEADER_STATUS_DOCUMENT_FIELDS, FOOTER_STATUS_DOCUMENT_FIELDS, + NETWORK_STATUS_DOCUMENT_FOOTER, + DOC_SIG, DEFAULT_PARAMS, PackageVersion, DirectoryAuthority, @@ -28,13 +30,6 @@ from stem.descriptor.router_status_entry import ( RouterStatusEntryMicroV3, )
-from test.mocking import ( - get_directory_authority, - get_network_status_document_v3, - DOC_SIG, - NETWORK_STATUS_DOCUMENT_FOOTER, -) - from test.unit.descriptor import get_resource
try: @@ -309,7 +304,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= Parses a minimal network status document. """
- document = get_network_status_document_v3() + document = NetworkStatusDocumentV3.create()
expected_known_flags = [ Flag.AUTHORITY, Flag.BADEXIT, Flag.EXIT, @@ -356,7 +351,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= Parses a minimal network status document. """
- document = get_network_status_document_v3({'vote-status': 'vote'}) + document = NetworkStatusDocumentV3.create({'vote-status': 'vote'})
expected_known_flags = [ Flag.AUTHORITY, Flag.BADEXIT, Flag.EXIT, @@ -400,7 +395,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
entry1 = RouterStatusEntryV3.create({'s': 'Fast'}) entry2 = RouterStatusEntryV3.create({'s': 'Valid'}) - content = get_network_status_document_v3(routers = (entry1, entry2), content = True) + content = NetworkStatusDocumentV3.content(routers = (entry1, entry2))
# first example: parsing via the NetworkStatusDocumentV3 constructor
@@ -432,7 +427,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= 's': 'Valid', })
- content = get_network_status_document_v3(routers = (entry1, entry2), content = True) + content = NetworkStatusDocumentV3.content(routers = (entry1, entry2))
descriptors = list(stem.descriptor.parse_file(io.BytesIO(content), 'network-status-consensus-3 1.0', document_handler = stem.descriptor.DocumentHandler.DOCUMENT)) self.assertEqual(1, len(descriptors)) @@ -451,12 +446,12 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
entry1 = RouterStatusEntryV3.create({'s': 'Fast'}) entry2 = RouterStatusEntryV3.create({'s': 'Valid'}) - content = get_network_status_document_v3(routers = (entry1, entry2), content = True) + content = NetworkStatusDocumentV3.content(routers = (entry1, entry2))
# the document that the entries refer to should actually be the minimal # descriptor (ie, without the entries)
- expected_document = get_network_status_document_v3() + expected_document = NetworkStatusDocumentV3.create()
descriptor_file = io.BytesIO(content) entries = list(_parse_file(descriptor_file)) @@ -477,7 +472,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= for entries in (HEADER_STATUS_DOCUMENT_FIELDS, FOOTER_STATUS_DOCUMENT_FIELDS): for field, in_votes, in_consensus, is_mandatory in entries: if is_mandatory and field != 'vote-status' and ((is_consensus and in_consensus) or (is_vote and in_votes)): - content = get_network_status_document_v3(attr, exclude = (field,), content = True) + content = NetworkStatusDocumentV3.content(attr, exclude = (field,)) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True) NetworkStatusDocumentV3(content, False) # constructs without validation
@@ -486,7 +481,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= Includes unrecognized content in the document. """
- document = get_network_status_document_v3({'pepperjack': 'is oh so tasty!'}) + document = NetworkStatusDocumentV3.create({'pepperjack': 'is oh so tasty!'}) self.assertEqual(['pepperjack is oh so tasty!'], document.get_unrecognized_lines())
def test_duplicate_fields(self): @@ -497,7 +492,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
for is_consensus in (True, False): attr = {'vote-status': 'consensus'} if is_consensus else {'vote-status': 'vote'} - lines = get_network_status_document_v3(attr, content = True).split(b'\n') + lines = NetworkStatusDocumentV3.content(attr).split(b'\n')
for index, line in enumerate(lines): if not is_consensus and lines[index].startswith(b'dir-source'): @@ -525,17 +520,17 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= different document version with the v3 parser. """
- document = get_network_status_document_v3({'network-status-version': '3'}) + document = NetworkStatusDocumentV3.create({'network-status-version': '3'}) self.assertEqual(3, document.version) self.assertEqual(None, document.version_flavor) self.assertEqual(False, document.is_microdescriptor)
- document = get_network_status_document_v3({'network-status-version': '3 microdesc'}) + document = NetworkStatusDocumentV3.create({'network-status-version': '3 microdesc'}) self.assertEqual(3, document.version) self.assertEqual('microdesc', document.version_flavor) self.assertEqual(True, document.is_microdescriptor)
- content = get_network_status_document_v3({'network-status-version': '4'}, content = True) + content = NetworkStatusDocumentV3.content({'network-status-version': '4'}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -548,11 +543,11 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= Parses the vote-status field. """
- document = get_network_status_document_v3({'vote-status': 'vote'}) + document = NetworkStatusDocumentV3.create({'vote-status': 'vote'}) self.assertEqual(False, document.is_consensus) self.assertEqual(True, document.is_vote)
- content = get_network_status_document_v3({'vote-status': 'consensus'}, content = True) + content = NetworkStatusDocumentV3.content({'vote-status': 'consensus'}) document = NetworkStatusDocumentV3(content) self.assertEqual(True, document.is_consensus) self.assertEqual(False, document.is_vote) @@ -564,7 +559,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value in test_values: - content = get_network_status_document_v3({'vote-status': test_value}, content = True) + content = NetworkStatusDocumentV3.content({'vote-status': test_value}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -576,11 +571,11 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= Parses the consensus-methods field. """
- document = get_network_status_document_v3({'vote-status': 'vote', 'consensus-methods': '12 3 1 780'}) + document = NetworkStatusDocumentV3.create({'vote-status': 'vote', 'consensus-methods': '12 3 1 780'}) self.assertEqual([12, 3, 1, 780], document.consensus_methods)
# check that we default to including consensus-method 1 - content = get_network_status_document_v3({'vote-status': 'vote'}, ('consensus-methods',), content = True) + content = NetworkStatusDocumentV3.content({'vote-status': 'vote'}, ('consensus-methods',)) document = NetworkStatusDocumentV3(content, False) self.assertEqual([1], document.consensus_methods) self.assertEqual(None, document.consensus_method) @@ -594,7 +589,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value in test_values: - content = get_network_status_document_v3({'vote-status': 'vote', 'consensus-methods': test_value}, content = True) + content = NetworkStatusDocumentV3.content({'vote-status': 'vote', 'consensus-methods': test_value}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
expected_value = [2, 3, 4] if test_value == '2 3 4' else [1] @@ -607,11 +602,11 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= Parses the consensus-method field. """
- document = get_network_status_document_v3({'consensus-method': '12'}) + document = NetworkStatusDocumentV3.create({'consensus-method': '12'}) self.assertEqual(12, document.consensus_method)
# check that we default to being consensus-method 1 - content = get_network_status_document_v3(exclude = ('consensus-method',), content = True) + content = NetworkStatusDocumentV3.content(exclude = ('consensus-method',)) document = NetworkStatusDocumentV3(content, False) self.assertEqual(1, document.consensus_method) self.assertEqual([], document.consensus_methods) @@ -625,7 +620,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value in test_values: - content = get_network_status_document_v3({'consensus-method': test_value}, content = True) + content = NetworkStatusDocumentV3.content({'consensus-method': test_value}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -640,7 +635,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= expected = datetime.datetime(2012, 9, 2, 22, 0, 0) test_value = '2012-09-02 22:00:00'
- document = get_network_status_document_v3({ + document = NetworkStatusDocumentV3.create({ 'vote-status': 'vote', 'published': test_value, 'valid-after': test_value, @@ -662,7 +657,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value in test_values: - content = get_network_status_document_v3({'vote-status': 'vote', 'published': test_value}, content = True) + content = NetworkStatusDocumentV3.content({'vote-status': 'vote', 'published': test_value}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -673,7 +668,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= Parses the voting-delay field. """
- document = get_network_status_document_v3({'voting-delay': '12 345'}) + document = NetworkStatusDocumentV3.create({'voting-delay': '12 345'}) self.assertEqual(12, document.vote_delay) self.assertEqual(345, document.dist_delay)
@@ -686,7 +681,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value in test_values: - content = get_network_status_document_v3({'voting-delay': test_value}, content = True) + content = NetworkStatusDocumentV3.content({'voting-delay': test_value}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -702,7 +697,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= expected = [stem.version.Version('1.2.3.4'), stem.version.Version('56.789.12.34-alpha')] test_value = '1.2.3.4,56.789.12.34-alpha'
- document = get_network_status_document_v3({'client-versions': test_value, 'server-versions': test_value}) + document = NetworkStatusDocumentV3.create({'client-versions': test_value, 'server-versions': test_value}) self.assertEqual(expected, document.client_versions) self.assertEqual(expected, document.server_versions)
@@ -717,7 +712,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= attr = field.replace('-', '_')
for test_value in test_values: - content = get_network_status_document_v3({field: test_value}, content = True) + content = NetworkStatusDocumentV3.content({field: test_value}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -740,7 +735,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value, expected_value in test_values: - document = get_network_status_document_v3({'package': '\npackage '.join(test_value)}) + document = NetworkStatusDocumentV3.create({'package': '\npackage '.join(test_value)}) self.assertEqual(expected_value, document.packages)
test_values = ( @@ -754,7 +749,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value in test_values: - content = get_network_status_document_v3({'package': test_value}, content = True) + content = NetworkStatusDocumentV3.content({'package': test_value}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -777,7 +772,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value, expected_value in test_values: - document = get_network_status_document_v3({'known-flags': test_value}) + document = NetworkStatusDocumentV3.create({'known-flags': test_value}) self.assertEqual(expected_value, document.known_flags)
def test_flag_thresholds(self): @@ -793,7 +788,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value, expected_value in test_values: - document = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': test_value}) + document = NetworkStatusDocumentV3.create({'vote-status': 'vote', 'flag-thresholds': test_value}) self.assertEqual(expected_value, document.flag_thresholds)
# parses a full entry found in an actual vote @@ -811,7 +806,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= str_type('enough-mtbf'): 1, }
- document = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': full_line}) + document = NetworkStatusDocumentV3.create({'vote-status': 'vote', 'flag-thresholds': full_line}) self.assertEqual(expected_value, document.flag_thresholds)
test_values = ( @@ -822,7 +817,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value in test_values: - content = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': test_value}, content = True) + content = NetworkStatusDocumentV3.content({'vote-status': 'vote', 'flag-thresholds': test_value}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -833,7 +828,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= Parses the parameters attributes. """
- document = get_network_status_document_v3(OrderedDict([ + document = NetworkStatusDocumentV3.create(OrderedDict([ ('vote-status', 'vote'), ('recommended-client-protocols', 'HSDir=1 HSIntro=3'), ('recommended-relay-protocols', 'Cons=1 Desc=1'), @@ -851,17 +846,17 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= General testing for the 'params' line, exercising the happy cases. """
- document = get_network_status_document_v3({'params': 'CircuitPriorityHalflifeMsec=30000 bwauthpid=1 unrecognized=-122'}) + document = NetworkStatusDocumentV3.create({'params': 'CircuitPriorityHalflifeMsec=30000 bwauthpid=1 unrecognized=-122'}) self.assertEqual(30000, document.params['CircuitPriorityHalflifeMsec']) self.assertEqual(1, document.params['bwauthpid']) self.assertEqual(-122, document.params['unrecognized'])
# empty params line - content = get_network_status_document_v3({'params': ''}, content = True) + content = NetworkStatusDocumentV3.content({'params': ''}) document = NetworkStatusDocumentV3(content, default_params = True) self.assertEqual(DEFAULT_PARAMS, document.params)
- content = get_network_status_document_v3({'params': ''}, content = True) + content = NetworkStatusDocumentV3.content({'params': ''}) document = NetworkStatusDocumentV3(content, default_params = False) self.assertEqual({}, document.params)
@@ -878,7 +873,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value in test_values: - content = get_network_status_document_v3({'params': test_value}, content = True) + content = NetworkStatusDocumentV3.content({'params': test_value}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -907,7 +902,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= )
for test_value, expected_value, is_ok in test_values: - content = get_network_status_document_v3({'params': test_value}, content = True) + content = NetworkStatusDocumentV3.content({'params': test_value})
if is_ok: document = NetworkStatusDocumentV3(content, default_params = False) @@ -922,7 +917,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= Check that the 'params' line is rejected if out of order. """
- content = get_network_status_document_v3({'params': 'unrecognized=-122 bwauthpid=1'}, content = True) + content = NetworkStatusDocumentV3.content({'params': 'unrecognized=-122 bwauthpid=1'}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False, default_params = False) @@ -934,7 +929,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= introduced. """
- content = get_network_status_document_v3({'consensus-method': '8'}, content = True) + content = NetworkStatusDocumentV3.content({'consensus-method': '8'}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -943,7 +938,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
# excludes a footer from a version that shouldn't have it
- document = get_network_status_document_v3({'consensus-method': '8'}, ('directory-footer', 'directory-signature')) + document = NetworkStatusDocumentV3.create({'consensus-method': '8'}, ('directory-footer', 'directory-signature')) self.assertEqual([], document.signatures) self.assertEqual([], document.get_unrecognized_lines())
@@ -952,13 +947,13 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= # # https://trac.torproject.org/7932
- document = get_network_status_document_v3( + document = NetworkStatusDocumentV3.create( { 'vote-status': 'vote', 'consensus-methods': '1 8', }, exclude = ('directory-footer',), - authorities = (get_directory_authority(is_vote = True),) + authorities = (DirectoryAuthority.create(is_vote = True),) )
self.assertEqual([DOC_SIG], document.signatures) @@ -969,7 +964,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= Tries to parse a descriptor with content on the 'directory-footer' line. """
- content = get_network_status_document_v3({'directory-footer': 'blarg'}, content = True) + content = NetworkStatusDocumentV3.content({'directory-footer': 'blarg'}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -989,7 +984,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= weight_entries.append('%s=%i' % (key, index - 5)) expected[key] = index - 5
- document = get_network_status_document_v3({'bandwidth-weights': ' '.join(weight_entries)}) + document = NetworkStatusDocumentV3.create({'bandwidth-weights': ' '.join(weight_entries)}) self.assertEqual(expected, document.bandwidth_weights)
def test_bandwidth_wights_malformed(self): @@ -1008,7 +1003,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
for test_value in test_values: weight_entry = base_weight_entry.replace('Wbe=5', test_value) - content = get_network_status_document_v3({'bandwidth-weights': weight_entry}, content = True) + content = NetworkStatusDocumentV3.content({'bandwidth-weights': weight_entry})
self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True) document = NetworkStatusDocumentV3(content, False) @@ -1021,7 +1016,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
weight_entry = ' '.join(['%s=5' % e for e in reversed(BANDWIDTH_WEIGHT_ENTRIES)])
- content = get_network_status_document_v3({'bandwidth-weights': weight_entry}, content = True) + content = NetworkStatusDocumentV3.content({'bandwidth-weights': weight_entry}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -1035,7 +1030,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= weight_entry = ' '.join(['%s=5' % e for e in BANDWIDTH_WEIGHT_ENTRIES]) expected = dict([(e, 5) for e in BANDWIDTH_WEIGHT_ENTRIES])
- content = get_network_status_document_v3({'vote-status': 'vote', 'bandwidth-weights': weight_entry}, content = True) + content = NetworkStatusDocumentV3.content({'vote-status': 'vote', 'bandwidth-weights': weight_entry}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -1049,7 +1044,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
# including the signature method field should work
- document = get_network_status_document_v3({ + document = NetworkStatusDocumentV3.create({ 'network-status-version': '3 microdesc', 'directory-signature': 'sha256 ' + NETWORK_STATUS_DOCUMENT_FOOTER[2][1], }) @@ -1058,7 +1053,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
# excluding the method should default to sha1
- document = get_network_status_document_v3({ + document = NetworkStatusDocumentV3.create({ 'network-status-version': '3 microdesc', })
@@ -1080,7 +1075,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= attrs = [DOC_SIG.identity, DOC_SIG.key_digest, DOC_SIG.signature] attrs[test_attr] = test_value
- content = get_network_status_document_v3({'directory-signature': '%s %s\n%s' % tuple(attrs)}, content = True) + content = NetworkStatusDocumentV3.content({'directory-signature': '%s %s\n%s' % tuple(attrs)}) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True) NetworkStatusDocumentV3(content, False) # checks that it's still parsable without validation
@@ -1097,7 +1092,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= 's': 'Valid', })
- document = get_network_status_document_v3(routers = (entry1, entry2)) + document = NetworkStatusDocumentV3.create(routers = (entry1, entry2))
self.assertTrue(entry1 in document.routers.values()) self.assertTrue(entry2 in document.routers.values()) @@ -1105,7 +1100,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= # try with an invalid RouterStatusEntry
entry3 = RouterStatusEntryV3(RouterStatusEntryV3.content({'r': 'ugabuga'}), False) - content = get_network_status_document_v3(routers = (entry3,), content = True) + content = NetworkStatusDocumentV3.content(routers = (entry3,))
self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True) document = NetworkStatusDocumentV3(content, False) @@ -1113,7 +1108,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
# try including with a microdescriptor consensus
- content = get_network_status_document_v3({'network-status-version': '3 microdesc'}, routers = (entry1,), content = True) + content = NetworkStatusDocumentV3.content({'network-status-version': '3 microdesc'}, routers = (entry1,)) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -1131,7 +1126,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= 's': 'Valid', })
- document = get_network_status_document_v3({'network-status-version': '3 microdesc'}, routers = (entry1, entry2)) + document = NetworkStatusDocumentV3.create({'network-status-version': '3 microdesc'}, routers = (entry1, entry2))
self.assertTrue(entry1 in document.routers.values()) self.assertTrue(entry2 in document.routers.values()) @@ -1140,7 +1135,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
entry3 = RouterStatusEntryMicroV3(RouterStatusEntryMicroV3.content({'r': 'ugabuga'}), False)
- content = get_network_status_document_v3({'network-status-version': '3 microdesc'}, routers = (entry3,), content = True) + content = NetworkStatusDocumentV3.content({'network-status-version': '3 microdesc'}, routers = (entry3,)) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -1148,7 +1143,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
# try including microdescriptor entry in a normal consensus
- content = get_network_status_document_v3(routers = (entry1,), content = True) + content = NetworkStatusDocumentV3.content(routers = (entry1,)) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, False) @@ -1161,11 +1156,11 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
for is_document_vote in (False, True): for is_authorities_vote in (False, True): - authority1 = get_directory_authority({'contact': 'doctor jekyll'}, is_vote = is_authorities_vote) - authority2 = get_directory_authority({'contact': 'mister hyde'}, is_vote = is_authorities_vote) + authority1 = DirectoryAuthority.create({'contact': 'doctor jekyll'}, is_vote = is_authorities_vote) + authority2 = DirectoryAuthority.create({'contact': 'mister hyde'}, is_vote = is_authorities_vote)
vote_status = 'vote' if is_document_vote else 'consensus' - content = get_network_status_document_v3({'vote-status': vote_status}, authorities = (authority1, authority2), content = True) + content = NetworkStatusDocumentV3.content({'vote-status': vote_status}, authorities = (authority1, authority2))
if is_document_vote == is_authorities_vote: if is_document_vote: @@ -1191,7 +1186,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= COMMITMENT_1 = '1 sha3-256 4CAEC248004A0DC6CE86EBD5F608C9B05500C70C AAAAAFd4/kAaklgYr4ijHZjXXy/B354jQfL31BFhhE46nuOHSPITyw== AAAAAFd4/kCpZeis3yJyr//rz8hXCeeAhHa4k3lAcAiMJd1vEMTPuw==' COMMITMENT_2 = '1 sha3-256 598536A9DD4E6C0F18B4AD4B88C7875A0A29BA31 AAAAAFd4/kC7S920awC5/HF5RfX4fKZtYqjm6qMh9G91AcjZm13DQQ=='
- authority = get_directory_authority(OrderedDict([ + authority = DirectoryAuthority.create(OrderedDict([ ('shared-rand-participate', ''), ('shared-rand-commit', '%s\nshared-rand-commit %s' % (COMMITMENT_1, COMMITMENT_2)), ('shared-rand-previous-value', '8 hAQLxyt0U3gu7QR2owixRCbIltcyPrz3B0YBfUshOkE='), @@ -1237,7 +1232,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= ]
for attr, expected_exception in test_values: - content = get_directory_authority(attr, content = True) + content = DirectoryAuthority.content(attr) self.assertRaisesRegexp(ValueError, re.escape(expected_exception), DirectoryAuthority, content, True)
authority = DirectoryAuthority(content, False) @@ -1254,11 +1249,11 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
legacy_content = 'dir-source gabelmoo-legacy 81349FC1F2DBA2C2C11B45CB9706637D480AB913 131.188.40.189 131.188.40.189 80 443'
- authority1 = get_directory_authority({'contact': 'doctor jekyll'}, is_vote = False) + authority1 = DirectoryAuthority.create({'contact': 'doctor jekyll'}, is_vote = False) authority2 = DirectoryAuthority(legacy_content, validate = True, is_vote = False) - authority3 = get_directory_authority({'contact': 'mister hyde'}, is_vote = False) + authority3 = DirectoryAuthority.create({'contact': 'mister hyde'}, is_vote = False)
- document = get_network_status_document_v3({'vote-status': 'consensus'}, authorities = (authority1, authority2, authority3)) + document = NetworkStatusDocumentV3.create({'vote-status': 'consensus'}, authorities = (authority1, authority2, authority3))
self.assertEqual((authority1, authority2, authority3), document.directory_authorities)
@@ -1270,11 +1265,11 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w= """
# make the dir-key-published field of the certiciate be malformed - authority_content = get_directory_authority(is_vote = True, content = True) + authority_content = DirectoryAuthority.content(is_vote = True) authority_content = authority_content.replace(b'dir-key-published 2011', b'dir-key-published 2011a') authority = DirectoryAuthority(authority_content, False, True)
- content = get_network_status_document_v3({'vote-status': 'vote'}, authorities = (authority,), content = True) + content = NetworkStatusDocumentV3.content({'vote-status': 'vote'}, authorities = (authority,)) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
document = NetworkStatusDocumentV3(content, validate = False) diff --git a/test/unit/descriptor/networkstatus/key_certificate.py b/test/unit/descriptor/networkstatus/key_certificate.py index bbcaaa0..a56c74a 100644 --- a/test/unit/descriptor/networkstatus/key_certificate.py +++ b/test/unit/descriptor/networkstatus/key_certificate.py @@ -5,13 +5,12 @@ Unit tests for the KeyCertificate of stem.descriptor.networkstatus. import datetime import unittest
-from stem.descriptor.networkstatus import KeyCertificate +import stem.descriptor
-from test.mocking import ( - get_key_certificate, - CRYPTO_BLOB, +from stem.descriptor.networkstatus import ( KEY_CERTIFICATE_HEADER, KEY_CERTIFICATE_FOOTER, + KeyCertificate, )
@@ -21,18 +20,18 @@ class TestKeyCertificate(unittest.TestCase): Parses a minimal key certificate. """
- certificate = get_key_certificate() + certificate = KeyCertificate.create()
self.assertEqual(3, certificate.version) self.assertEqual(None, certificate.address) self.assertEqual(None, certificate.dir_port) self.assertEqual('27B6B5996C426270A5C95488AA5BCEB6BCC86956', certificate.fingerprint) - self.assertTrue(CRYPTO_BLOB in certificate.identity_key) + self.assertTrue(stem.descriptor.CRYPTO_BLOB in certificate.identity_key) self.assertEqual(datetime.datetime(2011, 11, 28, 21, 51, 4), certificate.published) self.assertEqual(datetime.datetime(2012, 11, 28, 21, 51, 4), certificate.expires) - self.assertTrue(CRYPTO_BLOB in certificate.signing_key) + self.assertTrue(stem.descriptor.CRYPTO_BLOB in certificate.signing_key) self.assertEqual(None, certificate.crosscert) - self.assertTrue(CRYPTO_BLOB in certificate.certification) + self.assertTrue(stem.descriptor.CRYPTO_BLOB in certificate.certification) self.assertEqual([], certificate.get_unrecognized_lines())
def test_unrecognized_line(self): @@ -40,7 +39,7 @@ class TestKeyCertificate(unittest.TestCase): Includes unrecognized content in the descriptor. """
- certificate = get_key_certificate({'pepperjack': 'is oh so tasty!'}) + certificate = KeyCertificate.create({'pepperjack': 'is oh so tasty!'}) self.assertEqual(['pepperjack is oh so tasty!'], certificate.get_unrecognized_lines())
def test_first_and_last_lines(self): @@ -49,7 +48,7 @@ class TestKeyCertificate(unittest.TestCase): line or after the 'dir-key-certification' line. """
- content = get_key_certificate(content = True) + content = KeyCertificate.content()
for cert_text in (b'dir-address 127.0.0.1:80\n' + content, content + b'\ndir-address 127.0.0.1:80'): @@ -67,7 +66,7 @@ class TestKeyCertificate(unittest.TestCase): mandatory_fields = [entry[0] for entry in KEY_CERTIFICATE_HEADER + KEY_CERTIFICATE_FOOTER]
for excluded_field in mandatory_fields: - content = get_key_certificate(exclude = (excluded_field,), content = True) + content = KeyCertificate.content(exclude = (excluded_field,)) self.assertRaises(ValueError, KeyCertificate, content, True)
certificate = KeyCertificate(content, False) @@ -82,7 +81,7 @@ class TestKeyCertificate(unittest.TestCase): Includes blank lines, which should be ignored. """
- certificate = get_key_certificate({'dir-key-published': '2011-11-28 21:51:04\n\n\n'}) + certificate = KeyCertificate.create({'dir-key-published': '2011-11-28 21:51:04\n\n\n'}) self.assertEqual(datetime.datetime(2011, 11, 28, 21, 51, 4), certificate.published)
def test_version(self): @@ -91,14 +90,14 @@ class TestKeyCertificate(unittest.TestCase): different certificate version with the v3 parser. """
- certificate = get_key_certificate({'dir-key-certificate-version': '3'}) + certificate = KeyCertificate.create({'dir-key-certificate-version': '3'}) self.assertEqual(3, certificate.version)
- content = get_key_certificate({'dir-key-certificate-version': '4'}, content = True) + content = KeyCertificate.content({'dir-key-certificate-version': '4'}) self.assertRaises(ValueError, KeyCertificate, content, True) self.assertEqual(4, KeyCertificate(content, False).version)
- content = get_key_certificate({'dir-key-certificate-version': 'boo'}, content = True) + content = KeyCertificate.content({'dir-key-certificate-version': 'boo'}) self.assertRaises(ValueError, KeyCertificate, content, True) self.assertEqual(None, KeyCertificate(content, False).version)
@@ -107,7 +106,7 @@ class TestKeyCertificate(unittest.TestCase): Parses the dir-address field. """
- certificate = get_key_certificate({'dir-address': '127.0.0.1:80'}) + certificate = KeyCertificate.create({'dir-address': '127.0.0.1:80'}) self.assertEqual('127.0.0.1', certificate.address) self.assertEqual(80, certificate.dir_port)
@@ -123,7 +122,7 @@ class TestKeyCertificate(unittest.TestCase): )
for test_value in test_values: - content = get_key_certificate({'dir-address': test_value}, content = True) + content = KeyCertificate.content({'dir-address': test_value}) self.assertRaises(ValueError, KeyCertificate, content, True)
certificate = KeyCertificate(content, False) @@ -143,7 +142,7 @@ class TestKeyCertificate(unittest.TestCase): )
for test_value in test_values: - content = get_key_certificate({'fingerprint': test_value}, content = True) + content = KeyCertificate.content({'fingerprint': test_value}) self.assertRaises(ValueError, KeyCertificate, content, True)
certificate = KeyCertificate(content, False) @@ -165,7 +164,7 @@ class TestKeyCertificate(unittest.TestCase):
for field, attr in (('dir-key-published', 'published'), ('dir-key-expires', 'expires')): for test_value in test_values: - content = get_key_certificate({field: test_value}, content = True) + content = KeyCertificate.content({field: test_value}) self.assertRaises(ValueError, KeyCertificate, content, True)
certificate = KeyCertificate(content, False) @@ -179,16 +178,16 @@ class TestKeyCertificate(unittest.TestCase):
# the only non-mandatory field that we haven't exercised yet is dir-key-crosscert
- certificate = get_key_certificate({'dir-key-crosscert': '\n-----BEGIN ID SIGNATURE-----%s-----END ID SIGNATURE-----' % CRYPTO_BLOB}) - self.assertTrue(CRYPTO_BLOB in certificate.crosscert) + certificate = KeyCertificate.create({'dir-key-crosscert': '\n-----BEGIN ID SIGNATURE-----%s-----END ID SIGNATURE-----' % stem.descriptor.CRYPTO_BLOB}) + self.assertTrue(stem.descriptor.CRYPTO_BLOB in certificate.crosscert)
- test_value = '\n-----BEGIN ID SIGNATURE-----%s-----END UGABUGA SIGNATURE-----' % CRYPTO_BLOB + test_value = '\n-----BEGIN ID SIGNATURE-----%s-----END UGABUGA SIGNATURE-----' % stem.descriptor.CRYPTO_BLOB
for field, attr in (('dir-identity-key', 'identity_key'), ('dir-signing-key', 'signing_key'), ('dir-key-crosscert', 'crosscert'), ('dir-key-certification', 'certification')): - content = get_key_certificate({field: test_value}, content = True) + content = KeyCertificate.content({field: test_value}) self.assertRaises(ValueError, KeyCertificate, content, True)
certificate = KeyCertificate(content, False) @@ -199,5 +198,5 @@ class TestKeyCertificate(unittest.TestCase): Checks that we validate the type of crypto content we receive. """
- content = get_key_certificate({'dir-identity-key': '\n-----BEGIN MD5SUM-----%s-----END MD5SUM-----' % CRYPTO_BLOB}, content = True) + content = KeyCertificate.content({'dir-identity-key': '\n-----BEGIN MD5SUM-----%s-----END MD5SUM-----' % stem.descriptor.CRYPTO_BLOB}) self.assertRaises(ValueError, KeyCertificate, content, True) diff --git a/test/unit/tutorial.py b/test/unit/tutorial.py index 48d0abb..cdfb014 100644 --- a/test/unit/tutorial.py +++ b/test/unit/tutorial.py @@ -9,9 +9,9 @@ import stem.descriptor.remote
from stem.control import Controller from stem.descriptor.reader import DescriptorReader -from stem.descriptor.server_descriptor import RelayDescriptor from stem.descriptor.router_status_entry import RouterStatusEntryV2, RouterStatusEntryV3 -from test import mocking +from stem.descriptor.networkstatus import NetworkStatusDocumentV3 +from stem.descriptor.server_descriptor import RelayDescriptor from test.unit import exec_documentation_example
try: @@ -151,11 +151,7 @@ class TestTutorial(unittest.TestCase): for desc in parse_file(open('/home/atagar/.tor/cached-consensus')): print('found relay %s (%s)' % (desc.nickname, desc.fingerprint))
- test_file = io.BytesIO(mocking.get_network_status_document_v3( - routers = [RouterStatusEntryV3.create()], - content = True, - )) - + test_file = io.BytesIO(NetworkStatusDocumentV3.content(routers = [RouterStatusEntryV3.create()])) test_file.name = '/home/atagar/.tor/cached-consensus' open_mock.return_value = test_file
diff --git a/test/unit/tutorial_examples.py b/test/unit/tutorial_examples.py index b3ac813..c979f07 100644 --- a/test/unit/tutorial_examples.py +++ b/test/unit/tutorial_examples.py @@ -17,12 +17,12 @@ import stem.prereq
from stem.control import Controller from stem.util import str_type +from stem.descriptor.networkstatus import NetworkStatusDocumentV3 from stem.descriptor.remote import DIRECTORY_AUTHORITIES -from stem.descriptor.server_descriptor import RelayDescriptor from stem.descriptor.router_status_entry import ROUTER_STATUS_ENTRY_V3_HEADER, RouterStatusEntryV3 +from stem.descriptor.server_descriptor import RelayDescriptor
from test import mocking -from test.mocking import get_network_status_document_v3 from test.unit import exec_documentation_example
try: @@ -274,8 +274,8 @@ class TestTutorialExamples(unittest.TestCase): ]
query_mock().run.side_effect = [ - [get_network_status_document_v3(routers = (entry[0], entry[1], entry[2], entry[3], entry[4]))], - [get_network_status_document_v3(routers = (entry[5], entry[6], entry[7], entry[8], entry[9]))], + [NetworkStatusDocumentV3.create(routers = (entry[0], entry[1], entry[2], entry[3], entry[4]))], + [NetworkStatusDocumentV3.create(routers = (entry[5], entry[6], entry[7], entry[8], entry[9]))], ]
exec_documentation_example('compare_flags.py') @@ -332,7 +332,7 @@ class TestTutorialExamples(unittest.TestCase): for fingerprint, relay in consensus.routers.items(): print('%s: %s' % (fingerprint, relay.nickname))
- network_status = get_network_status_document_v3(routers = (RouterStatusEntryV3.create(),)) + network_status = NetworkStatusDocumentV3.create(routers = (RouterStatusEntryV3.create(),)) query_mock().run.return_value = [network_status] parse_file_mock.return_value = itertools.cycle([network_status])