tor-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
May 2017
- 16 participants
- 1135 discussions
commit 72b90a84f8bd9557c201be7c45c79a1ea73e1bed
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon May 1 12:15:13 2017 -0700
Microdescriptor creation
---
stem/descriptor/microdescriptor.py | 10 ++++++++
test/mocking.py | 27 ----------------------
test/unit/descriptor/microdescriptor.py | 41 ++++++++++++++++-----------------
3 files changed, 30 insertions(+), 48 deletions(-)
diff --git a/stem/descriptor/microdescriptor.py b/stem/descriptor/microdescriptor.py
index a1e5346..fb3c1e6 100644
--- a/stem/descriptor/microdescriptor.py
+++ b/stem/descriptor/microdescriptor.py
@@ -69,7 +69,9 @@ import hashlib
import stem.exit_policy
from stem.descriptor import (
+ CRYPTO_BLOB,
Descriptor,
+ _descriptor_content,
_descriptor_components,
_read_until_keywords,
_values,
@@ -102,6 +104,10 @@ SINGLE_FIELDS = (
'pr',
)
+MICRODESCRIPTOR = (
+ ('onion-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
+)
+
def _parse_file(descriptor_file, validate = False, **kwargs):
"""
@@ -256,6 +262,10 @@ class Microdescriptor(Descriptor):
'id': _parse_id_line,
}
+ @classmethod
+ def content(cls, attr = None, exclude = ()):
+ return _descriptor_content(attr, exclude, MICRODESCRIPTOR)
+
def __init__(self, raw_contents, validate = False, annotations = None):
super(Microdescriptor, self).__init__(raw_contents, lazy_load = not validate)
self._annotation_lines = annotations if annotations else []
diff --git a/test/mocking.py b/test/mocking.py
index ce9a458..f1d859e 100644
--- a/test/mocking.py
+++ b/test/mocking.py
@@ -13,9 +13,6 @@ Helper functions for creating mock objects.
get_message - stem.response.ControlMessage
get_protocolinfo_response - stem.response.protocolinfo.ProtocolInfoResponse
- stem.descriptor.microdescriptor
- get_microdescriptor - Microdescriptor
-
stem.descriptor.networkstatus
get_directory_authority - DirectoryAuthority
get_key_certificate - KeyCertificate
@@ -72,10 +69,6 @@ DOC_SIG = stem.descriptor.networkstatus.DocumentSignature(
'BF112F1C6D5543CFD0A32215ACABD4197B5279AD',
'-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB)
-MICRODESCRIPTOR = (
- ('onion-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
-)
-
ROUTER_STATUS_ENTRY_V2_HEADER = (
('r', 'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0'),
)
@@ -319,26 +312,6 @@ def _get_descriptor_content(attr = None, exclude = (), header_template = (), foo
return stem.util.str_tools._to_bytes('\n'.join(header_content + remainder + footer_content))
-def get_microdescriptor(attr = None, exclude = (), content = False):
- """
- Provides the descriptor content for...
- stem.descriptor.microdescriptor.Microdescriptor
-
- :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: Microdescriptor for the requested descriptor content
- """
-
- desc_content = _get_descriptor_content(attr, exclude, MICRODESCRIPTOR)
-
- if content:
- return desc_content
- else:
- return stem.descriptor.microdescriptor.Microdescriptor(desc_content, validate = True)
-
-
def get_router_status_entry_v2(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
diff --git a/test/unit/descriptor/microdescriptor.py b/test/unit/descriptor/microdescriptor.py
index f9350d2..35b8de0 100644
--- a/test/unit/descriptor/microdescriptor.py
+++ b/test/unit/descriptor/microdescriptor.py
@@ -11,11 +11,6 @@ import stem.descriptor
from stem.util import str_type
from stem.descriptor.microdescriptor import Microdescriptor
-from test.mocking import (
- get_microdescriptor,
- CRYPTO_BLOB,
-)
-
from test.unit.descriptor import get_resource
FIRST_ONION_KEY = """\
@@ -87,9 +82,9 @@ class TestMicrodescriptor(unittest.TestCase):
attributes.
"""
- desc = get_microdescriptor()
+ desc = Microdescriptor.create()
- self.assertTrue(CRYPTO_BLOB in desc.onion_key)
+ self.assertTrue(stem.descriptor.CRYPTO_BLOB in desc.onion_key)
self.assertEqual(None, desc.ntor_onion_key)
self.assertEqual([], desc.or_addresses)
self.assertEqual([], desc.family)
@@ -106,7 +101,7 @@ class TestMicrodescriptor(unittest.TestCase):
Includes unrecognized content in the descriptor.
"""
- desc = get_microdescriptor({'pepperjack': 'is oh so tasty!'})
+ desc = Microdescriptor.create({'pepperjack': 'is oh so tasty!'})
self.assertEqual(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
def test_proceeding_line(self):
@@ -114,7 +109,7 @@ class TestMicrodescriptor(unittest.TestCase):
Includes a line prior to the 'onion-key' entry.
"""
- desc_text = b'family Amunet1\n' + get_microdescriptor(content = True)
+ desc_text = b'family Amunet1\n' + Microdescriptor.content()
self.assertRaises(ValueError, Microdescriptor, desc_text, True)
desc = Microdescriptor(desc_text, validate = False)
@@ -125,7 +120,7 @@ class TestMicrodescriptor(unittest.TestCase):
Sanity test with both an IPv4 and IPv6 address.
"""
- desc_text = get_microdescriptor(content = True)
+ desc_text = Microdescriptor.content()
desc_text += b'\na 10.45.227.253:9001'
desc_text += b'\na [fd9f:2e19:3bcf::02:9970]:9001'
@@ -142,12 +137,12 @@ class TestMicrodescriptor(unittest.TestCase):
Check the family line.
"""
- desc = get_microdescriptor({'family': 'Amunet1 Amunet2 Amunet3'})
+ desc = Microdescriptor.create({'family': 'Amunet1 Amunet2 Amunet3'})
self.assertEqual(['Amunet1', 'Amunet2', 'Amunet3'], desc.family)
# try multiple family lines
- desc_text = get_microdescriptor(content = True)
+ desc_text = Microdescriptor.content()
desc_text += b'\nfamily Amunet1'
desc_text += b'\nfamily Amunet2'
@@ -163,7 +158,7 @@ class TestMicrodescriptor(unittest.TestCase):
field so we're not investing much effort here.
"""
- desc = get_microdescriptor({'p': 'accept 80,110,143,443'})
+ desc = Microdescriptor.create({'p': 'accept 80,110,143,443'})
self.assertEqual(stem.exit_policy.MicroExitPolicy('accept 80,110,143,443'), desc.exit_policy)
def test_protocols(self):
@@ -171,7 +166,7 @@ class TestMicrodescriptor(unittest.TestCase):
Basic check for 'pr' lines.
"""
- desc = get_microdescriptor({'pr': 'Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2'})
+ desc = Microdescriptor.create({'pr': 'Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2'})
self.assertEqual(10, len(desc.protocols))
def test_identifier(self):
@@ -179,16 +174,18 @@ class TestMicrodescriptor(unittest.TestCase):
Basic check for 'id' lines.
"""
- desc = get_microdescriptor({'id': 'rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4'})
+ desc = Microdescriptor.create({'id': 'rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4'})
self.assertEqual({'rsa1024': 'Cd47okjCHD83YGzThGBDptXs9Z4'}, desc.identifiers)
self.assertEqual('rsa1024', desc.identifier_type)
self.assertEqual('Cd47okjCHD83YGzThGBDptXs9Z4', desc.identifier)
# check when there's multiple key types
- desc_text = b'\n'.join((get_microdescriptor(content = True),
- b'id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4',
- b'id ed25519 50f6ddbecdc848dcc6b818b14d1'))
+ desc_text = b'\n'.join((
+ Microdescriptor.content(),
+ b'id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4',
+ b'id ed25519 50f6ddbecdc848dcc6b818b14d1',
+ ))
desc = Microdescriptor(desc_text, validate = True)
self.assertEqual({'rsa1024': 'Cd47okjCHD83YGzThGBDptXs9Z4', 'ed25519': '50f6ddbecdc848dcc6b818b14d1'}, desc.identifiers)
@@ -197,9 +194,11 @@ class TestMicrodescriptor(unittest.TestCase):
# check when there's conflicting keys
- desc_text = b'\n'.join((get_microdescriptor(content = True),
- b'id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4',
- b'id rsa1024 50f6ddbecdc848dcc6b818b14d1'))
+ desc_text = b'\n'.join((
+ Microdescriptor.content(),
+ b'id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4',
+ b'id rsa1024 50f6ddbecdc848dcc6b818b14d1',
+ ))
desc = Microdescriptor(desc_text)
self.assertEqual({}, desc.identifiers)
1
0
commit 7b5be00d5ddf5f668d5a366845693cfa215ed0b6
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon May 1 11:02:18 2017 -0700
RelayExtraInfoDescriptor creation
---
stem/descriptor/extrainfo_descriptor.py | 15 ++++++
test/mocking.py | 30 -----------
test/unit/descriptor/extrainfo_descriptor.py | 81 ++++++++++++++--------------
3 files changed, 55 insertions(+), 71 deletions(-)
diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index 716b75c..7a2e7b1 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -77,8 +77,10 @@ import stem.util.enum
import stem.util.str_tools
from stem.descriptor import (
+ CRYPTO_BLOB,
PGP_BLOCK_END,
Descriptor,
+ _descriptor_content,
_read_until_keywords,
_get_descriptor_components,
_value,
@@ -155,6 +157,15 @@ SINGLE_FIELDS = (
'exit-streams-opened',
)
+RELAY_EXTRAINFO_HEADER = (
+ ('extra-info', 'ninja B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48'),
+ ('published', '2012-05-05 17:03:50'),
+)
+
+RELAY_EXTRAINFO_FOOTER = (
+ ('router-signature', '\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB),
+)
+
_timestamp_re = re.compile('^(.*) \(([0-9]+) s\)( .*)?$')
_locale_re = re.compile('^[a-zA-Z0-9\?]{2}$')
@@ -914,6 +925,10 @@ class RelayExtraInfoDescriptor(ExtraInfoDescriptor):
'router-signature': _parse_router_signature_line,
})
+ @classmethod
+ def content(cls, attr = None, exclude = ()):
+ return _descriptor_content(attr, exclude, RELAY_EXTRAINFO_HEADER, RELAY_EXTRAINFO_FOOTER)
+
@lru_cache()
def digest(self):
# our digest is calculated from everything except our signature
diff --git a/test/mocking.py b/test/mocking.py
index caf7d44..dc08a61 100644
--- a/test/mocking.py
+++ b/test/mocking.py
@@ -17,7 +17,6 @@ Helper functions for creating mock objects.
get_microdescriptor - Microdescriptor
stem.descriptor.extrainfo_descriptor
- get_relay_extrainfo_descriptor - RelayExtraInfoDescriptor
get_bridge_extrainfo_descriptor - BridgeExtraInfoDescriptor
stem.descriptor.networkstatus
@@ -76,15 +75,6 @@ DOC_SIG = stem.descriptor.networkstatus.DocumentSignature(
'BF112F1C6D5543CFD0A32215ACABD4197B5279AD',
'-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB)
-RELAY_EXTRAINFO_HEADER = (
- ('extra-info', 'ninja B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48'),
- ('published', '2012-05-05 17:03:50'),
-)
-
-RELAY_EXTRAINFO_FOOTER = (
- ('router-signature', '\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB),
-)
-
BRIDGE_EXTRAINFO_HEADER = (
('extra-info', 'ec2bridgereaac65a3 1EC248422B57D9C0BD751892FE787585407479A4'),
('published', '2012-05-05 17:03:50'),
@@ -341,26 +331,6 @@ def _get_descriptor_content(attr = None, exclude = (), header_template = (), foo
return stem.util.str_tools._to_bytes('\n'.join(header_content + remainder + footer_content))
-def get_relay_extrainfo_descriptor(attr = None, exclude = (), content = False):
- """
- Provides the descriptor content for...
- stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor
-
- :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: RelayExtraInfoDescriptor for the requested descriptor content
- """
-
- desc_content = _get_descriptor_content(attr, exclude, RELAY_EXTRAINFO_HEADER, RELAY_EXTRAINFO_FOOTER)
-
- if content:
- return desc_content
- else:
- return stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor(desc_content, validate = True)
-
-
def get_bridge_extrainfo_descriptor(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index cab3440..61f5ee7 100644
--- a/test/unit/descriptor/extrainfo_descriptor.py
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -10,7 +10,7 @@ import stem.descriptor
from stem.descriptor.extrainfo_descriptor import RelayExtraInfoDescriptor, DirResponse, DirStat
-from test.mocking import get_relay_extrainfo_descriptor, get_bridge_extrainfo_descriptor, CRYPTO_BLOB
+from test.mocking import get_bridge_extrainfo_descriptor, CRYPTO_BLOB
from test.unit.descriptor import get_resource
@@ -183,8 +183,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
attributes.
"""
- desc = get_relay_extrainfo_descriptor()
-
+ desc = RelayExtraInfoDescriptor.create()
self.assertEqual('ninja', desc.nickname)
self.assertEqual('B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48', desc.fingerprint)
self.assertTrue(CRYPTO_BLOB in desc.signature)
@@ -194,7 +193,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
Includes unrecognized content in the descriptor.
"""
- desc = get_relay_extrainfo_descriptor({'pepperjack': 'is oh so tasty!'})
+ desc = RelayExtraInfoDescriptor.create({'pepperjack': 'is oh so tasty!'})
self.assertEqual(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
def test_proceeding_line(self):
@@ -202,7 +201,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
Includes a line prior to the 'extra-info' entry.
"""
- desc_text = b'exit-streams-opened port=80\n' + get_relay_extrainfo_descriptor(content = True)
+ desc_text = b'exit-streams-opened port=80\n' + RelayExtraInfoDescriptor.content()
self._expect_invalid_attr(desc_text)
def test_trailing_line(self):
@@ -210,7 +209,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
Includes a line after the 'router-signature' entry.
"""
- desc_text = get_relay_extrainfo_descriptor(content = True) + b'\nexit-streams-opened port=80'
+ desc_text = RelayExtraInfoDescriptor.content() + b'\nexit-streams-opened port=80'
self._expect_invalid_attr(desc_text)
def test_extrainfo_line_missing_fields(self):
@@ -228,7 +227,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({'extra-info': entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({'extra-info': entry})
desc = self._expect_invalid_attr(desc_text, 'nickname')
self.assertEqual(None, desc.nickname)
self.assertEqual(None, desc.fingerprint)
@@ -240,10 +239,10 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
"""
geoip_db_digest = '916A3CA8B7DF61473D5AE5B21711F35F301CE9E8'
- desc = get_relay_extrainfo_descriptor({'geoip-db-digest': geoip_db_digest})
+ desc = RelayExtraInfoDescriptor.create({'geoip-db-digest': geoip_db_digest})
self.assertEqual(geoip_db_digest, desc.geoip_db_digest)
- desc = get_relay_extrainfo_descriptor({'geoip6-db-digest': geoip_db_digest})
+ desc = RelayExtraInfoDescriptor.create({'geoip6-db-digest': geoip_db_digest})
self.assertEqual(geoip_db_digest, desc.geoip6_db_digest)
test_entries = (
@@ -255,10 +254,10 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({'geoip-db-digest': entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({'geoip-db-digest': entry})
self._expect_invalid_attr(desc_text, 'geoip_db_digest')
- desc_text = get_relay_extrainfo_descriptor({'geoip6-db-digest': entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({'geoip6-db-digest': entry})
self._expect_invalid_attr(desc_text, 'geoip6_db_digest')
def test_cell_circuits_per_decile(self):
@@ -272,7 +271,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in ('0', '11', '25'):
- desc = get_relay_extrainfo_descriptor({'cell-circuits-per-decile': entry})
+ desc = RelayExtraInfoDescriptor.create({'cell-circuits-per-decile': entry})
self.assertEqual(int(entry), desc.cell_circuits_per_decile)
test_entries = (
@@ -283,7 +282,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({'cell-circuits-per-decile': entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({'cell-circuits-per-decile': entry})
self._expect_invalid_attr(desc_text, 'cell_circuits_per_decile')
def test_dir_response_lines(self):
@@ -297,7 +296,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
unknown_attr = attr + '_unknown'
test_value = 'ok=0,unavailable=0,not-found=984,not-modified=0,something-new=7'
- desc = get_relay_extrainfo_descriptor({keyword: test_value})
+ desc = RelayExtraInfoDescriptor.create({keyword: test_value})
self.assertEqual(0, getattr(desc, attr)[DirResponse.OK])
self.assertEqual(0, getattr(desc, attr)[DirResponse.UNAVAILABLE])
self.assertEqual(984, getattr(desc, attr)[DirResponse.NOT_FOUND])
@@ -311,7 +310,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({keyword: entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
desc = self._expect_invalid_attr(desc_text)
self.assertEqual(None, getattr(desc, attr))
self.assertEqual(None, getattr(desc, unknown_attr))
@@ -327,7 +326,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
unknown_attr = attr + '_unknown'
test_value = 'complete=2712,timeout=32,running=4,min=741,d1=14507,d2=22702,q1=28881,d3=38277,d4=73729,md=111455,d6=168231,d7=257218,q3=319833,d8=390507,d9=616301,something-new=11,max=29917857'
- desc = get_relay_extrainfo_descriptor({keyword: test_value})
+ desc = RelayExtraInfoDescriptor.create({keyword: test_value})
self.assertEqual(2712, getattr(desc, attr)[DirStat.COMPLETE])
self.assertEqual(32, getattr(desc, attr)[DirStat.TIMEOUT])
self.assertEqual(4, getattr(desc, attr)[DirStat.RUNNING])
@@ -353,7 +352,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({keyword: entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
desc = self._expect_invalid_attr(desc_text)
self.assertEqual(None, getattr(desc, attr))
self.assertEqual(None, getattr(desc, unknown_attr))
@@ -363,7 +362,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
Parses the conn-bi-direct line with valid and invalid data.
"""
- desc = get_relay_extrainfo_descriptor({'conn-bi-direct': '2012-05-03 12:07:50 (500 s) 277431,12089,0,2134'})
+ desc = RelayExtraInfoDescriptor.create({'conn-bi-direct': '2012-05-03 12:07:50 (500 s) 277431,12089,0,2134'})
self.assertEqual(datetime.datetime(2012, 5, 3, 12, 7, 50), desc.conn_bi_direct_end)
self.assertEqual(500, desc.conn_bi_direct_interval)
self.assertEqual(277431, desc.conn_bi_direct_below)
@@ -384,7 +383,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({'conn-bi-direct': entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({'conn-bi-direct': entry})
desc = self._expect_invalid_attr(desc_text)
self.assertEqual(None, desc.conn_bi_direct_end)
self.assertEqual(None, desc.conn_bi_direct_interval)
@@ -410,7 +409,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for test_value, expected_value in test_entries:
- desc = get_relay_extrainfo_descriptor({keyword: test_value})
+ desc = RelayExtraInfoDescriptor.create({keyword: test_value})
self.assertEqual(expected_value, getattr(desc, attr))
test_entries = (
@@ -421,7 +420,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({keyword: entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
self._expect_invalid_attr(desc_text, attr)
def test_number_list_lines(self):
@@ -441,7 +440,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for test_value, expected_value in test_entries:
- desc = get_relay_extrainfo_descriptor({keyword: test_value})
+ desc = RelayExtraInfoDescriptor.create({keyword: test_value})
self.assertEqual(expected_value, getattr(desc, attr))
test_entries = (
@@ -451,7 +450,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry, expected in test_entries:
- desc_text = get_relay_extrainfo_descriptor({keyword: entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
self._expect_invalid_attr(desc_text, attr, expected)
def test_timestamp_lines(self):
@@ -463,7 +462,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
for keyword in ('published', 'geoip-start-time'):
attr = keyword.replace('-', '_')
- desc = get_relay_extrainfo_descriptor({keyword: '2012-05-03 12:07:50'})
+ desc = RelayExtraInfoDescriptor.create({keyword: '2012-05-03 12:07:50'})
self.assertEqual(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, attr))
test_entries = (
@@ -474,7 +473,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({keyword: entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
self._expect_invalid_attr(desc_text, attr)
def test_timestamp_and_interval_lines(self):
@@ -487,7 +486,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
end_attr = keyword.replace('-', '_').replace('dirreq', 'dir')
interval_attr = end_attr[:-4] + '_interval'
- desc = get_relay_extrainfo_descriptor({keyword: '2012-05-03 12:07:50 (500 s)'})
+ desc = RelayExtraInfoDescriptor.create({keyword: '2012-05-03 12:07:50 (500 s)'})
self.assertEqual(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, end_attr))
self.assertEqual(500, getattr(desc, interval_attr))
@@ -502,7 +501,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({'entry-stats-end': entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({'entry-stats-end': entry})
desc = self._expect_invalid_attr(desc_text)
self.assertEqual(None, desc.entry_stats_end)
self.assertEqual(None, desc.entry_stats_interval)
@@ -519,13 +518,13 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
interval_attr = base_attr + '_interval'
values_attr = base_attr + '_values'
- desc = get_relay_extrainfo_descriptor({keyword: '2012-05-03 12:07:50 (500 s) 50,11,5'})
+ desc = RelayExtraInfoDescriptor.create({keyword: '2012-05-03 12:07:50 (500 s) 50,11,5'})
self.assertEqual(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, end_attr))
self.assertEqual(500, getattr(desc, interval_attr))
self.assertEqual([50, 11, 5], getattr(desc, values_attr))
for test_value in ('', ' '):
- desc = get_relay_extrainfo_descriptor({'write-history': '2012-05-03 12:07:50 (500 s)%s' % test_value})
+ desc = RelayExtraInfoDescriptor.create({'write-history': '2012-05-03 12:07:50 (500 s)%s' % test_value})
self.assertEqual(datetime.datetime(2012, 5, 3, 12, 7, 50), desc.write_history_end)
self.assertEqual(500, desc.write_history_interval)
self.assertEqual([], desc.write_history_values)
@@ -540,7 +539,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({'write-history': entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({'write-history': entry})
desc = self._expect_invalid_attr(desc_text)
self.assertEqual(None, desc.write_history_end)
self.assertEqual(None, desc.write_history_interval)
@@ -562,7 +561,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for test_value, expected_value in test_entries:
- desc = get_relay_extrainfo_descriptor({keyword: test_value})
+ desc = RelayExtraInfoDescriptor.create({keyword: test_value})
self.assertEqual(expected_value, getattr(desc, attr))
test_entries = (
@@ -575,7 +574,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({keyword: entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
self._expect_invalid_attr(desc_text, attr)
def test_hidden_service_stats_end(self):
@@ -583,7 +582,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
Exercise the hidserv-stats-end, which should be a simple date.
"""
- desc = get_relay_extrainfo_descriptor({'hidserv-stats-end': '2012-05-03 12:07:50'})
+ desc = RelayExtraInfoDescriptor.create({'hidserv-stats-end': '2012-05-03 12:07:50'})
self.assertEqual(datetime.datetime(2012, 5, 3, 12, 7, 50), desc.hs_stats_end)
test_entries = (
@@ -597,7 +596,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({'hidserv-stats-end': entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({'hidserv-stats-end': entry})
self._expect_invalid_attr(desc_text, 'hs_stats_end')
def test_hidden_service_stats(self):
@@ -623,24 +622,24 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
for keyword, stat_attr, extra_attr in attributes:
# just the numeric stat (no extra attributes)
- desc = get_relay_extrainfo_descriptor({keyword: '345'})
+ desc = RelayExtraInfoDescriptor.create({keyword: '345'})
self.assertEqual(345, getattr(desc, stat_attr))
self.assertEqual({}, getattr(desc, extra_attr))
# values can be negative (#15276)
- desc = get_relay_extrainfo_descriptor({keyword: '-345'})
+ desc = RelayExtraInfoDescriptor.create({keyword: '-345'})
self.assertEqual(-345, getattr(desc, stat_attr))
self.assertEqual({}, getattr(desc, extra_attr))
# with extra attributes
- desc = get_relay_extrainfo_descriptor({keyword: '345 spiffy=true snowmen=neat'})
+ desc = RelayExtraInfoDescriptor.create({keyword: '345 spiffy=true snowmen=neat'})
self.assertEqual(345, getattr(desc, stat_attr))
self.assertEqual({'spiffy': 'true', 'snowmen': 'neat'}, getattr(desc, extra_attr))
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({keyword: entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
self._expect_invalid_attr(desc_text, stat_attr)
self._expect_invalid_attr(desc_text, extra_attr, {})
@@ -659,7 +658,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for test_value, expected_value in test_entries:
- desc = get_relay_extrainfo_descriptor({keyword: test_value})
+ desc = RelayExtraInfoDescriptor.create({keyword: test_value})
self.assertEqual(expected_value, getattr(desc, attr))
test_entries = (
@@ -670,7 +669,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = get_relay_extrainfo_descriptor({keyword: entry}, content = True)
+ desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
self._expect_invalid_attr(desc_text, attr)
def test_minimal_bridge_descriptor(self):
@@ -725,7 +724,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
self.assertEqual({'obfs3': (None, None, None)}, desc.transport)
self.assertEqual([], desc.get_unrecognized_lines())
- desc = get_relay_extrainfo_descriptor({'transport': 'obfs2 83.212.96.201:33570'})
+ desc = RelayExtraInfoDescriptor.create({'transport': 'obfs2 83.212.96.201:33570'})
self.assertEqual({'obfs2': ('83.212.96.201', 33570, [])}, desc.transport)
self.assertEqual([], desc.get_unrecognized_lines())
1
0
commit ceeec7989007371508b024d779fc12fd62a68bd6
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon May 1 11:11:18 2017 -0700
BridgeExtraInfoDescriptor creation
---
stem/descriptor/extrainfo_descriptor.py | 13 +++++++++++
test/mocking.py | 32 ----------------------------
test/unit/descriptor/extrainfo_descriptor.py | 27 +++++++++++++----------
3 files changed, 29 insertions(+), 43 deletions(-)
diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index 7a2e7b1..848ba3e 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -166,6 +166,15 @@ RELAY_EXTRAINFO_FOOTER = (
('router-signature', '\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB),
)
+BRIDGE_EXTRAINFO_HEADER = (
+ ('extra-info', 'ec2bridgereaac65a3 1EC248422B57D9C0BD751892FE787585407479A4'),
+ ('published', '2012-05-05 17:03:50'),
+)
+
+BRIDGE_EXTRAINFO_FOOTER = (
+ ('router-digest', '006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4'),
+)
+
_timestamp_re = re.compile('^(.*) \(([0-9]+) s\)( .*)?$')
_locale_re = re.compile('^[a-zA-Z0-9\?]{2}$')
@@ -961,6 +970,10 @@ class BridgeExtraInfoDescriptor(ExtraInfoDescriptor):
'router-digest': _parse_router_digest_line,
})
+ @classmethod
+ def content(cls, attr = None, exclude = ()):
+ return _descriptor_content(attr, exclude, BRIDGE_EXTRAINFO_HEADER, BRIDGE_EXTRAINFO_FOOTER)
+
def digest(self):
return self._digest
diff --git a/test/mocking.py b/test/mocking.py
index dc08a61..ce9a458 100644
--- a/test/mocking.py
+++ b/test/mocking.py
@@ -16,9 +16,6 @@ Helper functions for creating mock objects.
stem.descriptor.microdescriptor
get_microdescriptor - Microdescriptor
- stem.descriptor.extrainfo_descriptor
- get_bridge_extrainfo_descriptor - BridgeExtraInfoDescriptor
-
stem.descriptor.networkstatus
get_directory_authority - DirectoryAuthority
get_key_certificate - KeyCertificate
@@ -75,15 +72,6 @@ DOC_SIG = stem.descriptor.networkstatus.DocumentSignature(
'BF112F1C6D5543CFD0A32215ACABD4197B5279AD',
'-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB)
-BRIDGE_EXTRAINFO_HEADER = (
- ('extra-info', 'ec2bridgereaac65a3 1EC248422B57D9C0BD751892FE787585407479A4'),
- ('published', '2012-05-05 17:03:50'),
-)
-
-BRIDGE_EXTRAINFO_FOOTER = (
- ('router-digest', '006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4'),
-)
-
MICRODESCRIPTOR = (
('onion-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
)
@@ -331,26 +319,6 @@ def _get_descriptor_content(attr = None, exclude = (), header_template = (), foo
return stem.util.str_tools._to_bytes('\n'.join(header_content + remainder + footer_content))
-def get_bridge_extrainfo_descriptor(attr = None, exclude = (), content = False):
- """
- Provides the descriptor content for...
- stem.descriptor.extrainfo_descriptor.BridgeExtraInfoDescriptor
-
- :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: BridgeExtraInfoDescriptor for the requested descriptor content
- """
-
- desc_content = _get_descriptor_content(attr, exclude, BRIDGE_EXTRAINFO_HEADER, BRIDGE_EXTRAINFO_FOOTER)
-
- if content:
- return desc_content
- else:
- return stem.descriptor.extrainfo_descriptor.BridgeExtraInfoDescriptor(desc_content, validate = True)
-
-
def get_microdescriptor(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index 61f5ee7..2269000 100644
--- a/test/unit/descriptor/extrainfo_descriptor.py
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -8,9 +8,14 @@ import unittest
import stem.descriptor
-from stem.descriptor.extrainfo_descriptor import RelayExtraInfoDescriptor, DirResponse, DirStat
+from stem.descriptor.extrainfo_descriptor import (
+ RelayExtraInfoDescriptor,
+ BridgeExtraInfoDescriptor,
+ DirResponse,
+ DirStat,
+)
-from test.mocking import get_bridge_extrainfo_descriptor, CRYPTO_BLOB
+from test.mocking import CRYPTO_BLOB
from test.unit.descriptor import get_resource
@@ -677,7 +682,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
Basic sanity check that we can parse a descriptor with minimal attributes.
"""
- desc = get_bridge_extrainfo_descriptor()
+ desc = BridgeExtraInfoDescriptor.create()
self.assertEqual('ec2bridgereaac65a3', desc.nickname)
self.assertEqual('1EC248422B57D9C0BD751892FE787585407479A4', desc.fingerprint)
@@ -692,13 +697,13 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
Parses the 'bridge-ip-versions' line, which only appears in bridges.
"""
- desc = get_bridge_extrainfo_descriptor({'bridge-ip-versions': 'v4=16,v6=40'})
+ desc = BridgeExtraInfoDescriptor.create({'bridge-ip-versions': 'v4=16,v6=40'})
self.assertEqual({'v4': 16, 'v6': 40}, desc.ip_versions)
- desc = get_bridge_extrainfo_descriptor({'bridge-ip-versions': ''})
+ desc = BridgeExtraInfoDescriptor.create({'bridge-ip-versions': ''})
self.assertEqual({}, desc.ip_versions)
- desc_text = get_bridge_extrainfo_descriptor({'bridge-ip-versions': 'v4=24.5'}, content = True)
+ desc_text = BridgeExtraInfoDescriptor.content({'bridge-ip-versions': 'v4=24.5'})
self.assertRaises(ValueError, RelayExtraInfoDescriptor, desc_text, True)
def test_bridge_ip_transports_line(self):
@@ -706,13 +711,13 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
Parses the 'bridge-ip-transports' line, which only appears in bridges.
"""
- desc = get_bridge_extrainfo_descriptor({'bridge-ip-transports': '<OR>=16,<??>=40'})
+ desc = BridgeExtraInfoDescriptor.create({'bridge-ip-transports': '<OR>=16,<??>=40'})
self.assertEqual({'<OR>': 16, '<??>': 40}, desc.ip_transports)
- desc = get_bridge_extrainfo_descriptor({'bridge-ip-transports': ''})
+ desc = BridgeExtraInfoDescriptor.create({'bridge-ip-transports': ''})
self.assertEqual({}, desc.ip_transports)
- desc_text = get_bridge_extrainfo_descriptor({'bridge-ip-transports': '<OR>=24.5'}, content = True)
+ desc_text = BridgeExtraInfoDescriptor.content({'bridge-ip-transports': '<OR>=24.5'})
self.assertRaises(ValueError, RelayExtraInfoDescriptor, desc_text, True)
def test_transport_line(self):
@@ -720,7 +725,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
Basic exercise for both a bridge and relay's transport entry.
"""
- desc = get_bridge_extrainfo_descriptor({'transport': 'obfs3'})
+ desc = BridgeExtraInfoDescriptor.create({'transport': 'obfs3'})
self.assertEqual({'obfs3': (None, None, None)}, desc.transport)
self.assertEqual([], desc.get_unrecognized_lines())
@@ -729,7 +734,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
self.assertEqual([], desc.get_unrecognized_lines())
# multiple transport lines
- desc = get_bridge_extrainfo_descriptor({'transport': 'obfs3\ntransport obfs4'})
+ desc = BridgeExtraInfoDescriptor.create({'transport': 'obfs3\ntransport obfs4'})
self.assertEqual({'obfs3': (None, None, None), 'obfs4': (None, None, None)}, desc.transport)
self.assertEqual([], desc.get_unrecognized_lines())
1
0
commit 13224f9ecda24fd8b56af6a3f9f8a4a897191a61
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon May 1 11:49:08 2017 -0700
expect_invalid_attr() testing helper
Four unit test modules define their own nearly identical _expect_invalid_attr()
helper. Creating a unified one they can share. Presently I can only swap two of
the modules over because it requires create(). I'll migrate the rest as we add
it.
---
test/unit/descriptor/__init__.py | 25 ++++++++
test/unit/descriptor/extrainfo_descriptor.py | 90 +++++++++-------------------
test/unit/descriptor/server_descriptor.py | 60 +++++++------------
3 files changed, 76 insertions(+), 99 deletions(-)
diff --git a/test/unit/descriptor/__init__.py b/test/unit/descriptor/__init__.py
index b7696ac..cd35ae1 100644
--- a/test/unit/descriptor/__init__.py
+++ b/test/unit/descriptor/__init__.py
@@ -23,3 +23,28 @@ def get_resource(filename):
"""
return os.path.join(DESCRIPTOR_TEST_DATA, filename)
+
+
+def base_expect_invalid_attr(cls, default_attr, default_value, test, desc_attrs, attr = None, expected_value = None):
+ return base_expect_invalid_attr_for_text(cls, default_attr, default_value, test, cls.content(desc_attrs), attr, expected_value)
+
+
+def base_expect_invalid_attr_for_text(cls, default_attr, default_value, test, desc_text, attr = None, expected_value = None):
+ """
+ Asserts that construction will fail due to desc_text having a malformed
+ attribute. If an attr is provided then we check that it matches an expected
+ value when we're constructed without validation.
+ """
+
+ test.assertRaises(ValueError, cls, desc_text, True)
+ desc = cls(desc_text, validate = False)
+
+ if attr:
+ # check that the invalid attribute matches the expected value when
+ # constructed without validation
+
+ test.assertEqual(expected_value, getattr(desc, attr))
+ elif default_attr and default_value:
+ test.assertEqual(default_value, getattr(desc, default_attr)) # check a default attribute
+
+ return desc
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index 2269000..a59d0f0 100644
--- a/test/unit/descriptor/extrainfo_descriptor.py
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -3,6 +3,7 @@ Unit tests for stem.descriptor.extrainfo_descriptor.
"""
import datetime
+import functools
import re
import unittest
@@ -15,9 +16,14 @@ from stem.descriptor.extrainfo_descriptor import (
DirStat,
)
-from test.mocking import CRYPTO_BLOB
+from test.unit.descriptor import (
+ get_resource,
+ base_expect_invalid_attr,
+ base_expect_invalid_attr_for_text,
+)
-from test.unit.descriptor import get_resource
+expect_invalid_attr = functools.partial(base_expect_invalid_attr, RelayExtraInfoDescriptor, 'nickname', 'ninja')
+expect_invalid_attr_for_text = functools.partial(base_expect_invalid_attr_for_text, RelayExtraInfoDescriptor, 'nickname', 'ninja')
class TestExtraInfoDescriptor(unittest.TestCase):
@@ -191,7 +197,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
desc = RelayExtraInfoDescriptor.create()
self.assertEqual('ninja', desc.nickname)
self.assertEqual('B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48', desc.fingerprint)
- self.assertTrue(CRYPTO_BLOB in desc.signature)
+ self.assertTrue(stem.descriptor.CRYPTO_BLOB in desc.signature)
def test_unrecognized_line(self):
"""
@@ -206,16 +212,14 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
Includes a line prior to the 'extra-info' entry.
"""
- desc_text = b'exit-streams-opened port=80\n' + RelayExtraInfoDescriptor.content()
- self._expect_invalid_attr(desc_text)
+ expect_invalid_attr_for_text(self, b'exit-streams-opened port=80\n' + RelayExtraInfoDescriptor.content())
def test_trailing_line(self):
"""
Includes a line after the 'router-signature' entry.
"""
- desc_text = RelayExtraInfoDescriptor.content() + b'\nexit-streams-opened port=80'
- self._expect_invalid_attr(desc_text)
+ expect_invalid_attr_for_text(self, RelayExtraInfoDescriptor.content() + b'\nexit-streams-opened port=80')
def test_extrainfo_line_missing_fields(self):
"""
@@ -232,8 +236,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({'extra-info': entry})
- desc = self._expect_invalid_attr(desc_text, 'nickname')
+ desc = expect_invalid_attr(self, {'extra-info': entry}, 'nickname')
self.assertEqual(None, desc.nickname)
self.assertEqual(None, desc.fingerprint)
@@ -259,11 +262,8 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({'geoip-db-digest': entry})
- self._expect_invalid_attr(desc_text, 'geoip_db_digest')
-
- desc_text = RelayExtraInfoDescriptor.content({'geoip6-db-digest': entry})
- self._expect_invalid_attr(desc_text, 'geoip6_db_digest')
+ expect_invalid_attr(self, {'geoip-db-digest': entry}, 'geoip_db_digest')
+ expect_invalid_attr(self, {'geoip6-db-digest': entry}, 'geoip6_db_digest')
def test_cell_circuits_per_decile(self):
"""
@@ -287,8 +287,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({'cell-circuits-per-decile': entry})
- self._expect_invalid_attr(desc_text, 'cell_circuits_per_decile')
+ expect_invalid_attr(self, {'cell-circuits-per-decile': entry}, 'cell_circuits_per_decile')
def test_dir_response_lines(self):
"""
@@ -315,8 +314,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
- desc = self._expect_invalid_attr(desc_text)
+ desc = expect_invalid_attr(self, {keyword: entry})
self.assertEqual(None, getattr(desc, attr))
self.assertEqual(None, getattr(desc, unknown_attr))
@@ -357,8 +355,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
- desc = self._expect_invalid_attr(desc_text)
+ desc = expect_invalid_attr(self, {keyword: entry})
self.assertEqual(None, getattr(desc, attr))
self.assertEqual(None, getattr(desc, unknown_attr))
@@ -388,8 +385,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({'conn-bi-direct': entry})
- desc = self._expect_invalid_attr(desc_text)
+ desc = expect_invalid_attr(self, {'conn-bi-direct': entry})
self.assertEqual(None, desc.conn_bi_direct_end)
self.assertEqual(None, desc.conn_bi_direct_interval)
self.assertEqual(None, desc.conn_bi_direct_below)
@@ -425,8 +421,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
- self._expect_invalid_attr(desc_text, attr)
+ expect_invalid_attr(self, {keyword: entry}, attr)
def test_number_list_lines(self):
"""
@@ -455,8 +450,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry, expected in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
- self._expect_invalid_attr(desc_text, attr, expected)
+ expect_invalid_attr(self, {keyword: entry}, attr, expected)
def test_timestamp_lines(self):
"""
@@ -478,8 +472,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
- self._expect_invalid_attr(desc_text, attr)
+ expect_invalid_attr(self, {keyword: entry}, attr)
def test_timestamp_and_interval_lines(self):
"""
@@ -506,8 +499,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({'entry-stats-end': entry})
- desc = self._expect_invalid_attr(desc_text)
+ desc = expect_invalid_attr(self, {'entry-stats-end': entry})
self.assertEqual(None, desc.entry_stats_end)
self.assertEqual(None, desc.entry_stats_interval)
@@ -544,8 +536,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({'write-history': entry})
- desc = self._expect_invalid_attr(desc_text)
+ desc = expect_invalid_attr(self, {'write-history': entry})
self.assertEqual(None, desc.write_history_end)
self.assertEqual(None, desc.write_history_interval)
self.assertEqual(None, desc.write_history_values)
@@ -579,8 +570,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
- self._expect_invalid_attr(desc_text, attr)
+ expect_invalid_attr(self, {keyword: entry}, attr)
def test_hidden_service_stats_end(self):
"""
@@ -601,8 +591,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({'hidserv-stats-end': entry})
- self._expect_invalid_attr(desc_text, 'hs_stats_end')
+ expect_invalid_attr(self, {'hidserv-stats-end': entry}, 'hs_stats_end')
def test_hidden_service_stats(self):
"""
@@ -644,9 +633,8 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
self.assertEqual({'spiffy': 'true', 'snowmen': 'neat'}, getattr(desc, extra_attr))
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
- self._expect_invalid_attr(desc_text, stat_attr)
- self._expect_invalid_attr(desc_text, extra_attr, {})
+ expect_invalid_attr(self, {keyword: entry}, stat_attr)
+ expect_invalid_attr(self, {keyword: entry}, extra_attr, {})
def test_locale_mapping_lines(self):
"""
@@ -674,8 +662,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
)
for entry in test_entries:
- desc_text = RelayExtraInfoDescriptor.content({keyword: entry})
- self._expect_invalid_attr(desc_text, attr)
+ expect_invalid_attr(self, {keyword: entry}, attr)
def test_minimal_bridge_descriptor(self):
"""
@@ -737,24 +724,3 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
desc = BridgeExtraInfoDescriptor.create({'transport': 'obfs3\ntransport obfs4'})
self.assertEqual({'obfs3': (None, None, None), 'obfs4': (None, None, None)}, desc.transport)
self.assertEqual([], desc.get_unrecognized_lines())
-
- def _expect_invalid_attr(self, desc_text, attr = None, expected_value = None):
- """
- Asserts that construction will fail due to desc_text having a malformed
- attribute. If an attr is provided then we check that it matches an expected
- value when we're constructed without validation.
- """
-
- self.assertRaises(ValueError, RelayExtraInfoDescriptor, desc_text, True)
- desc = RelayExtraInfoDescriptor(desc_text, validate = False)
-
- if attr:
- # check that the invalid attribute matches the expected value when
- # constructed without validation
-
- self.assertEqual(expected_value, getattr(desc, attr))
- else:
- # check a default attribute
- self.assertEqual('ninja', desc.nickname)
-
- return desc
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index f30fed6..0014049 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -3,6 +3,7 @@ Unit tests for stem.descriptor.server_descriptor.
"""
import datetime
+import functools
import io
import pickle
import tarfile
@@ -20,7 +21,11 @@ from stem.util import str_type
from stem.descriptor.certificate import CertType, ExtensionType
from stem.descriptor.server_descriptor import RelayDescriptor, BridgeDescriptor
-from test.unit.descriptor import get_resource
+from test.unit.descriptor import (
+ get_resource,
+ base_expect_invalid_attr,
+ base_expect_invalid_attr_for_text,
+)
try:
# added in python 3.3
@@ -34,6 +39,9 @@ TARFILE_FINGERPRINTS = set([
str_type('1F43EE37A0670301AD9CB555D94AFEC2C89FDE86'),
])
+expect_invalid_attr = functools.partial(base_expect_invalid_attr, RelayDescriptor, 'nickname', 'caerSidi')
+expect_invalid_attr_for_text = functools.partial(base_expect_invalid_attr_for_text, RelayDescriptor, 'nickname', 'caerSidi')
+
class TestServerDescriptor(unittest.TestCase):
def test_with_tarfile_path(self):
@@ -455,7 +463,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
"""
desc_text = b'hibernate 1\n' + RelayDescriptor.content()
- self._expect_invalid_attr_for_text(desc_text)
+ expect_invalid_attr_for_text(self, desc_text)
def test_trailing_line(self):
"""
@@ -463,56 +471,56 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
"""
desc_text = RelayDescriptor.content() + b'\nhibernate 1'
- self._expect_invalid_attr_for_text(desc_text)
+ expect_invalid_attr_for_text(self, desc_text)
def test_nickname_missing(self):
"""
Constructs with a malformed router entry.
"""
- self._expect_invalid_attr({'router': ' 71.35.133.197 9001 0 0'}, 'nickname')
+ expect_invalid_attr(self, {'router': ' 71.35.133.197 9001 0 0'}, 'nickname')
def test_nickname_too_long(self):
"""
Constructs with a nickname that is an invalid length.
"""
- self._expect_invalid_attr({'router': 'saberrider2008ReallyLongNickname 71.35.133.197 9001 0 0'}, 'nickname')
+ expect_invalid_attr(self, {'router': 'saberrider2008ReallyLongNickname 71.35.133.197 9001 0 0'}, 'nickname')
def test_nickname_invalid_char(self):
"""
Constructs with an invalid relay nickname.
"""
- self._expect_invalid_attr({'router': '$aberrider2008 71.35.133.197 9001 0 0'}, 'nickname')
+ expect_invalid_attr(self, {'router': '$aberrider2008 71.35.133.197 9001 0 0'}, 'nickname')
def test_address_malformed(self):
"""
Constructs with an invalid ip address.
"""
- self._expect_invalid_attr({'router': 'caerSidi 371.35.133.197 9001 0 0'}, 'address')
+ expect_invalid_attr(self, {'router': 'caerSidi 371.35.133.197 9001 0 0'}, 'address')
def test_port_too_high(self):
"""
Constructs with an ORPort that is too large.
"""
- self._expect_invalid_attr({'router': 'caerSidi 71.35.133.197 900001 0 0'}, 'or_port')
+ expect_invalid_attr(self, {'router': 'caerSidi 71.35.133.197 900001 0 0'}, 'or_port')
def test_port_malformed(self):
"""
Constructs with an ORPort that isn't numeric.
"""
- self._expect_invalid_attr({'router': 'caerSidi 71.35.133.197 900a1 0 0'}, 'or_port')
+ expect_invalid_attr(self, {'router': 'caerSidi 71.35.133.197 900a1 0 0'}, 'or_port')
def test_port_newline(self):
"""
Constructs with a newline replacing the ORPort.
"""
- self._expect_invalid_attr({'router': 'caerSidi 71.35.133.197 \n 0 0'}, 'or_port')
+ expect_invalid_attr(self, {'router': 'caerSidi 71.35.133.197 \n 0 0'}, 'or_port')
def test_platform_empty(self):
"""
@@ -543,7 +551,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
Constructs with a protocols line without circuit versions.
"""
- self._expect_invalid_attr({'opt': 'protocols Link 1 2'}, 'circuit_protocols')
+ expect_invalid_attr(self, {'opt': 'protocols Link 1 2'}, 'circuit_protocols')
@patch('stem.prereq.is_crypto_available', Mock(return_value = False))
def test_published_leap_year(self):
@@ -552,7 +560,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
invalid.
"""
- self._expect_invalid_attr({'published': '2011-02-29 04:03:19'}, 'published')
+ expect_invalid_attr(self, {'published': '2011-02-29 04:03:19'}, 'published')
desc = RelayDescriptor.create({'published': '2012-02-29 04:03:19'})
self.assertEqual(datetime.datetime(2012, 2, 29, 4, 3, 19), desc.published)
@@ -562,7 +570,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
Constructs with a published entry without a time component.
"""
- self._expect_invalid_attr({'published': '2012-01-01'}, 'published')
+ expect_invalid_attr(self, {'published': '2012-01-01'}, 'published')
def test_read_and_write_history(self):
"""
@@ -632,7 +640,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
desc_text = RelayDescriptor.content({'<replace>': ''})
desc_text = desc_text.replace(b'<replace>', b'contact foo\ncontact bar')
- self._expect_invalid_attr_for_text(desc_text, 'contact', b'foo')
+ expect_invalid_attr_for_text(self, desc_text, 'contact', b'foo')
def test_missing_required_attr(self):
"""
@@ -661,7 +669,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
"""
fingerprint = '4F0C 867D F0EF 6816 0568 C826 838F 482C EA7C FE45'
- self._expect_invalid_attr({'opt fingerprint': fingerprint}, 'fingerprint', fingerprint.replace(' ', ''))
+ expect_invalid_attr(self, {'opt fingerprint': fingerprint}, 'fingerprint', fingerprint.replace(' ', ''))
def test_ipv6_policy(self):
"""
@@ -829,28 +837,6 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
desc = BridgeDescriptor(desc_text)
self.assertEqual(expected_or_addresses, desc.or_addresses)
- def _expect_invalid_attr(self, desc_attrs, attr = None, expected_value = None):
- self._expect_invalid_attr_for_text(RelayDescriptor.content(desc_attrs), attr, expected_value)
-
- def _expect_invalid_attr_for_text(self, desc_text, attr = None, expected_value = None):
- """
- Asserts that construction will fail due to desc_text having a malformed
- attribute. If an attr is provided then we check that it matches an expected
- value when we're constructed without validation.
- """
-
- self.assertRaises(ValueError, RelayDescriptor, desc_text, True)
- desc = RelayDescriptor(desc_text, validate = False)
-
- if attr:
- # check that the invalid attribute matches the expected value when
- # constructed without validation
-
- self.assertEqual(expected_value, getattr(desc, attr))
- else:
- # check a default attribute
- self.assertEqual('caerSidi', desc.nickname)
-
def test_pickleability(self):
"""
Checks that we can unpickle lazy loaded server descriptors.
1
0
commit bda32a24cc543d07e3dbc7d993f9ac08517030a9
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon Apr 24 09:11:01 2017 -0700
DescriptorType enum
Adding an enumeration for the common descriptor types. This is something I want
for descriptor creation, but that aside it can be used for parse so...
stem.descriptor.parse(my_input, 'server-descriptor 1.0')
... becomes..
stem.descriptor.parse(my_input, DescriptorType.SERVER)
---
stem/descriptor/__init__.py | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index 6e8c5a4..f80e6f6 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -17,6 +17,22 @@ Package for parsing and processing descriptor data.
|- get_unrecognized_lines - unparsed descriptor content
+- __str__ - string that the descriptor was made from
+.. data:: DescriptorType (enum)
+
+ Common descriptor types.
+
+ .. versionadded:: 1.6.0
+
+ =================== ===========
+ DescriptorType Description
+ =================== ===========
+ **SERVER** :class:`~stem.descriptor.server_descriptor.RelayDescriptor`
+ **EXTRA** :class:`~stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor`
+ **MICRO** :class:`~stem.descriptor.microdescriptor.Microdescriptor`
+ **CONSENSUS** :class:`~stem.descriptor.networkstatus.NetworkStatusDocumentV3`
+ **HIDDEN_SERVICE** :class:`~stem.descriptor.hidden_service_descriptor.HiddenServiceDescriptor`
+ =================== ===========
+
.. data:: DocumentHandler (enum)
Ways in which we can parse a
@@ -86,6 +102,14 @@ DocumentHandler = stem.util.enum.UppercaseEnum(
'BARE_DOCUMENT',
)
+DescriptorType = stem.util.enum.Enum(
+ ('SERVER', 'server-descriptor 1.0'),
+ ('EXTRAINFO', 'extra-info 1.0'),
+ ('MICRO', 'microdescriptor 1.0'),
+ ('CONSENSUS', 'network-status-consensus-3 1.0'),
+ ('HIDDEN_SERVICE', 'hidden-service-descriptor 1.0'),
+)
+
def parse_file(descriptor_file, descriptor_type = None, validate = False, document_handler = DocumentHandler.ENTRIES, normalize_newlines = None, **kwargs):
"""
1
0
commit 5245c052ba5cd9cd80d45f7bbda4eade2425d8ba
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon May 1 14:08:18 2017 -0700
RouterStatusEntry creation
---
stem/descriptor/router_status_entry.py | 28 +++++
test/mocking.py | 80 ---------------
test/unit/descriptor/networkstatus/document_v3.py | 26 +++--
test/unit/descriptor/router_status_entry.py | 118 +++++++++-------------
test/unit/response/events.py | 7 +-
test/unit/tutorial.py | 7 +-
test/unit/tutorial_examples.py | 18 ++--
7 files changed, 101 insertions(+), 183 deletions(-)
diff --git a/stem/descriptor/router_status_entry.py b/stem/descriptor/router_status_entry.py
index 129f6f7..973f833 100644
--- a/stem/descriptor/router_status_entry.py
+++ b/stem/descriptor/router_status_entry.py
@@ -29,6 +29,7 @@ import stem.util.str_tools
from stem.descriptor import (
KEYWORD_LINE,
Descriptor,
+ _descriptor_content,
_value,
_values,
_descriptor_components,
@@ -36,6 +37,21 @@ from stem.descriptor import (
_read_until_keywords,
)
+ROUTER_STATUS_ENTRY_V2_HEADER = (
+ ('r', 'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0'),
+)
+
+ROUTER_STATUS_ENTRY_V3_HEADER = (
+ ('r', 'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0'),
+ ('s', 'Fast Named Running Stable Valid'),
+)
+
+ROUTER_STATUS_ENTRY_MICRO_V3_HEADER = (
+ ('r', 'Konata ARIJF2zbqirB9IwsW0mQznccWww 2012-09-24 13:40:40 69.64.48.168 9001 9030'),
+ ('m', 'aiUklwBrua82obG5AsTX+iEpkjQA2+AQHxZ7GwMfY70'),
+ ('s', 'Fast Guard HSDir Named Running Stable V2Dir Valid'),
+)
+
_parse_pr_line = _parse_protocol_line('pr', 'protocols')
@@ -511,6 +527,10 @@ class RouterStatusEntryV2(RouterStatusEntry):
'digest': (None, _parse_r_line),
})
+ @classmethod
+ def content(cls, attr = None, exclude = ()):
+ return _descriptor_content(attr, exclude, ROUTER_STATUS_ENTRY_V2_HEADER)
+
def _name(self, is_plural = False):
return 'Router status entries (v2)' if is_plural else 'Router status entry (v2)'
@@ -604,6 +624,10 @@ class RouterStatusEntryV3(RouterStatusEntry):
'm': _parse_m_line,
})
+ @classmethod
+ def content(cls, attr = None, exclude = ()):
+ return _descriptor_content(attr, exclude, ROUTER_STATUS_ENTRY_V3_HEADER)
+
def _name(self, is_plural = False):
return 'Router status entries (v3)' if is_plural else 'Router status entry (v3)'
@@ -673,6 +697,10 @@ class RouterStatusEntryMicroV3(RouterStatusEntry):
'pr': _parse_pr_line,
})
+ @classmethod
+ def content(cls, attr = None, exclude = ()):
+ return _descriptor_content(attr, exclude, ROUTER_STATUS_ENTRY_MICRO_V3_HEADER)
+
def _name(self, is_plural = False):
return 'Router status entries (micro v3)' if is_plural else 'Router status entry (micro v3)'
diff --git a/test/mocking.py b/test/mocking.py
index 09d3631..c88784a 100644
--- a/test/mocking.py
+++ b/test/mocking.py
@@ -18,11 +18,6 @@ Helper functions for creating mock objects.
get_key_certificate - KeyCertificate
get_network_status_document_v2 - NetworkStatusDocumentV2
get_network_status_document_v3 - NetworkStatusDocumentV3
-
- stem.descriptor.router_status_entry
- get_router_status_entry_v2 - RouterStatusEntryV2
- get_router_status_entry_v3 - RouterStatusEntryV3
- get_router_status_entry_micro_v3 - RouterStatusEntryMicroV3
"""
import hashlib
@@ -58,21 +53,6 @@ DOC_SIG = stem.descriptor.networkstatus.DocumentSignature(
'BF112F1C6D5543CFD0A32215ACABD4197B5279AD',
'-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB)
-ROUTER_STATUS_ENTRY_V2_HEADER = (
- ('r', 'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0'),
-)
-
-ROUTER_STATUS_ENTRY_V3_HEADER = (
- ('r', 'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0'),
- ('s', 'Fast Named Running Stable Valid'),
-)
-
-ROUTER_STATUS_ENTRY_MICRO_V3_HEADER = (
- ('r', 'Konata ARIJF2zbqirB9IwsW0mQznccWww 2012-09-24 13:40:40 69.64.48.168 9001 9030'),
- ('m', 'aiUklwBrua82obG5AsTX+iEpkjQA2+AQHxZ7GwMfY70'),
- ('s', 'Fast Guard HSDir Named Running Stable V2Dir Valid'),
-)
-
AUTHORITY_HEADER = (
('dir-source', 'turtles 27B6B5996C426270A5C95488AA5BCEB6BCC86956 no.place.com 76.73.17.194 9030 9090'),
('contact', 'Mike Perry <email>'),
@@ -287,66 +267,6 @@ def _get_descriptor_content(attr = None, exclude = (), header_template = (), foo
return stem.util.str_tools._to_bytes('\n'.join(header_content + remainder + footer_content))
-def get_router_status_entry_v2(attr = None, exclude = (), content = False):
- """
- Provides the descriptor content for...
- stem.descriptor.router_status_entry.RouterStatusEntryV2
-
- :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: RouterStatusEntryV2 for the requested descriptor content
- """
-
- desc_content = _get_descriptor_content(attr, exclude, ROUTER_STATUS_ENTRY_V2_HEADER)
-
- if content:
- return desc_content
- else:
- return stem.descriptor.router_status_entry.RouterStatusEntryV2(desc_content, validate = True)
-
-
-def get_router_status_entry_v3(attr = None, exclude = (), content = False):
- """
- Provides the descriptor content for...
- stem.descriptor.router_status_entry.RouterStatusEntryV3
-
- :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: RouterStatusEntryV3 for the requested descriptor content
- """
-
- desc_content = _get_descriptor_content(attr, exclude, ROUTER_STATUS_ENTRY_V3_HEADER)
-
- if content:
- return desc_content
- else:
- return stem.descriptor.router_status_entry.RouterStatusEntryV3(desc_content, validate = True)
-
-
-def get_router_status_entry_micro_v3(attr = None, exclude = (), content = False):
- """
- Provides the descriptor content for...
- stem.descriptor.router_status_entry.RouterStatusEntryMicroV3
-
- :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: RouterStatusEntryMicroV3 for the requested descriptor content
- """
-
- desc_content = _get_descriptor_content(attr, exclude, ROUTER_STATUS_ENTRY_MICRO_V3_HEADER)
-
- if content:
- return desc_content
- else:
- return stem.descriptor.router_status_entry.RouterStatusEntryMicroV3(desc_content, validate = True)
-
-
def get_directory_authority(attr = None, exclude = (), is_vote = False, content = False):
"""
Provides the descriptor content for...
diff --git a/test/unit/descriptor/networkstatus/document_v3.py b/test/unit/descriptor/networkstatus/document_v3.py
index 6a2e784..d6ea34e 100644
--- a/test/unit/descriptor/networkstatus/document_v3.py
+++ b/test/unit/descriptor/networkstatus/document_v3.py
@@ -29,8 +29,6 @@ from stem.descriptor.router_status_entry import (
)
from test.mocking import (
- get_router_status_entry_v3,
- get_router_status_entry_micro_v3,
get_directory_authority,
get_network_status_document_v3,
DOC_SIG,
@@ -400,8 +398,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
# makes a consensus with a couple routers, both with the same nickname
- entry1 = get_router_status_entry_v3({'s': 'Fast'})
- entry2 = get_router_status_entry_v3({'s': 'Valid'})
+ entry1 = RouterStatusEntryV3.create({'s': 'Fast'})
+ entry2 = RouterStatusEntryV3.create({'s': 'Valid'})
content = get_network_status_document_v3(routers = (entry1, entry2), content = True)
# first example: parsing via the NetworkStatusDocumentV3 constructor
@@ -428,8 +426,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
# Simple sanity check that they provide the right type, and that the
# document includes or excludes the router status entries as appropriate.
- entry1 = get_router_status_entry_v3({'s': 'Fast'})
- entry2 = get_router_status_entry_v3({
+ entry1 = RouterStatusEntryV3.create({'s': 'Fast'})
+ entry2 = RouterStatusEntryV3.create({
'r': 'Nightfae AWt0XNId/OU2xX5xs5hVtDc5Mes 6873oEfM7fFIbxYtwllw9GPDwkA 2013-02-20 11:12:27 85.177.66.233 9001 9030',
's': 'Valid',
})
@@ -451,8 +449,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
Try parsing a document via the _parse_file() function.
"""
- entry1 = get_router_status_entry_v3({'s': 'Fast'})
- entry2 = get_router_status_entry_v3({'s': 'Valid'})
+ entry1 = RouterStatusEntryV3.create({'s': 'Fast'})
+ entry2 = RouterStatusEntryV3.create({'s': 'Valid'})
content = get_network_status_document_v3(routers = (entry1, entry2), content = True)
# the document that the entries refer to should actually be the minimal
@@ -1093,8 +1091,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
document.
"""
- entry1 = get_router_status_entry_v3({'s': 'Fast'})
- entry2 = get_router_status_entry_v3({
+ entry1 = RouterStatusEntryV3.create({'s': 'Fast'})
+ entry2 = RouterStatusEntryV3.create({
'r': 'Nightfae AWt0XNId/OU2xX5xs5hVtDc5Mes 6873oEfM7fFIbxYtwllw9GPDwkA 2013-02-20 11:12:27 85.177.66.233 9001 9030',
's': 'Valid',
})
@@ -1106,7 +1104,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
# try with an invalid RouterStatusEntry
- entry3 = RouterStatusEntryV3(get_router_status_entry_v3({'r': 'ugabuga'}, content = True), False)
+ entry3 = RouterStatusEntryV3(RouterStatusEntryV3.content({'r': 'ugabuga'}), False)
content = get_network_status_document_v3(routers = (entry3,), content = True)
self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
@@ -1127,8 +1125,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
document.
"""
- entry1 = get_router_status_entry_micro_v3({'s': 'Fast'})
- entry2 = get_router_status_entry_micro_v3({
+ entry1 = RouterStatusEntryMicroV3.create({'s': 'Fast'})
+ entry2 = RouterStatusEntryMicroV3.create({
'r': 'tornodeviennasil AcWxDFxrHetHYS5m6/MVt8ZN6AM 2013-03-13 22:09:13 78.142.142.246 443 80',
's': 'Valid',
})
@@ -1140,7 +1138,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
# try with an invalid RouterStatusEntry
- entry3 = RouterStatusEntryMicroV3(get_router_status_entry_micro_v3({'r': 'ugabuga'}, content = True), False)
+ entry3 = RouterStatusEntryMicroV3(RouterStatusEntryMicroV3.content({'r': 'ugabuga'}), False)
content = get_network_status_document_v3({'network-status-version': '3 microdesc'}, routers = (entry3,), content = True)
self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)
diff --git a/test/unit/descriptor/router_status_entry.py b/test/unit/descriptor/router_status_entry.py
index 43bcaee..37d763e 100644
--- a/test/unit/descriptor/router_status_entry.py
+++ b/test/unit/descriptor/router_status_entry.py
@@ -3,22 +3,27 @@ Unit tests for stem.descriptor.router_status_entry.
"""
import datetime
+import functools
import unittest
import stem.descriptor
from stem import Flag
-from stem.descriptor.router_status_entry import RouterStatusEntryV3, _base64_to_hex
from stem.exit_policy import MicroExitPolicy
from stem.version import Version
-from test.unit.descriptor import get_resource
+from test.unit.descriptor import (
+ get_resource,
+ base_expect_invalid_attr,
+ base_expect_invalid_attr_for_text,
+)
-from test.mocking import (
- get_router_status_entry_v2,
- get_router_status_entry_v3,
- get_router_status_entry_micro_v3,
+from stem.descriptor.router_status_entry import (
ROUTER_STATUS_ENTRY_V3_HEADER,
+ RouterStatusEntryV2,
+ RouterStatusEntryV3,
+ RouterStatusEntryMicroV3,
+ _base64_to_hex,
)
ENTRY_WITHOUT_ED25519 = """\
@@ -47,6 +52,9 @@ m 18,19,20 sha256=AkZH3gIvz3wunsroqh5izBJizdYuR7kn2oVbsvqgML8
m 21 sha256=AVp41YVxKEJCaoEf0+77Cdvyw5YgpyDXdob0+LSv/pE
"""
+expect_invalid_attr = functools.partial(base_expect_invalid_attr, RouterStatusEntryV3, 'nickname', 'caerSidi')
+expect_invalid_attr_for_text = functools.partial(base_expect_invalid_attr_for_text, RouterStatusEntryV3, 'nickname', 'caerSidi')
+
def vote_document():
class Stub(object):
@@ -86,7 +94,7 @@ class TestRouterStatusEntry(unittest.TestCase):
Parses a minimal v2 router status entry.
"""
- entry = get_router_status_entry_v2()
+ entry = RouterStatusEntryV2.create()
self.assertEqual(None, entry.document)
self.assertEqual('caerSidi', entry.nickname)
@@ -106,7 +114,7 @@ class TestRouterStatusEntry(unittest.TestCase):
Parses a minimal v3 router status entry.
"""
- entry = get_router_status_entry_v3()
+ entry = RouterStatusEntryV3.create()
expected_flags = set([Flag.FAST, Flag.NAMED, Flag.RUNNING, Flag.STABLE, Flag.VALID])
self.assertEqual(None, entry.document)
@@ -135,7 +143,7 @@ class TestRouterStatusEntry(unittest.TestCase):
Parses a minimal microdescriptor v3 router status entry.
"""
- entry = get_router_status_entry_micro_v3()
+ entry = RouterStatusEntryMicroV3.create()
expected_flags = set([Flag.FAST, Flag.GUARD, Flag.HSDIR, Flag.NAMED, Flag.RUNNING, Flag.STABLE, Flag.V2DIR, Flag.VALID])
self.assertEqual(None, entry.document)
@@ -222,21 +230,16 @@ class TestRouterStatusEntry(unittest.TestCase):
Parses a router status entry that's missing fields.
"""
- content = get_router_status_entry_v3(exclude = ('r', 's'), content = True)
- self._expect_invalid_attr(content, 'address')
-
- content = get_router_status_entry_v3(exclude = ('r',), content = True)
- self._expect_invalid_attr(content, 'address')
-
- content = get_router_status_entry_v3(exclude = ('s',), content = True)
- self._expect_invalid_attr(content, 'flags')
+ expect_invalid_attr_for_text(self, RouterStatusEntryV3.content(exclude = ('r', 's')), 'address')
+ expect_invalid_attr_for_text(self, RouterStatusEntryV3.content(exclude = ('r',)), 'address')
+ expect_invalid_attr_for_text(self, RouterStatusEntryV3.content(exclude = ('s',)), 'flags')
def test_unrecognized_lines(self):
"""
Parses a router status entry with new keywords.
"""
- entry = get_router_status_entry_v3({'z': 'New tor feature: sparkly unicorns!'})
+ entry = RouterStatusEntryV3.create({'z': 'New tor feature: sparkly unicorns!'})
self.assertEqual(['z New tor feature: sparkly unicorns!'], entry.get_unrecognized_lines())
def test_proceeding_line(self):
@@ -244,7 +247,7 @@ class TestRouterStatusEntry(unittest.TestCase):
Includes content prior to the 'r' line.
"""
- content = b'z some stuff\n' + get_router_status_entry_v3(content = True)
+ content = b'z some stuff\n' + RouterStatusEntryV3.content()
self.assertRaises(ValueError, RouterStatusEntryV3, content, True)
self.assertEqual(['z some stuff'], RouterStatusEntryV3(content, False).get_unrecognized_lines())
@@ -253,7 +256,7 @@ class TestRouterStatusEntry(unittest.TestCase):
Includes blank lines, which should be ignored.
"""
- content = get_router_status_entry_v3(content = True) + b'\n\nv Tor 0.2.2.35\n\n'
+ content = RouterStatusEntryV3.content() + b'\n\nv Tor 0.2.2.35\n\n'
entry = RouterStatusEntryV3(content)
self.assertEqual('Tor 0.2.2.35', entry.version_line)
@@ -262,7 +265,7 @@ class TestRouterStatusEntry(unittest.TestCase):
Duplicates linesin the entry.
"""
- lines = get_router_status_entry_v3(content = True).split(b'\n')
+ lines = RouterStatusEntryV3.content().split(b'\n')
for index, duplicate_line in enumerate(lines):
content = b'\n'.join(lines[:index] + [duplicate_line] + lines[index:])
@@ -291,9 +294,7 @@ class TestRouterStatusEntry(unittest.TestCase):
test_components = [comp[1] for comp in components]
test_components.remove(value)
r_line = ' '.join(test_components)
-
- content = get_router_status_entry_v3({'r': r_line}, content = True)
- self._expect_invalid_attr(content, attr)
+ expect_invalid_attr(self, {'r': r_line}, attr)
def test_malformed_nickname(self):
"""
@@ -308,7 +309,6 @@ class TestRouterStatusEntry(unittest.TestCase):
for value in test_values:
r_line = ROUTER_STATUS_ENTRY_V3_HEADER[0][1].replace('caerSidi', value)
- content = get_router_status_entry_v3({'r': r_line}, content = True)
# TODO: Initial whitespace is consumed as part of the keyword/value
# divider. This is a bug in the case of V3 router status entries, but
@@ -323,7 +323,7 @@ class TestRouterStatusEntry(unittest.TestCase):
if value == '':
value = None
- self._expect_invalid_attr(content, 'nickname')
+ expect_invalid_attr(self, {'r': r_line}, 'nickname')
def test_malformed_fingerprint(self):
"""
@@ -338,8 +338,7 @@ class TestRouterStatusEntry(unittest.TestCase):
for value in test_values:
r_line = ROUTER_STATUS_ENTRY_V3_HEADER[0][1].replace('p1aag7VwarGxqctS7/fS0y5FU+s', value)
- content = get_router_status_entry_v3({'r': r_line}, content = True)
- self._expect_invalid_attr(content, 'fingerprint')
+ expect_invalid_attr(self, {'r': r_line}, 'fingerprint')
def test_malformed_published_date(self):
"""
@@ -364,8 +363,7 @@ class TestRouterStatusEntry(unittest.TestCase):
for value in test_values:
r_line = ROUTER_STATUS_ENTRY_V3_HEADER[0][1].replace('2012-08-06 11:19:31', value)
- content = get_router_status_entry_v3({'r': r_line}, content = True)
- self._expect_invalid_attr(content, 'published')
+ expect_invalid_attr(self, {'r': r_line}, 'published')
def test_malformed_address(self):
"""
@@ -382,8 +380,7 @@ class TestRouterStatusEntry(unittest.TestCase):
for value in test_values:
r_line = ROUTER_STATUS_ENTRY_V3_HEADER[0][1].replace('71.35.150.29', value)
- content = get_router_status_entry_v3({'r': r_line}, content = True)
- self._expect_invalid_attr(content, 'address')
+ expect_invalid_attr(self, {'r': r_line}, 'address')
def test_malformed_port(self):
"""
@@ -412,9 +409,7 @@ class TestRouterStatusEntry(unittest.TestCase):
r_line = r_line[:-1] + value
attr = 'or_port' if include_or_port else 'dir_port'
-
- content = get_router_status_entry_v3({'r': r_line}, content = True)
- self._expect_invalid_attr(content, attr)
+ expect_invalid_attr(self, {'r': r_line}, attr)
def test_ipv6_addresses(self):
"""
@@ -427,12 +422,12 @@ class TestRouterStatusEntry(unittest.TestCase):
}
for a_line, expected in test_values.items():
- entry = get_router_status_entry_v3({'a': a_line})
+ entry = RouterStatusEntryV3.create({'a': a_line})
self.assertEqual(expected, entry.or_addresses)
# includes multiple 'a' lines
- content = get_router_status_entry_v3(content = True)
+ content = RouterStatusEntryV3.content()
content += b'\na [2607:fcd0:daaa:101::602c:bd62]:443'
content += b'\na [1148:fcd0:daaa:101::602c:bd62]:80'
@@ -452,8 +447,7 @@ class TestRouterStatusEntry(unittest.TestCase):
)
for a_line in test_values:
- content = get_router_status_entry_v3({'a': a_line}, content = True)
- self._expect_invalid_attr(content, expected_value = {})
+ expect_invalid_attr(self, {'a': a_line}, expected_value = {})
def test_flags(self):
"""
@@ -468,7 +462,7 @@ class TestRouterStatusEntry(unittest.TestCase):
}
for s_line, expected in test_values.items():
- entry = get_router_status_entry_v3({'s': s_line})
+ entry = RouterStatusEntryV3.create({'s': s_line})
self.assertEqual(expected, entry.flags)
# tries some invalid inputs
@@ -479,11 +473,10 @@ class TestRouterStatusEntry(unittest.TestCase):
}
for s_line, expected in test_values.items():
- content = get_router_status_entry_v3({'s': s_line}, content = True)
- self._expect_invalid_attr(content, 'flags', expected)
+ expect_invalid_attr(self, {'s': s_line}, 'flags', expected)
def test_protocols(self):
- desc = get_router_status_entry_v3({'pr': 'Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2'})
+ desc = RouterStatusEntryV3.create({'pr': 'Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2'})
self.assertEqual(10, len(desc.protocols))
def test_versions(self):
@@ -499,13 +492,12 @@ class TestRouterStatusEntry(unittest.TestCase):
}
for v_line, expected in test_values.items():
- entry = get_router_status_entry_v3({'v': v_line})
+ entry = RouterStatusEntryV3.create({'v': v_line})
self.assertEqual(expected, entry.version)
self.assertEqual(v_line, entry.version_line)
# tries an invalid input
- content = get_router_status_entry_v3({'v': 'Tor ugabuga'}, content = True)
- self._expect_invalid_attr(content, 'version')
+ expect_invalid_attr(self, {'v': 'Tor ugabuga'}, 'version')
def test_bandwidth(self):
"""
@@ -521,7 +513,7 @@ class TestRouterStatusEntry(unittest.TestCase):
}
for w_line, expected in test_values.items():
- entry = get_router_status_entry_v3({'w': w_line})
+ entry = RouterStatusEntryV3.create({'w': w_line})
self.assertEqual(expected[0], entry.bandwidth)
self.assertEqual(expected[1], entry.measured)
self.assertEqual(expected[2], entry.is_unmeasured)
@@ -547,8 +539,7 @@ class TestRouterStatusEntry(unittest.TestCase):
)
for w_line in test_values:
- content = get_router_status_entry_v3({'w': w_line}, content = True)
- self._expect_invalid_attr(content)
+ expect_invalid_attr(self, {'w': w_line})
def test_exit_policy(self):
"""
@@ -561,7 +552,7 @@ class TestRouterStatusEntry(unittest.TestCase):
}
for p_line, expected in test_values.items():
- entry = get_router_status_entry_v3({'p': p_line})
+ entry = RouterStatusEntryV3.create({'p': p_line})
self.assertEqual(expected, entry.exit_policy)
# tries some invalid inputs
@@ -573,8 +564,7 @@ class TestRouterStatusEntry(unittest.TestCase):
)
for p_line in test_values:
- content = get_router_status_entry_v3({'p': p_line}, content = True)
- self._expect_invalid_attr(content, 'exit_policy')
+ expect_invalid_attr(self, {'p': p_line}, 'exit_policy')
def test_microdescriptor_hashes(self):
"""
@@ -591,13 +581,13 @@ class TestRouterStatusEntry(unittest.TestCase):
}
for m_line, expected in test_values.items():
- content = get_router_status_entry_v3({'m': m_line}, content = True)
+ content = RouterStatusEntryV3.content({'m': m_line})
entry = RouterStatusEntryV3(content, document = vote_document())
self.assertEqual(expected, entry.microdescriptor_hashes)
# try with multiple 'm' lines
- content = get_router_status_entry_v3(content = True)
+ content = RouterStatusEntryV3.content()
content += b'\nm 11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'
content += b'\nm 31,32 sha512=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'
@@ -610,8 +600,7 @@ class TestRouterStatusEntry(unittest.TestCase):
self.assertEqual(expected, entry.microdescriptor_hashes)
# try without a document
- content = get_router_status_entry_v3({'m': '8,9,10,11,12'}, content = True)
- self._expect_invalid_attr(content, 'microdescriptor_hashes', expected_value = [])
+ expect_invalid_attr(self, {'m': '8,9,10,11,12'}, 'microdescriptor_hashes', expected_value = [])
# tries some invalid inputs
test_values = (
@@ -621,7 +610,7 @@ class TestRouterStatusEntry(unittest.TestCase):
)
for m_line in test_values:
- content = get_router_status_entry_v3({'m': m_line}, content = True)
+ content = RouterStatusEntryV3.content({'m': m_line})
self.assertRaises(ValueError, RouterStatusEntryV3, content, True, vote_document())
def test_with_carriage_returns(self):
@@ -638,18 +627,3 @@ class TestRouterStatusEntry(unittest.TestCase):
router = next(descriptors)
self.assertEqual([Flag.FAST, Flag.RUNNING, Flag.STABLE, Flag.VALID], router.flags)
-
- def _expect_invalid_attr(self, content, attr = None, expected_value = None):
- """
- Asserts that construction will fail due to content having a malformed
- attribute. If an attr is provided then we check that it matches an expected
- value when we're constructed without validation.
- """
-
- self.assertRaises(ValueError, RouterStatusEntryV3, content, True)
- entry = RouterStatusEntryV3(content, False)
-
- if attr:
- self.assertEqual(expected_value, getattr(entry, attr))
- else:
- self.assertEqual('caerSidi', entry.nickname)
diff --git a/test/unit/response/events.py b/test/unit/response/events.py
index 8a95156..6023db6 100644
--- a/test/unit/response/events.py
+++ b/test/unit/response/events.py
@@ -11,6 +11,7 @@ import stem.response.events
import stem.util.log
from stem import * # enums and exceptions
+from stem.descriptor.router_status_entry import RouterStatusEntryV3
from test import mocking
try:
@@ -934,12 +935,12 @@ class TestEvents(unittest.TestCase):
def test_new_consensus_event(self):
expected_desc = []
- expected_desc.append(mocking.get_router_status_entry_v3({
+ expected_desc.append(RouterStatusEntryV3.create({
'r': 'Beaver /96bKo4soysolMgKn5Hex2nyFSY pAJH9dSBp/CG6sPhhVY/5bLaVPM 2012-12-02 22:02:45 77.223.43.54 9001 0',
's': 'Fast Named Running Stable Valid',
}))
- expected_desc.append(mocking.get_router_status_entry_v3({
+ expected_desc.append(RouterStatusEntryV3.create({
'r': 'Unnamed /+fJRWjmIGNAL2C5rRZHq3R91tA 7AnpZjfdBpYzXnMNm+w1bTsFF6Y 2012-12-02 17:51:10 91.121.184.87 9001 0',
's': 'Fast Guard Running Stable Valid',
}))
@@ -950,7 +951,7 @@ class TestEvents(unittest.TestCase):
self.assertEqual(expected_desc, event.desc)
def test_ns_event(self):
- expected_desc = mocking.get_router_status_entry_v3({
+ expected_desc = RouterStatusEntryV3.create({
'r': 'whnetz dbBxYcJriTTrcxsuy4PUZcMRwCA VStM7KAIH/mXXoGDUpoGB1OXufg 2012-12-02 21:03:56 141.70.120.13 9001 9030',
's': 'Fast HSDir Named Stable V2Dir Valid',
})
diff --git a/test/unit/tutorial.py b/test/unit/tutorial.py
index a404b42..48d0abb 100644
--- a/test/unit/tutorial.py
+++ b/test/unit/tutorial.py
@@ -10,6 +10,7 @@ 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 test.unit import exec_documentation_example
@@ -127,7 +128,7 @@ class TestTutorial(unittest.TestCase):
@patch('sys.stdout', new_callable = StringIO)
@patch('stem.descriptor.remote.DescriptorDownloader')
def test_mirror_mirror_on_the_wall_1(self, downloader_mock, stdout_mock):
- downloader_mock().get_consensus().run.return_value = [mocking.get_router_status_entry_v2()]
+ downloader_mock().get_consensus().run.return_value = [RouterStatusEntryV2.create()]
exec_documentation_example('current_descriptors.py')
self.assertEqual('found relay caerSidi (A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB)\n', stdout_mock.getvalue())
@@ -136,7 +137,7 @@ class TestTutorial(unittest.TestCase):
@patch('stem.control.Controller.from_port', spec = Controller)
def test_mirror_mirror_on_the_wall_2(self, from_port_mock, stdout_mock):
controller = from_port_mock().__enter__()
- controller.get_network_statuses.return_value = [mocking.get_router_status_entry_v2()]
+ controller.get_network_statuses.return_value = [RouterStatusEntryV2.create()]
exec_documentation_example('descriptor_from_tor_control_socket.py')
self.assertEqual('found relay caerSidi (A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB)\n', stdout_mock.getvalue())
@@ -151,7 +152,7 @@ class TestTutorial(unittest.TestCase):
print('found relay %s (%s)' % (desc.nickname, desc.fingerprint))
test_file = io.BytesIO(mocking.get_network_status_document_v3(
- routers = [mocking.get_router_status_entry_v3()],
+ routers = [RouterStatusEntryV3.create()],
content = True,
))
diff --git a/test/unit/tutorial_examples.py b/test/unit/tutorial_examples.py
index 267fd3d..b3ac813 100644
--- a/test/unit/tutorial_examples.py
+++ b/test/unit/tutorial_examples.py
@@ -19,16 +19,12 @@ from stem.control import Controller
from stem.util import str_type
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 test import mocking
+from test.mocking import get_network_status_document_v3
from test.unit import exec_documentation_example
-from test.mocking import (
- get_router_status_entry_v3,
- ROUTER_STATUS_ENTRY_V3_HEADER,
- get_network_status_document_v3,
-)
-
try:
# added in python 3.3
from unittest.mock import Mock, patch
@@ -130,9 +126,9 @@ def _get_router_status(address = None, port = None, nickname = None, fingerprint
r_line = r_line.replace('p1aag7VwarGxqctS7/fS0y5FU+s', fingerprint_base64)
if s_line:
- return get_router_status_entry_v3({'r': r_line, 's': s_line})
+ return RouterStatusEntryV3.create({'r': r_line, 's': s_line})
else:
- return get_router_status_entry_v3({'r': r_line})
+ return RouterStatusEntryV3.create({'r': r_line})
class TestTutorialExamples(unittest.TestCase):
@@ -299,8 +295,8 @@ class TestTutorialExamples(unittest.TestCase):
directory_values[0].address = '131.188.40.189'
get_authorities_mock().values.return_value = directory_values
- entry_with_measurement = get_router_status_entry_v3({'w': 'Bandwidth=1 Measured=1'})
- entry_without_measurement = get_router_status_entry_v3()
+ entry_with_measurement = RouterStatusEntryV3.create({'w': 'Bandwidth=1 Measured=1'})
+ entry_without_measurement = RouterStatusEntryV3.create()
query1 = Mock()
query1.download_url = 'http://131.188.40.189:80/tor/status-vote/current/authority'
@@ -336,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 = (get_router_status_entry_v3(),))
+ network_status = get_network_status_document_v3(routers = (RouterStatusEntryV3.create(),))
query_mock().run.return_value = [network_status]
parse_file_mock.return_value = itertools.cycle([network_status])
1
0

02 May '17
commit 5caf80b2bd39964599ce70b5acca9e224c0f3b93
Author: Damian Johnson <atagar(a)torproject.org>
Date: Tue May 2 12:50:14 2017 -0700
Describe descriptor creation in our tutorials
Noting our new capability in our tutorials with a couple examples.
---
docs/_static/example/create_descriptor.py | 9 +++++
docs/_static/example/create_descriptor_content.py | 3 ++
docs/change_log.rst | 1 +
docs/tutorials/mirror_mirror_on_the_wall.rst | 40 +++++++++++++++++++++++
4 files changed, 53 insertions(+)
diff --git a/docs/_static/example/create_descriptor.py b/docs/_static/example/create_descriptor.py
new file mode 100644
index 0000000..29c5654
--- /dev/null
+++ b/docs/_static/example/create_descriptor.py
@@ -0,0 +1,9 @@
+from stem.descriptor.server_descriptor import RelayDescriptor
+
+# prints 'caerSidi (71.35.133.197:9001)'
+desc = RelayDescriptor.create()
+print("%s (%s:%s)" % (desc.nickname, desc.address, desc.or_port))
+
+# prints 'demo (127.0.0.1:80)'
+desc = RelayDescriptor.create({'router': 'demo 127.0.0.1 80 0 0'})
+print("%s (%s:%s)" % (desc.nickname, desc.address, desc.or_port))
diff --git a/docs/_static/example/create_descriptor_content.py b/docs/_static/example/create_descriptor_content.py
new file mode 100644
index 0000000..e4a9f2b
--- /dev/null
+++ b/docs/_static/example/create_descriptor_content.py
@@ -0,0 +1,3 @@
+from stem.descriptor.server_descriptor import RelayDescriptor
+
+print(RelayDescriptor.content({'router': 'demo 127.0.0.1 80 0 0'}))
diff --git a/docs/change_log.rst b/docs/change_log.rst
index f8985f9..b99d0b4 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -53,6 +53,7 @@ The following are only available within Stem's `git repository
* **Descriptors**
+ * Supporting `descriptor creation <tutorials/mirror_mirror_on_the_wall.html#can-i-create-descriptors>`_ (:trac:`10227`)
* Support and validation for `ed25519 certificates <api/descriptor/certificate.html>`_ (`spec <https://gitweb.torproject.org/torspec.git/tree/cert-spec.txt>`_, :trac:`21558`)
* Moved from the deprecated `pycrypto <https://www.dlitz.net/software/pycrypto/>`_ module to `cryptography <https://pypi.python.org/pypi/cryptography>`_ for validating signatures (:trac:`21086`)
* Sped descriptor reading by ~25% by deferring defaulting when validating
diff --git a/docs/tutorials/mirror_mirror_on_the_wall.rst b/docs/tutorials/mirror_mirror_on_the_wall.rst
index 9d018fc..d9ba6e0 100644
--- a/docs/tutorials/mirror_mirror_on_the_wall.rst
+++ b/docs/tutorials/mirror_mirror_on_the_wall.rst
@@ -9,6 +9,7 @@ with what they are and where to get them then you may want to skip to the end.
* :ref:`where-can-i-get-the-current-descriptors`
* :ref:`where-can-i-get-past-descriptors`
* :ref:`can-i-get-descriptors-from-the-tor-process`
+* :ref:`can-i-create-descriptors`
* :ref:`validating-the-descriptors-content`
* :ref:`saving-and-loading-descriptors`
* :ref:`putting-it-together`
@@ -164,6 +165,45 @@ through Tor's control socket...
.. _validating-the-descriptors-content:
+.. _can-i-create-descriptors:
+
+Can I create descriptors?
+-------------------------
+
+Besides reading descriptors you can create them too. This is most commonly done
+for test data. To do so simply use the
+:func:`~stem.descriptor.__init__.Descriptor.create` method of
+:class:`~stem.descriptor.__init__.Descriptor` subclasses...
+
+.. literalinclude:: /_static/example/create_descriptor.py
+ :language: python
+
+Unspecified mandatory fields are filled with mock data. You can also use
+:func:`~stem.descriptor.__init__.Descriptor.content` to get a string
+descriptor...
+
+.. literalinclude:: /_static/example/create_descriptor_content.py
+ :language: python
+
+::
+
+ router demo 127.0.0.1 80 0 0
+ published 2012-03-01 17:15:27
+ bandwidth 153600 256000 104590
+ reject *:*
+ onion-key
+ -----BEGIN RSA PUBLIC KEY-----
+ MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg
+ skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+
+ WPi4Fl2qryzTb3QO5r5x7T8OsG2IBUET1bLQzmtbC560SYR49IvVAgMBAAE=
+ -----END RSA PUBLIC KEY-----
+ signing-key
+ ...
+
+**Note:** Stem doesn't yet support signing its descriptors (:trac:`10227`).
+Until it does try using `leekspin <https://pypi.python.org/pypi/leekspin>`_
+instead.
+
Validating the descriptor's content
-----------------------------------
1
0

02 May '17
commit b164478ea9c0275bc668cef32e84f4026c05a246
Author: Damian Johnson <atagar(a)torproject.org>
Date: Tue May 2 09:37:47 2017 -0700
Merge remaining mocking module into util
Without descriptor creation there's really not much left. Merging the few
remaining helpers into the util module.
---
test/__init__.py | 3 +-
test/integ/control/base_controller.py | 4 +-
test/integ/control/controller.py | 10 +--
test/integ/socket/control_message.py | 4 +-
test/mocking.py | 118 --------------------------------
test/unit/connection/authentication.py | 9 +--
test/unit/control/controller.py | 7 +-
test/unit/doctest.py | 3 +-
test/unit/interpreter/commands.py | 11 +--
test/unit/response/add_onion.py | 14 ++--
test/unit/response/authchallenge.py | 6 +-
test/unit/response/events.py | 5 +-
test/unit/response/getconf.py | 14 ++--
test/unit/response/getinfo.py | 16 ++---
test/unit/response/mapaddress.py | 14 ++--
test/unit/response/protocolinfo.py | 25 +++----
test/unit/response/singleline.py | 12 ++--
test/unit/tutorial_examples.py | 5 +-
test/unit/util/proc.py | 5 +-
test/util.py | 120 ++++++++++++++++++++++++++++++---
20 files changed, 195 insertions(+), 210 deletions(-)
diff --git a/test/__init__.py b/test/__init__.py
index 7ab0853..8ea70d2 100644
--- a/test/__init__.py
+++ b/test/__init__.py
@@ -6,10 +6,9 @@ Unit and integration tests for the stem library.
"""
__all__ = [
- 'mocking',
'network',
'output',
'prompt',
'runner',
- 'utils',
+ 'util',
]
diff --git a/test/integ/control/base_controller.py b/test/integ/control/base_controller.py
index 0a51e1f..1e64445 100644
--- a/test/integ/control/base_controller.py
+++ b/test/integ/control/base_controller.py
@@ -11,8 +11,8 @@ import stem.control
import stem.socket
import stem.util.system
-import test.mocking
import test.runner
+import test.util
from test.util import require_controller
@@ -154,7 +154,7 @@ class TestBaseController(unittest.TestCase):
controller.msg('SETEVENTS CONF_CHANGED')
for i in range(10):
- controller.msg('SETCONF NodeFamily=%s' % test.mocking.random_fingerprint())
+ controller.msg('SETCONF NodeFamily=%s' % test.util.random_fingerprint())
test.runner.exercise_controller(self, controller)
controller.msg('SETEVENTS')
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index 48a50fe..895d435 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -18,7 +18,6 @@ import stem.response.protocolinfo
import stem.socket
import stem.util.str_tools
import stem.version
-import test.mocking
import test.network
import test.runner
@@ -29,6 +28,7 @@ from stem.version import Requirement
from test.util import (
register_new_capability,
+ random_fingerprint,
tor_version,
only_run_once,
require_controller,
@@ -150,7 +150,7 @@ class TestController(unittest.TestCase):
controller.add_event_listener(listener2, EventType.CONF_CHANGED, EventType.DEBUG)
# The NodeFamily is a harmless option we can toggle
- controller.set_conf('NodeFamily', test.mocking.random_fingerprint())
+ controller.set_conf('NodeFamily', random_fingerprint())
# Wait for the event. Assert that we get it within 10 seconds
event_notice1.wait(10)
@@ -167,7 +167,7 @@ class TestController(unittest.TestCase):
buffer2_size = len(event_buffer2)
- controller.set_conf('NodeFamily', test.mocking.random_fingerprint())
+ controller.set_conf('NodeFamily', random_fingerprint())
event_notice1.wait(10)
self.assertEqual(len(event_buffer1), 2)
event_notice1.clear()
@@ -204,7 +204,7 @@ class TestController(unittest.TestCase):
# trigger an event
- controller.set_conf('NodeFamily', test.mocking.random_fingerprint())
+ controller.set_conf('NodeFamily', random_fingerprint())
event_notice.wait(4)
self.assertTrue(len(event_buffer) >= 1)
@@ -217,7 +217,7 @@ class TestController(unittest.TestCase):
controller.connect()
controller.authenticate(password = test.runner.CONTROL_PASSWORD)
self.assertTrue(len(event_buffer) == 0)
- controller.set_conf('NodeFamily', test.mocking.random_fingerprint())
+ controller.set_conf('NodeFamily', random_fingerprint())
event_notice.wait(4)
self.assertTrue(len(event_buffer) >= 1)
diff --git a/test/integ/socket/control_message.py b/test/integ/socket/control_message.py
index 13075ca..646f732 100644
--- a/test/integ/socket/control_message.py
+++ b/test/integ/socket/control_message.py
@@ -7,12 +7,12 @@ import unittest
import stem.socket
import stem.version
-import test.mocking
import test.runner
from test.util import (
require_controller,
require_version,
+ random_fingerprint,
)
@@ -162,7 +162,7 @@ class TestControlMessage(unittest.TestCase):
# We'll receive both a CONF_CHANGED event and 'OK' response for the
# SETCONF, but not necessarily in any specific order.
- control_socket.send('SETCONF NodeFamily=%s' % test.mocking.random_fingerprint())
+ control_socket.send('SETCONF NodeFamily=%s' % random_fingerprint())
msg1 = control_socket.recv()
msg2 = control_socket.recv()
diff --git a/test/mocking.py b/test/mocking.py
deleted file mode 100644
index 6369598..0000000
--- a/test/mocking.py
+++ /dev/null
@@ -1,118 +0,0 @@
-# Copyright 2012-2017, Damian Johnson and The Tor Project
-# See LICENSE for licensing information
-
-"""
-Helper functions for creating mock objects.
-
-::
-
- get_all_combinations - provides all combinations of attributes
- random_fingerprint - provides a random relay fingerprint
-
- Instance Constructors
- get_message - stem.response.ControlMessage
- get_protocolinfo_response - stem.response.protocolinfo.ProtocolInfoResponse
-"""
-
-import hashlib
-import itertools
-import os
-import re
-
-import stem.descriptor.extrainfo_descriptor
-import stem.descriptor.hidden_service_descriptor
-import stem.descriptor.microdescriptor
-import stem.descriptor.networkstatus
-import stem.descriptor.router_status_entry
-import stem.descriptor.server_descriptor
-import stem.prereq
-import stem.response
-import stem.util.str_tools
-
-
-def get_all_combinations(attr, include_empty = False):
- """
- Provides an iterator for all combinations of a set of attributes. For
- instance...
-
- ::
-
- >>> list(test.mocking.get_all_combinations(['a', 'b', 'c']))
- [('a',), ('b',), ('c',), ('a', 'b'), ('a', 'c'), ('b', 'c'), ('a', 'b', 'c')]
-
- :param list attr: attributes to provide combinations for
- :param bool include_empty: includes an entry with zero items if True
- :returns: iterator for all combinations
- """
-
- # Makes an itertools.product() call for 'i' copies of attr...
- #
- # * itertools.product(attr) => all one-element combinations
- # * itertools.product(attr, attr) => all two-element combinations
- # * ... etc
-
- if include_empty:
- yield ()
-
- seen = set()
- for index in range(1, len(attr) + 1):
- product_arg = [attr for _ in range(index)]
-
- for item in itertools.product(*product_arg):
- # deduplicate, sort, and only provide if we haven't seen it yet
- item = tuple(sorted(set(item)))
-
- if item not in seen:
- seen.add(item)
- yield item
-
-
-def random_fingerprint():
- """
- Provides a random relay fingerprint.
- """
-
- return hashlib.sha1(os.urandom(20)).hexdigest().upper()
-
-
-def get_message(content, reformat = True):
- """
- Provides a ControlMessage with content modified to be parsable. This makes
- the following changes unless 'reformat' is false...
-
- * ensures the content ends with a newline
- * newlines are replaced with a carriage return and newline pair
-
- :param str content: base content for the controller message
- :param str reformat: modifies content to be more accommodating to being parsed
-
- :returns: stem.response.ControlMessage instance
- """
-
- if reformat:
- if not content.endswith('\n'):
- content += '\n'
-
- content = re.sub('([\r]?)\n', '\r\n', content)
-
- return stem.response.ControlMessage.from_str(content)
-
-
-def get_protocolinfo_response(**attributes):
- """
- Provides a ProtocolInfoResponse, customized with the given attributes. The
- base instance is minimal, with its version set to one and everything else
- left with the default.
-
- :param dict attributes: attributes to customize the response with
-
- :returns: stem.response.protocolinfo.ProtocolInfoResponse instance
- """
-
- protocolinfo_response = get_message('250-PROTOCOLINFO 1\n250 OK')
- stem.response.convert('PROTOCOLINFO', protocolinfo_response)
-
- for attr in attributes:
- setattr(protocolinfo_response, attr, attributes[attr])
-
- return protocolinfo_response
diff --git a/test/unit/connection/authentication.py b/test/unit/connection/authentication.py
index b7ccc0d..044f163 100644
--- a/test/unit/connection/authentication.py
+++ b/test/unit/connection/authentication.py
@@ -13,8 +13,9 @@ import unittest
import stem.connection
+import test.util
+
from stem.util import log
-from test import mocking
try:
# added in python 3.3
@@ -33,7 +34,7 @@ class TestAuthenticate(unittest.TestCase):
# tests where get_protocolinfo succeeds
- get_protocolinfo_mock.return_value = mocking.get_protocolinfo_response(
+ get_protocolinfo_mock.return_value = test.util.get_protocolinfo_response(
auth_methods = (stem.connection.AuthMethod.NONE, ),
)
@@ -85,7 +86,7 @@ class TestAuthenticate(unittest.TestCase):
stem.connection.AuthChallengeFailed(None, None),
stem.ControllerError(None))
- auth_method_combinations = mocking.get_all_combinations([
+ auth_method_combinations = test.util.get_all_combinations([
stem.connection.AuthMethod.NONE,
stem.connection.AuthMethod.PASSWORD,
stem.connection.AuthMethod.COOKIE,
@@ -93,7 +94,7 @@ class TestAuthenticate(unittest.TestCase):
stem.connection.AuthMethod.UNKNOWN,
], include_empty = True)
- protocolinfo = mocking.get_protocolinfo_response(cookie_path = '/tmp/blah')
+ protocolinfo = test.util.get_protocolinfo_response(cookie_path = '/tmp/blah')
for auth_methods in auth_method_combinations:
for auth_none_exc in all_auth_none_exc:
diff --git a/test/unit/control/controller.py b/test/unit/control/controller.py
index 47ac9d2..2cf8b7e 100644
--- a/test/unit/control/controller.py
+++ b/test/unit/control/controller.py
@@ -13,10 +13,11 @@ import stem.socket
import stem.util.system
import stem.version
+import test.util
+
from stem import ControllerError, DescriptorUnavailable, InvalidArguments, InvalidRequest, ProtocolError, UnsatisfiableRequest
from stem.control import _parse_circ_path, Listener, Controller, EventType
from stem.exit_policy import ExitPolicy
-from test import mocking
try:
# added in python 3.3
@@ -338,13 +339,13 @@ class TestControl(unittest.TestCase):
# use the handy mocked protocolinfo response
- get_protocolinfo_mock.return_value = mocking.get_protocolinfo_response()
+ get_protocolinfo_mock.return_value = test.util.get_protocolinfo_response()
# compare the str representation of these object, because the class
# does not have, nor need, a direct comparison operator
self.assertEqual(
- str(mocking.get_protocolinfo_response()),
+ str(test.util.get_protocolinfo_response()),
str(self.controller.get_protocolinfo())
)
diff --git a/test/unit/doctest.py b/test/unit/doctest.py
index 548831a..06a6eaa 100644
--- a/test/unit/doctest.py
+++ b/test/unit/doctest.py
@@ -14,7 +14,6 @@ import stem.util.str_tools
import stem.util.system
import stem.version
-import test.mocking
import test.util
try:
@@ -84,7 +83,7 @@ class TestDocumentation(unittest.TestCase):
'circuit-status': EXPECTED_CIRCUIT_STATUS,
}[arg]
- response = test.mocking.get_message(ADD_ONION_RESPONSE)
+ response = test.util.get_message(ADD_ONION_RESPONSE)
stem.response.convert('ADD_ONION', response)
controller.create_ephemeral_hidden_service.return_value = response
diff --git a/test/unit/interpreter/commands.py b/test/unit/interpreter/commands.py
index 63fedc5..c8cbc09 100644
--- a/test/unit/interpreter/commands.py
+++ b/test/unit/interpreter/commands.py
@@ -5,9 +5,10 @@ import stem
import stem.response
import stem.version
+import test.util
+
from stem.interpreter.commands import ControlInterpreter, _get_fingerprint
-from test import mocking
from test.unit.interpreter import CONTROLLER
try:
@@ -125,7 +126,7 @@ class TestInterpreterCommands(unittest.TestCase):
)
for content in event_contents:
- event = mocking.get_message(content)
+ event = test.util.get_message(content)
stem.response.convert('EVENT', event)
interpreter._received_events.append(event)
@@ -170,7 +171,7 @@ class TestInterpreterCommands(unittest.TestCase):
response = '250-version=0.2.5.1-alpha-dev (git-245ecfff36c0cecc)\r\n250 OK'
controller = Mock()
- controller.msg.return_value = mocking.get_message(response)
+ controller.msg.return_value = test.util.get_message(response)
interpreter = ControlInterpreter(controller)
@@ -185,7 +186,7 @@ class TestInterpreterCommands(unittest.TestCase):
response = '250-Log=notice stdout\r\n250 Address'
controller = Mock()
- controller.msg.return_value = mocking.get_message(response)
+ controller.msg.return_value = test.util.get_message(response)
interpreter = ControlInterpreter(controller)
@@ -194,7 +195,7 @@ class TestInterpreterCommands(unittest.TestCase):
def test_setevents(self):
controller = Mock()
- controller.msg.return_value = mocking.get_message('250 OK')
+ controller.msg.return_value = test.util.get_message('250 OK')
interpreter = ControlInterpreter(controller)
diff --git a/test/unit/response/add_onion.py b/test/unit/response/add_onion.py
index 1213f0c..048ca67 100644
--- a/test/unit/response/add_onion.py
+++ b/test/unit/response/add_onion.py
@@ -8,7 +8,7 @@ import stem
import stem.response
import stem.response.add_onion
-from test import mocking
+import test.util
WITH_PRIVATE_KEY = """250-ServiceID=gfzprpioee3hoppz
250-PrivateKey=RSA1024:MIICXgIBAAKBgQDZvYVxvKPTWhId/8Ss9fVxjAoFDsrJ3pk6HjHrEFRm3ypkK/vArbG9BrupzzYcyms+lO06O8b/iOSHuZI5mUEGkrYqQ+hpB2SkPUEzW7vcp8SQQivna3+LfkWH4JDqfiwZutU6MMEvU6g1OqK4Hll6uHbLpsfxkS/mGjyu1C9a9wIDAQABAoGBAJxsC3a25xZJqaRFfxwmIiptSTFy+/nj4T4gPQo6k/fHMKP/+P7liT9bm+uUwbITNNIjmPzxvrcKt+pNRR/92fizxr8QXr8l0ciVOLerbvdqvVUaQ/K1IVsblOLbactMvXcHactmqqLFUaZU9PPSDla7YkzikLDIUtHXQBEt4HEhAkEA/c4n+kpwi4odCaF49ESPbZC/Qejh7U9Tq10vAHzfrrGgQjnLw2UGDxJQXc9P12fGTvD2q3Q3VaMI8TKKFqZXsQJBANufh1zfP+xX/UfxJ4QzDUCHCu2gnyTDj3nG9Bc80E5g7NwR2VBXF1R+QQCK9GZcXd2y6vBYgrHOSUiLbVjGrycCQQDpOcs0zbjUEUuTsQUT+fiO50dJSrZpus6ZFxz85sMppeItWSzsVeYWbW7adYnZ2Gu72OPjM/0xPYsXEakhHSRRAkAxlVauNQjthv/72god4pi/VL224GiNmEkwKSa6iFRPHbrcBHuXk9IElWx/ft+mrHvUraw1DwaStgv9gNzzCghJAkEA08RegCRnIzuGvgeejLk4suIeCMD/11AvmSvxbRWS5rq1leSVo7uGLSnqDbwlzE4dGb5kH15NNAp14/l2Fu/yZg==
@@ -40,7 +40,7 @@ class TestAddOnionResponse(unittest.TestCase):
"""
# working case
- response = mocking.get_message(WITH_PRIVATE_KEY)
+ response = test.util.get_message(WITH_PRIVATE_KEY)
stem.response.convert('ADD_ONION', response)
# now this should be a AddOnionResponse (ControlMessage subclass)
@@ -57,7 +57,7 @@ class TestAddOnionResponse(unittest.TestCase):
Checks a response when there's a private key.
"""
- response = mocking.get_message(WITH_PRIVATE_KEY)
+ response = test.util.get_message(WITH_PRIVATE_KEY)
stem.response.convert('ADD_ONION', response)
self.assertEqual('gfzprpioee3hoppz', response.service_id)
@@ -70,7 +70,7 @@ class TestAddOnionResponse(unittest.TestCase):
Checks a response when there's client credentials.
"""
- response = mocking.get_message(WITH_CLIENT_AUTH)
+ response = test.util.get_message(WITH_CLIENT_AUTH)
stem.response.convert('ADD_ONION', response)
self.assertEqual('oekn5sqrvcu4wote', response.service_id)
@@ -83,7 +83,7 @@ class TestAddOnionResponse(unittest.TestCase):
Checks a response without a private key.
"""
- response = mocking.get_message(WITHOUT_PRIVATE_KEY)
+ response = test.util.get_message(WITHOUT_PRIVATE_KEY)
stem.response.convert('ADD_ONION', response)
self.assertEqual('gfzprpioee3hoppz', response.service_id)
@@ -95,7 +95,7 @@ class TestAddOnionResponse(unittest.TestCase):
Checks a response that lack an initial service id.
"""
- response = mocking.get_message(WRONG_FIRST_KEY)
+ response = test.util.get_message(WRONG_FIRST_KEY)
self.assertRaisesRegexp(stem.ProtocolError, 'ADD_ONION response should start with', stem.response.convert, 'ADD_ONION', response)
def test_no_key_type(self):
@@ -103,5 +103,5 @@ class TestAddOnionResponse(unittest.TestCase):
Checks a response that's missing the private key type.
"""
- response = mocking.get_message(MISSING_KEY_TYPE)
+ response = test.util.get_message(MISSING_KEY_TYPE)
self.assertRaisesRegexp(stem.ProtocolError, 'ADD_ONION PrivateKey lines should be of the form', stem.response.convert, 'ADD_ONION', response)
diff --git a/test/unit/response/authchallenge.py b/test/unit/response/authchallenge.py
index 2185ea8..6a6533c 100644
--- a/test/unit/response/authchallenge.py
+++ b/test/unit/response/authchallenge.py
@@ -8,7 +8,7 @@ import stem.response
import stem.response.authchallenge
import stem.socket
-from test import mocking
+import test.util
VALID_RESPONSE = '250 AUTHCHALLENGE \
SERVERHASH=B16F72DACD4B5ED1531F3FCC04B593D46A1E30267E636EA7C7F8DD7A2B7BAA05 \
@@ -27,7 +27,7 @@ class TestAuthChallengeResponse(unittest.TestCase):
Parses valid AUTHCHALLENGE responses.
"""
- control_message = mocking.get_message(VALID_RESPONSE)
+ control_message = test.util.get_message(VALID_RESPONSE)
stem.response.convert('AUTHCHALLENGE', control_message)
# now this should be a AuthChallengeResponse (ControlMessage subclass)
@@ -51,5 +51,5 @@ class TestAuthChallengeResponse(unittest.TestCase):
# constructed.
remaining_comp = auth_challenge_comp[:index] + auth_challenge_comp[index + 1:]
- control_message = mocking.get_message(' '.join(remaining_comp))
+ control_message = test.util.get_message(' '.join(remaining_comp))
self.assertRaises(stem.ProtocolError, stem.response.convert, 'AUTHCHALLENGE', control_message)
diff --git a/test/unit/response/events.py b/test/unit/response/events.py
index 6023db6..b14b739 100644
--- a/test/unit/response/events.py
+++ b/test/unit/response/events.py
@@ -10,9 +10,10 @@ import stem.response
import stem.response.events
import stem.util.log
+import test.util
+
from stem import * # enums and exceptions
from stem.descriptor.router_status_entry import RouterStatusEntryV3
-from test import mocking
try:
# added in python 3.3
@@ -456,7 +457,7 @@ TB_EMPTY_BAD_2 = '650 TB_EMPTY GLOBAL READ=93 WRITTEN=93 LAST=-100'
def _get_event(content):
- controller_event = mocking.get_message(content)
+ controller_event = test.util.get_message(content)
stem.response.convert('EVENT', controller_event)
return controller_event
diff --git a/test/unit/response/getconf.py b/test/unit/response/getconf.py
index 07c89fd..fb72ffc 100644
--- a/test/unit/response/getconf.py
+++ b/test/unit/response/getconf.py
@@ -8,7 +8,7 @@ import stem.response
import stem.response.getconf
import stem.socket
-from test import mocking
+import test.util
EMPTY_RESPONSE = '250 OK'
@@ -42,7 +42,7 @@ class TestGetConfResponse(unittest.TestCase):
Parses a GETCONF reply without options (just calling "GETCONF").
"""
- control_message = mocking.get_message(EMPTY_RESPONSE)
+ control_message = test.util.get_message(EMPTY_RESPONSE)
stem.response.convert('GETCONF', control_message)
# now this should be a GetConfResponse (ControlMessage subclass)
@@ -56,7 +56,7 @@ class TestGetConfResponse(unittest.TestCase):
Parses a GETCONF reply response for a single parameter.
"""
- control_message = mocking.get_message(SINGLE_RESPONSE)
+ control_message = test.util.get_message(SINGLE_RESPONSE)
stem.response.convert('GETCONF', control_message)
self.assertEqual({'DataDirectory': ['/home/neena/.tor']}, control_message.entries)
@@ -65,7 +65,7 @@ class TestGetConfResponse(unittest.TestCase):
Parses a GETCONF reply for muiltiple parameters.
"""
- control_message = mocking.get_message(BATCH_RESPONSE)
+ control_message = test.util.get_message(BATCH_RESPONSE)
stem.response.convert('GETCONF', control_message)
expected = {
@@ -82,7 +82,7 @@ class TestGetConfResponse(unittest.TestCase):
Parses a GETCONF reply containing a single key with multiple parameters.
"""
- control_message = mocking.get_message(MULTIVALUE_RESPONSE)
+ control_message = test.util.get_message(MULTIVALUE_RESPONSE)
stem.response.convert('GETCONF', control_message)
expected = {
@@ -97,7 +97,7 @@ class TestGetConfResponse(unittest.TestCase):
Parses a GETCONF reply that contains an error code with an unrecognized key.
"""
- control_message = mocking.get_message(UNRECOGNIZED_KEY_RESPONSE)
+ control_message = test.util.get_message(UNRECOGNIZED_KEY_RESPONSE)
self.assertRaises(stem.InvalidArguments, stem.response.convert, 'GETCONF', control_message)
try:
@@ -112,5 +112,5 @@ class TestGetConfResponse(unittest.TestCase):
GETCONF's spec.
"""
- control_message = mocking.get_message(INVALID_RESPONSE)
+ control_message = test.util.get_message(INVALID_RESPONSE)
self.assertRaises(stem.ProtocolError, stem.response.convert, 'GETCONF', control_message)
diff --git a/test/unit/response/getinfo.py b/test/unit/response/getinfo.py
index afd0748..2054a03 100644
--- a/test/unit/response/getinfo.py
+++ b/test/unit/response/getinfo.py
@@ -9,7 +9,7 @@ import stem.response.getinfo
import stem.socket
import stem.util.str_tools
-from test import mocking
+import test.util
EMPTY_RESPONSE = '250 OK'
@@ -57,7 +57,7 @@ class TestGetInfoResponse(unittest.TestCase):
Parses a GETINFO reply without options (just calling "GETINFO").
"""
- control_message = mocking.get_message(EMPTY_RESPONSE)
+ control_message = test.util.get_message(EMPTY_RESPONSE)
stem.response.convert('GETINFO', control_message)
# now this should be a GetInfoResponse (ControlMessage subclass)
@@ -71,7 +71,7 @@ class TestGetInfoResponse(unittest.TestCase):
Parses a GETINFO reply response for a single parameter.
"""
- control_message = mocking.get_message(SINGLE_RESPONSE)
+ control_message = test.util.get_message(SINGLE_RESPONSE)
stem.response.convert('GETINFO', control_message)
self.assertEqual({'version': b'0.2.3.11-alpha-dev'}, control_message.entries)
@@ -80,7 +80,7 @@ class TestGetInfoResponse(unittest.TestCase):
Parses a GETINFO reply for muiltiple parameters.
"""
- control_message = mocking.get_message(BATCH_RESPONSE)
+ control_message = test.util.get_message(BATCH_RESPONSE)
stem.response.convert('GETINFO', control_message)
expected = {
@@ -97,7 +97,7 @@ class TestGetInfoResponse(unittest.TestCase):
value.
"""
- control_message = mocking.get_message(MULTILINE_RESPONSE)
+ control_message = test.util.get_message(MULTILINE_RESPONSE)
stem.response.convert('GETINFO', control_message)
expected = {
@@ -113,7 +113,7 @@ class TestGetInfoResponse(unittest.TestCase):
entry.
"""
- control_message = mocking.get_message(NON_KEY_VALUE_ENTRY)
+ control_message = test.util.get_message(NON_KEY_VALUE_ENTRY)
self.assertRaises(stem.ProtocolError, stem.response.convert, 'GETINFO', control_message)
def test_unrecognized_key_response(self):
@@ -121,7 +121,7 @@ class TestGetInfoResponse(unittest.TestCase):
Parses a GETCONF reply that contains an error code with an unrecognized key.
"""
- control_message = mocking.get_message(UNRECOGNIZED_KEY_ENTRY)
+ control_message = test.util.get_message(UNRECOGNIZED_KEY_ENTRY)
self.assertRaises(stem.InvalidArguments, stem.response.convert, 'GETINFO', control_message)
try:
@@ -136,5 +136,5 @@ class TestGetInfoResponse(unittest.TestCase):
malformed according to the GETINFO's spec.
"""
- control_message = mocking.get_message(MISSING_MULTILINE_NEWLINE)
+ control_message = test.util.get_message(MISSING_MULTILINE_NEWLINE)
self.assertRaises(stem.ProtocolError, stem.response.convert, 'GETINFO', control_message)
diff --git a/test/unit/response/mapaddress.py b/test/unit/response/mapaddress.py
index 753f2e6..63813d4 100644
--- a/test/unit/response/mapaddress.py
+++ b/test/unit/response/mapaddress.py
@@ -8,7 +8,7 @@ import stem.response
import stem.response.mapaddress
import stem.socket
-from test import mocking
+import test.util
SINGLE_RESPONSE = """250 foo=bar"""
@@ -36,7 +36,7 @@ class TestMapAddressResponse(unittest.TestCase):
Parses a MAPADDRESS reply response with a single address mapping.
"""
- control_message = mocking.get_message(SINGLE_RESPONSE)
+ control_message = test.util.get_message(SINGLE_RESPONSE)
stem.response.convert('MAPADDRESS', control_message)
self.assertEqual({'foo': 'bar'}, control_message.entries)
@@ -45,7 +45,7 @@ class TestMapAddressResponse(unittest.TestCase):
Parses a MAPADDRESS reply with multiple address mappings
"""
- control_message = mocking.get_message(BATCH_RESPONSE)
+ control_message = test.util.get_message(BATCH_RESPONSE)
stem.response.convert('MAPADDRESS', control_message)
expected = {
@@ -62,11 +62,11 @@ class TestMapAddressResponse(unittest.TestCase):
Parses a MAPADDRESS replies that contain an error code due to hostname syntax errors.
"""
- control_message = mocking.get_message(UNRECOGNIZED_KEYS_RESPONSE)
+ control_message = test.util.get_message(UNRECOGNIZED_KEYS_RESPONSE)
self.assertRaises(stem.InvalidRequest, stem.response.convert, 'MAPADDRESS', control_message)
expected = {'23': '324'}
- control_message = mocking.get_message(PARTIAL_FAILURE_RESPONSE)
+ control_message = test.util.get_message(PARTIAL_FAILURE_RESPONSE)
stem.response.convert('MAPADDRESS', control_message)
self.assertEqual(expected, control_message.entries)
@@ -77,8 +77,8 @@ class TestMapAddressResponse(unittest.TestCase):
MAPADDRESS's spec.
"""
- control_message = mocking.get_message(INVALID_EMPTY_RESPONSE)
+ control_message = test.util.get_message(INVALID_EMPTY_RESPONSE)
self.assertRaises(stem.ProtocolError, stem.response.convert, 'MAPADDRESS', control_message)
- control_message = mocking.get_message(INVALID_RESPONSE)
+ control_message = test.util.get_message(INVALID_RESPONSE)
self.assertRaises(stem.ProtocolError, stem.response.convert, 'MAPADDRESS', control_message)
diff --git a/test/unit/response/protocolinfo.py b/test/unit/response/protocolinfo.py
index c95076a..d8fb762 100644
--- a/test/unit/response/protocolinfo.py
+++ b/test/unit/response/protocolinfo.py
@@ -12,8 +12,9 @@ import stem.util.proc
import stem.util.system
import stem.version
+import test.util
+
from stem.response.protocolinfo import AuthMethod
-from test import mocking
try:
# added in python 3.3
@@ -70,7 +71,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
"""
# working case
- control_message = mocking.get_message(NO_AUTH)
+ control_message = test.util.get_message(NO_AUTH)
stem.response.convert('PROTOCOLINFO', control_message)
# now this should be a ProtocolInfoResponse (ControlMessage subclass)
@@ -86,7 +87,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
self.assertRaises(TypeError, stem.response.convert, 'PROTOCOLINFO', 'hello world')
# attempt to convert a different message type
- bw_event_control_message = mocking.get_message('650 BW 32326 2856')
+ bw_event_control_message = test.util.get_message('650 BW 32326 2856')
self.assertRaises(stem.ProtocolError, stem.response.convert, 'PROTOCOLINFO', bw_event_control_message)
def test_no_auth(self):
@@ -94,7 +95,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
Checks a response when there's no authentication.
"""
- control_message = mocking.get_message(NO_AUTH)
+ control_message = test.util.get_message(NO_AUTH)
stem.response.convert('PROTOCOLINFO', control_message)
self.assertEqual(1, control_message.protocol_version)
@@ -108,7 +109,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
Checks a response with password authentication.
"""
- control_message = mocking.get_message(PASSWORD_AUTH)
+ control_message = test.util.get_message(PASSWORD_AUTH)
stem.response.convert('PROTOCOLINFO', control_message)
self.assertEqual((AuthMethod.PASSWORD, ), control_message.auth_methods)
@@ -118,7 +119,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
characters.
"""
- control_message = mocking.get_message(COOKIE_AUTH)
+ control_message = test.util.get_message(COOKIE_AUTH)
stem.response.convert('PROTOCOLINFO', control_message)
self.assertEqual((AuthMethod.COOKIE, ), control_message.auth_methods)
self.assertEqual('/tmp/my data\\"dir//control_auth_cookie', control_message.cookie_path)
@@ -128,7 +129,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
Checks a response with multiple authentication methods.
"""
- control_message = mocking.get_message(MULTIPLE_AUTH)
+ control_message = test.util.get_message(MULTIPLE_AUTH)
stem.response.convert('PROTOCOLINFO', control_message)
self.assertEqual((AuthMethod.COOKIE, AuthMethod.PASSWORD), control_message.auth_methods)
self.assertEqual('/home/atagar/.tor/control_auth_cookie', control_message.cookie_path)
@@ -138,7 +139,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
Checks a response with an unrecognized authtentication method.
"""
- control_message = mocking.get_message(UNKNOWN_AUTH)
+ control_message = test.util.get_message(UNKNOWN_AUTH)
stem.response.convert('PROTOCOLINFO', control_message)
self.assertEqual((AuthMethod.UNKNOWN, AuthMethod.PASSWORD), control_message.auth_methods)
self.assertEqual(('MAGIC', 'PIXIE_DUST'), control_message.unknown_auth_methods)
@@ -149,7 +150,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
information to be a valid response.
"""
- control_message = mocking.get_message(MINIMUM_RESPONSE)
+ control_message = test.util.get_message(MINIMUM_RESPONSE)
stem.response.convert('PROTOCOLINFO', control_message)
self.assertEqual(5, control_message.protocol_version)
@@ -164,7 +165,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
Checks an authentication cookie with a unicode path.
"""
- control_message = mocking.get_message(UNICODE_COOKIE_PATH)
+ control_message = test.util.get_message(UNICODE_COOKIE_PATH)
stem.response.convert('PROTOCOLINFO', control_message)
self.assertEqual(EXPECTED_UNICODE_PATH, control_message.cookie_path)
@@ -190,7 +191,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
with patch('stem.util.system.call') as call_mock:
call_mock.side_effect = call_function
- control_message = mocking.get_message(RELATIVE_COOKIE_PATH)
+ control_message = test.util.get_message(RELATIVE_COOKIE_PATH)
stem.response.convert('PROTOCOLINFO', control_message)
stem.connection._expand_cookie_path(control_message, stem.util.system.pid_by_name, 'tor')
@@ -201,6 +202,6 @@ class TestProtocolInfoResponse(unittest.TestCase):
# leaving the path unexpanded)
with patch('stem.util.system.call', Mock(return_value = None)):
- control_message = mocking.get_message(RELATIVE_COOKIE_PATH)
+ control_message = test.util.get_message(RELATIVE_COOKIE_PATH)
stem.response.convert('PROTOCOLINFO', control_message)
self.assertEqual('./tor-browser_en-US/Data/control_auth_cookie', control_message.cookie_path)
diff --git a/test/unit/response/singleline.py b/test/unit/response/singleline.py
index 3567d59..1c05aa5 100644
--- a/test/unit/response/singleline.py
+++ b/test/unit/response/singleline.py
@@ -7,7 +7,7 @@ import unittest
import stem.response
import stem.socket
-from test import mocking
+import test.util
MULTILINE_RESPONSE = """250-MULTI
250 LINE"""
@@ -15,22 +15,22 @@ MULTILINE_RESPONSE = """250-MULTI
class TestSingleLineResponse(unittest.TestCase):
def test_single_line_response(self):
- message = mocking.get_message('552 NOTOK')
+ message = test.util.get_message('552 NOTOK')
stem.response.convert('SINGLELINE', message)
self.assertEqual(False, message.is_ok())
- message = mocking.get_message('250 KK')
+ message = test.util.get_message('250 KK')
stem.response.convert('SINGLELINE', message)
self.assertEqual(True, message.is_ok())
- message = mocking.get_message('250 OK')
+ message = test.util.get_message('250 OK')
stem.response.convert('SINGLELINE', message)
self.assertEqual(True, message.is_ok(True))
- message = mocking.get_message('250 HMM')
+ message = test.util.get_message('250 HMM')
stem.response.convert('SINGLELINE', message)
self.assertEqual(False, message.is_ok(True))
def test_multi_line_response(self):
- message = mocking.get_message(MULTILINE_RESPONSE)
+ message = test.util.get_message(MULTILINE_RESPONSE)
self.assertRaises(stem.ProtocolError, stem.response.convert, 'SINGLELINE', message)
diff --git a/test/unit/tutorial_examples.py b/test/unit/tutorial_examples.py
index c979f07..2075bdf 100644
--- a/test/unit/tutorial_examples.py
+++ b/test/unit/tutorial_examples.py
@@ -15,6 +15,8 @@ import stem.response
import stem.descriptor.remote
import stem.prereq
+import test.util
+
from stem.control import Controller
from stem.util import str_type
from stem.descriptor.networkstatus import NetworkStatusDocumentV3
@@ -22,7 +24,6 @@ from stem.descriptor.remote import DIRECTORY_AUTHORITIES
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.unit import exec_documentation_example
try:
@@ -99,7 +100,7 @@ A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB: caerSidi
def _get_event(content):
- controller_event = mocking.get_message(content)
+ controller_event = test.util.get_message(content)
stem.response.convert('EVENT', controller_event)
return controller_event
diff --git a/test/unit/util/proc.py b/test/unit/util/proc.py
index 5f65212..00f2af0 100644
--- a/test/unit/util/proc.py
+++ b/test/unit/util/proc.py
@@ -6,9 +6,10 @@ import io
import re
import unittest
+import test.util
+
from stem.util import proc
from stem.util.connection import Connection
-from test import mocking
try:
from unittest.mock import Mock, patch
@@ -105,7 +106,7 @@ class TestProc(unittest.TestCase):
"""
# list of all combinations of args with respective return values
- stat_combinations = mocking.get_all_combinations([
+ stat_combinations = test.util.get_all_combinations([
('command', 'test_program'),
('utime', '0.13'),
('stime', '0.14'),
diff --git a/test/util.py b/test/util.py
index 864baab..7b6f312 100644
--- a/test/util.py
+++ b/test/util.py
@@ -18,6 +18,21 @@ Tasks are...
::
+ Initialization
+ |- check_stem_version - checks our version of stem
+ |- check_tor_version - checks our version of tor
+ |- check_python_version - checks our version of python
+ |- check_cryptography_version - checks our version of cryptography
+ |- check_pynacl_version - checks our version of pynacl
+ |- check_pyflakes_version - checks our version of pyflakes
+ |- check_pycodestyle_version - checks our version of pycodestyle
+ |- clean_orphaned_pyc - removes any *.pyc without a corresponding *.py
+ +- check_for_unused_tests - checks to see if any tests are missing from our settings
+
+Lastly, this module provides generally useful test helpers...
+
+::
+
Test Requirements
|- only_run_once - skip test if it has been ran before
|- require - skips the test unless a requirement is met
@@ -32,20 +47,15 @@ Tasks are...
|- require_ptrace - requires 'DisableDebuggerAttachment' to be set
+- require_online - skips unless targets allow for online tests
- Initialization
- |- check_stem_version - checks our version of stem
- |- check_tor_version - checks our version of tor
- |- check_python_version - checks our version of python
- |- check_cryptography_version - checks our version of cryptography
- |- check_pynacl_version - checks our version of pynacl
- |- check_pyflakes_version - checks our version of pyflakes
- |- check_pycodestyle_version - checks our version of pycodestyle
- |- clean_orphaned_pyc - removes any *.pyc without a corresponding *.py
- +- check_for_unused_tests - checks to see if any tests are missing from our settings
-
+ get_message - provides a ControlMessage instance
+ get_protocolinfo_response - provides a ProtocolInfoResponse instance
+ get_all_combinations - provides all combinations of attributes
+ random_fingerprint - provides a random relay fingerprint
tor_version - provides the version of tor we're testing against
"""
+import hashlib
+import itertools
import re
import os
import sys
@@ -464,6 +474,94 @@ def run_tasks(category, *tasks):
println()
+def get_all_combinations(attr, include_empty = False):
+ """
+ Provides an iterator for all combinations of a set of attributes. For
+ instance...
+
+ ::
+
+ >>> list(test.mocking.get_all_combinations(['a', 'b', 'c']))
+ [('a',), ('b',), ('c',), ('a', 'b'), ('a', 'c'), ('b', 'c'), ('a', 'b', 'c')]
+
+ :param list attr: attributes to provide combinations for
+ :param bool include_empty: includes an entry with zero items if True
+ :returns: iterator for all combinations
+ """
+
+ # Makes an itertools.product() call for 'i' copies of attr...
+ #
+ # * itertools.product(attr) => all one-element combinations
+ # * itertools.product(attr, attr) => all two-element combinations
+ # * ... etc
+
+ if include_empty:
+ yield ()
+
+ seen = set()
+ for index in range(1, len(attr) + 1):
+ product_arg = [attr for _ in range(index)]
+
+ for item in itertools.product(*product_arg):
+ # deduplicate, sort, and only provide if we haven't seen it yet
+ item = tuple(sorted(set(item)))
+
+ if item not in seen:
+ seen.add(item)
+ yield item
+
+
+def random_fingerprint():
+ """
+ Provides a random relay fingerprint.
+ """
+
+ return hashlib.sha1(os.urandom(20)).hexdigest().upper()
+
+
+def get_message(content, reformat = True):
+ """
+ Provides a ControlMessage with content modified to be parsable. This makes
+ the following changes unless 'reformat' is false...
+
+ * ensures the content ends with a newline
+ * newlines are replaced with a carriage return and newline pair
+
+ :param str content: base content for the controller message
+ :param str reformat: modifies content to be more accommodating to being parsed
+
+ :returns: stem.response.ControlMessage instance
+ """
+
+ if reformat:
+ if not content.endswith('\n'):
+ content += '\n'
+
+ content = re.sub('([\r]?)\n', '\r\n', content)
+
+ return stem.response.ControlMessage.from_str(content)
+
+
+def get_protocolinfo_response(**attributes):
+ """
+ Provides a ProtocolInfoResponse, customized with the given attributes. The
+ base instance is minimal, with its version set to one and everything else
+ left with the default.
+
+ :param dict attributes: attributes to customize the response with
+
+ :returns: stem.response.protocolinfo.ProtocolInfoResponse instance
+ """
+
+ protocolinfo_response = get_message('250-PROTOCOLINFO 1\n250 OK')
+ stem.response.convert('PROTOCOLINFO', protocolinfo_response)
+
+ for attr in attributes:
+ setattr(protocolinfo_response, attr, attributes[attr])
+
+ return protocolinfo_response
+
+
def tor_version():
"""
Provides the version of tor we're testing against.
1
0
commit 72679be355dfcde7513040b336d6c1824ea3d853
Author: Damian Johnson <atagar(a)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])
1
0
commit cf869a877683254ca7b262a989551bc9c6b76204
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon May 1 12:56:36 2017 -0700
HidenServiceDescriptor creation
---
stem/descriptor/hidden_service_descriptor.py | 31 ++++++++++-
test/mocking.py | 51 ------------------
test/unit/descriptor/hidden_service_descriptor.py | 65 ++++++++---------------
3 files changed, 52 insertions(+), 95 deletions(-)
diff --git a/stem/descriptor/hidden_service_descriptor.py b/stem/descriptor/hidden_service_descriptor.py
index 129bc83..a3b12b5 100644
--- a/stem/descriptor/hidden_service_descriptor.py
+++ b/stem/descriptor/hidden_service_descriptor.py
@@ -31,8 +31,10 @@ import stem.util.connection
import stem.util.str_tools
from stem.descriptor import (
+ CRYPTO_BLOB,
PGP_BLOCK_END,
Descriptor,
+ _descriptor_content,
_descriptor_components,
_read_until_keywords,
_bytes_for_block,
@@ -80,6 +82,20 @@ SINGLE_INTRODUCTION_POINT_FIELDS = [
BASIC_AUTH = 1
STEALTH_AUTH = 2
+HIDDEN_SERVICE_HEADER = (
+ ('rendezvous-service-descriptor', 'y3olqqblqw2gbh6phimfuiroechjjafa'),
+ ('version', '2'),
+ ('permanent-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
+ ('secret-id-part', 'e24kgecavwsznj7gpbktqsiwgvngsf4e'),
+ ('publication-time', '2015-02-23 20:00:00'),
+ ('protocol-versions', '2,3'),
+ ('introduction-points', '\n-----BEGIN MESSAGE-----\n-----END MESSAGE-----'),
+)
+
+HIDDEN_SERVICE_FOOTER = (
+ ('signature', '\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB),
+)
+
class IntroductionPoints(collections.namedtuple('IntroductionPoints', INTRODUCTION_POINTS_ATTR.keys())):
"""
@@ -204,6 +220,9 @@ class HiddenServiceDescriptor(Descriptor):
Moved from the deprecated `pycrypto
<https://www.dlitz.net/software/pycrypto/>`_ module to `cryptography
<https://pypi.python.org/pypi/cryptography>`_ for validating signatures.
+
+ .. versionchanged:: 1.6.0
+ Added the **skip_crypto_validation** constructor argument.
"""
ATTRIBUTES = {
@@ -230,7 +249,15 @@ class HiddenServiceDescriptor(Descriptor):
'signature': _parse_signature_line,
}
- def __init__(self, raw_contents, validate = False):
+ @classmethod
+ def content(cls, attr = None, exclude = ()):
+ return _descriptor_content(attr, exclude, HIDDEN_SERVICE_HEADER, HIDDEN_SERVICE_FOOTER)
+
+ @classmethod
+ def create(cls, attr = None, exclude = (), validate = True):
+ return cls(cls.content(attr, exclude), validate = validate, skip_crypto_validation = True)
+
+ def __init__(self, raw_contents, validate = False, skip_crypto_validation = False):
super(HiddenServiceDescriptor, self).__init__(raw_contents, lazy_load = not validate)
entries = _descriptor_components(raw_contents, validate, non_ascii_fields = ('introduction-points'))
@@ -248,7 +275,7 @@ class HiddenServiceDescriptor(Descriptor):
self._parse(entries, validate)
- if stem.prereq.is_crypto_available():
+ if not skip_crypto_validation and stem.prereq.is_crypto_available():
signed_digest = self._digest_for_signature(self.permanent_key, self.signature)
content_digest = self._digest_for_content(b'rendezvous-service-descriptor ', b'\nsignature\n')
diff --git a/test/mocking.py b/test/mocking.py
index f1d859e..09d3631 100644
--- a/test/mocking.py
+++ b/test/mocking.py
@@ -23,17 +23,12 @@ Helper functions for creating mock objects.
get_router_status_entry_v2 - RouterStatusEntryV2
get_router_status_entry_v3 - RouterStatusEntryV3
get_router_status_entry_micro_v3 - RouterStatusEntryMicroV3
-
- stem.descriptor.hidden-service_descriptor
- get_hidden_service_descriptor - HiddenServiceDescriptor
"""
-import base64
import hashlib
import itertools
import os
import re
-import textwrap
import stem.descriptor.extrainfo_descriptor
import stem.descriptor.hidden_service_descriptor
@@ -46,12 +41,6 @@ import stem.response
import stem.util.str_tools
try:
- # added in python 3.3
- from unittest.mock import Mock, patch
-except ImportError:
- from mock import Mock, patch
-
-try:
# added in python 2.7
from collections import OrderedDict
except ImportError:
@@ -138,20 +127,6 @@ NETWORK_STATUS_DOCUMENT_FOOTER = (
('directory-signature', '%s %s\n%s' % (DOC_SIG.identity, DOC_SIG.key_digest, DOC_SIG.signature)),
)
-HIDDEN_SERVICE_HEADER = (
- ('rendezvous-service-descriptor', 'y3olqqblqw2gbh6phimfuiroechjjafa'),
- ('version', '2'),
- ('permanent-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
- ('secret-id-part', 'e24kgecavwsznj7gpbktqsiwgvngsf4e'),
- ('publication-time', '2015-02-23 20:00:00'),
- ('protocol-versions', '2,3'),
- ('introduction-points', '\n-----BEGIN MESSAGE-----\n-----END MESSAGE-----'),
-)
-
-HIDDEN_SERVICE_FOOTER = (
- ('signature', '\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB),
-)
-
def get_all_combinations(attr, include_empty = False):
"""
@@ -372,32 +347,6 @@ def get_router_status_entry_micro_v3(attr = None, exclude = (), content = False)
return stem.descriptor.router_status_entry.RouterStatusEntryMicroV3(desc_content, validate = True)
-def get_hidden_service_descriptor(attr = None, exclude = (), content = False, introduction_points_lines = None):
- """
- Provides the descriptor content for...
- stem.descriptor.hidden_service_descriptor.HidenServiceDescriptor
-
- :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
- :param list introduction_points_lines: lines to be included in the introduction-points field
-
- :returns: HidenServiceDescriptor for the requested descriptor content
- """
-
- if (not attr or 'introduction-points' not in attr) and introduction_points_lines is not None:
- encoded = base64.b64encode(introduction_points_lines('\n'))
- attr['introduction-points'] = '\n-----BEGIN MESSAGE-----\n%s\n-----END MESSAGE-----' % '\n'.join(textwrap.wrap(encoded, 64))
-
- desc_content = _get_descriptor_content(attr, exclude, HIDDEN_SERVICE_HEADER, HIDDEN_SERVICE_FOOTER)
-
- if content:
- return desc_content
- else:
- with patch('stem.prereq.is_crypto_available', Mock(return_value = False)):
- return stem.descriptor.hidden_service_descriptor.HiddenServiceDescriptor(desc_content, validate = True)
-
-
def get_directory_authority(attr = None, exclude = (), is_vote = False, content = False):
"""
Provides the descriptor content for...
diff --git a/test/unit/descriptor/hidden_service_descriptor.py b/test/unit/descriptor/hidden_service_descriptor.py
index 6f9fb26..48f1d29 100644
--- a/test/unit/descriptor/hidden_service_descriptor.py
+++ b/test/unit/descriptor/hidden_service_descriptor.py
@@ -3,13 +3,12 @@ Unit tests for stem.descriptor.hidden_service_descriptor.
"""
import datetime
+import functools
import unittest
import stem.descriptor
import stem.prereq
-from test.mocking import CRYPTO_BLOB, get_hidden_service_descriptor
-from test.unit.descriptor import get_resource
from test.util import require_cryptography
from stem.descriptor.hidden_service_descriptor import (
@@ -18,6 +17,12 @@ from stem.descriptor.hidden_service_descriptor import (
HiddenServiceDescriptor,
)
+from test.unit.descriptor import (
+ get_resource,
+ base_expect_invalid_attr,
+ base_expect_invalid_attr_for_text,
+)
+
MESSAGE_BLOCK = """
-----BEGIN MESSAGE-----
%s
@@ -232,6 +237,9 @@ lj/7xMZWDrfyw5H86L0QiaZnkmD+nig1+S+Rn39mmuEgl2iwZO/ihlncUJQTEULb
-----END MESSAGE-----\
"""
+expect_invalid_attr = functools.partial(base_expect_invalid_attr, HiddenServiceDescriptor, 'descriptor_id', 'y3olqqblqw2gbh6phimfuiroechjjafa')
+expect_invalid_attr_for_text = functools.partial(base_expect_invalid_attr_for_text, HiddenServiceDescriptor, 'descriptor_id', 'y3olqqblqw2gbh6phimfuiroechjjafa')
+
class TestHiddenServiceDescriptor(unittest.TestCase):
def test_for_duckduckgo_with_validation(self):
@@ -403,18 +411,18 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
Basic sanity check that we can parse a hidden service descriptor with minimal attributes.
"""
- desc = get_hidden_service_descriptor()
+ desc = HiddenServiceDescriptor.create()
self.assertEqual('y3olqqblqw2gbh6phimfuiroechjjafa', desc.descriptor_id)
self.assertEqual(2, desc.version)
- self.assertTrue(CRYPTO_BLOB in desc.permanent_key)
+ self.assertTrue(stem.descriptor.CRYPTO_BLOB in desc.permanent_key)
self.assertEqual('e24kgecavwsznj7gpbktqsiwgvngsf4e', desc.secret_id_part)
self.assertEqual(datetime.datetime(2015, 2, 23, 20, 0, 0), desc.published)
self.assertEqual([2, 3], desc.protocol_versions)
self.assertEqual('-----BEGIN MESSAGE-----\n-----END MESSAGE-----', desc.introduction_points_encoded)
self.assertEqual([], desc.introduction_points_auth)
self.assertEqual(b'', desc.introduction_points_content)
- self.assertTrue(CRYPTO_BLOB in desc.signature)
+ self.assertTrue(stem.descriptor.CRYPTO_BLOB in desc.signature)
self.assertEqual([], desc.introduction_points())
def test_unrecognized_line(self):
@@ -422,7 +430,7 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
Includes unrecognized content in the descriptor.
"""
- desc = get_hidden_service_descriptor({'pepperjack': 'is oh so tasty!'})
+ desc = HiddenServiceDescriptor.create({'pepperjack': 'is oh so tasty!'})
self.assertEqual(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
def test_proceeding_line(self):
@@ -430,16 +438,14 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
Includes a line prior to the 'rendezvous-service-descriptor' entry.
"""
- desc_text = b'hibernate 1\n' + get_hidden_service_descriptor(content = True)
- self._expect_invalid_attr(desc_text)
+ expect_invalid_attr_for_text(self, b'hibernate 1\n' + HiddenServiceDescriptor.content())
def test_trailing_line(self):
"""
Includes a line after the 'router-signature' entry.
"""
- desc_text = get_hidden_service_descriptor(content = True) + b'\nhibernate 1'
- self._expect_invalid_attr(desc_text)
+ expect_invalid_attr_for_text(self, HiddenServiceDescriptor.content() + b'\nhibernate 1')
def test_required_fields(self):
"""
@@ -458,10 +464,10 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
}
for line in REQUIRED_FIELDS:
- desc_text = get_hidden_service_descriptor(content = True, exclude = (line,))
+ desc_text = HiddenServiceDescriptor.content(exclude = (line,))
expected = [] if line == 'protocol-versions' else None
- self._expect_invalid_attr(desc_text, line_to_attr[line], expected)
+ expect_invalid_attr_for_text(self, desc_text, line_to_attr[line], expected)
def test_invalid_version(self):
"""
@@ -475,8 +481,7 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
)
for test_value in test_values:
- desc_text = get_hidden_service_descriptor({'version': test_value}, content = True)
- self._expect_invalid_attr(desc_text, 'version')
+ expect_invalid_attr(self, {'version': test_value}, 'version')
def test_invalid_protocol_versions(self):
"""
@@ -495,8 +500,7 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
)
for test_value in test_values:
- desc_text = get_hidden_service_descriptor({'protocol-versions': test_value}, content = True)
- self._expect_invalid_attr(desc_text, 'protocol_versions', [])
+ expect_invalid_attr(self, {'protocol-versions': test_value}, 'protocol_versions', [])
def test_introduction_points_when_empty(self):
"""
@@ -505,14 +509,14 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
are valid according to the spec.
"""
- missing_field_desc = get_hidden_service_descriptor(exclude = ('introduction-points',))
+ missing_field_desc = HiddenServiceDescriptor.create(exclude = ('introduction-points',))
self.assertEqual(None, missing_field_desc.introduction_points_encoded)
self.assertEqual([], missing_field_desc.introduction_points_auth)
self.assertEqual(None, missing_field_desc.introduction_points_content)
self.assertEqual([], missing_field_desc.introduction_points())
- empty_field_desc = get_hidden_service_descriptor({'introduction-points': MESSAGE_BLOCK % ''})
+ empty_field_desc = HiddenServiceDescriptor.create({'introduction-points': MESSAGE_BLOCK % ''})
self.assertEqual((MESSAGE_BLOCK % '').strip(), empty_field_desc.introduction_points_encoded)
self.assertEqual([], empty_field_desc.introduction_points_auth)
@@ -530,30 +534,7 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
)
for test_value in test_values:
- desc_text = get_hidden_service_descriptor({'introduction-points': test_value}, content = True)
-
- desc = self._expect_invalid_attr(desc_text, 'introduction_points_encoded', test_value.strip())
+ desc = expect_invalid_attr(self, {'introduction-points': test_value}, 'introduction_points_encoded', test_value.strip())
self.assertEqual([], desc.introduction_points_auth)
self.assertEqual(None, desc.introduction_points_content)
self.assertEqual([], desc.introduction_points())
-
- def _expect_invalid_attr(self, desc_text, attr = None, expected_value = None):
- """
- Asserts that construction will fail due to desc_text having a malformed
- attribute. If an attr is provided then we check that it matches an expected
- value when we're constructed without validation.
- """
-
- self.assertRaises(ValueError, HiddenServiceDescriptor, desc_text, True)
- desc = HiddenServiceDescriptor(desc_text, validate = False)
-
- if attr:
- # check that the invalid attribute matches the expected value when
- # constructed without validation
-
- self.assertEqual(expected_value, getattr(desc, attr))
- else:
- # check a default attribute
- self.assertEqual('y3olqqblqw2gbh6phimfuiroechjjafa', desc.descriptor_id)
-
- return desc
1
0