finish Boss tests
This commit is contained in:
parent
d8d305407b
commit
53a911cc80
|
@ -21,7 +21,7 @@ from ._code import Code
|
||||||
from ._terminator import Terminator
|
from ._terminator import Terminator
|
||||||
from ._wordlist import PGPWordList
|
from ._wordlist import PGPWordList
|
||||||
from .errors import (ServerError, LonelyError, WrongPasswordError,
|
from .errors import (ServerError, LonelyError, WrongPasswordError,
|
||||||
KeyFormatError, OnlyOneCodeError)
|
KeyFormatError, OnlyOneCodeError, _UnknownPhaseError)
|
||||||
from .util import bytes_to_dict
|
from .util import bytes_to_dict
|
||||||
|
|
||||||
@attrs
|
@attrs
|
||||||
|
@ -126,11 +126,11 @@ class Boss(object):
|
||||||
# would require the Wormhole to be aware of Code (whereas right now
|
# would require the Wormhole to be aware of Code (whereas right now
|
||||||
# Wormhole only knows about this Boss instance, and everything else is
|
# Wormhole only knows about this Boss instance, and everything else is
|
||||||
# hidden away).
|
# hidden away).
|
||||||
def input_code(self, helper):
|
def input_code(self):
|
||||||
if self._did_start_code:
|
if self._did_start_code:
|
||||||
raise OnlyOneCodeError()
|
raise OnlyOneCodeError()
|
||||||
self._did_start_code = True
|
self._did_start_code = True
|
||||||
self._C.input_code(helper)
|
return self._C.input_code()
|
||||||
def allocate_code(self, code_length):
|
def allocate_code(self, code_length):
|
||||||
if self._did_start_code:
|
if self._did_start_code:
|
||||||
raise OnlyOneCodeError()
|
raise OnlyOneCodeError()
|
||||||
|
@ -175,17 +175,17 @@ class Boss(object):
|
||||||
assert isinstance(phase, type("")), type(phase)
|
assert isinstance(phase, type("")), type(phase)
|
||||||
assert isinstance(plaintext, type(b"")), type(plaintext)
|
assert isinstance(plaintext, type(b"")), type(plaintext)
|
||||||
if phase == "version":
|
if phase == "version":
|
||||||
self.got_version(plaintext)
|
self._got_version(plaintext)
|
||||||
elif re.search(r'^\d+$', phase):
|
elif re.search(r'^\d+$', phase):
|
||||||
self.got_phase(int(phase), plaintext)
|
self._got_phase(int(phase), plaintext)
|
||||||
else:
|
else:
|
||||||
# Ignore unrecognized phases, for forwards-compatibility. Use
|
# Ignore unrecognized phases, for forwards-compatibility. Use
|
||||||
# log.err so tests will catch surprises.
|
# log.err so tests will catch surprises.
|
||||||
log.err("received unknown phase '%s'" % phase)
|
log.err(_UnknownPhaseError("received unknown phase '%s'" % phase))
|
||||||
@m.input()
|
@m.input()
|
||||||
def got_version(self, plaintext): pass
|
def _got_version(self, plaintext): pass
|
||||||
@m.input()
|
@m.input()
|
||||||
def got_phase(self, phase, plaintext): pass
|
def _got_phase(self, phase, plaintext): pass
|
||||||
@m.input()
|
@m.input()
|
||||||
def got_key(self, key): pass
|
def got_key(self, key): pass
|
||||||
@m.input()
|
@m.input()
|
||||||
|
@ -210,7 +210,7 @@ class Boss(object):
|
||||||
self._their_versions = bytes_to_dict(plaintext)
|
self._their_versions = bytes_to_dict(plaintext)
|
||||||
# but this part is app-to-app
|
# but this part is app-to-app
|
||||||
app_versions = self._their_versions.get("app_versions", {})
|
app_versions = self._their_versions.get("app_versions", {})
|
||||||
self._W.got_versions(app_versions)
|
self._W.got_version(app_versions)
|
||||||
|
|
||||||
@m.output()
|
@m.output()
|
||||||
def S_send(self, plaintext):
|
def S_send(self, plaintext):
|
||||||
|
@ -279,8 +279,8 @@ class Boss(object):
|
||||||
S1_lonely.upon(error, enter=S4_closed, outputs=[W_close_with_error])
|
S1_lonely.upon(error, enter=S4_closed, outputs=[W_close_with_error])
|
||||||
|
|
||||||
S2_happy.upon(rx_welcome, enter=S2_happy, outputs=[process_welcome])
|
S2_happy.upon(rx_welcome, enter=S2_happy, outputs=[process_welcome])
|
||||||
S2_happy.upon(got_phase, enter=S2_happy, outputs=[W_received])
|
S2_happy.upon(_got_phase, enter=S2_happy, outputs=[W_received])
|
||||||
S2_happy.upon(got_version, enter=S2_happy, outputs=[process_version])
|
S2_happy.upon(_got_version, enter=S2_happy, outputs=[process_version])
|
||||||
S2_happy.upon(scared, enter=S3_closing, outputs=[close_scared])
|
S2_happy.upon(scared, enter=S3_closing, outputs=[close_scared])
|
||||||
S2_happy.upon(close, enter=S3_closing, outputs=[close_happy])
|
S2_happy.upon(close, enter=S3_closing, outputs=[close_happy])
|
||||||
S2_happy.upon(send, enter=S2_happy, outputs=[S_send])
|
S2_happy.upon(send, enter=S2_happy, outputs=[S_send])
|
||||||
|
@ -289,8 +289,8 @@ class Boss(object):
|
||||||
|
|
||||||
S3_closing.upon(rx_welcome, enter=S3_closing, outputs=[])
|
S3_closing.upon(rx_welcome, enter=S3_closing, outputs=[])
|
||||||
S3_closing.upon(rx_error, enter=S3_closing, outputs=[])
|
S3_closing.upon(rx_error, enter=S3_closing, outputs=[])
|
||||||
S3_closing.upon(got_phase, enter=S3_closing, outputs=[])
|
S3_closing.upon(_got_phase, enter=S3_closing, outputs=[])
|
||||||
S3_closing.upon(got_version, enter=S3_closing, outputs=[])
|
S3_closing.upon(_got_version, enter=S3_closing, outputs=[])
|
||||||
S3_closing.upon(happy, enter=S3_closing, outputs=[])
|
S3_closing.upon(happy, enter=S3_closing, outputs=[])
|
||||||
S3_closing.upon(scared, enter=S3_closing, outputs=[])
|
S3_closing.upon(scared, enter=S3_closing, outputs=[])
|
||||||
S3_closing.upon(close, enter=S3_closing, outputs=[])
|
S3_closing.upon(close, enter=S3_closing, outputs=[])
|
||||||
|
@ -299,8 +299,8 @@ class Boss(object):
|
||||||
S3_closing.upon(error, enter=S4_closed, outputs=[W_close_with_error])
|
S3_closing.upon(error, enter=S4_closed, outputs=[W_close_with_error])
|
||||||
|
|
||||||
S4_closed.upon(rx_welcome, enter=S4_closed, outputs=[])
|
S4_closed.upon(rx_welcome, enter=S4_closed, outputs=[])
|
||||||
S4_closed.upon(got_phase, enter=S4_closed, outputs=[])
|
S4_closed.upon(_got_phase, enter=S4_closed, outputs=[])
|
||||||
S4_closed.upon(got_version, enter=S4_closed, outputs=[])
|
S4_closed.upon(_got_version, enter=S4_closed, outputs=[])
|
||||||
S4_closed.upon(happy, enter=S4_closed, outputs=[])
|
S4_closed.upon(happy, enter=S4_closed, outputs=[])
|
||||||
S4_closed.upon(scared, enter=S4_closed, outputs=[])
|
S4_closed.upon(scared, enter=S4_closed, outputs=[])
|
||||||
S4_closed.upon(close, enter=S4_closed, outputs=[])
|
S4_closed.upon(close, enter=S4_closed, outputs=[])
|
||||||
|
|
|
@ -8,7 +8,7 @@ from twisted.python import log
|
||||||
from twisted.internet import defer, endpoints
|
from twisted.internet import defer, endpoints
|
||||||
from twisted.application import internet
|
from twisted.application import internet
|
||||||
from autobahn.twisted import websocket
|
from autobahn.twisted import websocket
|
||||||
from . import _interfaces
|
from . import _interfaces, errors
|
||||||
from .util import (bytes_to_hexstr, hexstr_to_bytes,
|
from .util import (bytes_to_hexstr, hexstr_to_bytes,
|
||||||
bytes_to_dict, dict_to_bytes)
|
bytes_to_dict, dict_to_bytes)
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ class RendezvousConnector(object):
|
||||||
meth = getattr(self, "_response_handle_"+mtype, None)
|
meth = getattr(self, "_response_handle_"+mtype, None)
|
||||||
if not meth:
|
if not meth:
|
||||||
# make tests fail, but real application will ignore it
|
# make tests fail, but real application will ignore it
|
||||||
log.err(ValueError("Unknown inbound message type %r" % (msg,)))
|
log.err(errors._UnknownMessageTypeError("Unknown inbound message type %r" % (msg,)))
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
return meth(msg)
|
return meth(msg)
|
||||||
|
|
|
@ -72,3 +72,7 @@ class WormholeClosed(Exception):
|
||||||
wormhole was already closed, or if it closes before a real result can be
|
wormhole was already closed, or if it closes before a real result can be
|
||||||
obtained."""
|
obtained."""
|
||||||
|
|
||||||
|
class _UnknownPhaseError(Exception):
|
||||||
|
"""internal exception type, for tests."""
|
||||||
|
class _UnknownMessageTypeError(Exception):
|
||||||
|
"""internal exception type, for tests."""
|
||||||
|
|
|
@ -30,9 +30,11 @@ class Dummy:
|
||||||
directlyProvides(self, iface)
|
directlyProvides(self, iface)
|
||||||
for meth in meths:
|
for meth in meths:
|
||||||
self.mock(meth)
|
self.mock(meth)
|
||||||
|
self.retval = None
|
||||||
def mock(self, meth):
|
def mock(self, meth):
|
||||||
def log(*args):
|
def log(*args):
|
||||||
self.events.append(("%s.%s" % (self.name, meth),) + args)
|
self.events.append(("%s.%s" % (self.name, meth),) + args)
|
||||||
|
return self.retval
|
||||||
setattr(self, meth, log)
|
setattr(self, meth, log)
|
||||||
|
|
||||||
class Send(unittest.TestCase):
|
class Send(unittest.TestCase):
|
||||||
|
@ -1109,13 +1111,13 @@ class Boss(unittest.TestCase):
|
||||||
wormhole = Dummy("w", events, None,
|
wormhole = Dummy("w", events, None,
|
||||||
"got_code", "got_key", "got_verifier", "got_version",
|
"got_code", "got_key", "got_verifier", "got_version",
|
||||||
"received", "closed")
|
"received", "closed")
|
||||||
welcome_handler = mock.Mock()
|
self._welcome_handler = mock.Mock()
|
||||||
versions = {"app": "version1"}
|
versions = {"app": "version1"}
|
||||||
reactor = None
|
reactor = None
|
||||||
journal = ImmediateJournal()
|
journal = ImmediateJournal()
|
||||||
tor_manager = None
|
tor_manager = None
|
||||||
b = MockBoss(wormhole, "side", "url", "appid", versions,
|
b = MockBoss(wormhole, "side", "url", "appid", versions,
|
||||||
welcome_handler, reactor, journal, tor_manager,
|
self._welcome_handler, reactor, journal, tor_manager,
|
||||||
timing.DebugTiming())
|
timing.DebugTiming())
|
||||||
t = b._T = Dummy("t", events, ITerminator, "close")
|
t = b._T = Dummy("t", events, ITerminator, "close")
|
||||||
s = b._S = Dummy("s", events, ISend, "send")
|
s = b._S = Dummy("s", events, ISend, "send")
|
||||||
|
@ -1134,22 +1136,199 @@ class Boss(unittest.TestCase):
|
||||||
self.assertEqual(events, [("w.got_code", "1-code")])
|
self.assertEqual(events, [("w.got_code", "1-code")])
|
||||||
events[:] = []
|
events[:] = []
|
||||||
|
|
||||||
|
b.rx_welcome("welcome")
|
||||||
|
self.assertEqual(self._welcome_handler.mock_calls, [mock.call("welcome")])
|
||||||
|
|
||||||
# pretend a peer message was correctly decrypted
|
# pretend a peer message was correctly decrypted
|
||||||
|
b.got_key(b"key")
|
||||||
|
b.got_verifier(b"verifier")
|
||||||
b.happy()
|
b.happy()
|
||||||
b.got_version({})
|
b.got_message("version", b"{}")
|
||||||
b.got_phase("phase1", b"msg1")
|
b.got_message("0", b"msg1")
|
||||||
self.assertEqual(events, [("w.got_version", {}),
|
self.assertEqual(events, [("w.got_key", b"key"),
|
||||||
|
("w.got_verifier", b"verifier"),
|
||||||
|
("w.got_version", {}),
|
||||||
("w.received", b"msg1"),
|
("w.received", b"msg1"),
|
||||||
])
|
])
|
||||||
events[:] = []
|
events[:] = []
|
||||||
|
|
||||||
|
b.send(b"msg2")
|
||||||
|
self.assertEqual(events, [("s.send", "0", b"msg2")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
b.close()
|
b.close()
|
||||||
self.assertEqual(events, [("t.close", "happy")])
|
self.assertEqual(events, [("t.close", "happy")])
|
||||||
events[:] = []
|
events[:] = []
|
||||||
|
|
||||||
b.closed()
|
b.closed()
|
||||||
self.assertEqual(events, [("w.closed", "reasons")])
|
self.assertEqual(events, [("w.closed", "happy")])
|
||||||
|
|
||||||
|
def test_lonely(self):
|
||||||
|
b, events = self.build()
|
||||||
|
b.set_code("1-code")
|
||||||
|
self.assertEqual(events, [("c.set_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.got_code("1-code")
|
||||||
|
self.assertEqual(events, [("w.got_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.close()
|
||||||
|
self.assertEqual(events, [("t.close", "lonely")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.closed()
|
||||||
|
self.assertEqual(len(events), 1, events)
|
||||||
|
self.assertEqual(events[0][0], "w.closed")
|
||||||
|
self.assertIsInstance(events[0][1], errors.LonelyError)
|
||||||
|
|
||||||
|
def test_server_error(self):
|
||||||
|
b, events = self.build()
|
||||||
|
b.set_code("1-code")
|
||||||
|
self.assertEqual(events, [("c.set_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
orig = {}
|
||||||
|
b.rx_error("server-error-msg", orig)
|
||||||
|
self.assertEqual(events, [("t.close", "errory")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.closed()
|
||||||
|
self.assertEqual(len(events), 1, events)
|
||||||
|
self.assertEqual(events[0][0], "w.closed")
|
||||||
|
self.assertIsInstance(events[0][1], errors.ServerError)
|
||||||
|
self.assertEqual(events[0][1].args[0], "server-error-msg")
|
||||||
|
|
||||||
|
def test_internal_error(self):
|
||||||
|
b, events = self.build()
|
||||||
|
b.set_code("1-code")
|
||||||
|
self.assertEqual(events, [("c.set_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.error(ValueError("catch me"))
|
||||||
|
self.assertEqual(len(events), 1, events)
|
||||||
|
self.assertEqual(events[0][0], "w.closed")
|
||||||
|
self.assertIsInstance(events[0][1], ValueError)
|
||||||
|
self.assertEqual(events[0][1].args[0], "catch me")
|
||||||
|
|
||||||
|
def test_close_early(self):
|
||||||
|
b, events = self.build()
|
||||||
|
b.set_code("1-code")
|
||||||
|
self.assertEqual(events, [("c.set_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.close() # before even w.got_code
|
||||||
|
self.assertEqual(events, [("t.close", "lonely")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.closed()
|
||||||
|
self.assertEqual(len(events), 1, events)
|
||||||
|
self.assertEqual(events[0][0], "w.closed")
|
||||||
|
self.assertIsInstance(events[0][1], errors.LonelyError)
|
||||||
|
|
||||||
|
def test_error_while_closing(self):
|
||||||
|
b, events = self.build()
|
||||||
|
b.set_code("1-code")
|
||||||
|
self.assertEqual(events, [("c.set_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.close()
|
||||||
|
self.assertEqual(events, [("t.close", "lonely")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.error(ValueError("oops"))
|
||||||
|
self.assertEqual(len(events), 1, events)
|
||||||
|
self.assertEqual(events[0][0], "w.closed")
|
||||||
|
self.assertIsInstance(events[0][1], ValueError)
|
||||||
|
|
||||||
|
def test_scary_version(self):
|
||||||
|
b, events = self.build()
|
||||||
|
b.set_code("1-code")
|
||||||
|
self.assertEqual(events, [("c.set_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.got_code("1-code")
|
||||||
|
self.assertEqual(events, [("w.got_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.scared()
|
||||||
|
self.assertEqual(events, [("t.close", "scary")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.closed()
|
||||||
|
self.assertEqual(len(events), 1, events)
|
||||||
|
self.assertEqual(events[0][0], "w.closed")
|
||||||
|
self.assertIsInstance(events[0][1], errors.WrongPasswordError)
|
||||||
|
|
||||||
|
def test_scary_phase(self):
|
||||||
|
b, events = self.build()
|
||||||
|
b.set_code("1-code")
|
||||||
|
self.assertEqual(events, [("c.set_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.got_code("1-code")
|
||||||
|
self.assertEqual(events, [("w.got_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.happy() # phase=version
|
||||||
|
|
||||||
|
b.scared() # phase=0
|
||||||
|
self.assertEqual(events, [("t.close", "scary")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.closed()
|
||||||
|
self.assertEqual(len(events), 1, events)
|
||||||
|
self.assertEqual(events[0][0], "w.closed")
|
||||||
|
self.assertIsInstance(events[0][1], errors.WrongPasswordError)
|
||||||
|
|
||||||
|
def test_unknown_phase(self):
|
||||||
|
b, events = self.build()
|
||||||
|
b.set_code("1-code")
|
||||||
|
self.assertEqual(events, [("c.set_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.got_code("1-code")
|
||||||
|
self.assertEqual(events, [("w.got_code", "1-code")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
b.happy() # phase=version
|
||||||
|
|
||||||
|
b.got_message("unknown-phase", b"spooky")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
self.flushLoggedErrors(errors._UnknownPhaseError)
|
||||||
|
|
||||||
|
def test_set_code_bad_format(self):
|
||||||
|
b, events = self.build()
|
||||||
|
with self.assertRaises(errors.KeyFormatError):
|
||||||
|
b.set_code("1 code")
|
||||||
|
|
||||||
|
def test_set_code_bad_twice(self):
|
||||||
|
b, events = self.build()
|
||||||
|
b.set_code("1-code")
|
||||||
|
with self.assertRaises(errors.OnlyOneCodeError):
|
||||||
|
b.set_code("1-code")
|
||||||
|
|
||||||
|
def test_input_code(self):
|
||||||
|
b, events = self.build()
|
||||||
|
b._C.retval = "helper"
|
||||||
|
helper = b.input_code()
|
||||||
|
self.assertEqual(events, [("c.input_code",)])
|
||||||
|
self.assertEqual(helper, "helper")
|
||||||
|
with self.assertRaises(errors.OnlyOneCodeError):
|
||||||
|
b.input_code()
|
||||||
|
|
||||||
|
def test_allocate_code(self):
|
||||||
|
b, events = self.build()
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._boss.PGPWordList", return_value=wl):
|
||||||
|
b.allocate_code(3)
|
||||||
|
self.assertEqual(events, [("c.allocate_code", 3, wl)])
|
||||||
|
with self.assertRaises(errors.OnlyOneCodeError):
|
||||||
|
b.allocate_code(3)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# #Send
|
# #Send
|
||||||
|
|
|
@ -124,7 +124,7 @@ class _DelegatedWormhole(object):
|
||||||
self._key = key # for derive_key()
|
self._key = key # for derive_key()
|
||||||
def got_verifier(self, verifier):
|
def got_verifier(self, verifier):
|
||||||
self._delegate.wormhole_verified(verifier)
|
self._delegate.wormhole_verified(verifier)
|
||||||
def got_versions(self, versions):
|
def got_version(self, versions):
|
||||||
self._delegate.wormhole_version(versions)
|
self._delegate.wormhole_version(versions)
|
||||||
def received(self, plaintext):
|
def received(self, plaintext):
|
||||||
self._delegate.wormhole_received(plaintext)
|
self._delegate.wormhole_received(plaintext)
|
||||||
|
@ -191,8 +191,8 @@ class _DeferredWormhole(object):
|
||||||
|
|
||||||
def allocate_code(self, code_length=2):
|
def allocate_code(self, code_length=2):
|
||||||
self._boss.allocate_code(code_length)
|
self._boss.allocate_code(code_length)
|
||||||
def input_code(self, stdio): # TODO
|
def input_code(self):
|
||||||
self._boss.input_code(stdio)
|
return self._boss.input_code()
|
||||||
def set_code(self, code):
|
def set_code(self, code):
|
||||||
self._boss.set_code(code)
|
self._boss.set_code(code)
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ class _DeferredWormhole(object):
|
||||||
for d in self._verifier_observers:
|
for d in self._verifier_observers:
|
||||||
d.callback(verifier)
|
d.callback(verifier)
|
||||||
self._verifier_observers[:] = []
|
self._verifier_observers[:] = []
|
||||||
def got_versions(self, versions):
|
def got_version(self, versions):
|
||||||
self._versions = versions
|
self._versions = versions
|
||||||
for d in self._version_observers:
|
for d in self._version_observers:
|
||||||
d.callback(versions)
|
d.callback(versions)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user