[tor-commits] [bridgedb/develop] Add mock descriptors and several Bridge class unittests.

isis at torproject.org isis at torproject.org
Thu Feb 19 02:21:14 UTC 2015


commit 7150bd19e86125bcaf48f8f48a960244047ae8b2
Author: Isis Lovecruft <isis at torproject.org>
Date:   Sun Dec 28 12:39:04 2014 +0000

    Add mock descriptors and several Bridge class unittests.
---
 lib/bridgedb/test/test_bridges.py |  406 +++++++++++++++++++++++++++++++++++++
 1 file changed, 406 insertions(+)

diff --git a/lib/bridgedb/test/test_bridges.py b/lib/bridgedb/test/test_bridges.py
index cca920d..8328e16 100644
--- a/lib/bridgedb/test/test_bridges.py
+++ b/lib/bridgedb/test/test_bridges.py
@@ -15,6 +15,7 @@ from binascii import a2b_hex
 import ipaddr
 import io
 import hashlib
+import os
 import warnings
 
 from twisted.trial import unittest
@@ -22,13 +23,90 @@ from twisted.trial import unittest
 from bridgedb import bridges
 from bridgedb.Bridges import FilteredBridgeSplitter
 from bridgedb.bridgerequest import BridgeRequestBase
+from bridgedb.parse import descriptors
 from bridgedb.parse.addr import PortList
+from bridgedb.parse.nickname import InvalidRouterNickname
+
 
 # Don't print "WARNING:root: Couldn't parse K=V from PT arg: ''" a bunch of
 # times while running the tests.
 warnings.filterwarnings("ignore", ".*Couldn't parse K=V from PT arg.*", Warning)
 
 
+BRIDGE_NETWORKSTATUS = '''\
+r FourfoldQuirked LDIlxIBTMQJeIR9Lblv0XDM/3Sw c4EVu2rO/iD/DJYBX/Ll38DGQWI 2014-12-22 21:51:27 179.178.155.140 36489 0
+a [6bf3:806b:78cd:d4b4:f6a7:4ced:cfad:dad4]:36488
+s Fast Guard Running Stable Valid
+w Bandwidth=1585163
+p reject 1-65535
+'''
+
+BRIDGE_SERVER_DESCRIPTOR = '''\
+ at purpose bridge
+router FourfoldQuirked 179.178.155.140 36489 0 0
+or-address [6bf3:806b:78cd:d4b4:f6a7:4ced:cfad:dad4]:36488
+platform Tor 0.2.3.24-rc on Linux
+opt protocols Link 1 2 Circuit 1
+published 2014-12-22 21:51:27
+opt fingerprint 2C32 25C4 8053 3102 5E21 1F4B 6E5B F45C 333F DD2C
+uptime 33200687
+bandwidth 1866688205 2110169275 1623207134
+opt extra-info-digest 4497E81715D958105C6A39D348163AD8F3080FB2
+onion-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBANKicvIGWp9WGKOJV8Fs3YKdTDrgxlggzyKgkW+MZWEPQ9lLcrmXqBdW
+nVK5EABByHnnxJfk+sm+6yDYxY/lFVL1SEP84pAK1Z21f4+grNlwox1DLyntXDdz
+BCZuRszuBYK3ncsk+ePQeUzRKQ/GZt9s/oy0IjtNbAoAoq7DKUVzAgMBAAE=
+-----END RSA PUBLIC KEY-----
+signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBALnJK7A9aZIp2ry9ruVYzm4VfaXzNHdTcvkXTrETu/jLXsosEwj9viSe
+Ry3W/uctbjzdwlIY0ZBUuV20q9bh+/c7Q0T8LOHBZouOy+nhFOUX+Q5YCG9cRnY0
+hBebYTzyplh0tT8xyYwcS8y6esL+gjVDLo6Og3QPhWRFQ4CyCic9AgMBAAE=
+-----END RSA PUBLIC KEY-----
+contact Somebody <somebody at example.com>
+ntor-onion-key aVmfOm9C046wM8ktGnpfBHSNj1Jm30M/m2P7W3a7Xn8
+reject *:*
+router-signature
+-----BEGIN SIGNATURE-----
+nxml4rTyTrj8dHcsFt2B4ACz2AN5CuZ2t5UF1BtXUpuzHmqVlg7imy8Cp2xIwoDa
+4uv/tTG32macauVnMHt0hSbtBF5nHfxU9G1T/XzdtL+KD8REDGky4allXnmvF6In
+rFtSn2OeZewvi8oYPmVYKgzHL6tzZxs2Sn/bOTj5sRw=
+-----END SIGNATURE-----
+'''
+
+BRIDGE_EXTRAINFO = '''\
+extra-info FourfoldQuirked 2C3225C4805331025E211F4B6E5BF45C333FDD2C
+published 2014-12-22 21:51:27
+write-history 2014-12-22 21:51:27 (900 s) 3188736,2226176,2866176
+read-history 2014-12-22 21:51:27 (900 s) 3891200,2483200,2698240
+dirreq-write-history 2014-12-22 21:51:27 (900 s) 1024,0,2048
+dirreq-read-history 2014-12-22 21:51:27 (900 s) 0,0,0
+geoip-db-digest 51AE9611B53880B2BCF9C71E735D73F33FAD2DFE
+geoip6-db-digest 26B0D55B20BEB496A3ADE7C6FDD866F5A81027F7
+dirreq-stats-end 2014-12-22 21:51:27 (86400 s)
+dirreq-v3-ips
+dirreq-v3-reqs
+dirreq-v3-resp ok=16,not-enough-sigs=0,unavailable=0,not-found=0,not-modified=0,busy=0
+dirreq-v3-direct-dl complete=0,timeout=0,running=0
+dirreq-v3-tunneled-dl complete=12,timeout=0,running=0
+transport obfs3 179.178.155.140:36490
+transport obfs2 179.178.155.140:36491
+transport scramblesuit 179.178.155.140:36492 password=ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
+transport obfs4 179.178.155.140:36493 iat-mode=0,node-id=25293f2761d658cc70c19515861842d712751bdc,public-key=02d20bbd7e394ad5999a4cebabac9619732c343a4cac99470c03e23ba2bdc2bc
+bridge-stats-end 2014-12-22 21:51:27 (86400 s)
+bridge-ips ca=8
+bridge-ip-versions v4=8,v6=0
+bridge-ip-transports <OR>=8
+router-signature
+-----BEGIN SIGNATURE-----
+cn4+8pQwCMPnHcp1s8wm7ZYsnd9AXJH6ysNlvQ63jsPCG9JdE5E8BwCThEgUccJI
+XILT4o+SveEQUG72R4bENsKxqV4rRNh1g6CNAbYhAITqrU9B+jImDgrBBW+XWT5K
+78ECRPn6Y4KsxFb0TIn7ddv9QjApyBJNIDMihH80Yng=
+-----END SIGNATURE-----
+'''
+
+
 class BridgeIntegrationTests(unittest.TestCase):
     """Integration tests to ensure that the new :class:`bridgedb.bridges.Bridge`
     class has compatible behaviour with the expected behaviour of the old
@@ -488,3 +566,331 @@ class PluggableTransportTests(unittest.TestCase):
         self.assertTrue(
             ("password=unicorns sharedsecret=foobar" in bridgeLine) or
             ("sharedsecret=foobar password=unicorns" in bridgeLine))
+
+
+class BridgeBackwardsCompatibilityTests(unittest.TestCase):
+    """Tests for :class:`bridgedb.bridges.BridgeBackwardsCompatibility`."""
+
+    def setUp(self):
+        self.nickname = "RouterNickname"
+        self.address = "23.23.23.23"
+        self.orPort = 9001
+        self.fingerprint = "0123456789ABCDEF0123456789ABCDEF01234567"
+        self.orAddresses = {"2006:42::123F": PortList(443, 9001, 1337),
+                            "2006:42::123E": PortList(9001, 993)}
+
+    def test_BridgeBackwardsCompatibility_init_with_PortList(self):
+        """Test initialisation with the usual number of valid arguments and
+        PortLists for the orAddresses.
+        """
+        bridge = bridges.BridgeBackwardsCompatibility(
+            self.nickname,
+            self.address,
+            self.orPort,
+            self.fingerprint,
+            self.orAddresses)
+        self.assertIsInstance(bridge, bridges.BridgeBackwardsCompatibility)
+
+    def test_BridgeBackwardsCompatibility_init_without_PortList(self):
+        """Test initialisation with the usual number of valid arguments and
+        integers for the orAddresses' ports.
+        """
+        bridge = bridges.BridgeBackwardsCompatibility(
+            self.nickname,
+            self.address,
+            self.orPort,
+            self.fingerprint,
+            {"2006:42::123F": 443,
+             "2006:42::123E": 9001})
+        self.assertIsInstance(bridge, bridges.BridgeBackwardsCompatibility)
+
+    def test_BridgeBackwardsCompatibility_init_without_address(self):
+        """Test initialisation without an IP address."""
+        bridge = bridges.BridgeBackwardsCompatibility(
+            nickname=self.nickname,
+            orport=self.orPort,
+            fingerprint=self.fingerprint,
+            or_addresses={"2006:42::123F": 443, "2006:42::123E": 9001})
+        self.assertIsInstance(bridge, bridges.BridgeBackwardsCompatibility)
+
+    def test_BridgeBackwardsCompatibility_init_invalid_orAddresses_address(self):
+        """Test initialisation with an invalid ORAddress."""
+        bridge = bridges.BridgeBackwardsCompatibility(
+            nickname=self.nickname,
+            ip=self.address,
+            orport=self.orPort,
+            fingerprint=self.fingerprint,
+            or_addresses={"10.1.2.3": 443, "2006:42::123E": 9001})
+        self.assertIsInstance(bridge, bridges.BridgeBackwardsCompatibility)
+        self.assertEqual(len(bridge.orAddresses), 1)
+
+    def test_BridgeBackwardsCompatibility_init_invalid_orAddresses_port(self):
+        """Test initialisation with an invalid ORPort."""
+        bridge = bridges.BridgeBackwardsCompatibility(
+            nickname=self.nickname,
+            ip=self.address,
+            orport=self.orPort,
+            fingerprint=self.fingerprint,
+            or_addresses={"2006:42::123F": 443, "2006:42::123E": "anyport"})
+        self.assertIsInstance(bridge, bridges.BridgeBackwardsCompatibility)
+        self.assertEqual(len(bridge.orAddresses), 1)
+
+    def test_BridgeBackwardsCompatibility_setStatus_running(self):
+        """Using setStatus() to set the Running flag should set Bridge.running
+        and Bridge.flags.running to True.
+        """
+        bridge = bridges.BridgeBackwardsCompatibility(
+            nickname=self.nickname,
+            ip=self.address,
+            orport="anyport",
+            fingerprint=self.fingerprint,
+            or_addresses={"2006:42::123F": 443, "2006:42::123E": 9001})
+        self.assertIsInstance(bridge, bridges.BridgeBackwardsCompatibility)
+        self.assertFalse(bridge.running)
+        self.assertFalse(bridge.flags.running)
+
+        bridge.setStatus(running=True)
+        self.assertTrue(bridge.running)
+        self.assertTrue(bridge.flags.running)
+
+    def test_BridgeBackwardsCompatibility_setStatus_running(self):
+        """Using setStatus() to set the Running and Stable flags should set
+        Bridge.running, Bridge.flags.running, Bridge.stable, and
+        Bridge.flags.stable.
+        """
+        bridge = bridges.BridgeBackwardsCompatibility(
+            nickname=self.nickname,
+            ip=self.address,
+            orport="anyport",
+            fingerprint=self.fingerprint,
+            or_addresses={"2006:42::123F": 443, "2006:42::123E": 9001})
+        self.assertIsInstance(bridge, bridges.BridgeBackwardsCompatibility)
+        self.assertFalse(bridge.running)
+        self.assertFalse(bridge.flags.running)
+        self.assertFalse(bridge.stable)
+        self.assertFalse(bridge.flags.stable)
+
+        bridge.setStatus(running=True, stable=True)
+        self.assertTrue(bridge.running)
+        self.assertTrue(bridge.flags.running)
+        self.assertTrue(bridge.stable)
+        self.assertTrue(bridge.flags.stable)
+
+
+class BridgeTests(unittest.TestCase):
+    """Tests for :class:`bridgedb.bridges.Bridge`."""
+
+    def _writeDescriptorFiles(self):
+        with open(self._networkstatusFile, 'w') as fh:
+            fh.write(BRIDGE_NETWORKSTATUS)
+            fh.flush()
+
+        with open(self._serverDescriptorFile, 'w') as fh:
+            fh.write(BRIDGE_SERVER_DESCRIPTOR)
+            fh.flush()
+
+        with open(self._extrainfoFile, 'w') as fh:
+            fh.write(BRIDGE_EXTRAINFO)
+            fh.flush()
+
+    def setUp(self):
+        def _cwd(filename):
+            return os.path.sep.join([os.getcwd(), filename])
+
+        self._networkstatusFile = _cwd('BridgeTests-networkstatus-bridges')
+        self._serverDescriptorFile = _cwd('BridgeTests-bridge-descriptors')
+        self._extrainfoFile = _cwd('BridgeTests-cached-extrainfo')
+
+        self._writeDescriptorFiles()
+
+        self.networkstatus = descriptors.parseNetworkStatusFile(
+            self._networkstatusFile)[0]
+        self.serverdescriptor = descriptors.parseServerDescriptorsFile(
+            self._serverDescriptorFile)[0]
+        self.extrainfo = descriptors.parseExtraInfoFiles(
+            self._extrainfoFile).values()[0]
+
+        self.bridge = bridges.Bridge()
+
+    def tearDown(self):
+        """Reset safelogging to its default (disabled) state, due to
+        test_Bridge_str_with_safelogging changing it.
+        """
+        bridges.safelog.safe_logging = False
+
+    def test_Bridge_nickname_del(self):
+        """The del method for the nickname property should reset the nickname
+        to None.
+        """
+        self.bridge.updateFromNetworkStatus(self.networkstatus)
+        self.assertEqual(self.bridge.nickname, "FourfoldQuirked")
+
+        del(self.bridge.nickname)
+        self.assertIsNone(self.bridge.nickname)
+        self.assertIsNone(self.bridge._nickname)
+
+    def test_Bridge_nickname_invalid(self):
+        """The del method for the nickname property should reset the nickname
+        to None.
+        """
+        # Create a networkstatus descriptor with an invalid nickname:
+        filename = self._networkstatusFile + "-invalid"
+        fh = open(filename, 'w')
+        invalid = BRIDGE_NETWORKSTATUS.replace(
+            "FourfoldQuirked",
+            "ThisRouterNicknameContainsWayMoreThanNineteenBytes")
+        fh.seek(0)
+        fh.write(invalid)
+        fh.flush()
+        fh.close()
+
+        self.assertRaises(InvalidRouterNickname,
+                          descriptors.parseNetworkStatusFile,
+                          filename)
+
+    def test_Bridge_orport_del(self):
+        """The del method for the orPort property should reset the orPort
+        to None.
+        """
+        self.bridge.updateFromNetworkStatus(self.networkstatus)
+        self.assertEqual(self.bridge.orPort, 36489)
+
+        del(self.bridge.orPort)
+        self.assertIsNone(self.bridge.orPort)
+        self.assertIsNone(self.bridge._orPort)
+
+    def test_Bridge_str_without_safelogging(self):
+        """The str() method of a Bridge should return an identifier for the
+        Bridge, which should be different if safelogging is enabled.
+        """
+        bridges.safelog.safe_logging = False
+
+        bridge = bridges.Bridge()
+        bridge.updateFromNetworkStatus(self.networkstatus)
+
+        identifier = str(bridge)
+        self.assertEqual(identifier,
+                         ''.join(['$', bridge.fingerprint,
+                                  '~', bridge.nickname]))
+
+    def test_Bridge_str_with_safelogging(self):
+        """The str() method of a Bridge should return an identifier for the
+        Bridge, which should be different if safelogging is enabled.
+        """
+        bridges.safelog.safe_logging = True
+
+        bridge = bridges.Bridge()
+        bridge.updateFromNetworkStatus(self.networkstatus)
+
+        identifier = str(bridge)
+        self.assertEqual(
+            identifier,
+            ''.join(['$$',
+                     hashlib.sha1(bridge.fingerprint).hexdigest().upper(),
+                     '~', bridge.nickname]))
+
+    def test_Bridge_str_without_fingerprint(self):
+        """The str() method of a Bridge should return an identifier for the
+        Bridge, which should be different if the fingerprint is unknown.
+        """
+        bridge = bridges.Bridge()
+        bridge.updateFromNetworkStatus(self.networkstatus)
+        del(bridge.fingerprint)
+
+        identifier = str(bridge)
+        self.assertEqual(identifier,
+                         ''.join(['$', '0'*40,
+                                  '~', bridge.nickname]))
+
+    def test_Bridge_updateFromServerDescriptor(self):
+        """ """
+        self.bridge.updateFromNetworkStatus(self.networkstatus)
+        self.bridge.updateFromServerDescriptor(self.serverdescriptor)
+
+        self.assertEqual(self.bridge.fingerprint,
+                         '2C3225C4805331025E211F4B6E5BF45C333FDD2C')
+
+    def test_Bridge_updateFromServerDescriptor_no_networkstatus(self):
+        """Parsing a server descriptor for a bridge which wasn't included in
+        the networkstatus document from the BridgeAuthority should raise a
+        ServerDescriptorWithoutNetworkstatus exception.
+        """
+        self.assertRaises(bridges.ServerDescriptorWithoutNetworkstatus,
+                          self.bridge.updateFromServerDescriptor,
+                          self.serverdescriptor)
+
+    def test_Bridge_updateFromExtraInfoDescriptor(self):
+        """Bridge.updateFromExtraInfoDescriptor() should add the expected
+        number of pluggable transports.
+        """
+        self.bridge.updateFromNetworkStatus(self.networkstatus)
+        self.assertEqual(self.bridge.fingerprint,
+                         '2C3225C4805331025E211F4B6E5BF45C333FDD2C')
+        self.assertEqual(self.bridge.bandwidthObserved, None)
+        self.assertEqual(len(self.bridge.transports), 0)
+
+        self.bridge.updateFromServerDescriptor(self.serverdescriptor)
+        self.assertEqual(self.bridge.fingerprint,
+                         '2C3225C4805331025E211F4B6E5BF45C333FDD2C')
+        self.assertEqual(self.bridge.bandwidthObserved, 1623207134)
+        self.assertEqual(len(self.bridge.transports), 0)
+
+        self.bridge.updateFromExtraInfoDescriptor(self.extrainfo)
+        self.assertEqual(self.bridge.fingerprint,
+                         '2C3225C4805331025E211F4B6E5BF45C333FDD2C')
+        self.assertEqual(self.bridge.bandwidthObserved, 1623207134)
+        self.assertEqual(len(self.bridge.transports), 4)
+
+    def test_Bridge_descriptorDigest(self):
+        """Parsing a networkstatus descriptor should result in
+        Bridge.descriptorDigest being set.
+        """
+        realdigest = "738115BB6ACEFE20FF0C96015FF2E5DFC0C64162"
+        self.bridge.updateFromNetworkStatus(self.networkstatus)
+        self.assertEqual(self.bridge.descriptorDigest, realdigest)
+
+    def test_Bridge_checkServerDescriptor(self):
+        """Parsing a server descriptor when the bridge's networkstatus document
+        didn't have a digest of the server descriptor should raise a
+        MissingServerDescriptorDigest.
+        """
+        # Create a networkstatus descriptor without a server descriptor digest:
+        filename = self._networkstatusFile + "-missing-digest"
+        fh = open(filename, 'w')
+        invalid = BRIDGE_NETWORKSTATUS.replace("c4EVu2rO/iD/DJYBX/Ll38DGQWI", "foo")
+        fh.seek(0)
+        fh.write(invalid)
+        fh.flush()
+        fh.close()
+
+        realdigest = "738115BB6ACEFE20FF0C96015FF2E5DFC0C64162"
+
+        #networkstatus = descriptors.parseNetworkStatusFile(filename)
+        #self.bridge.updateFromNetworkStatus(networkstatus[0])
+        #self.assertRaises(bridges.MissingServerDescriptorDigest,
+        #                  self.bridge.updateFromNetworkStatus,
+        #                  networkstatus[0])
+
+    def test_Bridge_checkServerDescriptor_digest_mismatch(self):
+        """Parsing a server descriptor whose digest doesn't match the one given
+        in the bridge's networkstatus document should raise a
+        ServerDescriptorDigestMismatch.
+        """
+        # Create a networkstatus descriptor without a server descriptor digest:
+        filename = self._networkstatusFile + "-mismatched-digest"
+        fh = open(filename, 'w')
+        invalid = BRIDGE_NETWORKSTATUS.replace("c4EVu2rO/iD/DJYBX/Ll38DGQWI",
+                                               "c4EVu2r1/iD/DJYBX/Ll38DGQWI")
+        fh.seek(0)
+        fh.write(invalid)
+        fh.flush()
+        fh.close()
+
+        realdigest = "738115BB6ACEFE20FF0C96015FF2E5DFC0C64162"
+        networkstatus = descriptors.parseNetworkStatusFile(filename)
+        self.bridge.updateFromNetworkStatus(networkstatus[0])
+        #self.bridge.updateFromServerDescriptor(self.serverdescriptor)
+
+        self.assertRaises(bridges.ServerDescriptorDigestMismatch,
+                          self.bridge.updateFromServerDescriptor,
+                          self.serverdescriptor)





More information about the tor-commits mailing list