tahoe-lafs/src/allmydata/test/test_i2p_provider.py

395 lines
15 KiB
Python
Raw Normal View History

import os
from twisted.trial import unittest
from twisted.internet import defer, error
from twisted.python.usage import UsageError
from six.moves import StringIO
import mock
from ..util import i2p_provider
from ..scripts import create_node, runner
def mock_txi2p(txi2p):
return mock.patch("allmydata.util.i2p_provider._import_txi2p",
return_value=txi2p)
def mock_i2p(i2p):
return mock.patch("allmydata.util.i2p_provider._import_i2p",
return_value=i2p)
def make_cli_config(basedir, *argv):
parent = runner.Options()
cli_config = create_node.CreateNodeOptions()
cli_config.parent = parent
cli_config.parseOptions(argv)
cli_config["basedir"] = basedir
cli_config.stdout = StringIO()
return cli_config
class TryToConnect(unittest.TestCase):
def test_try(self):
reactor = object()
txi2p = mock.Mock()
d = defer.succeed(True)
txi2p.testAPI = mock.Mock(return_value=d)
ep = object()
stdout = StringIO()
with mock.patch("allmydata.util.i2p_provider.clientFromString",
return_value=ep) as cfs:
d = i2p_provider._try_to_connect(reactor, "desc", stdout, txi2p)
r = self.successResultOf(d)
self.assertTrue(r)
cfs.assert_called_with(reactor, "desc")
txi2p.testAPI.assert_called_with(reactor, 'SAM', ep)
def test_try_handled_error(self):
reactor = object()
txi2p = mock.Mock()
d = defer.fail(error.ConnectError("oops"))
txi2p.testAPI = mock.Mock(return_value=d)
ep = object()
stdout = StringIO()
with mock.patch("allmydata.util.i2p_provider.clientFromString",
return_value=ep) as cfs:
d = i2p_provider._try_to_connect(reactor, "desc", stdout, txi2p)
r = self.successResultOf(d)
self.assertIs(r, None)
cfs.assert_called_with(reactor, "desc")
txi2p.testAPI.assert_called_with(reactor, 'SAM', ep)
self.assertEqual(stdout.getvalue(),
"Unable to reach I2P SAM API at 'desc': "
"An error occurred while connecting: oops.\n")
def test_try_unhandled_error(self):
reactor = object()
txi2p = mock.Mock()
d = defer.fail(ValueError("oops"))
txi2p.testAPI = mock.Mock(return_value=d)
ep = object()
stdout = StringIO()
with mock.patch("allmydata.util.i2p_provider.clientFromString",
return_value=ep) as cfs:
d = i2p_provider._try_to_connect(reactor, "desc", stdout, txi2p)
f = self.failureResultOf(d)
self.assertIsInstance(f.value, ValueError)
self.assertEqual(str(f.value), "oops")
cfs.assert_called_with(reactor, "desc")
txi2p.testAPI.assert_called_with(reactor, 'SAM', ep)
self.assertEqual(stdout.getvalue(), "")
class ConnectToI2P(unittest.TestCase):
def _do_test_connect(self, endpoint, reachable):
reactor = object()
txi2p = object()
args = []
if endpoint:
args = ["--i2p-sam-port=%s" % endpoint]
cli_config = make_cli_config("basedir", "--listen=i2p", *args)
stdout = cli_config.stdout
expected_port = "tcp:127.0.0.1:7656"
if endpoint:
expected_port = endpoint
tried = []
def _try_to_connect(reactor, port, stdout, txi2p):
tried.append( (reactor, port, stdout, txi2p) )
if not reachable:
return defer.succeed(None)
if port == expected_port:
return defer.succeed(True)
return defer.succeed(None)
with mock.patch("allmydata.util.i2p_provider._try_to_connect",
_try_to_connect):
d = i2p_provider._connect_to_i2p(reactor, cli_config, txi2p)
if not reachable:
f = self.failureResultOf(d)
self.assertIsInstance(f.value, ValueError)
self.assertEqual(str(f.value),
"unable to reach any default I2P SAM port")
return
successful_port = self.successResultOf(d)
self.assertEqual(successful_port, expected_port)
expected = [(reactor, "tcp:127.0.0.1:7656", stdout, txi2p)]
if endpoint:
expected = [(reactor, endpoint, stdout, txi2p)]
self.assertEqual(tried, expected)
def test_connect(self):
return self._do_test_connect(None, True)
def test_connect_endpoint(self):
return self._do_test_connect("tcp:other:port", True)
def test_connect_unreachable(self):
return self._do_test_connect(None, False)
class CreateDest(unittest.TestCase):
def test_no_txi2p(self):
with mock.patch("allmydata.util.i2p_provider._import_txi2p",
return_value=None):
d = i2p_provider.create_config("reactor", "cli_config")
f = self.failureResultOf(d)
self.assertIsInstance(f.value, ValueError)
self.assertEqual(str(f.value),
"Cannot create I2P Destination without txi2p. "
"Please 'pip install tahoe-lafs[i2p]' to fix this.")
def _do_test_launch(self, executable):
basedir = self.mktemp()
os.mkdir(basedir)
args = ["--listen=i2p", "--i2p-launch"]
if executable:
args.append("--i2p-executable=%s" % executable)
self.assertRaises(UsageError, make_cli_config, basedir, *args)
def test_launch(self):
return self._do_test_launch(None)
def test_launch_executable(self):
return self._do_test_launch("myi2p")
def test_sam_endpoint(self):
basedir = self.mktemp()
os.mkdir(basedir)
private_dir = os.path.join(basedir, "private")
os.mkdir(private_dir)
privkeyfile = os.path.abspath(os.path.join(private_dir, "i2p_dest.privkey"))
reactor = object()
cli_config = make_cli_config(basedir, "--listen=i2p")
connect_to_i2p = mock.Mock(return_value=defer.succeed("goodport"))
txi2p = mock.Mock()
ep = object()
dest = mock.Mock()
dest.host = "FOOBAR.b32.i2p"
txi2p.generateDestination = mock.Mock(return_value=defer.succeed(dest))
with mock_txi2p(txi2p):
with mock.patch("allmydata.util.i2p_provider._connect_to_i2p",
connect_to_i2p):
with mock.patch("allmydata.util.i2p_provider.clientFromString",
return_value=ep) as cfs:
d = i2p_provider.create_config(reactor, cli_config)
tahoe_config_i2p, i2p_port, i2p_location = self.successResultOf(d)
connect_to_i2p.assert_called_with(reactor, cli_config, txi2p)
cfs.assert_called_with(reactor, "goodport")
txi2p.generateDestination.assert_called_with(reactor, privkeyfile, 'SAM', ep)
expected = {"sam.port": "goodport",
"dest": "true",
"dest.port": "3457",
"dest.private_key_file": os.path.join("private",
"i2p_dest.privkey"),
}
self.assertEqual(tahoe_config_i2p, expected)
self.assertEqual(i2p_port, "listen:i2p")
self.assertEqual(i2p_location, "i2p:FOOBAR.b32.i2p:3457")
_None = object()
class FakeConfig(dict):
def get_config(self, section, option, default=_None, boolean=False):
if section != "i2p":
raise ValueError(section)
value = self.get(option, default)
if value is _None:
raise KeyError
return value
class Provider(unittest.TestCase):
def test_build(self):
i2p_provider.create("reactor", FakeConfig())
def test_handler_disabled(self):
p = i2p_provider.create("reactor", FakeConfig(enabled=False))
self.assertEqual(p.get_i2p_handler(), None)
def test_handler_no_i2p(self):
with mock_i2p(None):
p = i2p_provider.create("reactor", FakeConfig())
self.assertEqual(p.get_i2p_handler(), None)
def test_handler_sam_endpoint(self):
i2p = mock.Mock()
handler = object()
i2p.sam_endpoint = mock.Mock(return_value=handler)
ep = object()
reactor = object()
with mock_i2p(i2p):
p = i2p_provider.create(reactor,
FakeConfig(**{"sam.port": "ep_desc"}))
with mock.patch("allmydata.util.i2p_provider.clientFromString",
return_value=ep) as cfs:
h = p.get_i2p_handler()
cfs.assert_called_with(reactor, "ep_desc")
self.assertIs(h, handler)
2017-01-17 16:47:47 +00:00
i2p.sam_endpoint.assert_called_with(ep, keyfile=None)
def test_handler_launch(self):
i2p = mock.Mock()
handler = object()
i2p.launch = mock.Mock(return_value=handler)
reactor = object()
with mock_i2p(i2p):
p = i2p_provider.create(reactor,
FakeConfig(launch=True))
h = p.get_i2p_handler()
self.assertIs(h, handler)
i2p.launch.assert_called_with(i2p_configdir=None, i2p_binary=None)
def test_handler_launch_configdir(self):
i2p = mock.Mock()
handler = object()
i2p.launch = mock.Mock(return_value=handler)
reactor = object()
with mock_i2p(i2p):
p = i2p_provider.create(reactor,
FakeConfig(launch=True,
**{"i2p.configdir": "configdir"}))
h = p.get_i2p_handler()
self.assertIs(h, handler)
i2p.launch.assert_called_with(i2p_configdir="configdir", i2p_binary=None)
def test_handler_launch_configdir_executable(self):
i2p = mock.Mock()
handler = object()
i2p.launch = mock.Mock(return_value=handler)
reactor = object()
with mock_i2p(i2p):
p = i2p_provider.create(reactor,
FakeConfig(launch=True,
**{"i2p.configdir": "configdir",
"i2p.executable": "myi2p",
}))
h = p.get_i2p_handler()
self.assertIs(h, handler)
i2p.launch.assert_called_with(i2p_configdir="configdir", i2p_binary="myi2p")
def test_handler_configdir(self):
i2p = mock.Mock()
handler = object()
i2p.local_i2p = mock.Mock(return_value=handler)
reactor = object()
with mock_i2p(i2p):
p = i2p_provider.create(reactor,
FakeConfig(**{"i2p.configdir": "configdir"}))
h = p.get_i2p_handler()
i2p.local_i2p.assert_called_with("configdir")
self.assertIs(h, handler)
def test_handler_default(self):
i2p = mock.Mock()
handler = object()
i2p.default = mock.Mock(return_value=handler)
reactor = object()
with mock_i2p(i2p):
p = i2p_provider.create(reactor, FakeConfig())
h = p.get_i2p_handler()
self.assertIs(h, handler)
2017-01-17 16:47:47 +00:00
i2p.default.assert_called_with(reactor, keyfile=None)
class ProviderListener(unittest.TestCase):
def test_listener(self):
"""Does the I2P Provider object's get_listener() method correctly
convert the [i2p] section of tahoe.cfg into an
endpoint/descriptor?
"""
i2p = mock.Mock()
handler = object()
i2p.local_i2p = mock.Mock(return_value=handler)
reactor = object()
privkeyfile = os.path.join("private", "i2p_dest.privkey")
with mock_i2p(i2p):
p = i2p_provider.create(reactor,
FakeConfig(**{
"i2p.configdir": "configdir",
"sam.port": "good:port",
"dest": "true",
"dest.port": "3457",
"dest.private_key_file": privkeyfile,
}))
endpoint_or_description = p.get_listener()
self.assertEqual(endpoint_or_description,
"i2p:%s:3457:api=SAM:apiEndpoint=good\\:port" % privkeyfile)
class Provider_CheckI2PConfig(unittest.TestCase):
def test_default(self):
# default config doesn't start an I2P service, so it should be
# happy both with and without txi2p
p = i2p_provider.create("reactor", FakeConfig())
p.check_dest_config()
with mock_txi2p(None):
p = i2p_provider.create("reactor", FakeConfig())
p.check_dest_config()
def test_no_txi2p(self):
with mock_txi2p(None):
with self.assertRaises(ValueError) as ctx:
i2p_provider.create("reactor", FakeConfig(dest=True))
self.assertEqual(
str(ctx.exception),
"Cannot create I2P Destination without txi2p. "
"Please 'pip install tahoe-lafs[i2p]' to fix."
)
def test_no_launch_no_control(self):
with self.assertRaises(ValueError) as ctx:
i2p_provider.create("reactor", FakeConfig(dest=True))
self.assertEqual(
str(ctx.exception),
"[i2p] dest = true, but we have neither "
"sam.port= nor launch=true nor configdir="
)
def test_missing_keys(self):
with self.assertRaises(ValueError) as ctx:
i2p_provider.create("reactor",
FakeConfig(
dest=True,
**{"sam.port": "x",
}
))
self.assertEqual(str(ctx.exception), "[i2p] dest = true, "
"but dest.port= is missing")
with self.assertRaises(ValueError) as ctx:
i2p_provider.create("reactor",
FakeConfig(dest=True,
**{"sam.port": "x",
"dest.port": "y",
}))
self.assertEqual(
str(ctx.exception),
"[i2p] dest = true, "
"but dest.private_key_file= is missing"
)
def test_launch_not_implemented(self):
with self.assertRaises(NotImplementedError) as ctx:
i2p_provider.create("reactor",
FakeConfig(dest=True, launch=True,
**{"dest.port": "x",
"dest.private_key_file": "y",
}))
self.assertEqual(
str(ctx.exception),
"[i2p] launch is under development."
)
def test_ok(self):
i2p_provider.create(
"reactor",
FakeConfig(
dest=True, **{
"sam.port": "x",
"dest.port": "y",
"dest.private_key_file": "z",
}
)
)