merge test_wormhole_new into test_wormhole

This commit is contained in:
Brian Warner 2017-04-04 00:05:28 -07:00
parent 8d47194612
commit 8882e6f64e
2 changed files with 85 additions and 700 deletions

View File

@ -1,20 +1,14 @@
from __future__ import print_function, unicode_literals
import os, json, re, gc, io
from binascii import hexlify, unhexlify
import json, io, re
import mock
from twisted.trial import unittest
from twisted.internet import reactor
from twisted.internet.defer import Deferred, gatherResults, inlineCallbacks
from twisted.internet.defer import gatherResults, inlineCallbacks
from .common import ServerBase
from .. import wormhole, _rendezvous
from ..errors import (WrongPasswordError, WelcomeError, InternalError,
from ..errors import (WrongPasswordError,
KeyFormatError, WormholeClosed, LonelyError,
NoKeyError, OnlyOneCodeError)
from spake2 import SPAKE2_Symmetric
from ..timing import DebugTiming
from ..util import (bytes_to_dict, dict_to_bytes,
hexstr_to_bytes, bytes_to_hexstr)
from nacl.secret import SecretBox
APPID = "appid"
@ -53,587 +47,6 @@ class Welcome(unittest.TestCase):
("Server (at relay_url) says:\n message of\n the day\n"
"Server (at relay_url) says:\n second message\n"))
class Basic(unittest.TestCase):
def tearDown(self):
# flush out any errorful Deferreds left dangling in cycles
gc.collect()
def check_out(self, out, **kwargs):
# Assert that each kwarg is present in the 'out' dict. Ignore other
# keys ('msgid' in particular)
for key, value in kwargs.items():
self.assertIn(key, out)
self.assertEqual(out[key], value, (out, key, value))
def check_outbound(self, ws, types):
out = ws.outbound()
self.assertEqual(len(out), len(types), (out, types))
for i,t in enumerate(types):
self.assertEqual(out[i]["type"], t, (i,t,out))
return out
def make_pake(self, code, side, msg1):
sp2 = SPAKE2_Symmetric(wormhole.to_bytes(code),
idSymmetric=wormhole.to_bytes(APPID))
msg2 = sp2.start()
key = sp2.finish(msg1)
return key, msg2
def test_create(self):
wormhole._Wormhole(APPID, "relay_url", reactor, None, None, None)
def test_basic(self):
# We don't call w._start(), so this doesn't create a WebSocket
# connection. We provide a mock connection instead. If we wanted to
# exercise _connect, we'd mock out WSFactory.
# w._connect = lambda self: None
# w._event_connected(mock_ws)
# w._event_ws_opened()
# w._ws_dispatch_response(payload)
timing = DebugTiming()
with mock.patch("wormhole.wormhole._WelcomeHandler") as wh_c:
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing,
None)
wh = wh_c.return_value
self.assertEqual(w._ws_url, "relay_url")
self.assertTrue(w._flag_need_nameplate)
self.assertTrue(w._flag_need_to_build_msg1)
self.assertTrue(w._flag_need_to_send_PAKE)
v = w.verify()
w._drop_connection = mock.Mock()
ws = MockWebSocket()
w._event_connected(ws)
out = ws.outbound()
self.assertEqual(len(out), 0)
w._event_ws_opened(None)
out = ws.outbound()
self.assertEqual(len(out), 1)
self.check_out(out[0], type="bind", appid=APPID, side=w._side)
self.assertIn("id", out[0])
# WelcomeHandler should get called upon 'welcome' response. Its full
# behavior is exercised in 'Welcome' above.
WELCOME = {"foo": "bar"}
response(w, type="welcome", welcome=WELCOME)
self.assertEqual(wh.mock_calls, [mock.call.handle_welcome(WELCOME)])
# because we're connected, setting the code also claims the mailbox
CODE = "123-foo-bar"
w.set_code(CODE)
self.assertFalse(w._flag_need_to_build_msg1)
out = ws.outbound()
self.assertEqual(len(out), 1)
self.check_out(out[0], type="claim", nameplate="123")
# the server reveals the linked mailbox
response(w, type="claimed", mailbox="mb456")
# that triggers event_learned_mailbox, which should send open() and
# PAKE
self.assertEqual(w._mailbox_state, wormhole.OPEN)
out = ws.outbound()
self.assertEqual(len(out), 2)
self.check_out(out[0], type="open", mailbox="mb456")
self.check_out(out[1], type="add", phase="pake")
self.assertNoResult(v)
# server echoes back all "add" messages
response(w, type="message", phase="pake", body=out[1]["body"],
side=w._side)
self.assertNoResult(v)
# extract our outbound PAKE message
body = bytes_to_dict(hexstr_to_bytes(out[1]["body"]))
msg1 = hexstr_to_bytes(body["pake_v1"])
# next we build the simulated peer's PAKE operation
side2 = w._side + "other"
key, msg2 = self.make_pake(CODE, side2, msg1)
payload = {"pake_v1": bytes_to_hexstr(msg2)}
body_hex = bytes_to_hexstr(dict_to_bytes(payload))
response(w, type="message", phase="pake", body=body_hex, side=side2)
# hearing the peer's PAKE (msg2) makes us release the nameplate, send
# the confirmation message, and sends any queued phase messages. It
# doesn't deliver the verifier because we're still waiting on the
# confirmation message.
self.assertFalse(w._flag_need_to_see_mailbox_used)
self.assertEqual(w._key, key)
out = ws.outbound()
self.assertEqual(len(out), 2, out)
self.check_out(out[0], type="release")
self.check_out(out[1], type="add", phase="version")
self.assertNoResult(v)
# hearing a valid confirmation message doesn't throw an error
plaintext = json.dumps({}).encode("utf-8")
data_key = w._derive_phase_key(side2, "version")
confmsg = w._encrypt_data(data_key, plaintext)
version2_hex = hexlify(confmsg).decode("ascii")
response(w, type="message", phase="version", body=version2_hex,
side=side2)
# and it releases the verifier
verifier = self.successResultOf(v)
self.assertEqual(verifier,
w.derive_key("wormhole:verifier", SecretBox.KEY_SIZE))
# an outbound message can now be sent immediately
w.send(b"phase0-outbound")
out = ws.outbound()
self.assertEqual(len(out), 1)
self.check_out(out[0], type="add", phase="0")
# decrypt+check the outbound message
p0_outbound = unhexlify(out[0]["body"].encode("ascii"))
msgkey0 = w._derive_phase_key(w._side, "0")
p0_plaintext = w._decrypt_data(msgkey0, p0_outbound)
self.assertEqual(p0_plaintext, b"phase0-outbound")
# get() waits for the inbound message to arrive
md = w.get()
self.assertNoResult(md)
self.assertIn("0", w._receive_waiters)
self.assertNotIn("0", w._received_messages)
msgkey1 = w._derive_phase_key(side2, "0")
p0_inbound = w._encrypt_data(msgkey1, b"phase0-inbound")
p0_inbound_hex = hexlify(p0_inbound).decode("ascii")
response(w, type="message", phase="0", body=p0_inbound_hex,
side=side2)
p0_in = self.successResultOf(md)
self.assertEqual(p0_in, b"phase0-inbound")
self.assertNotIn("0", w._receive_waiters)
self.assertIn("0", w._received_messages)
# receiving an inbound message will queue it until get() is called
msgkey2 = w._derive_phase_key(side2, "1")
p1_inbound = w._encrypt_data(msgkey2, b"phase1-inbound")
p1_inbound_hex = hexlify(p1_inbound).decode("ascii")
response(w, type="message", phase="1", body=p1_inbound_hex,
side=side2)
self.assertIn("1", w._received_messages)
self.assertNotIn("1", w._receive_waiters)
p1_in = self.successResultOf(w.get())
self.assertEqual(p1_in, b"phase1-inbound")
self.assertIn("1", w._received_messages)
self.assertNotIn("1", w._receive_waiters)
d = w.close()
self.assertNoResult(d)
out = ws.outbound()
self.assertEqual(len(out), 1)
self.check_out(out[0], type="close", mood="happy")
self.assertEqual(w._drop_connection.mock_calls, [])
response(w, type="released")
self.assertEqual(w._drop_connection.mock_calls, [])
response(w, type="closed")
self.assertEqual(w._drop_connection.mock_calls, [mock.call()])
w._ws_closed(True, None, None)
self.assertEqual(self.successResultOf(d), None)
def test_close_wait_0(self):
# Close before the connection is established. The connection still
# gets established, but it is then torn down before sending anything.
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
w._drop_connection = mock.Mock()
d = w.close()
self.assertNoResult(d)
ws = MockWebSocket()
w._event_connected(ws)
w._event_ws_opened(None)
self.assertEqual(w._drop_connection.mock_calls, [mock.call()])
self.assertNoResult(d)
w._ws_closed(True, None, None)
self.successResultOf(d)
def test_close_wait_1(self):
# close before even claiming the nameplate
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
w._drop_connection = mock.Mock()
ws = MockWebSocket()
w._event_connected(ws)
w._event_ws_opened(None)
d = w.close()
self.check_outbound(ws, ["bind"])
self.assertNoResult(d)
self.assertEqual(w._drop_connection.mock_calls, [mock.call()])
self.assertNoResult(d)
w._ws_closed(True, None, None)
self.successResultOf(d)
def test_close_wait_2(self):
# Close after claiming the nameplate, but before opening the mailbox.
# The 'claimed' response arrives before we close.
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
w._drop_connection = mock.Mock()
ws = MockWebSocket()
w._event_connected(ws)
w._event_ws_opened(None)
CODE = "123-foo-bar"
w.set_code(CODE)
self.check_outbound(ws, ["bind", "claim"])
response(w, type="claimed", mailbox="mb123")
d = w.close()
self.check_outbound(ws, ["open", "add", "release", "close"])
self.assertNoResult(d)
self.assertEqual(w._drop_connection.mock_calls, [])
response(w, type="released")
self.assertNoResult(d)
self.assertEqual(w._drop_connection.mock_calls, [])
response(w, type="closed")
self.assertEqual(w._drop_connection.mock_calls, [mock.call()])
self.assertNoResult(d)
w._ws_closed(True, None, None)
self.successResultOf(d)
def test_close_wait_3(self):
# close after claiming the nameplate, but before opening the mailbox
# The 'claimed' response arrives after we start to close.
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
w._drop_connection = mock.Mock()
ws = MockWebSocket()
w._event_connected(ws)
w._event_ws_opened(None)
CODE = "123-foo-bar"
w.set_code(CODE)
self.check_outbound(ws, ["bind", "claim"])
d = w.close()
response(w, type="claimed", mailbox="mb123")
self.check_outbound(ws, ["release"])
self.assertNoResult(d)
self.assertEqual(w._drop_connection.mock_calls, [])
response(w, type="released")
self.assertEqual(w._drop_connection.mock_calls, [mock.call()])
self.assertNoResult(d)
w._ws_closed(True, None, None)
self.successResultOf(d)
def test_close_wait_4(self):
# close after both claiming the nameplate and opening the mailbox
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
w._drop_connection = mock.Mock()
ws = MockWebSocket()
w._event_connected(ws)
w._event_ws_opened(None)
CODE = "123-foo-bar"
w.set_code(CODE)
response(w, type="claimed", mailbox="mb456")
self.check_outbound(ws, ["bind", "claim", "open", "add"])
d = w.close()
self.check_outbound(ws, ["release", "close"])
self.assertNoResult(d)
self.assertEqual(w._drop_connection.mock_calls, [])
response(w, type="released")
self.assertNoResult(d)
self.assertEqual(w._drop_connection.mock_calls, [])
response(w, type="closed")
self.assertNoResult(d)
self.assertEqual(w._drop_connection.mock_calls, [mock.call()])
w._ws_closed(True, None, None)
self.successResultOf(d)
def test_close_wait_5(self):
# close after claiming the nameplate, opening the mailbox, then
# releasing the nameplate
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
w._drop_connection = mock.Mock()
ws = MockWebSocket()
w._event_connected(ws)
w._event_ws_opened(None)
CODE = "123-foo-bar"
w.set_code(CODE)
response(w, type="claimed", mailbox="mb456")
w._key = b""
msgkey = w._derive_phase_key("side2", "misc")
p1_inbound = w._encrypt_data(msgkey, b"")
p1_inbound_hex = hexlify(p1_inbound).decode("ascii")
response(w, type="message", phase="misc", side="side2",
body=p1_inbound_hex)
self.check_outbound(ws, ["bind", "claim", "open", "add",
"release"])
d = w.close()
self.check_outbound(ws, ["close"])
self.assertNoResult(d)
self.assertEqual(w._drop_connection.mock_calls, [])
response(w, type="released")
self.assertNoResult(d)
self.assertEqual(w._drop_connection.mock_calls, [])
response(w, type="closed")
self.assertNoResult(d)
self.assertEqual(w._drop_connection.mock_calls, [mock.call()])
w._ws_closed(True, None, None)
self.successResultOf(d)
def test_close_errbacks(self):
# make sure the Deferreds returned by verify() and get() are properly
# errbacked upon close
pass
def test_get_code_mock(self):
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
ws = MockWebSocket() # TODO: mock w._ws_send_command instead
w._event_connected(ws)
w._event_ws_opened(None)
self.check_outbound(ws, ["bind"])
gc_c = mock.Mock()
gc = gc_c.return_value = mock.Mock()
gc_d = gc.go.return_value = Deferred()
with mock.patch("wormhole.wormhole._GetCode", gc_c):
d = w.get_code()
self.assertNoResult(d)
gc_d.callback("123-foo-bar")
code = self.successResultOf(d)
self.assertEqual(code, "123-foo-bar")
def test_get_code_real(self):
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
ws = MockWebSocket()
w._event_connected(ws)
w._event_ws_opened(None)
self.check_outbound(ws, ["bind"])
d = w.get_code()
out = ws.outbound()
self.assertEqual(len(out), 1)
self.check_out(out[0], type="allocate")
# TODO: nameplate attributes go here
self.assertNoResult(d)
response(w, type="allocated", nameplate="123")
code = self.successResultOf(d)
self.assertIsInstance(code, type(""))
self.assert_(code.startswith("123-"))
pieces = code.split("-")
self.assertEqual(len(pieces), 3) # nameplate plus two words
self.assert_(re.search(r'^\d+-\w+-\w+$', code), code)
def _test_establish_key_hook(self, established, before):
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
if before:
d = w.establish_key()
if established is True:
w._key = b"key"
elif established is False:
w._key = None
else:
w._key = b"key"
w._error = WelcomeError()
if not before:
d = w.establish_key()
else:
w._maybe_notify_key()
if w._key is not None and established is True:
self.successResultOf(d)
elif established is False:
self.assertNot(d.called)
else:
self.failureResultOf(d)
def test_establish_key_hook(self):
for established in (True, False, "error"):
for before in (True, False):
self._test_establish_key_hook(established, before)
def test_establish_key_twice(self):
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
d = w.establish_key()
self.assertRaises(InternalError, w.establish_key)
del d
# make sure verify() can be called both before and after the verifier is
# computed
def _test_verifier(self, when, order, success):
assert when in ("early", "middle", "late")
assert order in ("key-then-version", "version-then-key")
assert isinstance(success, bool)
#print(when, order, success)
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
w._drop_connection = mock.Mock()
w._ws_send_command = mock.Mock()
w._mailbox_state = wormhole.OPEN
side2 = "side2"
d = None
if success:
w._key = b"key"
else:
w._key = b"wrongkey"
plaintext = json.dumps({}).encode("utf-8")
data_key = w._derive_phase_key(side2, "version")
confmsg = w._encrypt_data(data_key, plaintext)
w._key = None
if when == "early":
d = w.verify()
self.assertNoResult(d)
if order == "key-then-version":
w._key = b"key"
w._event_established_key()
else:
w._event_received_version(side2, confmsg)
if when == "middle":
d = w.verify()
if d:
self.assertNoResult(d) # still waiting for other msg
if order == "version-then-key":
w._key = b"key"
w._event_established_key()
else:
w._event_received_version(side2, confmsg)
if when == "late":
d = w.verify()
if success:
self.successResultOf(d)
else:
self.assertFailure(d, wormhole.WrongPasswordError)
self.flushLoggedErrors(WrongPasswordError)
def test_verifier(self):
for when in ("early", "middle", "late"):
for order in ("key-then-version", "version-then-key"):
for success in (False, True):
self._test_verifier(when, order, success)
def test_api_errors(self):
# doing things you're not supposed to do
pass
def test_welcome_error(self):
# A welcome message could arrive at any time, with an [error] key
# that should make us halt. In practice, though, this gets sent as
# soon as the connection is established, which limits the possible
# states in which we might see it.
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
w._drop_connection = mock.Mock()
ws = MockWebSocket()
w._event_connected(ws)
w._event_ws_opened(None)
self.check_outbound(ws, ["bind"])
d1 = w.get()
d2 = w.verify()
d3 = w.get_code()
# TODO (tricky): test w.input_code
self.assertNoResult(d1)
self.assertNoResult(d2)
self.assertNoResult(d3)
w._signal_error(WelcomeError("you are not actually welcome"), "pouty")
self.failureResultOf(d1, WelcomeError)
self.failureResultOf(d2, WelcomeError)
self.failureResultOf(d3, WelcomeError)
# once the error is signalled, all API calls should fail
self.assertRaises(WelcomeError, w.send, "foo")
self.assertRaises(WelcomeError,
w.derive_key, "foo", SecretBox.KEY_SIZE)
self.failureResultOf(w.get(), WelcomeError)
self.failureResultOf(w.verify(), WelcomeError)
def test_version_error(self):
# we should only receive the "version" message after we receive the
# PAKE message, by which point we should know the key. If the
# confirmation message doesn't decrypt, we signal an error.
timing = DebugTiming()
w = wormhole._Wormhole(APPID, "relay_url", reactor, None, timing, None)
w._drop_connection = mock.Mock()
ws = MockWebSocket()
w._event_connected(ws)
w._event_ws_opened(None)
w.set_code("123-foo-bar")
response(w, type="claimed", mailbox="mb456")
d1 = w.get()
d2 = w.verify()
self.assertNoResult(d1)
self.assertNoResult(d2)
out = ws.outbound()
# ["bind", "claim", "open", "add"]
self.assertEqual(len(out), 4)
self.assertEqual(out[3]["type"], "add")
sp2 = SPAKE2_Symmetric(b"", idSymmetric=wormhole.to_bytes(APPID))
msg2 = sp2.start()
payload = {"pake_v1": bytes_to_hexstr(msg2)}
body_hex = bytes_to_hexstr(dict_to_bytes(payload))
response(w, type="message", phase="pake", body=body_hex, side="s2")
self.assertNoResult(d1)
self.assertNoResult(d2) # verify() waits for confirmation
# sending a random version message will cause a confirmation error
confkey = w.derive_key("WRONG", SecretBox.KEY_SIZE)
nonce = os.urandom(wormhole.CONFMSG_NONCE_LENGTH)
badversion = wormhole.make_confmsg(confkey, nonce)
badversion_hex = hexlify(badversion).decode("ascii")
response(w, type="message", phase="version", body=badversion_hex,
side="s2")
self.failureResultOf(d1, WrongPasswordError)
self.failureResultOf(d2, WrongPasswordError)
# once the error is signalled, all API calls should fail
self.assertRaises(WrongPasswordError, w.send, "foo")
self.assertRaises(WrongPasswordError,
w.derive_key, "foo", SecretBox.KEY_SIZE)
self.failureResultOf(w.get(), WrongPasswordError)
self.failureResultOf(w.verify(), WrongPasswordError)
Basic.skip = "being replaced by test_wormhole_new"
# event orderings to exercise:
#
# * normal sender: set_code, send_phase1, connected, claimed, learn_msg2,
@ -645,27 +58,105 @@ Basic.skip = "being replaced by test_wormhole_new"
# * set_code, then connected
# * connected, receive_pake, send_phase, set_code
class Delegate:
def __init__(self):
self.code = None
self.verifier = None
self.messages = []
self.closed = None
def wormhole_got_code(self, code):
self.code = code
def wormhole_got_verifier(self, verifier):
self.verifier = verifier
def wormhole_receive(self, data):
self.messages.append(data)
def wormhole_closed(self, result):
self.closed = result
class Delegated(ServerBase, unittest.TestCase):
def test_delegated(self):
dg = Delegate()
w = wormhole.create(APPID, self.relayurl, reactor, delegate=dg)
w.close()
class Wormholes(ServerBase, unittest.TestCase):
# integration test, with a real server
def doBoth(self, d1, d2):
return gatherResults([d1, d2], True)
@inlineCallbacks
def test_allocate_default(self):
w1 = wormhole.create(APPID, self.relayurl, reactor)
w1.allocate_code()
code = yield w1.when_code()
mo = re.search(r"^\d+-\w+-\w+$", code)
self.assert_(mo, code)
# w.close() fails because we closed before connecting
yield self.assertFailure(w1.close(), LonelyError)
@inlineCallbacks
def test_allocate_more_words(self):
w1 = wormhole.create(APPID, self.relayurl, reactor)
w1.allocate_code(3)
code = yield w1.when_code()
mo = re.search(r"^\d+-\w+-\w+-\w+$", code)
self.assert_(mo, code)
yield self.assertFailure(w1.close(), LonelyError)
@inlineCallbacks
def test_basic(self):
w1 = wormhole.create(APPID, self.relayurl, reactor)
#w1.debug_set_trace("W1")
w2 = wormhole.create(APPID, self.relayurl, reactor)
#w2.debug_set_trace(" W2")
w1.allocate_code()
code = yield w1.when_code()
w2.set_code(code)
verifier1 = yield w1.when_verified()
verifier2 = yield w2.when_verified()
self.assertEqual(verifier1, verifier2)
version1 = yield w1.when_version()
version2 = yield w2.when_version()
# TODO: add the ability to set app-versions
self.assertEqual(version1, {})
self.assertEqual(version2, {})
w1.send(b"data1")
w2.send(b"data2")
dataX = yield w1.when_received()
dataY = yield w2.when_received()
self.assertEqual(dataX, b"data2")
self.assertEqual(dataY, b"data1")
yield w1.close()
yield w2.close()
version1_again = yield w1.when_version()
self.assertEqual(version1, version1_again)
c1 = yield w1.close()
self.assertEqual(c1, "happy")
c2 = yield w2.close()
self.assertEqual(c2, "happy")
@inlineCallbacks
def test_when_code_early(self):
w1 = wormhole.create(APPID, self.relayurl, reactor)
d = w1.when_code()
w1.set_code("1-abc")
code = self.successResultOf(d)
self.assertEqual(code, "1-abc")
yield self.assertFailure(w1.close(), LonelyError)
@inlineCallbacks
def test_when_code_late(self):
w1 = wormhole.create(APPID, self.relayurl, reactor)
w1.set_code("1-abc")
d = w1.when_code()
code = self.successResultOf(d)
self.assertEqual(code, "1-abc")
yield self.assertFailure(w1.close(), LonelyError)
@inlineCallbacks
def test_same_message(self):
@ -793,6 +284,8 @@ class Wormholes(ServerBase, unittest.TestCase):
w1.allocate_code()
code = yield w1.when_code()
w2.set_code(code+"not")
code2 = yield w2.when_code()
self.assertNotEqual(code, code2)
# That's enough to allow both sides to discover the mismatch, but
# only after the confirmation message gets through. API calls that
# don't wait will appear to work until the mismatched confirmation

View File

@ -1,108 +0,0 @@
from __future__ import print_function, unicode_literals
import re
from twisted.trial import unittest
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks
from .common import ServerBase
from .. import wormhole, errors
APPID = "appid"
class Delegate:
def __init__(self):
self.code = None
self.verifier = None
self.messages = []
self.closed = None
def wormhole_got_code(self, code):
self.code = code
def wormhole_got_verifier(self, verifier):
self.verifier = verifier
def wormhole_receive(self, data):
self.messages.append(data)
def wormhole_closed(self, result):
self.closed = result
class New(ServerBase, unittest.TestCase):
timeout = 2
@inlineCallbacks
def test_allocate(self):
w = wormhole.create(APPID, self.relayurl, reactor)
#w.debug_set_trace("W1")
w.allocate_code(2)
code = yield w.when_code()
self.assertEqual(type(code), type(""))
mo = re.search(r"^\d+-\w+-\w+$", code)
self.assert_(mo, code)
# w.close() fails because we closed before connecting
yield self.assertFailure(w.close(), errors.LonelyError)
def test_delegated(self):
dg = Delegate()
w = wormhole.create(APPID, self.relayurl, reactor, delegate=dg)
w.close()
@inlineCallbacks
def test_basic(self):
w1 = wormhole.create(APPID, self.relayurl, reactor)
#w1.debug_set_trace("W1")
w1.allocate_code(2)
code = yield w1.when_code()
mo = re.search(r"^\d+-\w+-\w+$", code)
self.assert_(mo, code)
w2 = wormhole.create(APPID, self.relayurl, reactor)
#w2.debug_set_trace(" W2")
w2.set_code(code)
code2 = yield w2.when_code()
self.assertEqual(code, code2)
verifier1 = yield w1.when_verified()
verifier2 = yield w2.when_verified()
self.assertEqual(verifier1, verifier2)
version1 = yield w1.when_version()
version2 = yield w2.when_version()
# TODO: add the ability to set app-versions
self.assertEqual(version1, {})
self.assertEqual(version2, {})
w1.send(b"data")
data = yield w2.when_received()
self.assertEqual(data, b"data")
w2.send(b"data2")
data2 = yield w1.when_received()
self.assertEqual(data2, b"data2")
version1_again = yield w1.when_version()
self.assertEqual(version1, version1_again)
c1 = yield w1.close()
self.assertEqual(c1, "happy")
c2 = yield w2.close()
self.assertEqual(c2, "happy")
@inlineCallbacks
def test_wrong_password(self):
w1 = wormhole.create(APPID, self.relayurl, reactor)
#w1.debug_set_trace("W1")
w1.allocate_code(2)
code = yield w1.when_code()
w2 = wormhole.create(APPID, self.relayurl, reactor)
w2.set_code(code+"NOT")
code2 = yield w2.when_code()
self.assertNotEqual(code, code2)
w1.send(b"data")
yield self.assertFailure(w2.when_received(), errors.WrongPasswordError)
# wait for w1.when_received, because if we close w1 before it has
# seen the VERSION message, we could legitimately get LonelyError
# instead of WrongPasswordError. w2 didn't send anything, so
# w1.when_received wouldn't ever callback, but it will errback when
# w1 gets the undecryptable VERSION.
yield self.assertFailure(w1.when_received(), errors.WrongPasswordError)
yield self.assertFailure(w1.close(), errors.WrongPasswordError)
yield self.assertFailure(w2.close(), errors.WrongPasswordError)